Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,22 @@ Enabled by default (`deployment-labels: 'true'`). The same three values are stam

> **Note:** labels are baked in at **build time**, so they are only applied on builds. Release (`v*`) and custom-tag runs that **retag** an existing image instead of rebuilding (see [Image tags](#image-tags--flux-image-automation)) do not get fresh labels — the retagged image keeps the labels from the branch build it was promoted from. This feature is independent of the annotations above; enable either, both, or neither.

### Fresh builds

Every build checks all referenced base images for updates (`pull: true`). Floating
tags pick up new base digests; digest-pinned references remain pinned. Build caching
is still enabled by default when the base image and build inputs are unchanged.

Set `docker-build-no-cache: 'true'` for scheduled package or virus-signature refreshes.
This re-executes Dockerfile steps and skips external cache import/export in both
single-arch and multi-arch builds. It increases build time and download traffic;
retag-only runs are unaffected.

```yaml
with:
docker-build-no-cache: 'true'
```

### Multi-Arch Images

Our recovery/failover regions have no ARM capacity, so images deployed there must ship both `linux/amd64` and `linux/arm64`. The action never cross-compiles or emulates: each architecture is built natively on its own runner, and the results are combined into one manifest list afterwards.
Expand Down Expand Up @@ -262,6 +278,7 @@ Notes:
| `multiarch-artifact-name` | Base name of the artifact carrying the per-architecture digests between the build and merge jobs (the architecture is appended) | `docker-digests` |
| `multiarch-digests-path` | Directory holding the per-architecture digest files | `/tmp/gitops-action-digests` |
| `docker-build-provenance` | Generate [provenance](https://docs.docker.com/build/attestations/slsa-provenance/) attestation for the build | `false` |
| `docker-build-no-cache` | Re-execute Dockerfile steps and skip external cache import/export. Base images are always checked for updates. | `false` |
| `docker-disable-retagging` | Disables retagging of existing images and run a new build instead | `false` |
| `deployment-annotations` | Stamp deployment-tracking annotations (`deploy.staffbase.com/*`) onto updated GitOps manifests. See [Deployment tracking annotations](#deployment-tracking-annotations) | `true` |
| `deployment-domain` | Key namespace for deployment-tracking metadata. Used verbatim for annotation keys (`<domain>/...`) and reversed to reverse-DNS for label keys (`com.staffbase.deploy.*`) | `deploy.staffbase.com` |
Expand Down
12 changes: 10 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ inputs:
description: "Generate provenance attestation for the build"
required: false
default: 'false'
docker-build-no-cache:
description: 'Re-execute Dockerfile steps without cached build results or external cache import/export. Base images are always checked for updates independently of this setting.'
required: false
default: 'false'
docker-build-outputs:
description: "Custom output destinations (e.g., type=registry,push=true,compression=zstd,force-compression=true). When set, this replaces the default push behavior - include push=true if pushing is desired."
required: false
Expand Down Expand Up @@ -169,6 +173,7 @@ runs:
INPUT_MULTIARCH_MODE: ${{ inputs.multiarch-mode }}
INPUT_DOCKER_BUILD_PLATFORMS: ${{ inputs.docker-build-platforms }}
INPUT_DOCKER_BUILD_OUTPUTS: ${{ inputs.docker-build-outputs }}
INPUT_DOCKER_BUILD_NO_CACHE: ${{ inputs.docker-build-no-cache }}
INPUT_DOCKER_REGISTRY: ${{ steps.preparation.outputs.primary_registry }}
INPUT_DOCKER_IMAGE: ${{ inputs.docker-image }}
INPUT_TAG_LIST: ${{ steps.preparation.outputs.tag_list }}
Expand Down Expand Up @@ -214,8 +219,11 @@ runs:
secrets: ${{ inputs.docker-build-secrets }}
secret-files: ${{ inputs.docker-build-secret-files }}
platforms: ${{ steps.build_config.outputs.platforms }}
cache-from: type=gha${{ steps.build_config.outputs.cache_suffix }}
cache-to: type=gha,mode=max${{ steps.build_config.outputs.cache_suffix }}
# Floating hardened base tags can receive patches without changing name.
pull: true

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Staffbase/workflow-enthusiasts do you think this change is fine as default?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sense. We probably don't want GitHub to cache these additionally. Chainguard recommends to tag your images like python:3.13@sha256:asddg.. so this wouldn't be cached anyway. Not sure what will be the impact on GitHub costs

no-cache: ${{ inputs.docker-build-no-cache }}
cache-from: ${{ steps.build_config.outputs.cache_from }}
cache-to: ${{ steps.build_config.outputs.cache_to }}
provenance: ${{ inputs.docker-build-provenance }}
outputs: ${{ steps.build_config.outputs.build_outputs }}

Expand Down
19 changes: 17 additions & 2 deletions scripts/resolve-build-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
# Required env vars: RUNNER_ARCH
# Optional env vars: INPUT_MULTIARCH_MODE, INPUT_DOCKER_BUILD_PLATFORMS,
# INPUT_DOCKER_BUILD_OUTPUTS, INPUT_TAG_LIST, INPUT_PUSH,
# INPUT_DOCKER_REGISTRY, INPUT_DOCKER_IMAGE
# INPUT_DOCKER_REGISTRY, INPUT_DOCKER_IMAGE,
# INPUT_DOCKER_BUILD_NO_CACHE
#
# Outputs (via GITHUB_OUTPUT): arch, platforms, tags, build_outputs
# Outputs (via GITHUB_OUTPUT): arch, platforms, tags, build_outputs,
# cache_suffix, cache_from, cache_to

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib/common.sh
Expand Down Expand Up @@ -65,8 +67,21 @@ if [[ "$MODE" == "build" ]]; then
CACHE_SUFFIX=",scope=${ARCH}"
fi

CACHE_FROM=""
CACHE_TO=""
# Fresh scheduled rebuilds have no use for external cache transfers.
if [[ "${INPUT_DOCKER_BUILD_NO_CACHE:-false}" == "false" ]]; then
CACHE_FROM="type=gha${CACHE_SUFFIX}"
CACHE_TO="type=gha,mode=max${CACHE_SUFFIX}"
elif [[ "$INPUT_DOCKER_BUILD_NO_CACHE" != "true" ]]; then
log_error "docker-build-no-cache must be 'true' or 'false'."
exit 1
fi

set_output "arch" "$ARCH"
set_output "cache_suffix" "$CACHE_SUFFIX"
set_output "cache_from" "$CACHE_FROM"
set_output "cache_to" "$CACHE_TO"
set_output "platforms" "$PLATFORMS"
set_output "tags" "$TAGS"
set_output "build_outputs" "$BUILD_OUTPUTS"
33 changes: 33 additions & 0 deletions tests/resolve-build-config.bats
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ teardown() {
assert_output_value "tags" "registry.example.com/private/my-service:dev-abcdef12"
assert_output_value "build_outputs" ""
assert_output_value "cache_suffix" ""
assert_output_value "cache_from" "type=gha"
assert_output_value "cache_to" "type=gha,mode=max"
}

@test "default mode keeps custom docker-build-outputs" {
Expand Down Expand Up @@ -54,6 +56,8 @@ teardown() {
assert_output_value "tags" ""
assert_output_value "build_outputs" "type=image,name=registry.example.com/private/my-service,push-by-digest=true,name-canonical=true,push=true"
assert_output_value "cache_suffix" ",scope=amd64"
assert_output_value "cache_from" "type=gha,scope=amd64"
assert_output_value "cache_to" "type=gha,mode=max,scope=amd64"
}

@test "build mode on ARM64 builds arm64 regardless of docker-build-platforms" {
Expand Down Expand Up @@ -106,3 +110,32 @@ teardown() {
assert_failure
assert_output --partial "Invalid multiarch-mode"
}

@test "no-cache omits external cache in single-arch and native multi-arch builds" {
export INPUT_DOCKER_BUILD_NO_CACHE="true"
for mode in "" build; do
export INPUT_MULTIARCH_MODE="$mode"
for arch in X64 ARM64; do
export RUNNER_ARCH="$arch"
# assert_output_value reads the first match, so start each run clean
: > "$GITHUB_OUTPUT"
run "$SCRIPT"
assert_success
assert_output_value "cache_from" ""
assert_output_value "cache_to" ""
done
done
}

@test "explicit false retains external cache and invalid no-cache values fail" {
export INPUT_DOCKER_BUILD_NO_CACHE="false"
run "$SCRIPT"
assert_success
assert_output_value "cache_from" "type=gha"
assert_output_value "cache_to" "type=gha,mode=max"

export INPUT_DOCKER_BUILD_NO_CACHE="yes"
run "$SCRIPT"
assert_failure
assert_output --partial "docker-build-no-cache must be 'true' or 'false'"
}
Loading