diff --git a/.github/workflows/agent-image.yaml b/.github/workflows/agent-image.yaml new file mode 100644 index 0000000..50b042c --- /dev/null +++ b/.github/workflows/agent-image.yaml @@ -0,0 +1,75 @@ +name: agent-image + +on: + workflow_call: + inputs: + is-latest: + description: "Also move the latest (or latest-dev) tag" + required: false + default: false + type: boolean + is-stable: + description: "Move latest instead of latest-dev" + required: false + default: false + type: boolean + +# One publish at a time per ref. GitHub keeps only one pending run per group +# and cancels it when a newer one arrives, so a shared group could drop a +# release build. Merges to main still run in order. A pre-release can race +# with a merge to main on latest-dev: if it finishes last, latest-dev points +# to the pre-release until the next merge that touches agent/. +concurrency: + group: agent-image-${{ github.ref }} + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # git describe needs the tags and the history. + fetch-depth: 0 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 + - name: Log in to ghcr.io + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Compute tags + id: tags + env: + IMAGE: ghcr.io/${{ github.repository_owner }}/image-cache-agent + IS_LATEST: ${{ inputs.is-latest }} + IS_STABLE: ${{ inputs.is-stable }} + run: | + # A tag push names the image after that tag: two tags can sit on + # the same commit, and describe would pick one of them. + if [ "$GITHUB_REF_TYPE" = "tag" ]; then + version="$GITHUB_REF_NAME" + else + version="$(git describe --tags --always --match='v[0-9]*')" + fi + tags="$IMAGE:$version" + if [ "$IS_LATEST" = "true" ]; then + if [ "$IS_STABLE" = "true" ]; then + tags="$tags,$IMAGE:latest" + else + tags="$tags,$IMAGE:latest-dev" + fi + fi + echo "tags=$tags" >> "$GITHUB_OUTPUT" + - name: Build and push + uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 + with: + context: agent + # The agent only ships for amd64 (see agent/DESIGN.md). + platforms: linux/amd64 + push: true + tags: ${{ steps.tags.outputs.tags }} + cache-from: type=gha,scope=image-cache-agent + cache-to: type=gha,mode=max,scope=image-cache-agent diff --git a/.github/workflows/post-merge.yaml b/.github/workflows/post-merge.yaml new file mode 100644 index 0000000..f7697b5 --- /dev/null +++ b/.github/workflows/post-merge.yaml @@ -0,0 +1,20 @@ +--- +name: Post Merge + +on: + push: + branches: [main] + paths: + - 'agent/**' + - '.github/workflows/agent-image.yaml' + - '.github/workflows/post-merge.yaml' + +jobs: + agent-image: + uses: ./.github/workflows/agent-image.yaml + permissions: + contents: read + packages: write + with: + is-latest: true + is-stable: false diff --git a/.github/workflows/promote.yaml b/.github/workflows/promote.yaml index da261a2..0749b7f 100644 --- a/.github/workflows/promote.yaml +++ b/.github/workflows/promote.yaml @@ -8,7 +8,18 @@ on: - "v*" jobs: + # Runs before the release is created, so a broken image blocks the release. + build: + uses: ./.github/workflows/agent-image.yaml + permissions: + contents: read + packages: write + with: + is-latest: true + is-stable: ${{ ! contains(github.ref_name, '-') }} + create-release: + needs: build uses: scality/workflows/.github/workflows/promote.yaml@f05282f5bd6d7e90e8536400cf468f0a54681a48 # v2.10.0 permissions: contents: write diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fcd6c7c..34baa17 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,8 +22,10 @@ The repository holds two toolchains, and three things built from them: Keep a change inside one component when you can. Reviewers read the repository that way, and the toolchains have nothing in common. Releases do not follow -that split yet: a tag cuts one version for the whole repository, and the RPMs -are what is attached to it. +that split yet: a tag cuts one version for the whole repository. The RPMs are +attached to it, and the agent image is published under the same tag. A merge +to `main` that touches `agent/` also publishes an image; the tags are listed +in [agent/README.md](agent/README.md#deploying). ## Development environment diff --git a/README.md b/README.md index 7d8aaa9..487e3fd 100644 --- a/README.md +++ b/README.md @@ -95,20 +95,21 @@ happens when an import fails, and how the package is built. ### The agent -Build the image, then deploy: `deploy` applies the CRD along with the rest. No -image is published yet, so the first step is yours. +The image is published at `ghcr.io/scality/image-cache-agent`. Deploy it from +a checkout of the same tag: `deploy` applies the CRD along with the rest, and +takes them from the checkout. ```console -make -C agent docker-build docker-push IMG=/image-cache-agent: -make -C agent deploy IMG=/image-cache-agent: +git checkout +make -C agent deploy IMG=ghcr.io/scality/image-cache-agent: ``` -Those two commands are enough from an amd64 machine, deploying into a -namespace that enforces the `privileged` Pod Security Standard. Anything else -takes a step or two, and [agent/README.md](agent/README.md#deploying) has them: -building for amd64 from another architecture, and the label the manifests leave -off the namespace they create. The flags, `--cache-path` and `--resync-period` -included, are under [Configuration](agent/README.md#configuration). +Those commands are enough to deploy into a namespace that enforces the +`privileged` Pod Security Standard. Anything else takes a step or two, and +[agent/README.md](agent/README.md#deploying) has them: which image tags exist, +building your own image, and the label the manifests leave off the namespace +they create. The flags, `--cache-path` and `--resync-period` included, are +under [Configuration](agent/README.md#configuration). Then declare what each node should cache: @@ -246,9 +247,9 @@ Each component is built and tested independently, and both CI workflows run on every pull request, since a required check that never runs leaves the pull request waiting forever; on pushes to `main` they are scoped by path. -Releases are not independent yet: a tag cuts one version for the repository, -and the artifacts attached are the two RPMs, for both EL versions. Publishing -the agent image is still to come. +Releases are not independent yet: a tag cuts one version for the repository. +It attaches the two RPMs, for both EL versions, and publishes the agent image +under the same tag. ## Development diff --git a/agent/AGENTS.md b/agent/AGENTS.md index a353c0d..0c0010b 100644 --- a/agent/AGENTS.md +++ b/agent/AGENTS.md @@ -104,10 +104,12 @@ Do not scaffold them back in: because controller-runtime defaults to `true` and would gate it the day someone turns leader election on. Every agent converges its own node, so there is no leader to elect. -- **No helm chart.** `make build-installer` renders the manifests into - `dist/install.yaml`, which is gitignored and attached to nothing: today the - way to deploy is this checkout's `make deploy`. Publishing an artifact is a - separate piece of work, not a missing chart. +- **No helm chart.** The image is published on ghcr.io (see + [README.md](README.md#deploying)), but the manifests are not. + `make build-installer` renders them into `dist/install.yaml`, which is + gitignored and attached to nothing: the way to deploy is this checkout's + `make deploy`. Publishing the manifests is a separate piece of work, not a + missing chart. ## Traps worth knowing diff --git a/agent/README.md b/agent/README.md index 8c91efb..a94d26c 100644 --- a/agent/README.md +++ b/agent/README.md @@ -28,8 +28,26 @@ All the commands below run from this directory. ## Deploying -Build and push the image, then deploy. `deploy` builds `config/default`, which -includes the CRD, so it covers what `install` does on its own. +The image is published at `ghcr.io/scality/image-cache-agent`, for amd64 only: + +| Tag | Image | +| --- | ----- | +| `vX.Y.Z`, `vX.Y.Z-
` | built from that release tag |
+| `latest` | the last release without a pre-release suffix to be built: re-running an older Promote moves it back |
+| `latest-dev` | the last pre-release, or the last merge to `main` that touches `agent/` |
+| `--g` | a merge to `main` that touches `agent/`, named by `git describe --tags`: `` commits after the last release tag |
+
+Deploy it from a checkout of the same tag. `deploy` builds `config/default`
+from this checkout, and only the image comes from ``: on another commit,
+the CRD and RBAC may not match the binary. `config/default` includes the CRD,
+so `deploy` covers what `install` does on its own.
+
+```console
+git checkout 
+make deploy IMG=ghcr.io/scality/image-cache-agent:
+```
+
+To deploy your own build, build and push it first:
 
 ```console
 make docker-build docker-push IMG=/image-cache-agent: