diff --git a/.github/workflows/component-build.yml b/.github/workflows/component-build.yml index 8d9d91f..38d3343 100644 --- a/.github/workflows/component-build.yml +++ b/.github/workflows/component-build.yml @@ -86,6 +86,12 @@ on: NEXT_SERVER_ACTIONS_ENCRYPTION_KEY: required: false description: 'Encryption key for Next.js Server Actions, this value is injected into the secrets of a docker image' + GH_ACTION_ACCESS_KEY_ID: + required: false + description: "AWS access key for the S3 cache bucket, used on ARC. Without it ARC builds use the GitHub cache." + GH_ACTION_SECRET_ACCESS_KEY: + required: false + description: "AWS secret key for the S3 cache bucket, used on ARC. Without it ARC builds use the GitHub cache." outputs: image-tag: description: 'The image tag that was built' @@ -186,6 +192,9 @@ jobs: if: ${{ !inputs.use-blacksmith-runners || inputs.use-arc-runners }} runs-on: ${{ needs.setup.outputs.runner }} timeout-minutes: 30 + env: + # On ARC the cache mounts go to S3 when the caller passes the bucket credentials, and the GitHub cache otherwise. + S3_CACHE: ${{ inputs.use-arc-runners && secrets.GH_ACTION_ACCESS_KEY_ID != '' }} steps: - name: Checkout uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 @@ -212,6 +221,39 @@ jobs: # docker driver does not support cache-to). - name: Set up Docker Buildx uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4 + # ARC builders start empty and the registry cache does not carry RUN --mount=type=cache + # contents (e.g. /root/.gradle), so persist them between builds. + - name: Restore Dockerfile cache mounts + id: cache-mounts + if: ${{ inputs.use-arc-runners && env.S3_CACHE != 'true' }} + uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5 + with: &cache-mounts + path: buildkit-cache-mounts + key: buildkit-cache-mounts-${{ inputs.docker-file-name }}-${{ hashFiles('**/*.gradle.kts', '**/gradle.properties', 'gradle/libs.versions.toml', 'gradle/wrapper/gradle-wrapper.properties') }} + restore-keys: | + buildkit-cache-mounts-${{ inputs.docker-file-name }}- + # The combined action, not restore/save: its post-step save must run after the + # cache-dance post step below has extracted the mounts from the builder. + - name: Restore Dockerfile cache mounts (S3) + id: cache-mounts-s3 + if: ${{ env.S3_CACHE == 'true' }} + uses: runs-on/cache@88d90644011a3a9957fd141a106f5a94f9794203 # v5.0.7 + env: + RUNS_ON_S3_BUCKET_CACHE: monta-github-ci-cache + RUNS_ON_RUNNER_NAME: "" + AWS_ACCESS_KEY_ID: ${{ secrets.GH_ACTION_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.GH_ACTION_SECRET_ACCESS_KEY }} + # Blank the ecr-put-image session exported by the credentials step above. + AWS_SESSION_TOKEN: "" + AWS_REGION: eu-west-1 + with: *cache-mounts + - name: Inject Dockerfile cache mounts into the builder + if: ${{ inputs.use-arc-runners }} + uses: reproducible-containers/buildkit-cache-dance@5422eac04292c961a382e0f584ea0f03ad9da723 # v3.4.0 + with: + dockerfile: ${{ inputs.docker-file-name }} + cache-dir: buildkit-cache-mounts + skip-extraction: ${{ steps.cache-mounts.outputs.cache-hit || steps.cache-mounts-s3.outputs.cache-hit }} - name: Build and push id: build uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7 diff --git a/.github/workflows/deploy-kotlin-v2.yml b/.github/workflows/deploy-kotlin-v2.yml index 63b2a00..f7e4177 100644 --- a/.github/workflows/deploy-kotlin-v2.yml +++ b/.github/workflows/deploy-kotlin-v2.yml @@ -195,6 +195,12 @@ on: LOKALISE_PROJECT_ID: required: false description: 'Lokalise project ID for Lokalise API' + GH_ACTION_ACCESS_KEY_ID: + required: false + description: "AWS access key for the S3 cache bucket, used on ARC. Without it ARC builds use the GitHub cache." + GH_ACTION_SECRET_ACCESS_KEY: + required: false + description: "AWS secret key for the S3 cache bucket, used on ARC. Without it ARC builds use the GitHub cache." JIRA_EMAIL: required: false description: 'Email for JIRA API authentication (used for ticket operations in changelog)' @@ -278,6 +284,8 @@ jobs: AWS_CDN_SECRET_ACCESS_KEY: ${{ secrets.AWS_CDN_SECRET_ACCESS_KEY }} LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} LOKALISE_PROJECT_ID: ${{ secrets.LOKALISE_PROJECT_ID }} + GH_ACTION_ACCESS_KEY_ID: ${{ secrets.GH_ACTION_ACCESS_KEY_ID }} + GH_ACTION_SECRET_ACCESS_KEY: ${{ secrets.GH_ACTION_SECRET_ACCESS_KEY }} deploy: name: Deploy needs: diff --git a/.github/workflows/deploy-kotlin.yml b/.github/workflows/deploy-kotlin.yml index d65bc7a..829321d 100644 --- a/.github/workflows/deploy-kotlin.yml +++ b/.github/workflows/deploy-kotlin.yml @@ -183,6 +183,12 @@ on: LOKALISE_PROJECT_ID: required: false description: 'Lokalise project ID for Lokalise API' + GH_ACTION_ACCESS_KEY_ID: + required: false + description: "AWS access key for the S3 cache bucket, used on ARC. Without it ARC builds use the GitHub cache." + GH_ACTION_SECRET_ACCESS_KEY: + required: false + description: "AWS secret key for the S3 cache bucket, used on ARC. Without it ARC builds use the GitHub cache." JIRA_EMAIL: required: false description: 'Email for JIRA API authentication (used for ticket operations in changelog)' @@ -266,6 +272,8 @@ jobs: AWS_CDN_SECRET_ACCESS_KEY: ${{ secrets.AWS_CDN_SECRET_ACCESS_KEY }} LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} LOKALISE_PROJECT_ID: ${{ secrets.LOKALISE_PROJECT_ID }} + GH_ACTION_ACCESS_KEY_ID: ${{ secrets.GH_ACTION_ACCESS_KEY_ID }} + GH_ACTION_SECRET_ACCESS_KEY: ${{ secrets.GH_ACTION_SECRET_ACCESS_KEY }} deploy: name: Deploy needs: diff --git a/CLAUDE.md b/CLAUDE.md index d873e45..9737dfb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,6 +6,7 @@ - **Changes**: Added `use-arc-runners` to `deploy-kotlin(-v2)`, `component-test-kotlin` and `component-build`; added the `arc-job-report` composite action ## Recent Changes (2026-09-29) +1. **Dockerfile cache mounts persisted on ARC image builds**: when `use-arc-runners` is set, `component-build.yml`'s buildx job restores and saves `RUN --mount=type=cache` contents (all Kotlin service Dockerfiles mount `/root/.gradle` around `./gradlew buildLayers`) via `reproducible-containers/buildkit-cache-dance`, which finds the mounts by parsing the Dockerfile. ARC builders start empty and the ECR registry cache (`cache-to ... mode=max`) does not carry cache mounts, so without this every deploy re-downloads all Gradle dependencies straight from Maven Central (the in-Docker Gradle has neither the runner's CodeArtifact init script nor AWS credentials). Storage follows the PR workflows: `runs-on/cache` on S3 when the caller passes the `GH_ACTION_*` secrets (new optional secrets on `component-build` and `deploy-kotlin(-v2)`), `actions/cache` otherwise. It must be the combined `runs-on/cache`, not `restore`/`save`: cache-dance extracts in its post step, and only a post-step save runs after that. The S3 step blanks `AWS_SESSION_TOKEN`, because the job's `ecr-put-image` credentials are exported into the environment before it. Key: Dockerfile path + hash of the Gradle build files, with a prefix restore-key. 1. **`use-arc-runners` for the Kotlin deploy workflows**: `deploy-kotlin.yml` and `deploy-kotlin-v2.yml` accept `use-arc-runners` and pass it to `component-test-kotlin.yml` and `component-build.yml`, which both gained the input. Tests run on the converter's ARC runner, with Setup on `arc-arm64-2cpu-4gb` and `LC_ALL=C.UTF-8`, and keep `setup-java`'s GitHub-backed Gradle cache. The image build runs the existing `build-self-hosted` job (docker buildx + ECR registry layer cache, the ARC README's recommendation) instead of `build-blacksmith`. The deploy, service-profile, release-tag and changelog jobs never used Blacksmith and are unchanged. Callers that don't opt in are unaffected. 2. **`arc-job-report` action**: New composite action at `.github/actions/arc-job-report` that keeps one PR comment with each job's ARC runner, duration, peak memory and CPU, a table of suggested runner-size changes, and a per-run history (commit, wall time, slowest job). Callers run it from a `workflow_run` trigger on their PR workflow (so it reports on the finished run and adds no job to the PR checks) and pass `github-token` + `grafana-token` (e.g. `secrets.GRAFANA_CLOUD_TOKEN`) as inputs; no checkout needed, the action ships its scripts. `job_resources.py` is kube-manifests' `infra/arc/scripts/job-resources.py` plus a `GRAFANA_TOKEN` path through Grafana's datasource proxy (CI has no `gcx`); keep the two in sync. Metrics are scraped every 60 s, so jobs under 2 min are left out of the suggestions and jobs under 1 min show no CPU at all (`rate()` needs two samples); waiting does not help because the pod is deleted when the job ends. Only bot-authored comments are updated, so local `--dry-run` testing never collides with CI. Built and trialled in service-ocpi#2599. diff --git a/docs/workflow-guide.md b/docs/workflow-guide.md index 748d97b..92e6ccd 100644 --- a/docs/workflow-guide.md +++ b/docs/workflow-guide.md @@ -162,7 +162,7 @@ jobs: | `service-identifier` | Yes | - | Service identifier (e.g., "ocpp", "vehicle") | | `runner-size` | No | "normal" | Runner size: "normal" or "large" | | `use-blacksmith-runners` | No | true | Run on Blacksmith arm64 cloud runners (default). Set to false to run on self-hosted linux-arm64 | -| `use-arc-runners` | No | false | Run on the self-hosted ARC arm64 runners (`normal` → `arc-arm64-4cpu-12gb`, `large` → `arc-arm64-8cpu-24gb`); takes precedence over `use-blacksmith-runners`. Builds with docker buildx and the ECR registry layer cache. | +| `use-arc-runners` | No | false | Run on the self-hosted ARC arm64 runners (`normal` → `arc-arm64-4cpu-12gb`, `large` → `arc-arm64-8cpu-24gb`); takes precedence over `use-blacksmith-runners`. Builds with docker buildx and the ECR registry layer cache. Dockerfile `RUN --mount=type=cache` contents (e.g. `/root/.gradle`) are kept between builds: in S3 when the `GH_ACTION_*` secrets are passed, the GitHub cache otherwise. | | `region` | No | "eu-west-1" | AWS region | | `docker-file-name` | No | "Dockerfile" | Dockerfile name | | `additional-build-args` | No | - | Additional Docker build arguments |