Skip to content

AxeForging/reusable-workflows

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

reusable-workflows

Shared GitHub Actions workflows and composite actions for the AxeForging immutable release pipeline.

Structure

.
├── actions/
│   ├── build-push-image/      # Build Docker image, push to AR, return digest
│   ├── promote-artifact/      # Add new tag to existing digest (no rebuild)
│   ├── deploy-cloudrun/       # Deploy to Cloud Run by digest (enforces @sha256:)
│   ├── pr-title-lint/         # Validate PR title (Conventional Commits), sticky comment
│   └── sticky-pr-comment/     # Create/update one PR comment by marker (no duplicates)
│
├── .github/
│   └── workflows/
│       ├── docker-build-push.yml  # Reusable: language-agnostic build + push golden artifact
│       ├── go-build-push.yml      # Reusable: Go convenience (go test + build)
│       ├── promote-artifact.yml   # Reusable: promote artifact tag
│       ├── cloudrun-deploy.yml    # Reusable: deploy to Cloud Run
│       └── pr-title-lint.yml      # Reusable: PR title lint (workflow_call)
│
├── docs/
│   └── immutable-pipeline/
│       └── README.md
│
├── scripts/
│   └── gcp-setup.sh
├── lefthook.yml
├── Makefile
└── README.md

Reusable Workflows

docker-build-push.yml — any language

Language-agnostic. Run your tests first, then call this. Works for Go, Node.js, Python, Java, or anything that has a Dockerfile.

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test          # or: pytest / go test / mvn test / cargo test

  build-golden:
    needs: [test]
    uses: AxeForging/reusable-workflows/.github/workflows/docker-build-push.yml@main
    with:
      image-name: my-service
      registry: ${{ vars.AR_REGION }}-docker.pkg.dev/${{ vars.AR_PROJECT_ID }}/services
      wif-provider: ${{ vars.WIF_PROVIDER }}
    secrets:
      wif-service-account: ${{ secrets.WIF_SERVICE_ACCOUNT }}

go-build-push.yml — Go convenience

Bundles go test + docker build + push in one call. For pure Go services that don't need a separate test job.

jobs:
  build:
    uses: AxeForging/reusable-workflows/.github/workflows/go-build-push.yml@main
    with:
      image-name: my-service
      registry: ${{ vars.AR_REGION }}-docker.pkg.dev/${{ vars.AR_PROJECT_ID }}/services
      wif-provider: ${{ vars.WIF_PROVIDER }}
    secrets:
      wif-service-account: ${{ secrets.WIF_SERVICE_ACCOUNT }}

promote-artifact.yml

Promotes an image tag without rebuilding (golden → RC, RC → final). Language-agnostic.

jobs:
  promote:
    uses: AxeForging/reusable-workflows/.github/workflows/promote-artifact.yml@main
    with:
      registry: ${{ vars.AR_REGION }}-docker.pkg.dev/${{ vars.AR_PROJECT_ID }}/services
      image: my-service
      source-tag: git-abc1234
      target-tag: v1.2.0-rc.1
      wif-provider: ${{ vars.WIF_PROVIDER }}
    secrets:
      wif-service-account: ${{ secrets.WIF_SERVICE_ACCOUNT }}

cloudrun-deploy.yml

Deploys to Cloud Run using a digest reference. Enforces @sha256: — mutable tag references are rejected. Language-agnostic.

jobs:
  deploy:
    uses: AxeForging/reusable-workflows/.github/workflows/cloudrun-deploy.yml@main
    with:
      service: my-service
      image: us-central1-docker.pkg.dev/my-project/services/my-service@sha256:abc123
      region: ${{ vars.CLOUDRUN_REGION }}
      project-id: ${{ vars.PROD_PROJECT_ID }}
      environment: production
      version: v1.2.0
      commit: ${{ github.sha }}
      wif-provider: ${{ vars.WIF_PROVIDER }}
    secrets:
      wif-service-account: ${{ secrets.WIF_SERVICE_ACCOUNT }}

pr-title-lint.yml — PR title validation

Validates PR titles against Conventional Commits and posts a single sticky comment explaining the expected format. No Jira ticket required by default; fully configurable.

on:
  pull_request:
    types: [opened, synchronize, reopened, edited]

jobs:
  pr-title:
    uses: AxeForging/reusable-workflows/.github/workflows/pr-title-lint.yml@main
    # optional:
    # with:
    #   extra-types: wip,hotfix
    #   custom-regex: '^[A-Z]+-\d+ - (feat|fix|chore): .+'
    #   expected-hint: "Use: <TICKET> - <type>: <description>"
    #   fail-on-error: 'false'

The reusable workflow requests contents:read + pull-requests:write itself — the caller passes nothing else. See actions/pr-title-lint for all inputs and the direct-action form.

Release triggers — why workflow_dispatch

RC creation and final release both use workflow_dispatch (manual trigger). This is intentional:

  • Production releases require explicit human approval — they are not automatic
  • Manual dispatch creates an audit trail in GitHub Actions (who triggered, when, with what inputs)
  • It matches the spec: "approved RC → create tag → deploy to production" — the approval is the act of triggering
  • The RC version input prevents accidental promotions

Rollback and hotfix also use workflow_dispatch for the same reason.

Composite Actions

Action Language Description
build-push-image any docker build + push, returns digest
promote-artifact any Tag same digest with new label
deploy-cloudrun any Deploy to Cloud Run, enforces digest ref, sets observability labels
pr-title-lint any Validate PR title (configurable), post sticky comment, fail on mismatch
sticky-pr-comment any Create/update a single PR comment by marker

Docs


GCP Setup

Run the interactive setup script — it asks for the values, runs all commands in order, and writes the results to scripts/gcp-setup.env (gitignored):

bash scripts/gcp-setup.sh

Or follow the manual steps below.

Manual setup — individual gcloud commands

Replace the variables at the top, then run the blocks in order.

# ── Variables ────────────────────────────────────────────────────────────────
AR_PROJECT_ID="your-artifact-project-id"   # Artifact Registry + WIF + CI SA
STG_PROJECT_ID="your-stg-project-id"       # Cloud Run staging
PROD_PROJECT_ID="your-prod-project-id"     # Cloud Run production
REGION="us-central1"
GITHUB_ORG="AxeForging"

SA_NAME="ci-pipeline"
SA_EMAIL="${SA_NAME}@${AR_PROJECT_ID}.iam.gserviceaccount.com"

WIF_POOL="github-pool"
WIF_PROVIDER="github-provider"
AR_REPO="services"

1 — Enable required APIs

# artifact-project: AR, IAM, WIF
gcloud services enable \
  artifactregistry.googleapis.com \
  iam.googleapis.com \
  iamcredentials.googleapis.com \
  cloudresourcemanager.googleapis.com \
  --project "$AR_PROJECT_ID"

# stg and prod: Cloud Run
for ENV_PROJECT in "$STG_PROJECT_ID" "$PROD_PROJECT_ID"; do
  gcloud services enable \
    run.googleapis.com \
    iam.googleapis.com \
    cloudresourcemanager.googleapis.com \
    --project "$ENV_PROJECT"
done

2 — Create the CI service account (lives in artifact-project)

gcloud iam service-accounts create "$SA_NAME" \
  --display-name "CI Pipeline" \
  --project "$AR_PROJECT_ID"

3 — Create the Workload Identity Pool (lives in artifact-project)

gcloud iam workload-identity-pools create "$WIF_POOL" \
  --location global \
  --display-name "GitHub Actions Pool" \
  --project "$AR_PROJECT_ID"

4 — Create the OIDC provider (GitHub)

gcloud iam workload-identity-pools providers create-oidc "$WIF_PROVIDER" \
  --location global \
  --workload-identity-pool "$WIF_POOL" \
  --display-name "GitHub provider" \
  --issuer-uri "https://token.actions.githubusercontent.com" \
  --attribute-mapping \
    "google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
  --attribute-condition "assertion.repository_owner == '${GITHUB_ORG}'" \
  --project "$AR_PROJECT_ID"

5 — Bind the service account to the pool

WIF_POOL_RESOURCE=$(gcloud iam workload-identity-pools describe "$WIF_POOL" \
  --location global \
  --project "$AR_PROJECT_ID" \
  --format "value(name)")

gcloud iam service-accounts add-iam-policy-binding "$SA_EMAIL" \
  --role roles/iam.workloadIdentityUser \
  --member "principalSet://iam.googleapis.com/${WIF_POOL_RESOURCE}/attribute.repository_owner/${GITHUB_ORG}" \
  --project "$AR_PROJECT_ID"

6 — Grant CI SA write access to Artifact Registry

gcloud projects add-iam-policy-binding "$AR_PROJECT_ID" \
  --member "serviceAccount:${SA_EMAIL}" \
  --role roles/artifactregistry.writer \
  --condition None

7 — Grant CI SA deploy access to stg and prod

for ENV_PROJECT in "$STG_PROJECT_ID" "$PROD_PROJECT_ID"; do
  gcloud projects add-iam-policy-binding "$ENV_PROJECT" \
    --member "serviceAccount:${SA_EMAIL}" \
    --role roles/run.developer \
    --condition None

  gcloud projects add-iam-policy-binding "$ENV_PROJECT" \
    --member "serviceAccount:${SA_EMAIL}" \
    --role roles/iam.serviceAccountUser \
    --condition None
done

8 — Grant Cloud Run compute agents read access to Artifact Registry

# Cloud Run in stg/prod needs to pull images from artifact-project's AR
for ENV_PROJECT in "$STG_PROJECT_ID" "$PROD_PROJECT_ID"; do
  PROJECT_NUMBER=$(gcloud projects describe "$ENV_PROJECT" --format "value(projectNumber)")
  COMPUTE_SA="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
  gcloud projects add-iam-policy-binding "$AR_PROJECT_ID" \
    --member "serviceAccount:${COMPUTE_SA}" \
    --role roles/artifactregistry.reader \
    --condition None
done

9 — Create the Artifact Registry Docker repository

gcloud artifacts repositories create "$AR_REPO" \
  --repository-format docker \
  --location "$REGION" \
  --description "Container images for all services" \
  --project "$AR_PROJECT_ID"

10 — Print the values you need for GitHub secrets/variables

echo "── GitHub Secrets ──────────────────────────────────────────────────────"
echo "WIF_SERVICE_ACCOUNT:  ${SA_EMAIL}"

echo ""
echo "── GitHub Variables ────────────────────────────────────────────────────"
echo "WIF_PROVIDER:"
gcloud iam workload-identity-pools providers describe "$WIF_PROVIDER" \
  --location global \
  --workload-identity-pool "$WIF_POOL" \
  --project "$AR_PROJECT_ID" \
  --format "value(name)"
echo ""
echo "AR_PROJECT_ID:    ${AR_PROJECT_ID}"
echo "AR_REGION:        ${REGION}"
echo "STG_PROJECT_ID:   ${STG_PROJECT_ID}"
echo "PROD_PROJECT_ID:  ${PROD_PROJECT_ID}"
echo "CLOUDRUN_REGION:  ${REGION}"
echo ""
echo "Artifact Registry: ${REGION}-docker.pkg.dev/${AR_PROJECT_ID}/${AR_REPO}"

About

Shared GitHub Actions reusable workflows and composite actions for the AxeForging immutable release pipeline

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors