Skip to content
This repository was archived by the owner on Mar 21, 2025. It is now read-only.

Commit 5c777ad

Browse files
authored
B2B-866: Implement iterative configuration generation (#9)
1 parent 834c525 commit 5c777ad

File tree

9 files changed

+209
-11
lines changed

9 files changed

+209
-11
lines changed

.github/workflows/test.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,24 @@ jobs:
5151
- run: echo '${{ steps.plain.outputs.result }}'
5252
- if: ${{ toJSON(steps.plain.outputs.result) != '"root\ntestA\ntestB\n"' }}
5353
run: 'false'
54+
55+
- uses: ./
56+
id: loop
57+
with:
58+
output_properties: 'true'
59+
patterns: |
60+
- test/assets/conf1.yml
61+
- test/assets/{{ item }}/conf1.yml
62+
loop: |
63+
a
64+
a/c
65+
b
66+
- run: echo '${{ steps.loop.outputs.result }}'
67+
- if: ${{ steps.loop.outputs.result != '{"a":{"test":"a1"},"a/c":{"test":"c1"},"b":{"test":"b1"}}' }}
68+
run: 'false'
69+
- if: ${{ steps.loop.outputs.a != '{"test":"a1"}' }}
70+
run: 'false'
71+
- if: ${{ steps.loop.outputs.a_c != '{"test":"c1"}' }}
72+
run: 'false'
73+
- if: ${{ steps.loop.outputs.b != '{"test":"b1"}' }}
74+
run: 'false'

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,34 @@ Output each property of the object as JSON.
125125

126126
Default: `false`.
127127

128+
### `loop`
129+
130+
If `loop` is defined, then the action is repeated as many times as the number of rows in the variable's content.
131+
All found mentions of `{{ item }}` in the pattern are replaced with a row from the `loop` variable.
132+
The iterative execution of the action returns the object as JSON, where the key contains a row,
133+
and the value is the result of the execution of the action according to the pattern.
134+
Also it has output for each row as a serialized key with value in JSON if `output_properties` is enabled.
135+
136+
Example:
137+
138+
```yml
139+
- uses: blablacar/action-config-levels@master
140+
id: config
141+
with:
142+
patterns: |
143+
- {{ item }}/common.yml
144+
- {{ item }}/${{ env.ENV }}.yml
145+
loop: |
146+
dir1
147+
dir2/subdir
148+
149+
- run: echo '${{ steps.config.outputs.result }}'
150+
151+
- run: echo '${{ steps.config.outputs.dir1 }}'
152+
153+
- run: echo '${{ steps.config.outputs.dir2_subdir }}'
154+
```
155+
128156
## Outputs
129157

130158
### `result`

action.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ inputs:
3131
Output each property of the object as JSON.
3232
Default: 'false'.
3333
default: 'false'
34+
loop:
35+
description: |
36+
If `loop` is defined, then the action is repeated as many times as the number of rows in the variable's content.
37+
All found mentions of `{{ item }}` in the pattern are replaced with a row from the `loop` variable.
38+
The iterative execution of the action returns the object as JSON, where the key contains a row,
39+
and the value is the result of the execution of the action according to the pattern.
40+
Also it has output for each row as a serialized key with value in JSON if `output_properties` is enabled.
3441
outputs:
3542
result:
3643
description: Merged configuration as JSON or plain text.

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/licenses.txt

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,31 @@ The above copyright notice and this permission notice shall be included in all c
1010

1111
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1212

13+
@actions/http-client
14+
MIT
15+
Actions Http Client for Node.js
16+
17+
Copyright (c) GitHub, Inc.
18+
19+
All rights reserved.
20+
21+
MIT License
22+
23+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
24+
associated documentation files (the "Software"), to deal in the Software without restriction,
25+
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
26+
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
27+
subject to the following conditions:
28+
29+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
30+
31+
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
32+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
33+
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
34+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
35+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36+
37+
1338
@vercel/ncc
1439
MIT
1540
Copyright 2018 ZEIT, Inc.
@@ -316,6 +341,44 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
316341
THE SOFTWARE.
317342

318343

344+
tunnel
345+
MIT
346+
The MIT License (MIT)
347+
348+
Copyright (c) 2012 Koichi Kobayashi
349+
350+
Permission is hereby granted, free of charge, to any person obtaining a copy
351+
of this software and associated documentation files (the "Software"), to deal
352+
in the Software without restriction, including without limitation the rights
353+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
354+
copies of the Software, and to permit persons to whom the Software is
355+
furnished to do so, subject to the following conditions:
356+
357+
The above copyright notice and this permission notice shall be included in
358+
all copies or substantial portions of the Software.
359+
360+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
361+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
362+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
363+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
364+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
365+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
366+
THE SOFTWARE.
367+
368+
369+
uuid
370+
MIT
371+
The MIT License (MIT)
372+
373+
Copyright (c) 2010-2020 Robert Kieffer and other contributors
374+
375+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
376+
377+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
378+
379+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
380+
381+
319382
wrappy
320383
ISC
321384
The ISC License

lib/action.js

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const logWrapper = require('./log')
22
const { getFiles, getLevels } = require('./levels')
33
const { getType, mergeLevels } = require('./merge')
4+
const { modifyPattern } = require('./parser')
45

56
function configLevels(patterns, options, log = console) {
67
const files = getFiles(patterns)
@@ -9,8 +10,12 @@ function configLevels(patterns, options, log = console) {
910
return mergeLevels(levels, options, log)
1011
}
1112

13+
function safeKey(key) {
14+
return key.replace(/[^\w_-]+/gu, '_')
15+
}
16+
1217
function setOutputAndPrint(core, key, value) {
13-
const outputKey = key.replace(/[^\w_-]+/gu, '_')
18+
const outputKey = safeKey(key)
1419
if (outputKey.match(/^[a-zA-Z_]/u) === null) {
1520
core.warning(`Can't set output key "${outputKey}". Name of output key must start with a letter or _.`)
1621
return
@@ -28,7 +33,8 @@ function setOutputAndPrint(core, key, value) {
2833
function run(core) {
2934
const log = logWrapper(core)
3035
const patterns = core.getInput('patterns', {required: true})
31-
const outputProperties = core.getInput('output_properties')
36+
const outputProperties = core.getBooleanInput('output_properties')
37+
const loop = core.getMultilineInput('loop')
3238
const options = {
3339
mergeObject: core.getInput('merge_object'),
3440
mergeArray: core.getInput('merge_array'),
@@ -48,17 +54,44 @@ function run(core) {
4854
return
4955
}
5056

51-
core.info('patterns:')
52-
core.info(patterns)
57+
let result
5358

54-
const result = configLevels(patterns, options, log)
59+
/* TODO: Loop it using external action (action-loop) based on
60+
* - https://github.com/nektos/act/blob/master/pkg/runner/step_action_remote.go
61+
* - https://github.com/cardinalby/github-action-ts-run-api
62+
*/
63+
if (loop.length) {
64+
result = {}
65+
66+
for (const item of loop) {
67+
core.startGroup(`Pattern processing with '${item}' item`)
68+
const modifiedPattern = modifyPattern(patterns, item)
69+
const resultValue = processingLevels(core, modifiedPattern, options, log)
70+
if (resultValue === null) {
71+
core.endGroup()
72+
continue
73+
}
74+
75+
result[item] = resultValue
76+
77+
if (outputProperties) {
78+
setOutputAndPrint(core, item, resultValue)
79+
}
80+
core.endGroup()
81+
}
82+
83+
setOutputAndPrint(core, 'result', result)
84+
85+
return
86+
}
87+
88+
result = processingLevels(core, patterns, options, log)
5589
if (result === null) {
56-
core.info('Nothing to output.')
5790
return
5891
}
5992

6093
const resultType = getType([result])
61-
if (outputProperties === 'true' && resultType === 'object') {
94+
if (outputProperties && resultType === 'object') {
6295
for (const [key, value] of Object.entries(result)) {
6396
setOutputAndPrint(core, key, value)
6497
}
@@ -67,6 +100,18 @@ function run(core) {
67100
setOutputAndPrint(core, 'result', result)
68101
}
69102

103+
function processingLevels(core, patterns, options, log) {
104+
core.info('patterns:')
105+
core.info(patterns)
106+
107+
const result = configLevels(patterns, options, log)
108+
if (result === null) {
109+
core.info('Nothing to output.')
110+
}
111+
112+
return result
113+
}
114+
70115
module.exports = {
71116
configLevels,
72117
run

lib/parser.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
function modifyPattern(patterns, item) {
3+
const reItem = /\{\{(.*?)\}\}/
4+
const patternContent = patterns.split('\n')
5+
const options = {item: item}
6+
7+
for (let i = 0; i < patternContent.length; i++) {
8+
for (let match = patternContent[i].match(reItem), result; match;) {
9+
result = options[match[1].trim()]
10+
patternContent[i] = patternContent[i].replace(match[0], result ? result : '')
11+
match = patternContent[i].match(reItem)
12+
}
13+
}
14+
15+
return patternContent.join('\n')
16+
}
17+
18+
module.exports = {
19+
modifyPattern
20+
}

test/parser.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const parser = require('../lib/parser')
2+
3+
describe('test modifyPattern()', () => {
4+
test('expect modifed pattern in several rows', () => {
5+
expect(parser.modifyPattern('- test/{{ item }}/**/foo.yml\n- test/{{ item }}/**/boo.yml', 'a'))
6+
.toEqual('- test/a/**/foo.yml\n- test/a/**/boo.yml')
7+
})
8+
test('expect several modification in one row', () => {
9+
expect(parser.modifyPattern('- test/{{item}}/**/{{item}}/foo.yml', 'a')).toEqual('- test/a/**/a/foo.yml')
10+
})
11+
test('expect brackets to be removed if the variable does not exist', () => {
12+
expect(parser.modifyPattern('- test/{{ boo }}/**/foo.yml', 'a')).toEqual('- test//**/foo.yml')
13+
})
14+
})

yarn.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33

44

55
"@actions/core@^1.9.1":
6-
version "1.9.1"
7-
resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.9.1.tgz#97c0201b1f9856df4f7c3a375cdcdb0c2a2f750b"
8-
integrity sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==
6+
version "1.10.0"
7+
resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.10.0.tgz#44551c3c71163949a2f06e94d9ca2157a0cfac4f"
8+
integrity sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==
99
dependencies:
1010
"@actions/http-client" "^2.0.1"
1111
uuid "^8.3.2"

0 commit comments

Comments
 (0)