In earlier articles, we covered migrating repositories from Bitbucket Cloud, securely updating project dependencies, and modernizing CI/CD workflows by translating Jenkins and Bash-based pipelines to GitHub Actions. With those foundations in place, the final step is to migrate native Bitbucket Pipelines and complete the transition to a fully unified CI/CD system.
This article focuses on translating YAML-based Bitbucket Pipelines into GitHub Actions workflows. By doing so, all pipelines across projects follow the same execution model, improving consistency, maintainability, and long-term scalability.

Understanding Bitbucket Pipelines Structure
Bitbucket Pipelines are defined in a bitbucket-pipelines.yml file located at the root of the repository. This file controls how CI/CD workflows behave, including:
- When pipelines are triggered
- Which steps or jobs are executed
- Job ordering and dependencies
- Artifact creation and reuse between steps
Before migrating to GitHub Actions, it’s essential to fully understand the behavior of the existing pipeline so it can be accurately reproduced.
Example Bitbucket Pipeline Configuration
Below is an example Bitbucket pipeline used in this project:
definitions:
commonItems:
&setupEnvironment |
export COMMIT=$(echo $BITBUCKET_COMMIT | cut -b 1-8)
git submodule update --init --recursive
echo "#define REVISION_NUMBER 0x$COMMIT" > src/revision.h
git:
&gitSubmoduleUpdate |
git submodule update --init
verify_release_version: &verify_release_version
step:
name: Verify release version
script:
- git submodule update --init
- ./scripts/check_release_version.sh
cpplint: &cpplint
step:
name: Lint code
image: my-registry/lint-image:latest
script:
- *gitSubmoduleUpdate
- ./scripts/lint.sh
test: &test
step:
name: Run tests
runs-on:
- self.hosted
- linux
script:
- *setupEnvironment
- make test
build: &build
step:
name: Build project
runs-on:
- self.hosted
- linux
script:
- *setupEnvironment
- make build
artifacts:
- build/**
upload: &upload
step:
name: Upload build output
script:
- export DESTINATION=builds/$BITBUCKET_BRANCH
- mkdir -p $DESTINATION
- cp -r build/* $DESTINATION/
- scp -r builds user@remote-server:/path/to/storage
Key Characteristics of the Bitbucket Pipeline
Several important behaviors stand out:
- Reusable scripts using YAML anchors (&setupEnvironment)
- Parallel execution of steps to speed up builds
- Artifacts generated in one step and reused later
- Branch-specific pipelines, controlling which jobs run on main, feature/*, or release/*
Understanding these patterns is critical before translating the pipeline to GitHub Actions.
Mapping Bitbucket Pipelines to GitHub Actions
GitHub Actions workflows are defined under .github/workflows/, typically with one workflow per file. Each workflow specifies:
- Triggers that control when the workflow runs
- Jobs representing units of work
- Dependencies between jobs using needs
- Artifacts passed between jobs
Migrated GitHub Actions Workflow Example
Below is the migrated workflow that mirrors the Bitbucket pipeline behavior:
name: CI Workflow
on:
push:
branches:
- main
- 'feature/*'
- 'release/*'
env:
BUCKET: example-bucket
AWS_REGION: region-value
jobs:
lint:
name: Lint code
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- run: ./scripts/lint.sh
test:
name: Run tests
runs-on: [ "self-hosted", "ap-southeast-2", "ubuntu", "x64" ]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- run: ./scripts/setup_environment.sh
- run: make test
build:
name: Build project
runs-on: [ "self-hosted", "ap-southeast-2", "ubuntu", "x64" ]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- run: ./scripts/setup_environment.sh
- run: make build
- uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: build/
release:
name: Release build
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/download-artifact@v4
with:
name: build-artifacts
path: build/
- run: |
mkdir -p deploy/${GITHUB_REF_NAME}
tar -czf deploy/${GITHUB_REF_NAME}/artifacts.tar.gz -C build .
echo "{\"artifact\":\"artifacts.tar.gz\",\"revision\":\"${GITHUB_SHA}\"}" > deploy/${GITHUB_REF_NAME}/release_info.json
- name: Upload to S3
run: |
aws s3 sync deploy "s3://${BUCKET}/" \
--region $AWS_REGION \
--acl private
Key Concepts in the Migrated Workflow
Workflow Triggers
The workflow runs automatically on pushes to main, feature/*, and release/*, replicating Bitbucket’s branch-based pipelines.
Jobs and Parallel Execution
Jobs such as lint and test can run in parallel, while others wait for dependencies.
Job Dependencies
The needs keyword ensures the release job only starts after build completes successfully.
Artifact Handling
Artifacts are uploaded in the build job and downloaded in the release job using GitHub’s native artifact actions.
Environment Variables and S3 Uploads
Environment variables define bucket and region values, and artifacts are uploaded using standard AWS CLI commands. A dedicated GitHub Action could also be used for S3 uploads if preferred.
Completing the CI/CD Migration
With native Bitbucket Pipelines successfully migrated to GitHub Actions, all CI/CD workflows now run on a single, unified automation platform. This ensures consistent behavior across projects, simplifies maintenance, and provides a secure and scalable foundation for ongoing development.
This migration journey demonstrates a practical path for moving repositories, securing dependencies, and modernizing pipelines-bringing all automation under GitHub Actions in a structured and maintainable way.
Check out more of our blog posts here.