Skip to content

Commit c02a7a6

Browse files
authored
Merge pull request #225 from dscho/manual-test-with-choice
Convert the `manual-test` workflow to allow specifying which runner OS to use
2 parents 9ca996a + 877e019 commit c02a7a6

File tree

2 files changed

+121
-31
lines changed

2 files changed

+121
-31
lines changed

.github/workflows/manual-test.yml

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,73 @@
1-
name: Manual test matrix
2-
on: workflow_dispatch
3-
4-
jobs:
5-
test:
6-
strategy:
7-
fail-fast: false
8-
matrix:
9-
runs-on:
10-
- ubuntu-22.04
1+
name: Manual test
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
runs-on:
6+
type: choice
7+
description: 'The runner pool to run the job on'
8+
required: true
9+
default: ubuntu-24.04
10+
options:
1111
- ubuntu-24.04
12-
- macos-13
13-
- macos-14
12+
- ubuntu-22.04
13+
- macos-15-large
1414
- macos-15
15-
- windows-2019
16-
- windows-2022
15+
- macos-14-large
16+
- macos-14
17+
- macos-13
18+
- macos-13-xlarge
1719
- windows-2025
18-
limit-access-to-actor:
19-
- true
20-
- false
21-
runs-on: ${{ matrix.runs-on }}
20+
- windows-2022
21+
- windows-2019
22+
- windows-11-arm
23+
container-runs-on:
24+
type: choice
25+
description: 'The Docker container to run the job on (this overrides the `runs-on` input)'
26+
required: false
27+
default: '(none)'
28+
options:
29+
- '(none)'
30+
- fedora:latest
31+
- archlinux:latest
32+
- ubuntu:latest
33+
limit-access-to-actor:
34+
type: choice
35+
description: 'Whether to limit access to the actor only'
36+
required: true
37+
default: 'auto'
38+
options:
39+
- auto
40+
- 'true'
41+
- 'false'
42+
43+
jobs:
44+
test:
45+
if: ${{ inputs.container-runs-on == '(none)' }}
46+
runs-on: ${{ inputs.runs-on }}
2247
steps:
48+
- uses: msys2/setup-msys2@v2
49+
# The public preview of GitHub-hosted Windows/ARM64 runners lacks
50+
# a working MSYS2 installation, so we need to set it up ourselves.
51+
if: ${{ inputs.runs-on == 'windows-11-arm' }}
52+
with:
53+
msystem: 'CLANGARM64'
54+
# We cannot use `C:\` because `msys2/setup-msys2` erroneously
55+
# believes that an MSYS2 exists at `C:\msys64`, but it doesn't,
56+
# which is the entire reason why we need to set it up in this
57+
# here step... However, by using `C:\.\` we can fool that
58+
# overzealous check.
59+
location: C:\.\
2360
- uses: actions/checkout@v4
2461
- uses: ./
2562
with:
26-
limit-access-to-actor: ${{ matrix.limit-access-to-actor }}
63+
limit-access-to-actor: ${{ inputs.limit-access-to-actor }}
2764
test-container:
28-
strategy:
29-
fail-fast: false
30-
matrix:
31-
container-runs-on:
32-
- fedora:latest
33-
- archlinux:latest
34-
- ubuntu:latest
35-
limit-access-to-actor:
36-
- true
37-
- false
65+
if: ${{ inputs.container-runs-on != '(none)' }}
3866
runs-on: ubuntu-latest
3967
container:
40-
image: ${{ matrix.container-runs-on }}
68+
image: ${{ inputs.container-runs-on }}
4169
steps:
4270
- uses: actions/checkout@v4
4371
- uses: ./
4472
with:
45-
limit-access-to-actor: ${{ matrix.limit-access-to-actor }}
73+
limit-access-to-actor: ${{ inputs.limit-access-to-actor }}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env node
2+
3+
// Update the `runs-on` options of the `manual-test.yml` workflow file with the
4+
// latest available images from the GitHub Actions runner images README file.
5+
6+
(async () => {
7+
const fs = require('fs')
8+
9+
const readme = await (await fetch("https://github.com/actions/runner-images/raw/HEAD/README.md")).text()
10+
11+
// This will be the first `ubuntu` one.
12+
let defaultOption = ''
13+
14+
const choices = readme
15+
// Get the "Available Images" section
16+
.split(/\n## Available Images\n/)[1]
17+
.split(/##\s*[^#]/)[0]
18+
// Split by lines
19+
.split('\n')
20+
.map(line => {
21+
// The relevant lines are table rows; The first column is the image name,
22+
// the second one contains a relatively free-form list of the `runs-on`
23+
// options that we are interested in. Those `runs-on` options are
24+
// surrounded by backticks.
25+
const match = line.match(/^\|\s*([^|]+)\s*\|([^|]*)`([^`|]+)`\s*\|/)
26+
if (!match) return false // Skip e.g. the table header and empty lines
27+
let runsOn = match[3] // default to the last `runs-on` option
28+
const alternatives = match[2]
29+
.split(/`([^`]*)`/) // split by backticks
30+
.filter((_, i) => (i % 2)) // keep only the text between backticks
31+
.sort((a, b) => a.length - b.length) // order by length
32+
if (alternatives.length > 0 && alternatives[0].length < runsOn.length) runsOn = alternatives[0]
33+
if (!defaultOption && match[3].startsWith('ubuntu-')) defaultOption = runsOn
34+
return runsOn
35+
})
36+
.filter(runsOn => runsOn)
37+
38+
// The Windows/ARM64 runners are in public preview (and for the time being,
39+
// not listed in the `runner-images` README file), so we need to add this
40+
// manually.
41+
if (!choices.includes('windows-11-arm')) choices.push('windows-11-arm')
42+
43+
// Now edit the `manual-test` workflow definition
44+
const ymlPath = `${__dirname}/manual-test.yml`
45+
const yml = fs.readFileSync(ymlPath, 'utf8')
46+
47+
// We want to replace the `runs-on` options and the `default` value. This
48+
// would be easy if there was a built-in YAML parser and renderer in Node.js,
49+
// but there is none. Therefore, we use a regular expression to find certain
50+
// "needles" near the beginning of the file: first `workflow_dispatch:`,
51+
// after that `runs-on:` and then `default:` and `options:`. Then we replace
52+
// the `default` value and the `options` values with the new ones.
53+
const [, beforeDefault, beforeOptions, optionsIndent, afterOptions] =
54+
yml.match(/^([^]*?workflow_dispatch:[^]*?runs-on:[^]*?default:)(?:.*)([^]*?options:)(\n +- )(?:.*)(?:\3.*)*([^]*)/) || []
55+
if (!beforeDefault) throw new Error(`The 'manual-test.yml' file does not match the expected format!`)
56+
const newYML =
57+
`${beforeDefault} ${defaultOption}${[beforeOptions, ...choices].join(optionsIndent)}${afterOptions}`
58+
fs.writeFileSync(ymlPath, newYML)
59+
})().catch(e => {
60+
console.error(e)
61+
process.exitCode = 1
62+
})

0 commit comments

Comments
 (0)