Skip to content

Publishing Role Images

Every published role image must carry two OCI labels that jackin reads at launch time:

LabelValuePurpose
jackin.construct_versionThe version tag from the FROM line (e.g. 0.2)Staleness detection — jackin warns when the published image was built from an older construct than the one currently resolved for a new build
jackin.role_git_shaThe git commit SHA of the role repo at build timeVersion display and staleness tracking

Role repositories on GitHub should call the jackin-project/jackin-role-action reusable workflow. It handles label injection, Docker layer cache ordering, GitHub API authentication, multi-platform builds (linux/amd64 and linux/arm64), image signing, and manifest assembly automatically.

A minimal caller workflow in your role repo:

.github/workflows/publish-image.yml
name: Publish Image
on:
push:
branches: [main]
paths:
- 'Dockerfile'
- 'jackin.role.toml'
- '.github/workflows/publish-image.yml'
workflow_dispatch:
jobs:
publish:
uses: jackin-project/jackin-role-action/.github/workflows/publish.yml@<SHA> # pin to a release SHA
permissions:
contents: read
id-token: write
secrets:
registry-username: ${{ secrets.DOCKERHUB_USERNAME }}
registry-password: ${{ secrets.DOCKERHUB_TOKEN }}
gh-readonly-token: ${{ secrets.GH_READONLY_TOKEN }}

Replace <SHA> with the pinned commit SHA from the jackin-role-action releases. Renovate can keep the pin current automatically — the generated role repo scaffold includes a Renovate configuration with the right pattern.

What the workflow does automatically:

  • Runs jackin-role validate and jackin-role construct-version to extract the construct version from your FROM line.
  • Applies jackin.construct_version and jackin.role_git_sha as OCI labels at build time via docker/build-push-action’s labels: input — no ARG or LABEL declarations needed in your Dockerfile.
  • Builds separate image manifests for linux/amd64 and linux/arm64.
  • Assembles and pushes a multi-platform manifest list.
  • Signs the image with Sigstore cosign.

Your Dockerfile only needs to declare the tools your role installs. The FROM projectjackin/construct:<version>-<distro> line is the construct version source of truth — the workflow reads it automatically.

If your role repo is not on GitHub, or if you use a different CI/CD system (GitLab CI, Jenkins, Buildkite, CircleCI, etc.), you must apply the two labels yourself at build time.

  1. Determine the construct version

    Read the version from your Dockerfile’s FROM line. The version is the part between : and - in the construct tag:

    FROM projectjackin/construct:0.3-trixie
    # ^^^— this is the construct version

    If the jackin-role binary is available in your CI environment (download it from the jackin releases), you can extract it automatically:

    Terminal window
    CONSTRUCT_VERSION=$(jackin-role construct-version .)

    Otherwise, parse it directly from the FROM line:

    Terminal window
    CONSTRUCT_VERSION=$(grep -m1 '^FROM projectjackin/construct:' Dockerfile \
    | sed 's|FROM projectjackin/construct:\([^-]*\)-.*|\1|')
  2. Get the role commit SHA

    Terminal window
    ROLE_GIT_SHA=$(git rev-parse HEAD)
  3. Build the image with both labels

    Pass both labels using --label flags:

    Terminal window
    docker build \
    --label "jackin.construct_version=${CONSTRUCT_VERSION}" \
    --label "jackin.role_git_sha=${ROLE_GIT_SHA}" \
    -t your-registry/your-role:latest \
    .

    No ARG or LABEL declarations are needed in the Dockerfile.

ARG instructions in a Dockerfile invalidate all subsequent layers when the argument value changes. A git SHA changes on every commit — so if you declare ARG ROLE_GIT_SHA inside the Dockerfile, every commit busts the layer cache for everything below it.

Using --label at build time (or the labels: input in docker/build-push-action) bypasses this: the label is applied to the final image manifest without touching the Dockerfile instruction stream. Your expensive tool-installation RUN layers remain cached across commits.

.gitlab-ci.yml
build-and-push:
stage: build
image: docker:27
services:
- docker:27-dind
script:
- CONSTRUCT_VERSION=$(grep -m1 '^FROM projectjackin/construct:' Dockerfile | sed 's|FROM projectjackin/construct:\([^-]*\)-.*|\1|')
- ROLE_GIT_SHA=${CI_COMMIT_SHA}
- docker login -u "${REGISTRY_USERNAME}" -p "${REGISTRY_PASSWORD}"
- >
docker build
--label "jackin.construct_version=${CONSTRUCT_VERSION}"
--label "jackin.role_git_sha=${ROLE_GIT_SHA}"
-t "${REGISTRY_IMAGE}:latest"
.
- docker push "${REGISTRY_IMAGE}:latest"

The reusable workflow accepts a registry input (defaults to Docker Hub) and matching registry-username / registry-password secrets. To publish to a different registry, pass the appropriate values:

jobs:
publish:
uses: jackin-project/jackin-role-action/.github/workflows/publish.yml@<SHA>
permissions:
contents: read
id-token: write
packages: write
secrets:
registry-username: ${{ github.actor }}
registry-password: ${{ secrets.GITHUB_TOKEN }}

Set your jackin.role.toml published_image to the full GHCR path:

jackin.role.toml
published_image = "ghcr.io/your-org/jackin-your-role"

jackin’ must be able to pull the image at launch time. If you publish to a private registry, the credentials must be available in the operator’s Docker environment before running jackin load or jackin console. Configure Docker credentials on the operator’s machine using docker login <registry> or a credential helper.

  • The final stage must start from projectjackin/construct:<version>-<distro> (see Role Repositories for the full contract).
  • No ARG CONSTRUCT_VERSION or ARG ROLE_GIT_SHA declarations are needed. The workflow or --label flags at build time handle both.
  • The construct version jackin reads for staleness detection comes from the FROM line — not from a label or ARG. Keep the version tag in the FROM line accurate and let Renovate track updates.