Skip to content

Commit

Permalink
Support annotations (#327)
Browse files Browse the repository at this point in the history
* Support annotations

* Fix

* Install the latest buildx

* Rename to annotations

* Rename to index-annotations

* Fix test
  • Loading branch information
int128 committed Feb 10, 2024
1 parent 56ae9aa commit 1d3dff8
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 20 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/e2e-docker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,16 @@ jobs:
- run: yarn package

# run the action
- uses: docker/setup-buildx-action@v3.0.0
- uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
id: metadata
with:
images: ghcr.io/${{ github.repository }}/e2e
- name: docker-manifest-create-action (dry-run)
uses: ./
with:
push: false
index-annotations: ${{ steps.metadata.outputs.labels }}
sources: |
${{ needs.build-linux-amd64.outputs.image-uri }}
${{ needs.build-linux-arm64.outputs.image-uri }}
Expand All @@ -70,14 +76,11 @@ jobs:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
id: metadata
with:
images: ghcr.io/${{ github.repository }}/e2e
- name: docker-manifest-create-action
id: build
uses: ./
with:
index-annotations: ${{ steps.metadata.outputs.labels }}
tags: ${{ steps.metadata.outputs.tags }}
sources: |
${{ needs.build-linux-amd64.outputs.image-uri }}
Expand Down
13 changes: 8 additions & 5 deletions .github/workflows/e2e-kaniko.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,17 @@ jobs:
- run: yarn package

# run the action
- uses: docker/setup-buildx-action@v3.0.0
- uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
id: metadata
with:
images: ghcr.io/${{ github.repository }}/e2e
flavor: latest=false,suffix=-kaniko
- name: docker-manifest-create-action (dry-run)
uses: ./
with:
push: false
index-annotations: ${{ steps.metadata.outputs.labels }}
sources: |
${{ needs.build-linux-amd64.outputs.image-uri }}
Expand All @@ -78,15 +85,11 @@ jobs:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
id: metadata
with:
images: ghcr.io/${{ github.repository }}/e2e
flavor: latest=false,suffix=-kaniko
- name: docker-manifest-create-action@v2
id: build
uses: ./
with:
index-annotations: ${{ steps.metadata.outputs.labels }}
tags: ${{ steps.metadata.outputs.tags }}
sources: |
${{ needs.build-linux-amd64.outputs.image-uri }}
Expand Down
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ jobs:
- uses: int128/docker-manifest-create-action@v2
id: build
with:
index-annotations: ${{ steps.metadata.outputs.labels }}
tags: ${{ steps.metadata.outputs.tags }}
sources: |
ghcr.io/${{ github.repository }}@${{ needs.build-linux-amd64.outputs.digest }}
Expand Down Expand Up @@ -176,6 +177,7 @@ jobs:
- uses: int128/docker-manifest-create-action@v2
id: build
with:
index-annotations: ${{ steps.metadata.outputs.labels }}
tags: ${{ steps.metadata.outputs.tags }}
sources: |
${{ steps.ecr.outputs.registry }}/${{ github.repository }}@${{ needs.build-linux-amd64.outputs.digest }}
Expand All @@ -188,14 +190,18 @@ This action requires Docker Buildx.

### Inputs

| Name | Default | Description |
| --------- | ---------------------------- | -------------------------------------------------- |
| `push` | `true` | Push the manifest to the registry |
| `tags` | (required if `push` is true) | Tags of the destination images (multi-line string) |
| `sources` | (required) | Image URIs of the sources (multi-line string) |
| Name | Default | Description |
| ------------------- | ---------------------------- | ------------------------------------------------------ |
| `push` | `true` | Push the manifest to the registry |
| `index-annotations` | - | Add annotations to the image index (multi-line string) |
| `tags` | (required if `push` is true) | Tags of the destination images (multi-line string) |
| `sources` | (required) | Image URIs of the sources (multi-line string) |

If `push` is false, this action runs `docker buildx imagetools create --dry-run`.

If `index-annotations` is set, this action adds `--annotation`.
See https://docs.docker.com/engine/reference/commandline/buildx_imagetools_create/#annotation for details.

### Outputs

| Name | Description |
Expand Down
3 changes: 3 additions & 0 deletions action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ inputs:
description: Push the manifest to the registry
required: false
default: 'true'
index-annotations:
description: Add annotations to the image index (multi-line string)
required: false
tags:
description: Tags of the destination images (multi-line string)
required: false
Expand Down
1 change: 1 addition & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { run } from './run'
const main = async (): Promise<void> => {
const outputs = await run({
push: core.getBooleanInput('push', { required: true }),
indexAnnotations: core.getMultilineInput('index-annotations'),
tags: core.getMultilineInput('tags'),
sources: core.getMultilineInput('sources', { required: true }),
})
Expand Down
35 changes: 29 additions & 6 deletions src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as exec from '@actions/exec'

type Inputs = {
push: boolean
indexAnnotations: string[]
tags: string[]
sources: string[]
}
Expand All @@ -18,27 +19,49 @@ export const run = async (inputs: Inputs): Promise<Outputs> => {
core.endGroup()

if (!inputs.push) {
await dryRunCreateManifest(inputs.sources)
await dryRunCreateManifest(inputs.sources, inputs.indexAnnotations)
return { digest: undefined }
}

assert(inputs.tags.length > 0, 'tags must be set')
for (const tag of inputs.tags) {
await createManifest(tag, inputs.sources)
await createManifest(tag, inputs.sources, inputs.indexAnnotations)
}
const digest = await getDigest(inputs.tags[0])
return { digest }
}

const dryRunCreateManifest = async (sources: string[]) => {
await exec.exec('docker', ['buildx', 'imagetools', 'create', '--dry-run', ...sources])
const dryRunCreateManifest = async (sources: string[], indexAnnotations: string[]) => {
await exec.exec('docker', [
'buildx',
'imagetools',
'create',
'--dry-run',
...toAnnotationFlags(indexAnnotations),
...sources,
])
}

const createManifest = async (destination: string, sources: string[]) => {
await exec.exec('docker', ['buildx', 'imagetools', 'create', '-t', destination, ...sources])
const createManifest = async (destination: string, sources: string[], indexAnnotations: string[]) => {
await exec.exec('docker', [
'buildx',
'imagetools',
'create',
...toAnnotationFlags(indexAnnotations),
'-t',
destination,
...sources,
])
await exec.exec('docker', ['buildx', 'imagetools', 'inspect', destination])
}

const toAnnotationFlags = (indexAnnotations: string[]): string[] =>
indexAnnotations.flatMap((a) => [
'--annotation',
// https://docs.docker.com/engine/reference/commandline/buildx_imagetools_create/#annotation
`index:${a}`,
])

const getDigest = async (tag: string): Promise<string> => {
const { stdout } = await exec.getExecOutput('docker', [
'buildx',
Expand Down
44 changes: 44 additions & 0 deletions tests/run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ it('should run docker buildx imagetools', async () => {

const outputs = await run({
push: true,
indexAnnotations: [],
tags: ['ghcr.io/int128/docker-manifest-create-action:main'],
sources: [
'ghcr.io/int128/docker-manifest-create-action@sha256:0000000000000000000000000000000000000000000000000000000000000000',
Expand Down Expand Up @@ -44,6 +45,7 @@ it('should run docker buildx imagetools', async () => {
it('should run docker buildx imagetools --dry-run if push is false', async () => {
const outputs = await run({
push: false,
indexAnnotations: [],
tags: [],
sources: [
'ghcr.io/int128/docker-manifest-create-action@sha256:0000000000000000000000000000000000000000000000000000000000000000',
Expand All @@ -61,3 +63,45 @@ it('should run docker buildx imagetools --dry-run if push is false', async () =>
'ghcr.io/int128/docker-manifest-create-action@sha256:0000000000000000000000000000000000000000000000000000000000000001',
])
})

it('should add annotations', async () => {
jest.mocked(exec).getExecOutput.mockResolvedValue({
exitCode: 0,
stdout: '"sha256:f000000000000000000000000000000000000000000000000000000000000000"',
stderr: '',
})

const outputs = await run({
push: true,
indexAnnotations: [
'org.opencontainers.image.revision=0123456789012345678901234567890123456789',
'org.opencontainers.image.created=2021-01-01T00:00:00Z',
],
tags: ['ghcr.io/int128/docker-manifest-create-action:main'],
sources: [
'ghcr.io/int128/docker-manifest-create-action@sha256:0000000000000000000000000000000000000000000000000000000000000000',
],
})
expect(outputs).toStrictEqual({
digest: 'sha256:f000000000000000000000000000000000000000000000000000000000000000',
})

expect(exec.exec).toHaveBeenCalledWith('docker', [
'buildx',
'imagetools',
'create',
'--annotation',
'index:org.opencontainers.image.revision=0123456789012345678901234567890123456789',
'--annotation',
'index:org.opencontainers.image.created=2021-01-01T00:00:00Z',
'-t',
'ghcr.io/int128/docker-manifest-create-action:main',
'ghcr.io/int128/docker-manifest-create-action@sha256:0000000000000000000000000000000000000000000000000000000000000000',
])
expect(exec.exec).toHaveBeenCalledWith('docker', [
'buildx',
'imagetools',
'inspect',
'ghcr.io/int128/docker-manifest-create-action:main',
])
})

0 comments on commit 1d3dff8

Please sign in to comment.