Skip to main content

CI/CD Translation Reference

Mapping GitLab CI concepts and patterns to GitHub Actions equivalents. Use alongside gh actions-importer for automated translation, then review and adjust using this reference.

Pipeline Structure​

GitLab CIGitHub ActionsNotes
.gitlab-ci.yml.github/workflows/*.ymlGitHub supports multiple workflow files
stages:Jobs with needs: dependenciesNo explicit stage ordering; use needs: for DAG
extends:Reusable workflows / composite actionsuses: ./.github/workflows/reusable.yml
include:Reusable workflowsuses: org/repo/.github/workflows/shared.yml@main
needs:needs:Same concept, same keyword
resource_group:concurrency: groupsconcurrency: { group: ..., cancel-in-progress: false }
environment:environment:Same concept; GitHub adds protection rules

Docker & Services​

GitLab CIGitHub ActionsNotes
image:runs-on: + container:runs-on selects the runner; container sets the Docker image
services:services: in container jobsRequires container: on the job; services don't work on bare runners
macOS runners (saas-macos-medium-m1)runs-on: macos-14M1-based, available on Enterprise
pages jobactions/deploy-pages or Cloudflare PagesSee docs hosting section

Artifacts & Cache​

GitLab CIGitHub ActionsNotes
artifacts:actions/upload-artifact / actions/download-artifactGitLab passes artifacts between stages automatically; GitHub requires explicit steps
cache:actions/cacheOr use built-in caching in setup actions like actions/setup-node with cache: npm

Variables & Secrets​

GitLab CIGitHub ActionsNotes
CI/CD VariablesRepository/org secrets + variablesSecrets are encrypted; variables are plaintext
CI_JOB_TOKENGITHUB_TOKENAuto-generated per workflow run
CI_REGISTRY / CI_REGISTRY_IMAGEghcr.ioGitHub Container Registry
CI_COMMIT_SHAgithub.shaContext expressions
CI_COMMIT_REF_NAMEgithub.ref_nameContext expressions
CI_PIPELINE_SOURCEgithub.event_namepush, pull_request, workflow_dispatch, etc.
Protected variablesEnvironment secretsSecrets scoped to specific environments

Triggers & Conditions​

GitLab CIGitHub ActionsNotes
rules: / only: / except:on: triggers + job if: conditionsGitHub triggers are more explicit
when: manualworkflow_dispatch or environment protectionworkflow_dispatch makes the whole workflow manual; for a single job, use env protection

Laravel Vapor Deploys​

Vapor does not need DinD or a pre-built base image

The GitLab setup shipped a separate vapor-base.Dockerfile, pushed it to the GitLab container registry, and referenced it from staging.Dockerfile / production.Dockerfile. That whole pipeline is unnecessary on GitHub: vapor deploy builds the Docker image internally, so the env Dockerfiles can inherit from laravelphp/vapor:phpXX directly and fold in any runtime extras (ghostscript, fontconfig, etc.). Validated on coniglio (commit 9ec2f8b, before coniglio moved to Cloud) and farfalla-integrations. Applies to farfalla, castoro.

Commit message env var

GitLab exposes $CI_COMMIT_MESSAGE automatically; GitHub does not. For vapor deploy --message, set GITHUB_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} (or github.event.pull_request.title for PR-triggered staging) in the deploy step's env: block.

Laravel Cloud Deploys​

Laravel Cloud deploys fire from a webhook URL. The GitHub Actions job only POSTs to that URL; the actual build runs server-side on Laravel Cloud.

Hooks are async, image builds run server-side

curl -X POST <hook> returns immediately; the deploy happens in the background on Laravel Cloud. Don't script post-deploy health checks expecting the deploy to be done when curl returns. And don't reach for a beefier runner to speed up the deploy step — the GitHub runner only fires a curl, the image build happens on Cloud. Validated on coniglio, medusa, farfalla-https-guard.

Use the hardened curl form

Bare curl -X POST returns 0 on 4xx/5xx, so a rejected hook silently marks the deploy green. Use the same form as all migrated repos:

curl --fail-with-body -sS \
--retry 3 --retry-all-errors \
--connect-timeout 10 --max-time 30 \
-X POST "${{ secrets.LARAVEL_CLOUD_*_DEPLOY_HOOK }}?commit_hash=${{ github.sha }}"

--fail-with-body is the load-bearing flag; the retries/timeouts guard against transient network blips.

Pick the right SHA for the event

github.sha works for push events (merges to dev/main), but on pull_request events it's the ephemeral merge commit GitHub creates against the base branch — a SHA that doesn't exist on the PR's branch and won't resolve on Cloud's dashboard. For PR-triggered deploy jobs (e.g. manual-deploy-staging), use github.event.pull_request.head.sha instead. See Deploy approval pattern for the full job split.

Commit .cloud/config.json

When the Laravel Cloud project's source integration is switched from GitLab to GitHub, Cloud generates a .cloud/config.json file in the repo with the project's organization and application IDs. Commit it — Cloud reads it to identify the project on subsequent deploys. Non-secret. See coniglio and farfalla-https-guard for examples.

Gotchas​

Sentry/deploy integration changes

Sentry release tracking and deploy notifications need to be reconfigured for the GitHub integration. Projects moving to Laravel Cloud will transition from Sentry to Nightwatch. Affected repos: farfalla, medusa, farfalla-integrations, coniglio.

Cross-repo triggers

GitLab has built-in cross-project pipeline triggers. GitHub Actions uses repository_dispatch events with a PAT or GitHub App token to trigger workflows in other repos. Affected repos: micelio (triggers criceto), volpe/farfalla (Delfino version validation).

X

Graph View