Skip to content

Commit ab7f3a1

Browse files
Add ignoreTags support to exclude old versions from packaging (#118)
* Add ignoreTags support to exclude old versions from packaging Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> * Add ignoreTags support to add-action and update-action scripts Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> * Fix JSDoc typo and add regex validation for ignore-tags patterns Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> * Simplify --ignore-tags to accept version prefixes instead of regex patterns Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> * Add helper script to add ignoreTags to existing actions and fix JSON syntax in README Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> * Remove --all flag from add-ignore-tags.sh, require specific action Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
1 parent 4044787 commit ab7f3a1

File tree

7 files changed

+205
-7
lines changed

7 files changed

+205
-7
lines changed

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,39 @@ Preview versions are intentionally excluded. For example: `v2-beta`
1515

1616
Optional args may be supplied to control which refs are included. See `script/add-action.sh --help` for more info.
1717

18+
### Ignoring old versions
19+
20+
To exclude certain old version tags from being packaged, add an `ignoreTags` array to the action config JSON file. Each entry is a regex pattern that will be tested against tag names.
21+
22+
**When adding a new action**, use the `--ignore-tags` option with simple version prefixes:
23+
24+
```bash
25+
./script/add-action.sh --ignore-tags "v1,v2" actions/checkout
26+
```
27+
28+
This will automatically generate regex patterns that match `v1`, `v1.x`, `v2`, `v2.x`, etc.
29+
30+
**For existing actions**, use the helper script to add ignore tags:
31+
32+
```bash
33+
./script/add-ignore-tags.sh --ignore-tags "v1,v2" actions/checkout
34+
```
35+
36+
Or add `ignoreTags` directly to the JSON config file:
37+
38+
```json
39+
{
40+
"owner": "actions",
41+
"repo": "checkout",
42+
"ignoreTags": [
43+
"^v1(\\..*)?$",
44+
"^v2(\\..*)?$"
45+
]
46+
}
47+
```
48+
49+
Tags matching any of the patterns will be excluded from the generated scripts while remaining in the config for historical reference. The `ignoreTags` field is preserved when running `update-action.sh`.
50+
1851
### How to use this in the self-hosted runner?
1952

2053
Please read the doc @kenmuse has put together at: https://www.kenmuse.com/blog/building-github-actions-runner-images-with-an-action-archive-cache/

script/add-ignore-tags.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
script_dir="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
6+
7+
# Minimum node version
8+
$script_dir/internal/check-node.sh
9+
10+
# Add ignore tags to the action
11+
node "$script_dir/internal/add-ignore-tags.js" $*
12+
13+
# Regenerate action scripts
14+
$script_dir/internal/generate-scripts.sh

script/internal/action-config.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ class ActionConfig {
2626
*/
2727
patterns = []
2828

29+
/**
30+
* Tag patterns to ignore during packaging
31+
* @type {string[]|undefined}
32+
*/
33+
ignoreTags = undefined
34+
2935
/**
3036
* Branch versions (ref to commit SHA)
3137
* @type {{[ref: string]: string}}
@@ -63,12 +69,13 @@ exports.TagVersion = TagVersion
6369
/**
6470
* Adds a new action config file
6571
* @param {string} owner
66-
* @param {string} repos
72+
* @param {string} repo
6773
* @param {string[]} patternStrings
6874
* @param {string} defaultBranch
75+
* @param {string[]|undefined} ignoreTags
6976
* @returns {Promise}
7077
*/
71-
async function add(owner, repo, patternStrings, defaultBranch) {
78+
async function add(owner, repo, patternStrings, defaultBranch, ignoreTags) {
7279
assert.ok(owner, "Arg 'owner' must not be empty")
7380
assert.ok(repo, "Arg 'repo' must not be empty")
7481
assert.ok(patternStrings, "Arg 'patternStrings' must not be null")
@@ -84,6 +91,9 @@ async function add(owner, repo, patternStrings, defaultBranch) {
8491
config.owner = owner
8592
config.repo = repo
8693
config.patterns = patternStrings
94+
if (ignoreTags && ignoreTags.length > 0) {
95+
config.ignoreTags = ignoreTags
96+
}
8797
config.defaultBranch = defaultBranch
8898

8999
const tempDir = path.join(paths.temp, `${owner}_${repo}`)

script/internal/add-action.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ async function main() {
1414
const repo = args.repo
1515
const patterns = args.patterns
1616
const defaultBranch = args.defaultBranch || 'master'
17+
const ignoreTags = args.ignoreTags
1718

1819
// File exists?
1920
const file = actionConfig.getFilePath(owner, repo)
@@ -23,7 +24,7 @@ async function main() {
2324
await fsHelper.reinitTemp()
2425

2526
// Add the config
26-
await actionConfig.add(owner, repo, patterns, defaultBranch)
27+
await actionConfig.add(owner, repo, patterns, defaultBranch, ignoreTags)
2728
}
2829
catch (err) {
2930
// Help
@@ -50,6 +51,7 @@ class Args {
5051
repo = ''
5152
patterns = []
5253
defaultBranch = ''
54+
ignoreTags = []
5355
}
5456

5557
/**
@@ -58,7 +60,7 @@ class Args {
5860
*/
5961
function getArgs() {
6062
// Parse
61-
const parsedArgs = argHelper.parse([], ['default-branch'])
63+
const parsedArgs = argHelper.parse([], ['default-branch', 'ignore-tags'])
6264
if (parsedArgs.arguments.length < 1) {
6365
argHelper.throwError('Expected at least one arg')
6466
}
@@ -81,17 +83,32 @@ function getArgs() {
8183
}
8284
}
8385

86+
// Parse ignore-tags (comma-separated version prefixes like v1,v2)
87+
// These are converted to regex patterns that match the version and all its sub-versions
88+
let ignoreTags = []
89+
if (parsedArgs.options['ignore-tags']) {
90+
const prefixes = parsedArgs.options['ignore-tags'].split(',').map(t => t.trim()).filter(t => t)
91+
for (const prefix of prefixes) {
92+
// Convert simple version prefix like "v1" to regex pattern "^v1(\\..*)?$"
93+
// This matches "v1", "v1.0", "v1.0.0", etc.
94+
const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
95+
ignoreTags.push(`^${escapedPrefix}(\\..*)?$`)
96+
}
97+
}
98+
8499
return {
85100
owner: splitNwo[0],
86101
repo: splitNwo[1],
87102
patterns: patterns,
88-
defaultBranch: parsedArgs.options['default-branch']
103+
defaultBranch: parsedArgs.options['default-branch'],
104+
ignoreTags: ignoreTags
89105
}
90106
}
91107

92108
function printUsage() {
93-
console.error('USAGE: add-action.sh [--default-branch branch] nwo [(+|-)regexp [...]]')
109+
console.error('USAGE: add-action.sh [--default-branch branch] [--ignore-tags versions] nwo [(+|-)regexp [...]]')
94110
console.error(` --default-branch Default branch name. For example: master`)
111+
console.error(` --ignore-tags Comma-separated version prefixes to ignore. For example: v1,v2`)
95112
console.error(` nwo Name with owner. For example: actions/checkout`)
96113
console.error(` regexp Refs to include or exclude. Default: ${actionConfig.defaultPatterns.join(' ')}`)
97114
}

script/internal/add-ignore-tags.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
const actionConfig = require('./action-config')
2+
const argHelper = require('./arg-helper')
3+
const debugHelper = require('./debug-helper')
4+
const fs = require('fs')
5+
6+
async function main() {
7+
try {
8+
// Command line args
9+
const args = getArgs()
10+
11+
// Get the action config file
12+
const file = actionConfig.getFilePath(args.owner, args.repo)
13+
debugHelper.debug(`file: ${file}`)
14+
15+
// Load the config
16+
const config = await actionConfig.loadFromPath(file)
17+
18+
// Add ignore tags
19+
if (!config.ignoreTags) {
20+
config.ignoreTags = []
21+
}
22+
23+
// Add new patterns (avoid duplicates)
24+
for (const pattern of args.ignoreTags) {
25+
if (!config.ignoreTags.includes(pattern)) {
26+
config.ignoreTags.push(pattern)
27+
}
28+
}
29+
30+
// Write config back
31+
await fs.promises.writeFile(file, JSON.stringify(config, null, ' '))
32+
console.log(`Updated config file: ${file}`)
33+
console.log(` ignoreTags: ${JSON.stringify(config.ignoreTags)}`)
34+
}
35+
catch (err) {
36+
// Help
37+
if (err.code === argHelper.helpCode) {
38+
printUsage()
39+
return
40+
}
41+
42+
// Arg error?
43+
if (err.code === argHelper.errorCode) {
44+
printUsage()
45+
console.error('')
46+
}
47+
48+
// Print error
49+
debugHelper.debug(err.stack)
50+
console.error(`ERROR: ${err.message}`)
51+
process.exitCode = 1
52+
}
53+
}
54+
55+
class Args {
56+
owner = ''
57+
repo = ''
58+
ignoreTags = []
59+
}
60+
61+
/**
62+
* Get the command line args
63+
* @returns {Args}
64+
*/
65+
function getArgs() {
66+
const parsedArgs = argHelper.parse([], ['ignore-tags'])
67+
const result = new Args()
68+
69+
// Validate ignore-tags is provided
70+
if (!parsedArgs.options['ignore-tags']) {
71+
argHelper.throwError('--ignore-tags is required')
72+
}
73+
74+
// Parse ignore-tags (comma-separated version prefixes like v1,v2)
75+
const prefixes = parsedArgs.options['ignore-tags'].split(',').map(t => t.trim()).filter(t => t)
76+
for (const prefix of prefixes) {
77+
// Convert simple version prefix like "v1" to regex pattern "^v1(\\..*)?$"
78+
// This matches "v1", "v1.0", "v1.0.0", etc.
79+
const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
80+
result.ignoreTags.push(`^${escapedPrefix}(\\..*)?$`)
81+
}
82+
83+
// Validate exactly one arg
84+
if (parsedArgs.arguments.length !== 1) {
85+
argHelper.throwError('Expected exactly one arg (nwo)')
86+
}
87+
88+
const nwo = parsedArgs.arguments[0]
89+
const splitNwo = nwo.split('/')
90+
if (splitNwo.length !== 2 || !splitNwo[0] || !splitNwo[1]) {
91+
argHelper.throwError(`Invalid nwo '${nwo}'`)
92+
}
93+
result.owner = splitNwo[0]
94+
result.repo = splitNwo[1]
95+
96+
return result
97+
}
98+
99+
function printUsage() {
100+
console.error('USAGE: add-ignore-tags.sh --ignore-tags versions nwo')
101+
console.error(` --ignore-tags Comma-separated version prefixes to ignore. For example: v1,v2`)
102+
console.error(` nwo Name with owner. For example: actions/checkout`)
103+
}
104+
105+
main()

script/internal/generate-scripts.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ for json_file in $script_dir/../../config/actions/*.json; do
4040
curl_download_commands+=("curl -s -S -L -o '$sha.zip' 'https://api.github.com/repos/$owner/$repo/zipball/$sha'")
4141
done
4242

43+
# Get an array of ignoreTags patterns (if present)
44+
ignore_patterns=()
45+
IFS=$'\n' read -r -d '' -a ignore_patterns < <( echo "$json" | jq --raw-output '.ignoreTags // [] | .[]' && printf '\0' )
46+
4347
# Get an array of tag info. Each item contains "<tag> <commit_sha>"
4448
tag_info=()
4549
IFS=$'\n' read -r -d '' -a tag_info < <( echo "$json" | jq --raw-output '.tags | to_entries | .[] | .key + " " + .value.commit' && printf '\0' )
@@ -49,6 +53,20 @@ for json_file in $script_dir/../../config/actions/*.json; do
4953
tag="${split[0]}"
5054
sha="${split[1]}"
5155

56+
# Check if the tag matches any ignore pattern
57+
skip_tag=false
58+
for pattern in "${ignore_patterns[@]}"; do
59+
if [[ "$tag" =~ $pattern ]]; then
60+
echo "Ignoring tag '$tag' (matches pattern '$pattern')"
61+
skip_tag=true
62+
break
63+
fi
64+
done
65+
66+
if [ "$skip_tag" = true ]; then
67+
continue
68+
fi
69+
5270
# Append curl download command
5371
curl_download_commands+=("curl -s -S -L -o '$sha.tar.gz' 'https://api.github.com/repos/$owner/$repo/tarball/$sha'")
5472
curl_download_commands+=("curl -s -S -L -o '$sha.zip' 'https://api.github.com/repos/$owner/$repo/zipball/$sha'")

script/internal/update-action.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ async function main() {
2222
const repo = config.repo
2323
const patterns = config.patterns
2424
const defaultBranch = config.defaultBranch
25+
const ignoreTags = config.ignoreTags
2526
assert.ok(patterns && patterns.length, 'Existing patterns must not be empty')
26-
await actionConfig.add(owner, repo, patterns, defaultBranch)
27+
await actionConfig.add(owner, repo, patterns, defaultBranch, ignoreTags)
2728
}
2829
}
2930
catch (err) {

0 commit comments

Comments
 (0)