-
Notifications
You must be signed in to change notification settings - Fork 0
Automate tag bumping, use field in workflow to set when to create new tag #168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
df3fb4b
Automate tag bumping, use field in workflow to set when to create new…
skeet70 c1c6659
Merge branch 'main' into add-tag-automation
skeet70 115a771
Code review changes
skeet70 f570d26
Make the rust example testable so the comment on PRs is green
skeet70 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| #!/bin/bash | ||
|
|
||
| # Lists the floating version tags that need to be created or moved so that every | ||
| # reusable workflow's and composite action's declared version tag matches its current | ||
| # content. See RELEASING.md and move-tags.yaml for details. | ||
| # | ||
| # Each reusable workflow and composite action declares its major version in a | ||
| # "# tag-version: vN" comment in its yaml file. For each declaration, the tag "NAME-vN" | ||
| # is printed on stdout if it doesn't exist, or if the workflow's files (the yaml file | ||
| # plus any ".github/NAME.*.sh" helper scripts, or the action's directory) differ | ||
| # between the tag and HEAD. | ||
| # | ||
| # A reusable workflow or action without a usable declaration is a misconfiguration: | ||
| # it's reported on stderr and the script exits nonzero, after checking everything. | ||
| # This repo's own CI workflows don't need declarations and are skipped silently. | ||
| # | ||
| # Must be run from the repo root, with HEAD at the commit tags should move to and all | ||
| # existing tags fetched. The caller is expected to `git tag -f` and force push each | ||
| # printed tag. | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| if [ "$#" -ne 0 ] ; then | ||
| echo "Usage: $0" 1>&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| TAGS=() | ||
| ERRORS=0 | ||
|
|
||
| # Check one workflow or action. Arguments are its name, the yaml file declaring its | ||
| # version, and the pathspecs that make up its content. Appends to TAGS and sets ERRORS. | ||
| check() { | ||
| local name="$1" | ||
| local vfile="$2" | ||
| shift 2 | ||
|
|
||
| local version | ||
| version=$(grep -E -o '^# tag-version: v[0-9]+' "$vfile" | head -1 | grep -E -o '[0-9]+$' || true) | ||
| if [ -z "$version" ] ; then | ||
| # Only reusable workflows and actions need a version; this repo's own CI doesn't. | ||
| if [[ "$vfile" == .github/actions/* ]] || grep -q workflow_call "$vfile" ; then | ||
| echo "❌ $name: no '# tag-version: vN' comment in $vfile; it will never be released" 1>&2 | ||
| ERRORS=1 | ||
| fi | ||
| return 0 | ||
| fi | ||
|
|
||
| # Never touch a tag older than the newest existing one. That means the comment is | ||
| # stale or was decremented, and moving the old tag would break its consumers. | ||
| local newest="" | ||
| local t n | ||
| while IFS= read -r t ; do | ||
| n="${t#"$name"-v}" | ||
| [[ "$n" =~ ^[0-9]+$ ]] || continue | ||
| if [ -z "$newest" ] || [ "$n" -gt "$newest" ] ; then | ||
| newest="$n" | ||
| fi | ||
| done < <(git tag --list "$name-v*") | ||
| if [ -n "$newest" ] && [ "$newest" -gt "$version" ] ; then | ||
| echo "❌ $name: tag $name-v$newest exists but $vfile declares v$version; fix the comment" 1>&2 | ||
| ERRORS=1 | ||
| return 0 | ||
| fi | ||
|
|
||
| local tag="$name-v$version" | ||
| if ! git rev-parse -q --verify "refs/tags/$tag" > /dev/null ; then | ||
| TAGS+=("$tag") | ||
| elif ! git diff --quiet "refs/tags/$tag" HEAD -- "$@" ; then | ||
| TAGS+=("$tag") | ||
| fi | ||
| } | ||
|
|
||
| check_all() { | ||
| local f d base name vfile | ||
| for f in .github/workflows/*.yaml .github/workflows/*.yml ; do | ||
| [ -f "$f" ] || continue | ||
| base=$(basename "$f") | ||
| name="${base%.*}" | ||
| check "$name" "$f" "$f" ".github/$name.*.sh" | ||
| done | ||
| for d in .github/actions/*/ ; do | ||
| [ -d "$d" ] || continue | ||
| name=$(basename "$d") | ||
| vfile="" | ||
| for f in "$d"action.yaml "$d"action.yml ; do | ||
| [ -f "$f" ] && vfile="$f" | ||
| done | ||
| [ -z "$vfile" ] && continue | ||
| check "$name" "$vfile" "$d" | ||
| done | ||
| } | ||
|
|
||
| check_all | ||
| if [ "${#TAGS[@]}" -gt 0 ] ; then | ||
| printf '%s\n' "${TAGS[@]}" | sort | ||
| fi | ||
| exit "$ERRORS" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,203 @@ | ||
| Describe 'move-tags.list.sh' | ||
| SCRIPT="$PWD/move-tags.list.sh" | ||
|
|
||
| cleanup() { | ||
| if [ -n "${SANDBOX:-}" ] ; then | ||
| cd / | ||
| rm -rf "$SANDBOX" | ||
| fi | ||
| } | ||
| After 'cleanup' | ||
|
|
||
| # Create a sandbox git repo to run the script in. | ||
| setup_repo() { | ||
| SANDBOX=$(mktemp -d) | ||
| cd "$SANDBOX" || return 1 | ||
| export GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null \ | ||
| GIT_AUTHOR_NAME=spec GIT_AUTHOR_EMAIL=spec@example.com \ | ||
| GIT_COMMITTER_NAME=spec GIT_COMMITTER_EMAIL=spec@example.com | ||
| git init -q -b main . | ||
| mkdir -p .github/workflows | ||
| } | ||
|
|
||
| commit_all() { | ||
| git add -A . | ||
| git commit -q -m 'commit' | ||
| } | ||
|
|
||
| # A minimal reusable workflow declaring the given tag-version. | ||
| reusable_workflow() { | ||
| printf '# tag-version: %s\non:\n workflow_call:\n' "$1" | ||
| } | ||
|
|
||
| new_workflow() { | ||
| setup_repo | ||
| reusable_workflow v1 > .github/workflows/foo.yaml | ||
| commit_all | ||
| "$SCRIPT" | ||
| } | ||
| It 'creates the tag for a new workflow' | ||
| When call new_workflow | ||
| The output should equal 'foo-v1' | ||
| The status should be success | ||
| End | ||
|
|
||
| up_to_date_workflow() { | ||
| setup_repo | ||
| reusable_workflow v1 > .github/workflows/foo.yaml | ||
| commit_all | ||
| git tag foo-v1 | ||
| "$SCRIPT" | ||
| } | ||
| It 'is quiet when the tag matches the content' | ||
| When call up_to_date_workflow | ||
| The output should equal '' | ||
| The status should be success | ||
| End | ||
|
|
||
| changed_workflow() { | ||
| setup_repo | ||
| reusable_workflow v1 > .github/workflows/foo.yaml | ||
| commit_all | ||
| git tag foo-v1 | ||
| printf '# a change\n' >> .github/workflows/foo.yaml | ||
| commit_all | ||
| "$SCRIPT" | ||
| } | ||
| It 'moves the tag when the workflow file changed' | ||
| When call changed_workflow | ||
| The output should equal 'foo-v1' | ||
| The status should be success | ||
| End | ||
|
|
||
| unrelated_change() { | ||
| setup_repo | ||
| reusable_workflow v1 > .github/workflows/foo.yaml | ||
| commit_all | ||
| git tag foo-v1 | ||
| printf 'docs\n' > README.md | ||
| commit_all | ||
| "$SCRIPT" | ||
| } | ||
| It 'ignores changes outside the workflow files' | ||
| When call unrelated_change | ||
| The output should equal '' | ||
| The status should be success | ||
| End | ||
|
|
||
| changed_helper_script() { | ||
| setup_repo | ||
| reusable_workflow v1 > .github/workflows/foo.yaml | ||
| commit_all | ||
| git tag foo-v1 | ||
| printf 'echo hi\n' > .github/foo.build.sh | ||
| commit_all | ||
| "$SCRIPT" | ||
| } | ||
| It 'moves the tag when a helper script changed' | ||
| When call changed_helper_script | ||
| The output should equal 'foo-v1' | ||
| The status should be success | ||
| End | ||
|
|
||
| major_bump() { | ||
| setup_repo | ||
| reusable_workflow v1 > .github/workflows/foo.yaml | ||
| commit_all | ||
| git tag foo-v1 | ||
| reusable_workflow v2 > .github/workflows/foo.yaml | ||
| commit_all | ||
| "$SCRIPT" | ||
| } | ||
| It 'creates the new tag on a major bump, leaving the old one alone' | ||
| When call major_bump | ||
| The output should equal 'foo-v2' | ||
| The status should be success | ||
| End | ||
|
|
||
| decremented_version() { | ||
| setup_repo | ||
| reusable_workflow v2 > .github/workflows/foo.yaml | ||
| commit_all | ||
| git tag foo-v2 | ||
| reusable_workflow v1 > .github/workflows/foo.yaml | ||
| commit_all | ||
| "$SCRIPT" | ||
| } | ||
| It 'fails rather than move a tag older than the newest existing one' | ||
| When call decremented_version | ||
| The output should equal '' | ||
| The stderr should not equal '' | ||
| The status should be failure | ||
| End | ||
|
|
||
| reusable_without_version() { | ||
| setup_repo | ||
| printf 'on:\n workflow_call:\n' > .github/workflows/foo.yaml | ||
| commit_all | ||
| "$SCRIPT" | ||
| } | ||
| It 'fails for a reusable workflow with no tag-version comment' | ||
| When call reusable_without_version | ||
| The output should equal '' | ||
| The stderr should not equal '' | ||
| The status should be failure | ||
| End | ||
|
|
||
| good_and_bad_workflows() { | ||
| setup_repo | ||
| reusable_workflow v1 > .github/workflows/foo.yaml | ||
| printf 'on:\n workflow_call:\n' > .github/workflows/bad.yaml | ||
| commit_all | ||
| "$SCRIPT" | ||
| } | ||
| It 'still lists good tags while failing on a misconfigured workflow' | ||
| When call good_and_bad_workflows | ||
| The output should equal 'foo-v1' | ||
| The stderr should include 'bad' | ||
| The status should be failure | ||
| End | ||
|
|
||
| repo_ci_without_version() { | ||
| setup_repo | ||
| printf 'on:\n push:\n' > .github/workflows/repo-ci.yaml | ||
| commit_all | ||
| "$SCRIPT" | ||
| } | ||
| It 'silently skips non-reusable workflows with no tag-version comment' | ||
| When call repo_ci_without_version | ||
| The output should equal '' | ||
| The stderr should equal '' | ||
| The status should be success | ||
| End | ||
|
|
||
| changed_action() { | ||
| setup_repo | ||
| mkdir -p .github/actions/tsp | ||
| printf '# tag-version: v1\nname: tsp\n' > .github/actions/tsp/action.yaml | ||
| commit_all | ||
| git tag tsp-v1 | ||
| printf 'echo hi\n' > .github/actions/tsp/helper.sh | ||
| commit_all | ||
| "$SCRIPT" | ||
| } | ||
| It 'moves the tag when any file in a composite action changed' | ||
| When call changed_action | ||
| The output should equal 'tsp-v1' | ||
| The status should be success | ||
| End | ||
|
|
||
| multiple_workflows() { | ||
| setup_repo | ||
| reusable_workflow v1 > .github/workflows/foo.yaml | ||
| reusable_workflow v2 > .github/workflows/bar.yaml | ||
| commit_all | ||
| "$SCRIPT" | ||
| } | ||
| It 'prints multiple tags sorted' | ||
| When call multiple_workflows | ||
| The line 1 of output should equal 'bar-v2' | ||
| The line 2 of output should equal 'foo-v1' | ||
| The status should be success | ||
| End | ||
| End |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| # tag-version: v1 | ||
| name: Get TSP ref | ||
|
|
||
| on: | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How come this changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something changed with a new version of gawk or difference between the CI version and local, and it was causing the shellcheck stuff to fail. This way avoids extra tool drift. I need to triple check the
10#bit there.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, it's to force it to be decimal, if bash/C reads leading zeros it defaults to octal.