diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index cc5ca3f..c46ec96 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -326,3 +326,258 @@ jobs: echo "Expected 'No breaking changes' but got '$output'" >&2 exit 1 fi + + # --------------------------------------------------------------------- + # .oasdiff.yaml config-file support — verify all three free actions + # pick up .oasdiff.yaml from the repo root automatically (via the CLI's + # cwd-based config-file lookup; the runner mounts $GITHUB_WORKSPACE as + # the container's WORKDIR). + # --------------------------------------------------------------------- + + oasdiff_breaking_yaml_config_fail_on: + runs-on: ubuntu-latest + name: Test breaking action picks up fail-on from .oasdiff.yaml + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Drop .oasdiff.yaml at repo root + run: | + cat > .oasdiff.yaml <&2 + exit 1 + fi + delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') + output=$(cat <<-$delimiter + ${{ steps.test_yaml_fail_on.outputs.breaking }} + $delimiter + ) + # Even when fail-on triggers, the entrypoint should still render + # the report. Check that the breaking output is non-empty and + # matches the same shape as a normal fail-on-input run. + if [ "$output" != "1 changes: 1 error, 0 warning, 0 info" ]; then + echo "Expected '1 changes: 1 error, 0 warning, 0 info' to be rendered alongside fail-on, got: '$output'" >&2 + exit 1 + fi + + oasdiff_breaking_yaml_config_err_ignore: + runs-on: ubuntu-latest + name: Test breaking action picks up err-ignore from .oasdiff.yaml + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Drop .oasdiff.yaml at repo root + run: | + cat > .oasdiff.yaml <&2 + exit 1 + fi + + oasdiff_changelog_yaml_config_level: + runs-on: ubuntu-latest + name: Test changelog action picks up level from .oasdiff.yaml + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Drop .oasdiff.yaml at repo root + run: | + cat > .oasdiff.yaml <&2 + exit 1 + fi + + oasdiff_diff_yaml_config_exclude_elements: + runs-on: ubuntu-latest + name: Test diff action picks up exclude-elements from .oasdiff.yaml + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Drop .oasdiff.yaml at repo root + run: | + cat > .oasdiff.yaml <&2 + exit 1 + fi + + # --------------------------------------------------------------------- + # pr-comment entrypoint: oasdiff exit-code tolerance. + # + # The pr-comment script wraps the oasdiff changelog invocation so a + # non-zero exit (e.g. fail-on triggered from .oasdiff.yaml) does not + # abort the script under set -e. Real failures (no output) still + # abort. These two jobs run pr-comment/entrypoint.sh directly with a + # stubbed oasdiff on PATH so we don't need a Docker image, an + # oasdiff-token, or a reachable oasdiff-service. + # --------------------------------------------------------------------- + + pr_comment_tolerates_oasdiff_fail_on: + runs-on: ubuntu-latest + name: Test pr-comment proceeds when oasdiff exits non-zero with output + steps: + - uses: actions/checkout@v6 + - name: Stub oasdiff to simulate fail-on triggered + run: | + set -euo pipefail + mkdir -p /tmp/stub + cat > /tmp/stub/oasdiff <<'STUB' + #!/bin/sh + # Simulate fail-on: emit valid JSON, then exit non-zero. + echo '[]' + exit 1 + STUB + chmod +x /tmp/stub/oasdiff + + # Minimum env the entrypoint reads + mkdir -p /tmp/run + export GITHUB_REF=refs/pull/123/merge + export GITHUB_REPOSITORY=foo/bar + export GITHUB_SHA=deadbeef + export GITHUB_BASE_REF=main + export GITHUB_STEP_SUMMARY=/tmp/run/step-summary + cat > /tmp/run/event.json <&1) + rc=$? + set -e + echo "--- entrypoint output ---" + echo "$out" + echo "--- exit code: $rc ---" + + if [ "$rc" -ne 0 ]; then + echo "FAIL: expected exit 0 (script should reach the no-token skip), got $rc" >&2 + exit 1 + fi + if ! echo "$out" | grep -q "::notice::.*View API changes"; then + echo "FAIL: script aborted before emitting the review-page notice; the oasdiff fail-on tolerance fix is missing" >&2 + exit 1 + fi + if ! echo "$out" | grep -q "No oasdiff-token provided"; then + echo "FAIL: script aborted before reaching the no-token skip" >&2 + exit 1 + fi + echo "PASS" + + pr_comment_aborts_on_oasdiff_real_failure: + runs-on: ubuntu-latest + name: Test pr-comment aborts when oasdiff exits non-zero with no output + steps: + - uses: actions/checkout@v6 + - name: Stub oasdiff to simulate a real failure + run: | + set -euo pipefail + mkdir -p /tmp/stub + cat > /tmp/stub/oasdiff <<'STUB' + #!/bin/sh + # Simulate a real failure: no stdout, non-zero exit. + echo "oasdiff: spec not found" >&2 + exit 2 + STUB + chmod +x /tmp/stub/oasdiff + + mkdir -p /tmp/run + export GITHUB_REF=refs/pull/123/merge + export GITHUB_REPOSITORY=foo/bar + export GITHUB_SHA=deadbeef + export GITHUB_BASE_REF=main + export GITHUB_STEP_SUMMARY=/tmp/run/step-summary + cat > /tmp/run/event.json <&1) + rc=$? + set -e + echo "--- entrypoint output ---" + echo "$out" + echo "--- exit code: $rc ---" + + if [ "$rc" -ne 2 ]; then + echo "FAIL: expected exit 2 (oasdiff exit propagated), got $rc" >&2 + exit 1 + fi + if ! echo "$out" | grep -q "ERROR: oasdiff exited 2 with no output"; then + echo "FAIL: expected the explicit no-output error message" >&2 + exit 1 + fi + if echo "$out" | grep -q "::notice::.*View API changes"; then + echo "FAIL: script proceeded past the oasdiff failure; the early-abort branch is missing" >&2 + exit 1 + fi + echo "PASS" diff --git a/breaking/entrypoint.sh b/breaking/entrypoint.sh index 21f7600..1d5bba3 100755 --- a/breaking/entrypoint.sh +++ b/breaking/entrypoint.sh @@ -77,17 +77,23 @@ if [ -n "$warn_ignore" ]; then fi echo "flags: $flags" -# Check for breaking changes -if [ -n "$flags" ]; then - breaking_changes=$(oasdiff breaking "$base" "$revision" $flags) -else - breaking_changes=$(oasdiff breaking "$base" "$revision") +# Run 1: capture the default-format report and the exit code, applying +# --fail-on if the input requested it. Tolerate non-zero exit so we can +# still render the report and write GITHUB_OUTPUT below — the caller's +# fail-on (whether from the input or from oasdiff.yaml) is preserved +# via $exit_code at the end. +fail_on_flag="" +if [ -n "$fail_on" ]; then + fail_on_flag="--fail-on $fail_on" fi +exit_code=0 +breaking_changes=$(oasdiff breaking "$base" "$revision" $flags $fail_on_flag) || exit_code=$? -# Updating GitHub Action summary with formatted output -flags_with_githubactions="$flags --format githubactions" -# Writes the summary to log and updates GitHub Action summary -oasdiff breaking "$base" "$revision" $flags_with_githubactions +# Run 2: render annotations to stdout via --format githubactions so +# GitHub parses them onto the PR's "Files changed" tab. Tolerate +# non-zero exit (could be triggered by oasdiff.yaml fail-on); the +# authoritative exit code is from Run 1. +oasdiff breaking "$base" "$revision" $flags --format githubactions || true # *** GitHub Action step output *** @@ -118,8 +124,4 @@ fi echo "$delimiter" >>"$GITHUB_OUTPUT" -# First output the changes (above) and then run oasdiff to check --fail-on -if [ -n "$fail_on" ]; then - flags="$flags --fail-on $fail_on" - oasdiff breaking "$base" "$revision" $flags > /dev/null -fi +exit $exit_code diff --git a/pr-comment/entrypoint.sh b/pr-comment/entrypoint.sh index c360549..a86fbdd 100755 --- a/pr-comment/entrypoint.sh +++ b/pr-comment/entrypoint.sh @@ -29,11 +29,17 @@ if [ "$composed" = "true" ]; then flags="$flags -c" fi -# Run oasdiff changelog with JSON output -if [ -n "$flags" ]; then - changelog=$(oasdiff changelog "$base" "$revision" --format json $flags) -else - changelog=$(oasdiff changelog "$base" "$revision" --format json) +# Run oasdiff changelog with JSON output. Tolerate a non-zero exit so +# fail-on settings in oasdiff.yaml don't abort the script before we get +# the chance to post the PR comment — fail-on's job is to gate the +# workflow on the *result*, which the service determines, not to block +# us from collecting the JSON. Real failures (missing file, parse error) +# still abort because they leave $changelog empty. +oasdiff_exit=0 +changelog=$(oasdiff changelog "$base" "$revision" --format json $flags) || oasdiff_exit=$? +if [ "$oasdiff_exit" -ne 0 ] && [ -z "$changelog" ]; then + echo "ERROR: oasdiff exited $oasdiff_exit with no output" >&2 + exit $oasdiff_exit fi # If no changes, use empty array