Skip to content

feat: container image digest pinning in actions-lock.json with update/upgrade integration#25619

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/fix-container-image-digest-pinning-again
Draft

feat: container image digest pinning in actions-lock.json with update/upgrade integration#25619
Copilot wants to merge 3 commits intomainfrom
copilot/fix-container-image-digest-pinning-again

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 10, 2026

All MCP container images (node:lts-alpine, ghcr.io/github/gh-aw-firewall/*, ghcr.io/github/gh-aw-mcpg, ghcr.io/github/github-mcp-server) were pulled by mutable tag with no SHA-256 pinning — node:lts-alpine is additionally floating across LTS generations. This extends the existing action-pin infrastructure to cover Docker images.

Storage — actions-lock.json

New containers section alongside entries, keyed by image tag:

{
  "entries": { ... },
  "containers": {
    "node:lts-alpine": {
      "image": "node:lts-alpine",
      "digest": "sha256:abc123...",
      "pinned_image": "node:lts-alpine@sha256:abc123..."
    }
  }
}

ActionCache gains GetContainerPin / SetContainerPin / DeleteContainerPin. Loading a legacy file (no containers) is fully backward-compatible.

Compilation

  • collectDockerImages applies cached pins, substituting tag@sha256:… for any image with a stored entry.
  • WorkflowData.DockerImages accumulates the resolved (possibly pinned) refs so they can be surfaced in the lock file.
  • WorkflowData.DockerImagePins accumulates full GHAWManifestContainer entries (image, digest, pinned_image) for the manifest.
  • Lock file headers now emit a # Container images used: comment section.
  • gh-aw-manifest JSON gains a "containers":[…] field with full structured container info including SHA-256 digest when available:
    "containers":[{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.17"},{"image":"node:lts-alpine","digest":"sha256:abc...","pinned_image":"node:lts-alpine@sha256:abc..."}]

Update/Upgrade integration — pkg/cli/update_container_pins.go

UpdateContainerPins() is called from both gh aw update and gh aw upgrade (after action pins, before compile):

  1. Scans existing .lock.yml files for download_docker_images.sh invocations to discover in-use image tags.
  2. Resolves each tag's digest via docker buildx imagetools inspect (no pull required); falls back to docker pull + docker inspect.
  3. Saves pins to actions-lock.json; next compile embeds the @sha256:… suffix.
  4. Docker unavailability is non-fatal — the update/upgrade continues without pinning.

- Extend ActionCache with ContainerPins map (stored in actions-lock.json)
- Add ContainerPin type with image, digest, and pinned_image fields
- Modify collectDockerImages to apply cached digest pins and store images in WorkflowData
- Add DockerImages field to WorkflowData for header/manifest tracking
- Extend GHAWManifest with containers field (in gh-aw-manifest JSON)
- Add Container images used: section to lock file header comments
- Create UpdateContainerPins CLI function that resolves digests via Docker
- Integrate container pin update into upgrade command (step 3b)
- Recompile all lock files with updated manifest and header format

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/688c5141-cf5f-4a69-9935-1dad34a50e18

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix container image pull with SHA-256 digest pinning feat: container image digest pinning in actions-lock.json with upgrade integration Apr 10, 2026
Copilot AI requested a review from pelikhan April 10, 2026 12:12
@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot

  • rename container_pins to containers
  • resolve current used containers
  • store containers full info in gh-aw-manifest (including sha)

…o manifest, add update command integration

- Rename ActionCache JSON key from container_pins to containers
- Add GHAWManifestContainer type with image/digest/pinned_image fields
- Change GHAWManifest.Containers from []string to []GHAWManifestContainer
- Track DockerImagePins []GHAWManifestContainer in WorkflowData for manifest
- Add UpdateContainerPins to update command (not just upgrade)
- Recompile all lock files

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/0d5cf327-a467-40fa-8347-f0e797db7dd4

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 10, 2026

@copilot

  • rename container_pins to containers...

Done in 88fe858:

  1. Renamed container_pinscontainers — the JSON key in actions-lock.json is now "containers" (both the struct tag and the marshalSorted output).

  2. Resolve containers in update commandUpdateContainerPins is now also called from RunUpdateWorkflows (the gh aw update flow), not just from upgrade.

  3. Full container info in gh-aw-manifest — added a new GHAWManifestContainer type (image, digest, pinned_image) and changed GHAWManifest.Containers from []string to []GHAWManifestContainer. The manifest now embeds structured objects like:

    "containers":[{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.17"},{"image":"node:lts-alpine","digest":"sha256:abc...","pinned_image":"node:lts-alpine@sha256:abc..."}]

    Images without a cached pin emit only image; pinned images include digest and pinned_image.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE go env ck &#39;**/*.cjs&#39; &#39;*GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE go env ck &#39;**/*.cjs&#39; &#39;*GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/xGO111MODULE (http block)
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE ortcfg env d/gh-aw/main.go d/gh-aw/capitaliGOMOD 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/xGO111MODULE (http block)
  • https://api.github.com/repos/astral-sh/setup-uv/git/ref/tags/eac588ad8def6316056a12d4907a9d4d84ff7a3b
    • Triggering command: /usr/bin/gh gh api /repos/astral-sh/setup-uv/git/ref/tags/eac588ad8def6316056a12d4907a9d4d84ff7a3b --jq .object.sha ck &#39;scripts/**/*GOINSECURE GO111MODULE $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node (http block)
    • Triggering command: /usr/bin/gh gh api /repos/astral-sh/setup-uv/git/ref/tags/eac588ad8def6316056a12d4907a9d4d84ff7a3b --jq .object.sha ck &#39;scripts/**/*GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh (http block)
  • https://api.github.com/repos/github/gh-aw
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw --jq .visibility -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env iew.lock.yml GO111MODULE r: $owner, name: $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/githubnext/agentics/git/ref/tags/-
    • Triggering command: /usr/bin/gh gh api /repos/githubnext/agentics/git/ref/tags/- --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh -c &#34;prettier&#34; --che-f GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go go (http block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title feat: container image digest pinning in actions-lock.json with upgrade integration feat: container image digest pinning in actions-lock.json with update/upgrade integration Apr 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP container images pulled without SHA-256 digest pinning; node:lts-alpine uses floating LTS tag

2 participants