8383 required : false
8484 default : " ."
8585 container :
86- description : " Docker container image to run CI steps in. When specified, steps will execute inside this container instead of checking out code. The container should have the project code and dependencies pre-installed."
86+ description : |
87+ Container configuration to run CI steps in.
88+ Accepts either a string (container image name) or a JSON object with container options.
89+
90+ String format (simple):
91+ ```
92+ container: "node:18"
93+ ```
94+
95+ JSON object format (advanced):
96+ ```json
97+ {
98+ "image": "node:18",
99+ "env": {
100+ "NODE_ENV": "production"
101+ },
102+ "ports": [8080],
103+ "volumes": ["/tmp:/tmp"],
104+ "options": "--cpus 2"
105+ }
106+ ```
107+
108+ All properties from GitHub's container specification are supported except credentials (use secrets instead).
109+ See https://docs.github.com/en/actions/how-tos/write-workflows/choose-where-workflows-run/run-jobs-in-a-container
110+
111+ When specified, steps will execute inside this container instead of checking out code.
112+ The container should have the project code and dependencies pre-installed.
87113 type : string
88114 required : false
89115 default : " "
105131permissions : {}
106132
107133jobs :
134+ parse-container :
135+ name : 📦 Parse Container Configuration
136+ if : inputs.container != ''
137+ runs-on : ${{ inputs.runs-on && fromJson(inputs.runs-on) || 'ubuntu-latest' }}
138+ permissions : {}
139+ outputs :
140+ config : ${{ steps.parse.outputs.config }}
141+ steps :
142+ - id : parse
143+ uses : actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
144+ env :
145+ CONTAINER_INPUT : ${{ inputs.container }}
146+ with :
147+ script : |
148+ const containerInput = process.env.CONTAINER_INPUT.trim();
149+
150+ // Check if input is a JSON object or a simple string
151+ const isJson = containerInput.startsWith('{');
152+
153+ let config = {
154+ image: '',
155+ options: '--user root:root'
156+ };
157+
158+ if (isJson) {
159+ try {
160+ const container = JSON.parse(containerInput);
161+
162+ // Set image
163+ config.image = container.image || '';
164+
165+ // Add env if provided
166+ if (container.env && Object.keys(container.env).length > 0) {
167+ config.env = container.env;
168+ }
169+
170+ // Add ports if provided
171+ if (container.ports && container.ports.length > 0) {
172+ config.ports = container.ports;
173+ }
174+
175+ // Add volumes if provided
176+ if (container.volumes && container.volumes.length > 0) {
177+ config.volumes = container.volumes;
178+ }
179+
180+ // Merge user options with default --user root:root
181+ if (container.options) {
182+ config.options = `${config.options} ${container.options}`;
183+ }
184+ } catch (error) {
185+ core.setFailed(`Failed to parse container input as JSON: ${error.message}`);
186+ return;
187+ }
188+ } else {
189+ // Simple string format - just the image name
190+ config.image = containerInput;
191+ }
192+
193+ core.setOutput('config', JSON.stringify(config));
194+
108195 code-ql :
109196 name : 🛡️ CodeQL Analysis
110197 if : inputs.checks == true && inputs.code-ql != ''
@@ -131,10 +218,9 @@ jobs:
131218 setup :
132219 name : ⚙️ Setup
133220 runs-on : ${{ inputs.runs-on && fromJson(inputs.runs-on) || 'ubuntu-latest' }}
134- container :
135- image : ${{ inputs.container != '' && inputs.container || null }}
136- # Root user is required to use GitHub Actions features inside the container
137- options : --user root:root
221+ container : ${{ inputs.container != '' && fromJSON(needs.parse-container.outputs.config) || null }}
222+ needs : parse-container
223+ if : ${{ always() && !cancelled() && !failure() }}
138224 permissions :
139225 contents : read
140226 # FIXME: This is a workaround for having workflow ref. See https://github.com/orgs/community/discussions/38659
@@ -248,13 +334,12 @@ jobs:
248334
249335 lint :
250336 name : 👕 Lint
251- if : inputs.checks == true && inputs.lint
337+ if : inputs.checks == true && inputs.lint && always() && !cancelled() && !failure()
252338 runs-on : ${{ inputs.runs-on && fromJson(inputs.runs-on) || 'ubuntu-latest' }}
253- container :
254- image : ${{ inputs.container != '' && inputs.container || null }}
255- # Root user is required to use GitHub Actions features inside the container
256- options : --user root:root
257- needs : setup
339+ container : ${{ inputs.container != '' && fromJSON(needs.parse-container.outputs.config) || null }}
340+ needs :
341+ - parse-container
342+ - setup
258343 # jscpd:ignore-start
259344 permissions :
260345 contents : read
@@ -304,14 +389,13 @@ jobs:
304389
305390 build :
306391 name : 🏗️ Build
307- if : inputs.checks == true
392+ if : inputs.checks == true && always() && !cancelled() && !failure()
308393 runs-on : ${{ inputs.runs-on && fromJson(inputs.runs-on) || 'ubuntu-latest' }}
309394 # jscpd:ignore-start
310- container :
311- image : ${{ inputs.container != '' && inputs.container || null }}
312- # Root user is required to use GitHub Actions features inside the container
313- options : --user root:root
314- needs : setup
395+ container : ${{ inputs.container != '' && fromJSON(needs.parse-container.outputs.config) || null }}
396+ needs :
397+ - parse-container
398+ - setup
315399 permissions :
316400 contents : read
317401 # FIXME: This is a workaround for having workflow ref. See https://github.com/orgs/community/discussions/38659
@@ -352,13 +436,11 @@ jobs:
352436
353437 test :
354438 name : 🧪 Test
355- if : inputs.checks == true && inputs.test
439+ if : inputs.checks == true && inputs.test && always() && !cancelled() && !failure()
356440 runs-on : ${{ inputs.runs-on && fromJson(inputs.runs-on) || 'ubuntu-latest' }}
357- container :
358- image : ${{ inputs.container != '' && inputs.container || null }}
359- # Root user is required to use GitHub Actions features inside the container
360- options : --user root:root
441+ container : ${{ inputs.container != '' && fromJSON(needs.parse-container.outputs.config) || null }}
361442 needs :
443+ - parse-container
362444 - setup
363445 - build
364446 permissions :
0 commit comments