From bb-pipelines-author-skill
Bitbucket Pipelines expert — writes, reviews, refactors, debugs, and reworks bitbucket-pipelines.yml. Use when: working on pipeline YAML, restructuring or extracting CI/CD steps, configuring artifacts/caches/services, setting up triggers/schedules/conditions, splitting or merging steps, passing data between steps, removing flaky logic, overriding pipeline sections, adding deployments with OIDC or runners, building Docker images, or cleaning up existing pipelines. Also triggers on "bitbucket-pipelines.yml", "pipelines.yml", "pipeline step", "artifacts", "scheduled run", "parallel steps", "deploy stage", "pipeline triggers", "pipeline variables", "CI/CD", or any Bitbucket CI/CD refactoring task. Activates when pipeline changes appear inside a broader feature rework, branch cleanup, code migration, or multi-file refactoring task.
How this skill is triggered — by the user, by Claude, or both
Slash command
/bb-pipelines-author-skill:bb-pipelines-author-skillThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This is the summary skill. It covers 80% of cases inline and points to `references/` for full YAML examples and ecosystem-specific details.
This is the summary skill. It covers 80% of cases inline and points to references/ for full YAML examples and ecosystem-specific details.
When this skill doesn't cover an edge case:
https://support.atlassian.com/bitbucket-cloud/docs/<topic>/site:support.atlassian.com bitbucket pipelines <query>Mode detection: YAML provided -> Review mode. Requirements provided -> Greenfield mode. Ambiguous -> ask.
Greenfield: Gather requirements (language, deploy targets, Docker). Walk decision trees. Load reference files for full YAML. Validate with checklist.
Review: Follow 6-step protocol. Output: summary + findings table + improved YAML + changelog.
Reference files (load on demand):
references/architecture-patterns.md -- skeletons, triggers, clone, conditionalsreferences/step-patterns.md -- caches, services, artifacts, after-script, output-variablesreferences/deployment-patterns.md -- staged deploys, OIDC, runners, child/exported pipelines, importsreferences/docker-patterns.md -- build+push, BuildKit, multi-arch, layer caching, v2-to-v3references/review-improvement-patterns.md -- before/after patterns, build minutes playbook| Scenario | Type | Notes |
|---|---|---|
| CI on every push (catch-all) | default | Branches without a specific pipeline |
| Branch-specific logic | branches | Globs: 'feature/*', '{staging,production}' |
| PR checks | pull-requests | Matches SOURCE branch, not target |
| Release on tag | tags | Requires git push --tags |
| Manual-only or scheduled | custom | Never auto-triggered |
| Compound conditions, PR lifecycle, chaining | triggers + custom | Expression language, changeset functions |
If both a branches pipeline and a triggers condition match, both run.
Events: repository-push, pullrequest-created/updated/push/fulfilled/rejected/reviewer-status-updated, pipeline-completed, deployment-completed.
triggers is top-level (same level as pipelines). Only custom pipelines can be referenced. Max 20 conditions per trigger type.
triggers:
pullrequest-fulfilled:
- condition: >
BITBUCKET_PR_DESTINATION_BRANCH == "main"
&& changesetInclude("src/**", "package.json")
pipelines:
- auto-deploy
Operators: ==, !=, &&, ||, !, (), >, >=, <, <=. Functions: glob(), changesetInclude(), changesetExclude(). No $ prefix on variables. Secured vars NOT supported. Max 1000 chars.
Start 1x. Bump to 2x only on OOM. Never 4x+ without benchmarking -- if <25% faster than 2x, optimize the build. Set size per-step. Memory is shared between step and services.
| Size | RAM | CPU | Minutes multiplier | Plan |
|---|---|---|---|---|
| 1x | 4 GB | 2 shared | 1x | Free |
| 2x | 8 GB | 4 shared | 2x | Free |
| 4x | 16 GB | 8 dedicated | 4x | Standard+ |
| 8x+ | 32-128 GB | 16-64 dedicated | 8-32x | Standard+ |
Plan budgets: Free = 50 min/mo, Standard = 2,500, Premium = 3,500.
atlassian-ip-ranges: true routes builds through static IPs for firewall allowlisting -- requires size: 4x or greater.
Top-level key order: image -> options -> clone -> definitions -> pipelines -> triggers.
image: node:20-alpine
options:
max-time: 30
clone:
depth: 1
definitions:
caches:
pnpm:
key:
files:
- pnpm-lock.yaml
path: .pnpm-store
steps:
- step: &install
name: Install
caches:
- pnpm
script:
- corepack enable
- pnpm install --frozen-lockfile
artifacts:
- node_modules/**
pipelines:
pull-requests:
'**':
- step: *install
- parallel:
fail-fast: true
steps:
- step:
name: Lint
script:
- corepack enable && pnpm run lint
- step:
name: Test
script:
- corepack enable && pnpm test
branches:
main:
- step: *install
- step:
name: Build
script:
- corepack enable && pnpm run build
artifacts:
- dist/**
- step:
name: Deploy Staging
deployment: Staging
concurrency-group: "deploy-staging"
oidc: true
script:
- ./deploy.sh staging
- step:
name: Deploy Production
deployment: Production
trigger: manual
concurrency-group: "deploy-production"
oidc: true
script:
- ./deploy.sh production
Clone: depth: 1 globally, depth: full per-step for git history, enabled: false for notification steps, lfs: true per-step only. skip-ssl-verify: true is step-level, self-hosted only.
Image pinning: Always specific tags (node:20-alpine). Never :latest, :current, :lts, untagged. run-as-user: 1000 -- UID must exist in image.
Monorepo: condition.changesets.includePaths. Cannot mix includePaths/excludePaths on same step.
${{ }}: Parse-time injection. Secured variables NOT supported (resolve to empty).
Pipeline Imports: Same-repo (no export: true) or cross-repo (export: true). Max 50 sources. Formats: {filepath}, {slug}:{branch}, {slug}:{branch}:{filepath}.
-> Full patterns: references/architecture-patterns.md
-> Look up: "dynamic-pipelines" or "pipeline-imports" on support.atlassian.com
options: docker: true globally -- use services: [docker] per-step (1 GB wasted per non-Docker step)max-time -- default 120 min burns credits on stuck buildsimage: and service/DinD images)feature/*: fails; use 'feature/*':Shape: build -> parallel test -> deploy. Use artifacts: download: false on non-consuming steps. Use key.files caches tied to lockfiles.
| Ecosystem | key.files | path |
|---|---|---|
| Node (pnpm) | pnpm-lock.yaml | .pnpm-store |
| Node (npm) | package-lock.json | .npm-cache |
| Python (uv) | uv.lock | .uv-cache |
| Python (Poetry) | poetry.lock | .venv |
| Go | go.sum | /root/go/pkg/mod |
| Java (Gradle) | build.gradle, gradle.lockfile | ~/.gradle/caches |
| Ruby | Gemfile.lock | vendor/bundle |
Default time-based caches expire after 7 days. Always use key.files for correct invalidation.
definitions:
caches:
pnpm-store:
key:
files:
- pnpm-lock.yaml
path: .pnpm-store
Artifacts: Negation globs ("!dist/**/*.map"). Named artifacts with selective download:. capture-on: fail for debug logs. 1 GB/group, 14-day expiry. Parallel steps cannot consume each other's.
Services: Shared on localhost. 4 GB total (1x). Max 5 (excl. docker). Always health-check wait before connecting.
after-script: Runs regardless of outcome. $BITBUCKET_EXIT_CODE only here.
output-variables: Write to $BITBUCKET_PIPELINES_VARIABLES_PATH. 50 max, 100 KB combined.
| Scenario | Config |
|---|---|
| Core tests | fail (default) |
| Flaky E2E | on-fail: { strategy: retry, maxRetryCount: 2 } |
| Optional lint | on-fail: { strategy: ignore } |
| CI parallel group | fail-fast: true |
| Production deploy | trigger: manual (cannot be first step) |
| Deploy locking | concurrency-group: "deploy-production" |
on-fail at step/parallel/stage level; step overrides parent. maxRetryCount: 1-10. Artifacts expire 14 days -- rebuild in manual steps if needed.
-> Full patterns: references/step-patterns.md
artifacts: download: false on non-consuming stepsnode_modules directly -- cache package manager store with lockfile key--build-arg -- use --secret mount|| true swallowing failures -- use on-fail: { strategy: ignore } or retryUse stages for manual gates on step groups, deployment over multiple steps, group-level condition/on-fail. Skip when linear or needing parallel inside (stages CANNOT contain parallel groups). Stages cannot contain manual/conditional steps (stage itself can be). Max: Free 10, Standard/Premium 100 steps/stage.
Set oidc: true per step. Write $BITBUCKET_STEP_OIDC_TOKEN to file. JWT claims: sub, aud, repositoryUuid, branchName, deploymentEnvironment.
- step:
oidc: true
image: amazon/aws-cli:2.15.30
script:
- export AWS_REGION=eu-west-1
- export AWS_ROLE_ARN=arn:aws:iam::123456789012:role/bitbucket-deploy
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $AWS_WEB_IDENTITY_TOKEN_FILE
- aws s3 sync dist/ s3://myapp-production/
Custom audiences (object form): max 10 per step, 150 chars each. Per-step overrides global options.oidc.audiences.
OIDC without oidc: true: $BITBUCKET_STEP_OIDC_TOKEN is silently empty. Auth fails with no clear error. [CRIT]
environment vs deploymentenvironment = env-scoped vars only, no lock. deployment = full tracking, dashboard, concurrency control. Use deployment only on steps that mutate infrastructure.
Labels: self.hosted (required, first), linux, linux.shell, linux.arm64 (needs atlassian/default-image:4+), windows, macos. Self-hosted size caps at 8x; shell runners ignore size. No matching runner = immediate failure. oidc defaults true on self-hosted. Runner v5+ supports volume mounts and S3/GCS storage backends.
| Need | Solution |
|---|---|
| Same-file reuse | YAML anchors (definitions.steps with &anchor/*anchor) |
| Parameterized (same repo) | Child pipelines (input-variables, max 20) |
| Cross-repo sharing | Exported pipelines (export: true) or definitions.imports |
| Named version-pinned sources | definitions.imports (max 50 per file) |
| Deployment tracking | Anchors or exported -- child pipelines lack deployment |
Child pipeline constraints: No script, caches, services, artifacts, deployment, oidc. No secured vars in input-variables. Max 20 input-variables.
Use - variables: with allowed-values for dropdowns, plain name/default for text input. Renders UI pickers in Bitbucket.
-> Full patterns: references/deployment-patterns.md
-> Look up: "configure-oidc-with-bitbucket" on support.atlassian.com
oidc: truedeployment on read-only steps -- use environment insteadconcurrency-group on deploy stepsv2 (default) for simple docker build + push and pipe-only steps. v3 for docker buildx/multi-arch, --privileged, RUN --mount=type=ssh (silently fails on v2). Start v2; switch only when needed. v2 blocks: --cap-add, --device, --mount, --privileged, --volume, and others. Port 29418 reserved.
runtime: { cloud: { version: "3" } } per-step (not globally)image: docker:28-cli (CLI not auto-mounted in v3)caches: [docker] with --cache-from/--cache-toexport DOCKER_BUILDKIT=1 (already default)script_memory = size_total - docker_memory - other_services_memory. 1x+Docker = 2048 MB for script. 2x+Docker = 6144 MB. OOM? Increase size or reduce service memory. Max 5 services (excl. docker).
Never --build-arg with secrets -- use --secret mount + RUN --mount=type=secret in Dockerfile. Tag with $BITBUCKET_COMMIT, never :latest. BuildKit is default -- never export DOCKER_BUILDKIT=1.
Layer caching: Predefined docker cache does NOT cache BuildKit layers. Use --cache-from type=registry,ref=... / --cache-to type=inline, or S3-based on v3.
Pipes: Pre-packaged Docker containers. Cache with services: [docker] + caches: [docker]. Cloud-only (not Bitbucket Data Center).
-> Full patterns: references/docker-patterns.md
-> Look up: "docker-in-bitbucket-pipelines" on support.atlassian.com
RUN --mount=type=ssh on v2 -- silently fails; use v3caches: [docker] with BuildKit -- does not cache BuildKit layersexport DOCKER_BUILDKIT=1 -- already default; removeruntime: v3 -- v3 doesn't auto-mount CLI; set per-step with docker:28-cli--build-arg BUILDKIT_INLINE_CACHE=1 with explicit cache flags -- redundantcaches: [docker] in steps with both pipes and own builds -- pipe images fill the cache; split into separate steps:latest? Missing OIDC? --build-arg? Secured in ${{ }}?key.files? artifacts: download: false? clone: depth: 1?size justified? Parallel = same minutes, less wall-clockon-fail? max-time? after-script? Health-check waits? concurrency-group?CRIT (must fix): Hardcoded keys, --build-arg secrets, secured var in ${{ }}/child pipeline, wrong deployment order, manual first step, mixed OIDC/long-lived keys, mutable image tags, --ssh/--mount=type=ssh on v2 (silent fail), $BITBUCKET_STEP_OIDC_TOKEN without oidc: true (silent empty), || true/|| exit 0 swallowing test/deploy failures (exception: after-script cleanup commands where non-zero exit is expected).
WARN (should fix): Missing concurrency-group, 2x on lint, no fail-fast, bad cache keys, global docker: true, global runtime: v3 without CLI image, missing max-time/after-script/clone: depth/on-fail: retry, install without cache, redundant BUILDKIT_INLINE_CACHE=1, docker cache mixed with own builds.
INFO (nice to have): Non-alpine images, no anchors, expression near 1000-char limit.
Output: ## Summary (one line) -> ## Findings (table: #, Severity, Location, Issue, Fix) -> ## Improved Pipeline (complete YAML) -> ## Changelog (bulleted, prefixed by severity). Do NOT add features the user didn't have. Always output complete YAML.
oidc: true with short-lived tokens--cache-from/--cache-todefinitions.steps with &anchor/*anchorafter-script + on-fail + health checks|| true on tests -> on-fail: { strategy: ignore } or { strategy: retry }-> Full patterns: references/review-improvement-patterns.md
Structure:
pipelines: key existstrigger: manual is never first stepSecurity:
$VARIABLE--build-arg with secrets -- use --secret mount${{ }} templatesinput-variablesoidc: true set on every step using $BITBUCKET_STEP_OIDC_TOKENPerformance:
key.files tied to lockfilessize: 2x+ justified (lint/notify/deploy = 1x)Reliability:
concurrency-group|| true on important commandsmax-time set (never rely on 120-min default)--secret, never --build-argscript:image: and services); pipes version-pinned--privileged only on v3 steps; consistent credential strategy across envstrigger: manual; OIDC audiences scopedTabs forbidden (use 2 spaces). Quote booleans as branch names ('true':). Quote colons in names ('Build: Production'). | for bash blocks, > for PowerShell. Anchor names: no [ ] { } ,. <<: replaces script: lists, never appends. Use / in paths, never \.
${{ }} = parse time (no secured/output vars). $VAR = runtime. Precedence: Pipeline > Deployment > Repository > Workspace > Default. $BITBUCKET_BRANCH null on detached HEAD. $BITBUCKET_EXIT_CODE only in after-script. Windows: $env:VAR. Never name a variable PATH. Value limit: 120K chars. Output-variables: 50 max, 100 KB. Expression: 1000 chars max, no $ prefix.
| Symptom | Fix |
|---|---|
| "Step is not valid" | Indent properties under - step: |
| "Pipeline could not be parsed" | Quote globs: 'feature/*' |
trigger: manual first step | Add automated step before |
docker: command not found (v3) | image: docker:28-cli |
| OOM killed | Size up or reduce service memory |
| Cache miss every run | Match key.files to actual lockfiles |
| Connection refused | Add health-check wait loop |
| Pipeline runs twice | Use branches or triggers, not both |
| Deployment order invalid | Test -> Staging -> Production |
| Artifacts missing | Check producer globs; check download: false |
| Pipeline not triggered (bulk) | Push <=5 new refs; or use API |
${{ }} -- empty at parse timetrigger: manual first step -- pipeline won't start--build-arg with secrets -- baked into layer historyRUN --mount=type=ssh on v2 -- silently fails$BITBUCKET_STEP_OIDC_TOKEN without oidc: true -- token is silently emptyPATH as pipeline variable -- breaks all commands0.0.0.0, never ::1docker cache + BuildKit -- doesn't cache BuildKit layers| Action | Savings per run |
|---|---|
clone: depth: 1 globally | 5-15 sec/step |
artifacts: download: false on non-consuming steps | 5-30 sec/step |
node:20-alpine over node:20 | 10-30 sec/step |
| Lockfile-keyed cache | 30-120 sec/cached step |
Downsize 2x lint steps to 1x | 50% minutes on those steps |
condition.changesets in monorepo | Skip entire steps |
max-time: 15 (from default 120) | Up to 105 min on stuck builds |
Per-step services: [docker] over global docker: true | ~512 MB freed/non-Docker step |
Cloud-only (no Data Center). Manual steps always block. No Atlassian-hosted Windows runners. IPv4 only. Storage: Free 1 GB, Standard 5 GB, Premium 10 GB.
| Resource | Free | Standard/Premium |
|---|---|---|
| Build minutes/month | 50 | 2,500 / 3,500 |
| Steps per pipeline | 100 | 100 |
| Steps per stage | 10 | 100 |
| Concurrent steps | 10 | 600 |
| Import sources per file | 50 | 50 |
| Child pipeline input-variables | 20 | 20 |
| Output-variables per pipeline | 50 | 50 |
| OIDC audiences per step | 10 | 10 |
| Artifact expiry | 14 days | 14 days |
| Max step time | 720 min | 720 min |
| Storage (LFS/artifacts/caches) | 1 GB | 5 / 10 GB |
-> Look up: "bitbucket-pipelines-configuration-reference" on support.atlassian.com for any property. -> Look up: "variables-and-secrets" for the complete variable list. -> Look up: "limitations-of-bitbucket-pipelines" for platform limits.
npx claudepluginhub jenyapoyarkov/bb-pipelines-author-skill --plugin bb-pipelines-author-skillWrites and optimizes Buildkite pipeline YAML: step types, caching, parallelism, annotations, retry, dynamic pipelines, matrix builds, plugins, notifications, artifacts, concurrency.
Generates production-ready GitLab CI/CD pipelines (.gitlab-ci.yml), stages, and jobs following best practices; validates syntax and compliance for builds, deploys, and scans.
Design, debug, and harden GitHub Actions CI/CD workflows including reusable workflows, matrix builds, self-hosted runners, OIDC authentication, caching, environments, secrets, and release automation.