Skip to content

nurdsoft/ci-workflows

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ci-workflows

Reusable, function-named CI/CD building blocks for Nurdsoft projects.

Each action is named for the pipeline function it performs, not a language or tool — so the implementation underneath is pluggable while the public interface stays stable. The repo ships three concerns and keeps only the generic two: it provisions (auth, toolchain install) and orchestrates (artifacts, PR comments, notifications); the project-specific commands live behind a runner contract you control (or pass inline via run).

Contents

Path Type Function
.github/workflows/version.yml Reusable workflow Cut a SemVer release (stable or RC prerelease)
actions/auth Action Obtain cloud credentials (OIDC)
actions/setup Action Install runtime + deps (+ EAS login)
actions/verify Action Lint / type-check / test
actions/build Action Produce a deployable artifact — static or Docker image
actions/deploy Action Ship the artifact — static site, Cloud Run service, or Cloud Run job (+ optional Cloud Scheduler)
actions/plan Action Preview an infrastructure change
actions/apply Action Apply an infrastructure change
actions/notify Action Post the pipeline result

Design

  • Function-named directories — swap Node→Bun or GCP→AWS by changing inputs, not the directory.
  • No forced Makefile — phase actions take a run command (or a runner); make is only the default our repos opt into (see Runner contract).
  • Provision vs execute vs orchestrate — credentials and tools are installed here; the commands that use them live in the consumer.
  • Reusable workflow for releaseversion.yml is a workflow (not an action) because cutting a release needs its own contents: write job.

Usage

Callers wire the actions into a job graph and supply their own values. Two illustrative shapes:

App pipeline — static site (verify, release, build, deploy)

jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: nurdsoft/ci-workflows/actions/verify@v2

  version:
    needs: [verify]
    uses: nurdsoft/ci-workflows/.github/workflows/version.yml@v2
    permissions: { contents: write }
    with:
      rc-line: "1-rc"          # rc off non-default branches; stable on default

  build:
    needs: [version]
    runs-on: ubuntu-latest
    steps:
      - uses: nurdsoft/ci-workflows/actions/build@v2
        with:
          run: <your build command>     # or rely on `make build`
          output: artifact

  deploy:
    needs: [build]
    runs-on: ubuntu-latest
    steps:
      - uses: nurdsoft/ci-workflows/actions/deploy@v2
        with:
          run: <your deploy command>    # or rely on `make deploy`
          download-artifact: "true"
          gcp-wif-provider: ${{ secrets.WIF_PROVIDER }}
          gcp-service-account: ${{ secrets.SERVICE_ACCOUNT }}

App pipeline — Docker + Cloud Run (release, build, deploy)

Activated by passing image-name to build and cloudrun-service to deploy. The static-build and static-deploy steps are skipped automatically — existing callers are unaffected.

jobs:
  version:
    uses: nurdsoft/ci-workflows/.github/workflows/version.yml@v2
    permissions: { contents: write }
    with:
      rc-line: "1-rc"          # rc off non-default branches; stable on default

  build:
    needs: [version]
    runs-on: ubuntu-latest
    environment: dev
    steps:
      - uses: nurdsoft/ci-workflows/actions/build@v2
        with:
          gcp-wif-provider: ${{ secrets.GCP_WIF_PROVIDER }}
          gcp-service-account: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }}
          gcp-project-id: ${{ secrets.GCP_PROJECT_ID }}
          gcp-region: ${{ secrets.GCP_REGION }}
          gcp-repository: ${{ secrets.GCP_REPOSITORY }}
          image-name: ${{ secrets.IMAGE_NAME }}
          gcp-secret-name: ${{ secrets.GCP_SECRET_NAME }}   # fetched → .env.production at build time

  deploy:
    needs: [build]
    runs-on: ubuntu-latest
    environment: dev
    steps:
      - uses: nurdsoft/ci-workflows/actions/deploy@v2
        with:
          gcp-wif-provider: ${{ secrets.GCP_WIF_PROVIDER }}
          gcp-service-account: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }}
          gcp-project-id: ${{ secrets.GCP_PROJECT_ID }}
          gcp-region: ${{ secrets.GCP_REGION }}
          gcp-repository: ${{ secrets.GCP_REPOSITORY }}
          image-name: ${{ secrets.IMAGE_NAME }}
          cloudrun-service: ${{ secrets.CLOUDRUN_SERVICE_NAME }}
          gcp-secret-name: ${{ secrets.GCP_SECRET_NAME }}   # fetched → injected as Cloud Run env vars
          cloudrun-flags: "--allow-unauthenticated --ingress=internal-and-cloud-load-balancing"

App pipeline — GHCR pull + retag + Cloud Run (pre-built image)

For services whose Docker image is built and published to GHCR by a separate process (e.g. a commerce platform). Activated by passing ghcr-image to build alongside image-name. The action pulls the pre-built image, re-tags it for GCP Artifact Registry (SHA + latest), and pushes it — no Dockerfile or build-time secrets required. Takes priority over the Docker build+push path.

jobs:
  version:
    uses: nurdsoft/ci-workflows/.github/workflows/version.yml@v2
    permissions: { contents: write }
    with:
      rc-line: "1-rc"

  build:
    needs: [version]
    runs-on: ubuntu-latest
    environment: dev
    steps:
      - uses: nurdsoft/ci-workflows/actions/build@v2
        with:
          gcp-wif-provider: ${{ secrets.GCP_WIF_PROVIDER }}
          gcp-service-account: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }}
          gcp-project-id: ${{ secrets.GCP_PROJECT_ID }}
          gcp-region: ${{ secrets.GCP_REGION }}
          gcp-repository: ${{ secrets.GCP_REGISTRY }}
          image-name: ${{ vars.SERVICE_NAME }}
          ghcr-image: ghcr.io/org/repo:latest   # source image; triggers pull+retag path

  deploy:
    needs: [build]
    runs-on: ubuntu-latest
    environment: dev
    steps:
      - uses: nurdsoft/ci-workflows/actions/deploy@v2
        with:
          gcp-wif-provider: ${{ secrets.GCP_WIF_PROVIDER }}
          gcp-service-account: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }}
          gcp-project-id: ${{ secrets.GCP_PROJECT_ID }}
          gcp-region: ${{ secrets.GCP_REGION }}
          gcp-repository: ${{ secrets.GCP_REGISTRY }}
          image-name: ${{ vars.SERVICE_NAME }}
          cloudrun-service: ${{ vars.SERVICE_NAME }}
          gcp-secret-name: ${{ secrets.GCP_SECRET_NAME }}
          cloudrun-flags: >-
            --vpc-connector="${{ secrets.GCP_VPC_CONNECTOR }}"
            --ingress=internal-and-cloud-load-balancing

App pipeline — Docker + Cloud Run job with Cloud Scheduler

Activated by passing cloudrun-job to deploy instead of cloudrun-service. Optionally reconciles a Cloud Scheduler trigger (created if missing, updated if existing) when scheduler-name and schedule-time are set.

jobs:
  version:
    uses: nurdsoft/ci-workflows/.github/workflows/version.yml@v2
    permissions: { contents: write }
    with:
      rc-line: "1-rc"

  build:
    needs: [version]
    runs-on: ubuntu-latest
    environment: dev
    steps:
      - uses: nurdsoft/ci-workflows/actions/build@v2
        with:
          gcp-wif-provider: ${{ secrets.GCP_WIF_PROVIDER }}
          gcp-service-account: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }}
          gcp-project-id: ${{ secrets.GCP_PROJECT_ID }}
          gcp-region: ${{ secrets.GCP_REGION }}
          gcp-repository: ${{ secrets.GCP_REPOSITORY }}
          image-name: ${{ secrets.IMAGE_NAME }}
          gcp-secret-name: ${{ secrets.GCP_SECRET_NAME }}

  deploy-job:
    needs: [build]
    runs-on: ubuntu-latest
    environment: dev
    steps:
      - uses: nurdsoft/ci-workflows/actions/deploy@v2
        with:
          gcp-wif-provider: ${{ secrets.GCP_WIF_PROVIDER }}
          gcp-service-account: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }}
          gcp-project-id: ${{ secrets.GCP_PROJECT_ID }}
          gcp-region: ${{ secrets.GCP_REGION }}
          gcp-repository: ${{ secrets.GCP_REPOSITORY }}
          image-name: ${{ secrets.IMAGE_NAME }}
          cloudrun-job: ${{ secrets.CLOUDRUN_JOB_NAME }}
          gcp-secret-name: ${{ secrets.GCP_SECRET_NAME }}
          cloudrun-flags: >-
            --command="/app/server"
            --args="worker"
            --vpc-connector=${{ secrets.VPC_CONNECTOR }}
          scheduler-name: my-job-scheduler-trigger   # omit to skip scheduler management
          schedule-time: "0 * * * *"                 # cron expression — hourly

Infrastructure pipeline — plan then apply the same plan

jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: nurdsoft/ci-workflows/actions/plan@v2
        with:
          env: <env>
          aws-role-arn: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ${{ secrets.AWS_REGION }}
          github-token: ${{ secrets.GITHUB_TOKEN }}

  apply:
    needs: [plan]
    runs-on: ubuntu-latest
    steps:
      - uses: nurdsoft/ci-workflows/actions/apply@v2
        with:
          env: <env>
          aws-role-arn: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ${{ secrets.AWS_REGION }}

Runner contract

Phase actions (build, deploy, plan, apply) run a command — supply it any of three ways: pass run/run-* directly (no Makefile), set runner to your tool (just, task, npm run), or implement the default make targets.

Phase Default command(s) Env provided
build make build ENV, APP_VERSION*
deploy make deploy ENV
plan make tf-init, make tf-plan (+ tf-fmt/tf-validate for terraform) ENV, TARGET
apply make tf-init, make tf-apply ENV, TARGET

* build exports APP_VERSION under the name given by version-env-var.

Self-contained — no contract, no Makefile: auth, setup, verify, notify, and the version.yml reusable workflow.

Docker / Cloud Run path: when image-name (build) or cloudrun-service / cloudrun-job (deploy) is set, the runner contract is bypassed entirely — the action handles auth, build, and deploy against GCP Artifact Registry and Cloud Run directly. No Makefile targets required.

Cloud Run job path: when cloudrun-job (deploy) is set instead of cloudrun-service, the action deploys a Cloud Run job and optionally reconciles a Cloud Scheduler trigger (created if missing, updated if existing). Pass scheduler-name and schedule-time to enable scheduling; omit both to skip it.

GHCR pull + retag path: when ghcr-image (build) is also set, the action pulls the pre-built image from GHCR and re-tags it for Artifact Registry instead of building from source. No Dockerfile or build-time secrets needed.

Versioning

Pin to the major tag (@v2). Breaking changes ship under a new major; the previous major stays in place for un-migrated callers. Third-party actions are SHA-pinned and bumped by Dependabot.

Contributing

See CONTRIBUTING.md. PRs are linted with actionlint (and shellcheck over composite run: blocks) via .github/workflows/ci.yml.

About

Shared reusable GitHub Actions workflows for Nurdsoft projects (SemVer release pipeline)

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors