Skip to content

Commit b2391d3

Browse files
authored
Merge pull request #193 from crazy-max/images-opts
attribute to enable/disable images
2 parents be6d2cc + 2f4dd14 commit b2391d3

File tree

9 files changed

+302
-33
lines changed

9 files changed

+302
-33
lines changed

.github/workflows/ci.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,21 @@ jobs:
146146
prefix=foo-
147147
suffix=-bar
148148
149+
images:
150+
runs-on: ubuntu-latest
151+
steps:
152+
-
153+
name: Checkout
154+
uses: actions/checkout@v3
155+
-
156+
name: Docker meta
157+
uses: ./
158+
with:
159+
images: |
160+
name=${{ env.DOCKER_IMAGE }}
161+
name=ghcr.io/name/app,enable=${{ github.event_name == 'pull_request' }}
162+
name=ghcr.io/name/release,enable=${{ startsWith(github.ref, 'refs/tags/') }}
163+
149164
labels:
150165
runs-on: ubuntu-latest
151166
steps:
@@ -226,7 +241,8 @@ jobs:
226241
id: docker_meta
227242
uses: ./
228243
with:
229-
images: ${{ env.DOCKER_IMAGE }}
244+
images: |
245+
${{ env.DOCKER_IMAGE }}
230246
tags: |
231247
type=schedule
232248
type=ref,event=branch

README.md

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ ___
1919
* [Customizing](#customizing)
2020
* [inputs](#inputs)
2121
* [outputs](#outputs)
22+
* [`images` input](#images-input)
2223
* [`flavor` input](#flavor-input)
2324
* [`tags` input](#tags-input)
2425
* [`type=schedule`](#typeschedule)
@@ -125,7 +126,8 @@ jobs:
125126
id: meta
126127
uses: docker/metadata-action@v3
127128
with:
128-
images: name/app
129+
images: |
130+
name/app
129131
tags: |
130132
type=ref,event=branch
131133
type=ref,event=pr
@@ -202,7 +204,8 @@ jobs:
202204
id: meta
203205
uses: docker/metadata-action@v3
204206
with:
205-
images: name/app
207+
images: |
208+
name/app
206209
tags: |
207210
type=ref,event=branch
208211
type=ref,event=pr
@@ -264,33 +267,51 @@ Following inputs can be used as `step.with` keys
264267
> org.opencontainers.image.vendor=MyCompany
265268
> ```
266269

267-
> `CSV` type is a comma-delimited string
268-
> ```yaml
269-
> images: name/app,ghcr.io/name/app
270-
> ```
271-
272-
| Name | Type | Description |
273-
|---------------------|----------|------------------------------------|
274-
| `images` | List/CSV | List of Docker images to use as base name for tags |
275-
| `tags` | List | List of [tags](#tags-input) as key-value pair attributes |
276-
| `flavor` | List | [Flavor](#flavor-input) to apply |
277-
| `labels` | List | List of custom labels |
278-
| `sep-tags` | String | Separator to use for tags output (default `\n`) |
279-
| `sep-labels` | String | Separator to use for labels output (default `\n`) |
280-
| `bake-target` | String | Bake target name (default `docker-metadata-action`) |
270+
| Name | Type | Description |
271+
|---------------------|--------|----------------------------------------------------------|
272+
| `images` | List | List of Docker images to use as base name for tags |
273+
| `tags` | List | List of [tags](#tags-input) as key-value pair attributes |
274+
| `flavor` | List | [Flavor](#flavor-input) to apply |
275+
| `labels` | List | List of custom labels |
276+
| `sep-tags` | String | Separator to use for tags output (default `\n`) |
277+
| `sep-labels` | String | Separator to use for labels output (default `\n`) |
278+
| `bake-target` | String | Bake target name (default `docker-metadata-action`) |
281279

282280
### outputs
283281

284282
Following outputs are available
285283

286-
| Name | Type | Description |
287-
|---------------|---------|---------------------------------------|
288-
| `version` | String | Docker image version |
289-
| `tags` | String | Docker tags |
290-
| `labels` | String | Docker labels |
291-
| `json` | String | JSON output of tags and labels |
284+
| Name | Type | Description |
285+
|---------------|---------|-------------------------------------------------------------------------------|
286+
| `version` | String | Docker image version |
287+
| `tags` | String | Docker tags |
288+
| `labels` | String | Docker labels |
289+
| `json` | String | JSON output of tags and labels |
292290
| `bake-file` | File | [Bake definition file](https://github.com/docker/buildx#file-definition) path |
293291

292+
## `images` input
293+
294+
`images` defines a list of Docker images to use as base name for [`tags`](#tags-input):
295+
296+
```yaml
297+
images: |
298+
name/foo
299+
ghcr.io/name/bar
300+
# or
301+
name=name/foo
302+
name=ghcr.io/name/bar
303+
```
304+
305+
Extended attributes and default values:
306+
307+
```yaml
308+
images: |
309+
name=,enable=true
310+
```
311+
312+
* `name=<string>` image base name
313+
* `enable=<true|false>` enable this entry (default `true`)
314+
294315
## `flavor` input
295316

296317
`flavor` defines a global behavior for [`tags`](#tags-input):

__tests__/image.test.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import {describe, expect, test} from '@jest/globals';
2+
import {Transform, Image} from '../src/image';
3+
4+
describe('transform', () => {
5+
// prettier-ignore
6+
test.each([
7+
[
8+
[
9+
`name/foo`
10+
],
11+
[
12+
{
13+
name: `name/foo`,
14+
enable: true,
15+
}
16+
] as Image[],
17+
false
18+
],
19+
[
20+
[
21+
`name/foo,name/bar`
22+
],
23+
[
24+
{
25+
name: `name/foo`,
26+
enable: true,
27+
},
28+
{
29+
name: `name/bar`,
30+
enable: true,
31+
}
32+
] as Image[],
33+
false
34+
],
35+
[
36+
[
37+
`name/foo`,
38+
`name/bar`
39+
],
40+
[
41+
{
42+
name: `name/foo`,
43+
enable: true,
44+
},
45+
{
46+
name: `name/bar`,
47+
enable: true,
48+
}
49+
] as Image[],
50+
false
51+
],
52+
[
53+
[
54+
`name=name/bar`,
55+
`name/foo,enable=false`,
56+
`name=ghcr.io/name/foo,enable=true`
57+
],
58+
[
59+
{
60+
name: `name/bar`,
61+
enable: true,
62+
},
63+
{
64+
name: `name/foo`,
65+
enable: false,
66+
},
67+
{
68+
name: `ghcr.io/name/foo`,
69+
enable: true,
70+
},
71+
] as Image[],
72+
false
73+
],
74+
[
75+
[`value=name/foo`], undefined, true
76+
],
77+
[
78+
[`name/foo,enable=bar`], undefined, true
79+
],
80+
[
81+
[`name/foo,bar=baz`], undefined, true
82+
],
83+
[
84+
[`name=,enable=true`], undefined, true
85+
],
86+
[
87+
[`name/foo,name=name/bar,enable=true`], undefined, true
88+
]
89+
])('given %p', async (l: string[], expected: Image[], invalid: boolean) => {
90+
try {
91+
const images = Transform(l);
92+
expect(images).toEqual(expected);
93+
} catch (err) {
94+
if (!invalid) {
95+
console.error(err);
96+
}
97+
// eslint-disable-next-line jest/no-conditional-expect
98+
expect(true).toBe(invalid);
99+
}
100+
});
101+
});

__tests__/meta.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,39 @@ describe('push', () => {
693693
"org.opencontainers.image.revision=860c1904a1ce19322e91ac35af1ab07466440c37",
694694
"org.opencontainers.image.licenses=MIT"
695695
]
696+
],
697+
[
698+
'push20',
699+
'event_push_dev.env',
700+
{
701+
images: [
702+
'org/app',
703+
'ghcr.io/user/app,enable=false'
704+
],
705+
tags: [
706+
`type=edge,branch=master`,
707+
`type=ref,event=branch,enable=false`,
708+
`type=sha,format=long`
709+
],
710+
} as Inputs,
711+
{
712+
main: 'sha-860c1904a1ce19322e91ac35af1ab07466440c37',
713+
partial: [],
714+
latest: false
715+
} as Version,
716+
[
717+
'org/app:sha-860c1904a1ce19322e91ac35af1ab07466440c37'
718+
],
719+
[
720+
"org.opencontainers.image.title=Hello-World",
721+
"org.opencontainers.image.description=This your first repo!",
722+
"org.opencontainers.image.url=https://github.com/octocat/Hello-World",
723+
"org.opencontainers.image.source=https://github.com/octocat/Hello-World",
724+
"org.opencontainers.image.version=sha-860c1904a1ce19322e91ac35af1ab07466440c37",
725+
"org.opencontainers.image.created=2020-01-10T00:30:00.000Z",
726+
"org.opencontainers.image.revision=860c1904a1ce19322e91ac35af1ab07466440c37",
727+
"org.opencontainers.image.licenses=MIT"
728+
]
696729
]
697730
])('given %p with %p event', tagsLabelsTest);
698731
});

dist/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function tmpDir(): string {
2727

2828
export function getInputs(): Inputs {
2929
return {
30-
images: getInputList('images'),
30+
images: getInputList('images', true),
3131
tags: getInputList('tags', true),
3232
flavor: getInputList('flavor', true),
3333
labels: getInputList('labels', true),

src/image.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {parse} from 'csv-parse/sync';
2+
import * as core from '@actions/core';
3+
4+
export interface Image {
5+
name: string;
6+
enable: boolean;
7+
}
8+
9+
export function Transform(inputs: string[]): Image[] {
10+
let images: Image[] = [];
11+
12+
// backward compatibility with old format
13+
if (inputs.length == 1) {
14+
let newformat = false;
15+
const fields = parse(inputs[0], {
16+
relaxColumnCount: true,
17+
skipEmptyLines: true
18+
})[0];
19+
for (const field of fields) {
20+
const parts = field
21+
.toString()
22+
.split('=')
23+
.map(item => item.trim());
24+
if (parts.length == 1) {
25+
images.push({name: parts[0].toLowerCase(), enable: true});
26+
} else {
27+
newformat = true;
28+
break;
29+
}
30+
}
31+
if (!newformat) {
32+
return output(images);
33+
}
34+
}
35+
36+
images = [];
37+
for (const input of inputs) {
38+
const image: Image = {name: '', enable: true};
39+
const fields = parse(input, {
40+
relaxColumnCount: true,
41+
skipEmptyLines: true
42+
})[0];
43+
for (const field of fields) {
44+
const parts = field
45+
.toString()
46+
.split('=')
47+
.map(item => item.trim());
48+
if (parts.length == 1) {
49+
image.name = parts[0].toLowerCase();
50+
} else {
51+
const key = parts[0].toLowerCase();
52+
const value = parts[1];
53+
switch (key) {
54+
case 'name': {
55+
image.name = value.toLowerCase();
56+
break;
57+
}
58+
case 'enable': {
59+
if (!['true', 'false'].includes(value)) {
60+
throw new Error(`Invalid enable attribute value: ${input}`);
61+
}
62+
image.enable = /true/i.test(value);
63+
break;
64+
}
65+
default: {
66+
throw new Error(`Unknown image attribute: ${input}`);
67+
}
68+
}
69+
}
70+
}
71+
if (image.name.length == 0) {
72+
throw new Error(`Image name attribute empty: ${input}`);
73+
}
74+
images.push(image);
75+
}
76+
return output(images);
77+
}
78+
79+
function output(images: Image[]): Image[] {
80+
core.startGroup(`Processing images input`);
81+
for (const image of images) {
82+
core.info(`name=${image.name},enable=${image.enable}`);
83+
}
84+
core.endGroup();
85+
return images;
86+
}

0 commit comments

Comments
 (0)