Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ Breaking changes are prefixed with "Breaking Change: ".

### Added

- `autotag-from-changelog` now exposes `tag_created` and `tag` outputs so
callers can react to whether a new tag was pushed.
- `expect_step_output` test helper for asserting GitHub Actions step outputs.
- `get-workflow-ref` action: resolve the ref a caller used to invoke a reusable
workflow by parsing the caller's workflow file — no API calls or extra
permissions needed.
Expand Down
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ sources `test_helpers.sh` and validates behavior.
production scripts. Run each command on its own line so that `bash -e`
(the default for GitHub Actions `run` steps) halts on failure and the
return code is checked automatically.
- Use snake_case for multi-word action output names (e.g., `tag_created`
instead of `tag-created`). Hyphens in output names are parsed as subtraction
in GitHub Actions expressions.
- In workflow YAML files, always look up and use the latest major version of
built-in GitHub Actions (e.g., `actions/checkout`, `actions/upload-artifact`).
Do not rely on memorized version numbers — they go stale.
Expand All @@ -70,3 +73,7 @@ sources `test_helpers.sh` and validates behavior.
on error paths without logging or returning the error. If ignoring an
error is genuinely correct (e.g., best-effort cleanup), add a comment
explaining why it's safe to ignore.

## Commit messages
- If a commit updates or adds a specific action, prefix the commit with that
action.
10 changes: 9 additions & 1 deletion autotag-from-changelog/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ inputs:
changelog-path:
description: Path to the changelog file
default: CHANGELOG.md
outputs:
tag_created:
description: Whether a new tag was created ("true" or "false")
value: ${{ steps.autotag.outputs.tag_created }}
tag:
description: The tag that was created (empty if no tag was created)
value: ${{ steps.autotag.outputs.tag }}
runs:
using: composite
steps:
- shell: bash
- id: autotag
shell: bash
env:
CHANGELOG_PATH: ${{ inputs.changelog-path }}
run: ${{ github.action_path }}/auto-tag-release.sh
5 changes: 5 additions & 0 deletions autotag-from-changelog/auto-tag-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ parse_changelog "$changelog"
# Check if the version string is empty.
if [ -z "$version" ]; then
echo "No released version found in CHANGELOG.md, skipping."
set_output "tag_created" "false"
exit 0
fi

Expand All @@ -71,6 +72,7 @@ if [ -n "$unreleased_content" ]; then
# be tagged — if it isn't, something went wrong.
if [ "$tag_exists" = true ]; then
echo "Content under [Unreleased] and ${tag} already tagged, nothing to do."
set_output "tag_created" "false"
exit 0
else
log_error "CHANGELOG.md has content under [Unreleased] but ${tag} is not tagged. Tag the previous release before adding new entries."
Expand All @@ -80,6 +82,7 @@ fi

if [ "$tag_exists" = true ]; then
echo "Tag ${tag} already exists, nothing to do."
set_output "tag_created" "false"
exit 0
fi

Expand All @@ -96,3 +99,5 @@ if [[ "$tag" =~ ^v([0-9]+)\.[0-9]+\.[0-9]+ ]]; then
git push --force origin "$major_tag"
log_notice "Updated ${major_tag} successfully"
fi
set_output "tag_created" "true"
set_output "tag" "$tag"
10 changes: 10 additions & 0 deletions autotag-from-changelog/auto-tag-release_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ run_parse_test "parse: content before unreleased is ignored" "0.9.0" "true" CHAN
# Integration tests
# =============================================

GITHUB_OUTPUT="$TMPDIR/github_output"
export GITHUB_OUTPUT

Comment on lines +154 to +156
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The integration tests reuse a single $GITHUB_OUTPUT file across multiple runs, and expect_output checks the last occurrence of a key. This can cause false positives when a particular invocation fails to write an output at all (a prior run’s value may still be the “last” match). To make the assertions reliably tied to each invocation, truncate or replace $GITHUB_OUTPUT before each script run that you validate outputs for (or otherwise scope the grep to only new lines).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — each integration test now truncates $GITHUB_OUTPUT before running the script, so assertions are scoped to that invocation. 🤖

# --- Content under [Unreleased], previous version not tagged — should fail ---
cat <<'EOF' > CHANGELOG.md
## [Unreleased]
Expand All @@ -172,8 +175,11 @@ cat <<'EOF' > CHANGELOG.md
## [1.0.0] - 2026-01-01
EOF

: > "$GITHUB_OUTPUT"
expect_success_output "creates new tag" "Tagged v1.0.0 successfully" \
env CHANGELOG_PATH=CHANGELOG.md "$SCRIPT_DIR/auto-tag-release.sh"
expect_step_output "creates new tag — tag_created output" "tag_created" "true"
expect_step_output "creates new tag — tag output" "tag" "v1.0.0"

# Verify the tag was actually created
if git rev-parse v1.0.0 >/dev/null; then
Expand All @@ -188,8 +194,10 @@ fi
verify_major_tag_points_to v1 v1.0.0

# --- Tag already exists — should skip ---
: > "$GITHUB_OUTPUT"
expect_success_output "tag already exists" "already exists" \
env CHANGELOG_PATH=CHANGELOG.md "$SCRIPT_DIR/auto-tag-release.sh"
expect_step_output "tag already exists — tag_created output" "tag_created" "false"

# --- Content under [Unreleased], previous version already tagged — should pass ---
cat <<'EOF' > CHANGELOG.md
Expand All @@ -210,8 +218,10 @@ cat <<'EOF' > CHANGELOG.md
## [Unreleased]
EOF

: > "$GITHUB_OUTPUT"
expect_success_output "no released version" "No released version found" \
env CHANGELOG_PATH=CHANGELOG.md "$SCRIPT_DIR/auto-tag-release.sh"
expect_step_output "no released version — tag_created output" "tag_created" "false"

# --- No [Unreleased] section — should skip ---
cat <<'EOF' > CHANGELOG.md
Expand Down
23 changes: 23 additions & 0 deletions test_helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,29 @@ expect_failure_output() {
_run_test "$name" "nonzero" "$expected_output" "$@"
}

# expect_step_output "test name" "key" "expected_value"
# Asserts that GITHUB_OUTPUT contains key=expected_value.
# Checks the last occurrence of the key so tests that reuse the same file work.
expect_step_output() {
local name="$1" key="$2" expected="$3"
local actual=""
if [ ! -f "${GITHUB_OUTPUT}" ]; then
echo "FAIL: $name — GITHUB_OUTPUT file does not exist"
FAIL=$((FAIL + 1))
return
fi
if grep --quiet --fixed-strings "${key}=" "${GITHUB_OUTPUT}"; then
actual=$(grep --fixed-strings "${key}=" "${GITHUB_OUTPUT}" | tail -1 | cut -d= -f2-)
fi
if [ "$actual" = "$expected" ]; then
echo "PASS: $name"
PASS=$((PASS + 1))
else
echo "FAIL: $name — expected output $key=$expected, got '$actual'"
FAIL=$((FAIL + 1))
fi
}

print_results() {
echo ""
echo "Results: $PASS passed, $FAIL failed"
Expand Down