Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support annotations #327

Merged
merged 7 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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',
])
})