-
Notifications
You must be signed in to change notification settings - Fork 14
feat(github): expand github workflows and actions #57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
gondoi
merged 27 commits into
master
from
feature/devops-1276-create-shared-github-actions
May 11, 2026
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
4fd0971
feat(github): expand github workflows and actions
gondoi cd800e7
fix(github): fix issues found by cursorbot
gondoi e7e51b6
feat(docker): add options for build and helm destinations
gondoi 6293511
fix(helm): output to correct directory
gondoi 2e1f35f
fix(docker): don't spin up an unused instance
gondoi e5505c8
fix(helm): update permissions for aws oidc
gondoi f51926f
fix(docker): remove duplicate IDs
gondoi 5b3ad60
fix(docker): avoid shell/heredoc injection
gondoi 3c68b88
fix(docker): remove potential duplicate metadata info
gondoi bdf1eb6
fix(docker): use consistent output
gondoi c1d23f6
fix(helm): reduce package ambiguity on push
gondoi 3dc6efa
fix(docker): fix printf output
gondoi 5490f7a
fix(docker): use consistent image output
gondoi 480525e
chore(docker): change for testing
gondoi e992274
fix(github): fixup bad tr command
gondoi 4eab368
fix(docker): use jq to parse output
gondoi 8befca4
fix(github): missing digetsts
gondoi 5e97f18
fix(docker): reference stoikov build
gondoi 22d3efb
fix(github): fix push race condition
gondoi cef57bf
fix(github): remove testing references
gondoi 6d51ed7
fix(github): update cursorbot findings
gondoi f37dc38
fix(github): prevent unsafe interpolation
gondoi 3dab98c
fix(github): prevent unintented globbing
gondoi 341437f
chore(github): final tests
gondoi 51a1bf7
Revert "chore(github): final tests"
gondoi fdd6b12
fix(github): update self workflow call reference
gondoi d26b7e7
fix(github): allow backwards compatible publish-docker
gondoi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| # Reusable OCI & Docker composites (`FuelLabs/github-actions`) | ||
|
|
||
| **Public** repository. Anyone may **`uses:`** the composites and `workflow_call` workflows. Fuel teams typically pin a **semver ref**; internal policies are **your org’s** (approvals, private registries, etc.). | ||
|
|
||
| This file documents the **OCI / Docker / Helm** composites and their callable workflow wrappers. **[`publish-docker-image.yml`](.github/workflows/publish-docker-image.yml)** is a **legacy** contract (GHCR `GITHUB_CONTAINER_*` secrets, `images` / `docker_file` input names) that **forwards to** [`docker-build-push.yml`](.github/workflows/docker-build-push.yml) in this repo. Prefer **`docker-build-push`** for new ECR OIDC / multi-arch / Warp; keep **`publish-docker-image`** only for existing `uses:` pins. [notify-slack-action.yml](.github/workflows/notify-slack-action.yml) is a separate **reusable** workflow used on failure (including from **`publish-docker-image`**). | ||
|
|
||
| ## Artifacts (this “Fuel OCI” set) | ||
|
|
||
| | Kind | Path | Purpose | | ||
| |------|------|---------| | ||
| | Composite | `.github/actions/docker-build-push` | ECR private/public OIDC or registry login; **Buildx** + QEMU, or **Warp** | | ||
| | Composite | `.github/actions/helm-publish-oci` | Non-PR Helm **OCI** publish (lint, push) via registry token or AWS OIDC (ECR) | | ||
| | Composite | `.github/actions/slack-notify-failure` | Small Slack failure step (`ravsamhq/notify-slack-action`) | | ||
| | Reusable workflow | `.github/workflows/docker-build-push.yml` | Native per-platform runner builds + digest merge (default), or Warp direct push | | ||
| | Reusable workflow | `.github/workflows/helm-publish-oci.yml` | Same for Helm | | ||
| | Reusable workflow (legacy) | `.github/workflows/publish-docker-image.yml` | Same implementation as `docker-build-push` (wraps the row above) + old secret/input names + Slack on failure | | ||
|
|
||
| **Not in scope for these composites:** PR-only Helm, `helm-cleanup-pr`, preview charts. | ||
|
|
||
| **Related (same repo, different path style):** [`setups/docker`](../setups/docker/) is a small composite for **GHCR login and compose**; use the table above for **build + push** or **Helm OCI**. | ||
|
|
||
| ## How callable workflows resolve composites | ||
|
|
||
| Callers’ jobs check out the **consumer** repository. A reusable workflow in **this** repo must **not** use `./.github/actions/...` — that path would resolve in the **caller**, not here. Composite steps use a **fully qualified** `uses: FuelLabs/github-actions/.github/actions/<name>@<ref>`, where **`<ref>` is a string literal in the workflow file** (e.g. `@master`). GitHub does **not** allow the `env` context in a step’s `uses:` (runtime error: `Unrecognized named-value: 'env'`). Do not use `...@${{ env.… }}`. | ||
|
|
||
| **Releases:** in `docker-build-push.yml` and `helm-publish-oci.yml`, set the `...@<ref>` on the composite to the **same** tag/SHA you are about to publish (e.g. `...@v1.0.0` on the commit you tag). Default branch can keep `...@master` for development. Consumers who pin `uses: .../docker-build-push.yml@v1.0.0` get the workflow and composite at that ref together. | ||
|
|
||
| ## Secrets | ||
|
|
||
| | Mechanism | In composite? | How to pass | | ||
| |-----------|----------------|-------------| | ||
| | `secrets.*` in `action.yml` | **No** | `with:` from the caller (`password: ${{ secrets.x }}` — still masked) | | ||
| | Reusable workflow | **Yes** | `on.workflow_call.secrets`, caller `secrets: inherit` or explicit map | | ||
|
|
||
| `secrets: inherit` on **composite** actions is not supported; use a callable workflow if you want one secrets mapping. | ||
|
|
||
| ## Examples | ||
|
|
||
| **Callable** — Docker (pin replaces `v1.0.0` when you release): | ||
|
|
||
| ```yaml | ||
| jobs: | ||
| image: | ||
| uses: FuelLabs/github-actions/.github/workflows/docker-build-push.yml@v1.0.0 | ||
| secrets: inherit | ||
| with: | ||
| auth-mode: registry-login | ||
| dockerfile: Dockerfile | ||
| image: ghcr.io/fuellabs/myapp | ||
| build-backend: native | ||
| runs-on-amd64: ubuntu-latest | ||
| runs-on-arm64: ubuntu-24.04-arm | ||
| ``` | ||
|
|
||
| **Callable** — Docker to ECR Public (OIDC): | ||
|
|
||
| ```yaml | ||
| jobs: | ||
| image: | ||
| uses: FuelLabs/github-actions/.github/workflows/docker-build-push.yml@v1.0.0 | ||
| with: | ||
| auth-mode: ecr-public-oidc | ||
| aws-role-arn: ${{ secrets.AWS_ROLE_ARN }} | ||
| aws-region: us-east-1 | ||
| dockerfile: Dockerfile | ||
| image: public.ecr.aws/your-alias/myapp | ||
| build-backend: native | ||
| ``` | ||
|
|
||
| **Callable** — Docker via Warp (no native digest merge): | ||
|
|
||
| ```yaml | ||
| jobs: | ||
| image: | ||
| uses: FuelLabs/github-actions/.github/workflows/docker-build-push.yml@v1.0.0 | ||
| secrets: inherit | ||
| with: | ||
| auth-mode: ecr-oidc | ||
| aws-role-arn: ${{ secrets.AWS_ROLE_ARN }} | ||
| dockerfile: Dockerfile | ||
| image: 123.dkr.ecr.us-east-1.amazonaws.com/myapp | ||
| build-backend: warp | ||
| profile-name: my-warp-profile | ||
| ``` | ||
| **Callable** — Helm to GHCR (`registry-login`; needs `packages: write` in the **called** job — workflow already sets it): | ||
|
|
||
| ```yaml | ||
| jobs: | ||
| chart: | ||
| uses: FuelLabs/github-actions/.github/workflows/helm-publish-oci.yml@v1.0.0 | ||
| with: | ||
| auth-mode: registry-login | ||
| chart-folder: helm/my-chart | ||
| registry-url: oci://ghcr.io/${{ github.repository_owner }}/charts | ||
| secrets: | ||
| REGISTRY_USERNAME: ${{ github.actor }} | ||
| REGISTRY_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| ``` | ||
|
|
||
| **Callable** — Helm to AWS ECR (`ecr-oidc`): | ||
|
|
||
| ```yaml | ||
| jobs: | ||
| chart: | ||
| uses: FuelLabs/github-actions/.github/workflows/helm-publish-oci.yml@v1.0.0 | ||
| with: | ||
| auth-mode: ecr-oidc | ||
| aws-role-arn: ${{ secrets.AWS_ROLE_ARN }} | ||
| aws-region: us-east-1 | ||
| chart-folder: helm/my-chart | ||
| registry-url: oci://123456789012.dkr.ecr.us-east-1.amazonaws.com/charts | ||
| # Optional if registry-url includes host (recommended) | ||
| # registry-host: 123456789012.dkr.ecr.us-east-1.amazonaws.com | ||
| ``` | ||
|
|
||
| **Composite** (consumer writes full `permissions`): | ||
|
|
||
| ```yaml | ||
| - uses: FuelLabs/github-actions/.github/actions/docker-build-push@v1.0.0 | ||
| with: | ||
| auth-mode: ecr-oidc | ||
| aws-role-arn: ${{ secrets.AWS_ROLE_ARN }} | ||
| image: 123.dkr.ecr.us-east-1.amazonaws.com/app | ||
| dockerfile: Dockerfile | ||
| ``` | ||
|
|
||
| ### `slack-notify-failure` vs `notify-slack-action.yml` | ||
|
|
||
| - **`.github/actions/slack-notify-failure`**: **composite** — add as a step, pass `github_token` + `slack_webhook` via `with:`. | ||
| - **`.github/workflows/notify-slack-action.yml`**: older **reusable workflow** (checkout, Rust toolchain) — use only if you already depend on it; new work should prefer the **composite** above. | ||
|
|
||
| ## Pinning | ||
|
|
||
| Third-party `uses:` in composites are pinned. Bump in PRs. This repo is **not** the same as **Terraform** tags in `infrastructure-tools` — use **`github-actions`’ own** releases. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,227 @@ | ||
| # Bump third-party majors via PR after review (supply chain). | ||
| name: Docker build and push | ||
| description: Build and push a container image (ECR private/public OIDC or registry login; Buildx or Warp). | ||
|
|
||
| inputs: | ||
| auth-mode: | ||
| description: 'ecr-oidc | ecr-public-oidc | registry-login' | ||
| required: true | ||
| aws-role-arn: | ||
| description: IAM role ARN for ECR (auth-mode ecr-oidc or ecr-public-oidc) | ||
| required: false | ||
| aws-region: | ||
| description: AWS region for ECR (public ECR generally uses us-east-1) | ||
| required: false | ||
| default: us-east-1 | ||
| registry: | ||
| description: Registry host (registry-login), e.g. ghcr.io | ||
| required: false | ||
| default: ghcr.io | ||
| username: | ||
| description: Registry username (registry-login) | ||
| required: false | ||
| password: | ||
| description: 'Registry password or PAT (registry-login). Caller passes secrets.* in workflow with: — masked in logs.' | ||
| required: false | ||
| image: | ||
| description: Image name for docker/metadata-action (without tag) | ||
| required: true | ||
| tags: | ||
| description: Multiline docker/metadata-action tag rules (empty = ECR-style sha + dated raw tag) | ||
| required: false | ||
| default: '' | ||
| flavor: | ||
| description: Optional global metadata / tag flavor (docker/metadata-action), e.g. 'latest=auto' | ||
| required: false | ||
| default: '' | ||
| labels: | ||
| description: Optional extra image labels (docker/metadata-action, multiline KEY=VAL) | ||
| required: false | ||
| default: '' | ||
| context: | ||
| description: Docker build context path | ||
| required: false | ||
| default: . | ||
| dockerfile: | ||
| description: Path to Dockerfile | ||
| required: true | ||
| build-args: | ||
| description: Build-args (multiline KEY=VAL) | ||
| required: false | ||
| platforms: | ||
| description: Comma-separated platforms, e.g. linux/amd64,linux/arm64 | ||
| required: false | ||
| default: linux/amd64 | ||
| build-backend: | ||
| description: 'buildx | warp' | ||
| required: false | ||
| default: buildx | ||
| profile-name: | ||
| description: Warp profile name (build-backend warp — required for org Warp projects) | ||
| required: false | ||
| push-by-digest: | ||
| description: 'true | false (buildx only). true pushes canonical digest refs (for manifest merge flows).' | ||
| required: false | ||
| default: 'false' | ||
| cache-scope: | ||
| description: Optional GHA cache scope (buildx only), e.g. linux-amd64 | ||
| required: false | ||
| default: '' | ||
| setup-only: | ||
| description: 'true | false. If true, only auth/login setup is performed (no metadata/build/push).' | ||
| required: false | ||
| default: 'false' | ||
| metadata-only: | ||
| description: 'true | false. If true, run auth/login + metadata generation only (no build/push).' | ||
| required: false | ||
| default: 'false' | ||
| push: | ||
| description: 'true | false' | ||
| required: false | ||
| default: 'true' | ||
|
|
||
| outputs: | ||
| image: | ||
| description: Repository/image name without tag (matches inputs.image — not docker/metadata bake image.name, which can be comma-separated tagged refs) | ||
| value: ${{ inputs.setup-only != 'true' && inputs.image || '' }} | ||
| imageid: | ||
| description: Image ID from build backend | ||
| value: ${{ (inputs.setup-only != 'true' && inputs.metadata-only != 'true') && (inputs.build-backend == 'warp' && steps.publish-warp.outputs.imageId || (inputs.push-by-digest == 'true' && steps.publish-buildx-digest.outputs.imageId || steps.publish-buildx.outputs.imageId)) || '' }} | ||
| digest: | ||
| description: Image digest | ||
| value: ${{ (inputs.setup-only != 'true' && inputs.metadata-only != 'true') && (inputs.build-backend == 'warp' && steps.publish-warp.outputs.digest || (inputs.push-by-digest == 'true' && steps.publish-buildx-digest.outputs.digest || steps.publish-buildx.outputs.digest)) || '' }} | ||
| metadata: | ||
| description: docker/metadata-action bake JSON (same schema for Buildx digest merge, Buildx push, Warp — excludes setup-only login-only runs) | ||
| value: ${{ inputs.setup-only != 'true' && steps.meta.outputs.json || '' }} | ||
| tags: | ||
| description: docker/metadata-action computed tags list | ||
| value: ${{ inputs.setup-only != 'true' && steps.meta.outputs.tags || '' }} | ||
| labels: | ||
| description: docker/metadata-action computed labels list | ||
| value: ${{ inputs.setup-only != 'true' && steps.meta.outputs.labels || '' }} | ||
| version: | ||
| description: docker/metadata-action version (primary tag component for image:version) | ||
| value: ${{ inputs.setup-only != 'true' && steps.meta.outputs.version || '' }} | ||
|
|
||
| runs: | ||
| using: composite | ||
| steps: | ||
| - name: Configure AWS credentials (ECR OIDC) | ||
| if: inputs.auth-mode == 'ecr-oidc' || inputs.auth-mode == 'ecr-public-oidc' | ||
| uses: aws-actions/configure-aws-credentials@v4 | ||
| with: | ||
| role-to-assume: ${{ inputs.aws-role-arn }} | ||
| aws-region: ${{ inputs.aws-region }} | ||
|
|
||
| - name: Login to Amazon ECR (private) | ||
| if: inputs.auth-mode == 'ecr-oidc' | ||
| id: login-ecr | ||
| uses: aws-actions/amazon-ecr-login@v2 | ||
|
|
||
| - name: Login to Amazon ECR Public | ||
| if: inputs.auth-mode == 'ecr-public-oidc' | ||
| id: login-ecr-public | ||
| uses: aws-actions/amazon-ecr-login@v2 | ||
| with: | ||
| registry-type: public | ||
|
|
||
| - name: Log in to container registry | ||
| if: inputs.auth-mode == 'registry-login' | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ inputs.registry }} | ||
| username: ${{ inputs.username }} | ||
| password: ${{ inputs.password }} | ||
|
|
||
| - name: Resolve metadata tag lines | ||
| id: tag-lines | ||
| if: inputs.setup-only != 'true' | ||
| shell: bash | ||
| env: | ||
| # Do not embed inputs in the script body; pass through env to avoid shell/heredoc injection | ||
| INPUT_TAGS: ${{ inputs.tags }} | ||
| run: | | ||
| set -euo pipefail | ||
| if [ -r /proc/sys/kernel/random/uuid ]; then | ||
| delim="GHA_OUT_$(tr -d -- '-\n\r' < /proc/sys/kernel/random/uuid)" | ||
| else | ||
| delim="GHA_OUT_${RANDOM}_$$_$(date +%s%N)" | ||
| fi | ||
| { | ||
| echo "tags<<${delim}" | ||
| if [ -n "$INPUT_TAGS" ]; then | ||
| printf '%s\n' "$INPUT_TAGS" | ||
| else | ||
| printf '%s\n' 'type=sha,prefix=' | ||
| printf '%s\n' "type=raw,value=sha-{{sha}}-{{date 'YYYYMMDDHHmmss'}}" | ||
| fi | ||
| echo "$delim" | ||
| } >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Docker meta | ||
| id: meta | ||
| if: inputs.setup-only != 'true' | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: | | ||
| ${{ inputs.image }} | ||
| tags: ${{ steps.tag-lines.outputs.tags }} | ||
| flavor: | | ||
| ${{ inputs.flavor }} | ||
| labels: | | ||
| ${{ inputs.labels }} | ||
|
|
||
| - name: Set up QEMU (Buildx cross-arch) | ||
| if: inputs.setup-only != 'true' && inputs.metadata-only != 'true' && inputs.build-backend == 'buildx' && (contains(inputs.platforms, ',') || contains(inputs.platforms, 'arm')) | ||
| uses: docker/setup-qemu-action@v3 | ||
|
|
||
| - name: Set up Docker Buildx | ||
| if: inputs.setup-only != 'true' && inputs.metadata-only != 'true' && inputs.build-backend == 'buildx' | ||
| uses: docker/setup-buildx-action@v3 | ||
|
|
||
| - name: Build and push (Buildx canonical digest) | ||
| if: inputs.setup-only != 'true' && inputs.metadata-only != 'true' && inputs.build-backend == 'buildx' && inputs.push-by-digest == 'true' | ||
| uses: docker/build-push-action@v6 | ||
| id: publish-buildx-digest | ||
| with: | ||
| context: ${{ inputs.context }} | ||
| file: ${{ inputs.dockerfile }} | ||
| # Default provenance/SBOM adds extra manifests; digest merge + ECR then often | ||
| # references a descriptor imagetools cannot resolve ("not found"). | ||
| provenance: false | ||
| sbom: false | ||
| outputs: type=image,name=${{ inputs.image }},push-by-digest=true,name-canonical=true,push=true | ||
| labels: ${{ steps.meta.outputs.labels }} | ||
| build-args: ${{ inputs.build-args }} | ||
| platforms: ${{ inputs.platforms }} | ||
| cache-from: ${{ inputs.cache-scope != '' && format('type=gha,scope={0}', inputs.cache-scope) || 'type=gha' }} | ||
| cache-to: ${{ inputs.cache-scope != '' && format('type=gha,mode=max,scope={0}', inputs.cache-scope) || 'type=gha,mode=max' }} | ||
|
|
||
| - name: Build and push (Buildx) | ||
| if: inputs.setup-only != 'true' && inputs.metadata-only != 'true' && inputs.build-backend == 'buildx' && inputs.push-by-digest != 'true' | ||
| uses: docker/build-push-action@v6 | ||
| id: publish-buildx | ||
| with: | ||
| context: ${{ inputs.context }} | ||
| file: ${{ inputs.dockerfile }} | ||
| push: ${{ inputs.push == 'true' }} | ||
| tags: ${{ steps.meta.outputs.tags }} | ||
| labels: ${{ steps.meta.outputs.labels }} | ||
| build-args: ${{ inputs.build-args }} | ||
| platforms: ${{ inputs.platforms }} | ||
| cache-from: ${{ inputs.cache-scope != '' && format('type=gha,scope={0}', inputs.cache-scope) || 'type=gha' }} | ||
| cache-to: ${{ inputs.cache-scope != '' && format('type=gha,mode=max,scope={0}', inputs.cache-scope) || 'type=gha,mode=max' }} | ||
|
|
||
| - name: Build and push (Warp) | ||
| if: inputs.setup-only != 'true' && inputs.metadata-only != 'true' && inputs.build-backend == 'warp' | ||
| uses: Warpbuilds/build-push-action@v6 | ||
| id: publish-warp | ||
| with: | ||
| context: ${{ inputs.context }} | ||
| file: ${{ inputs.dockerfile }} | ||
| push: ${{ inputs.push == 'true' }} | ||
| tags: ${{ steps.meta.outputs.tags }} | ||
| labels: ${{ steps.meta.outputs.labels }} | ||
| build-args: ${{ inputs.build-args }} | ||
| platforms: ${{ inputs.platforms }} | ||
| profile-name: ${{ inputs.profile-name }} | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.