diff --git a/CHANGELOG.md b/CHANGELOG.md index 4382ce4..1c08c60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/CLAUDE.md b/CLAUDE.md index 5414086..a425382 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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. @@ -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. diff --git a/autotag-from-changelog/action.yml b/autotag-from-changelog/action.yml index ddf7840..f60336e 100644 --- a/autotag-from-changelog/action.yml +++ b/autotag-from-changelog/action.yml @@ -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 diff --git a/autotag-from-changelog/auto-tag-release.sh b/autotag-from-changelog/auto-tag-release.sh index e3b57f3..7d4d830 100755 --- a/autotag-from-changelog/auto-tag-release.sh +++ b/autotag-from-changelog/auto-tag-release.sh @@ -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 @@ -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." @@ -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 @@ -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" diff --git a/autotag-from-changelog/auto-tag-release_test.sh b/autotag-from-changelog/auto-tag-release_test.sh index 1375586..7d73627 100755 --- a/autotag-from-changelog/auto-tag-release_test.sh +++ b/autotag-from-changelog/auto-tag-release_test.sh @@ -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 + # --- Content under [Unreleased], previous version not tagged — should fail --- cat <<'EOF' > CHANGELOG.md ## [Unreleased] @@ -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 @@ -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 @@ -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 diff --git a/test_helpers.sh b/test_helpers.sh index 14c866e..3ffc493 100644 --- a/test_helpers.sh +++ b/test_helpers.sh @@ -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"