From 312dca29b8df971e8992244f88705fc3c12c376c Mon Sep 17 00:00:00 2001 From: Reuven Harrison Date: Sat, 23 May 2026 11:39:36 +0300 Subject: [PATCH 1/4] breaking,changelog: preserve https:// URLs in free /review base_file The free breaking and changelog actions built the /review base_file parameter with `sed 's/.*://'`, which strips the git-ref prefix from inputs like "origin/main:openapi.yaml". For URL-shaped base/revision inputs (https://...), the same sed also strips "https:" and leaves a broken "//host/..." that the /review page cannot fetch, so the page renders the access-denied screen and misreports the cause as a permissions problem. pr-comment already got this fix in #120. This applies the same strip_ref_prefix helper (passes http(s):// through unchanged, strips the ref prefix otherwise) to breaking and changelog, so all three actions emit a well-formed base_file for every supported input shape (git ref, local path, http/s URL). Co-Authored-By: Claude Opus 4.7 (1M context) --- breaking/entrypoint.sh | 14 ++++++++++++-- changelog/entrypoint.sh | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/breaking/entrypoint.sh b/breaking/entrypoint.sh index cf77f4a..38869e9 100755 --- a/breaking/entrypoint.sh +++ b/breaking/entrypoint.sh @@ -109,8 +109,18 @@ if [ -n "$breaking_changes" ] && ! echo "$breaking_changes" | head -n 1 | grep - write_output "$(echo "$breaking_changes" | head -n 1)" "$breaking_changes" # Emit upgrade notice pointing to the free review page urlencode() { printf '%s' "$1" | jq -sRr @uri; } - base_path=$(echo "$base" | sed 's/.*://') - rev_path=$(echo "$revision" | sed 's/.*://') + # Strip the git-ref prefix ("origin/main:openapi.yaml" -> "openapi.yaml") + # but pass http(s):// URLs through unchanged. A naive `sed 's/.*://'` would + # also eat "https:" and emit a broken "//host/..." that the /review page + # can't fetch (it renders the misleading access-denied screen). + strip_ref_prefix() { + case "$1" in + http://*|https://*) printf '%s' "$1" ;; + *) printf '%s' "$1" | sed 's/.*://' ;; + esac + } + base_path=$(strip_ref_prefix "$base") + rev_path=$(strip_ref_prefix "$revision") owner="${GITHUB_REPOSITORY%%/*}" repo="${GITHUB_REPOSITORY#*/}" head_sha=$(jq -r '.pull_request.head.sha // empty' "$GITHUB_EVENT_PATH" 2>/dev/null || echo "") diff --git a/changelog/entrypoint.sh b/changelog/entrypoint.sh index 1b4db77..0088e0d 100755 --- a/changelog/entrypoint.sh +++ b/changelog/entrypoint.sh @@ -101,8 +101,18 @@ if [ -n "$output" ] && ! echo "$output" | head -n 1 | grep -q "^No "; then write_output "$output" # Emit upgrade notice pointing to the free review page urlencode() { printf '%s' "$1" | jq -sRr @uri; } - base_path=$(echo "$base" | sed 's/.*://') - rev_path=$(echo "$revision" | sed 's/.*://') + # Strip the git-ref prefix ("origin/main:openapi.yaml" -> "openapi.yaml") + # but pass http(s):// URLs through unchanged. A naive `sed 's/.*://'` would + # also eat "https:" and emit a broken "//host/..." that the /review page + # can't fetch (it renders the misleading access-denied screen). + strip_ref_prefix() { + case "$1" in + http://*|https://*) printf '%s' "$1" ;; + *) printf '%s' "$1" | sed 's/.*://' ;; + esac + } + base_path=$(strip_ref_prefix "$base") + rev_path=$(strip_ref_prefix "$revision") owner="${GITHUB_REPOSITORY%%/*}" repo="${GITHUB_REPOSITORY#*/}" head_sha=$(jq -r '.pull_request.head.sha // empty' "$GITHUB_EVENT_PATH" 2>/dev/null || echo "") From 3ea1de57f5da122de4066f4975fbe74df0b4cdca Mon Sep 17 00:00:00 2001 From: Reuven Harrison Date: Sat, 23 May 2026 12:34:34 +0300 Subject: [PATCH 2/4] ci: split test.yaml into per-action workflow files test.yaml had grown to 863 lines and 22 jobs covering all four actions. Split it into one workflow per action (test-diff, test-breaking, test-changelog, test-pr-comment) so each file maps to the action it exercises and stays readable. Pure move: the jobs are byte-identical to the original set; only the file boundaries and the per-file name/on: headers are new. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/test-breaking.yaml | 275 ++++++++ .github/workflows/test-changelog.yaml | 89 +++ .github/workflows/test-diff.yaml | 119 ++++ .github/workflows/test-pr-comment.yaml | 395 +++++++++++ .github/workflows/test.yaml | 863 ------------------------- 5 files changed, 878 insertions(+), 863 deletions(-) create mode 100644 .github/workflows/test-breaking.yaml create mode 100644 .github/workflows/test-changelog.yaml create mode 100644 .github/workflows/test-diff.yaml create mode 100644 .github/workflows/test-pr-comment.yaml delete mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/test-breaking.yaml b/.github/workflows/test-breaking.yaml new file mode 100644 index 0000000..625c5fc --- /dev/null +++ b/.github/workflows/test-breaking.yaml @@ -0,0 +1,275 @@ +name: 'Test oasdiff breaking action' +on: + pull_request: + push: +jobs: + oasdiff_breaking: + runs-on: ubuntu-latest + name: Test breaking changes + env: + OASDIFF_ACTION_TEST_EXPECTED_OUTPUT: "1 changes: 1 error, 0 warning, 0 info" + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Running breaking action + id: test_breaking_changes + uses: ./breaking + with: + base: 'specs/base.yaml' + revision: 'specs/revision-breaking.yaml' + output-to-file: 'breaking.txt' + - name: Test breaking changes action output + run: | + delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') + output=$(cat <<-$delimiter + ${{ steps.test_breaking_changes.outputs.breaking }} + $delimiter + ) + if [ "$output" != "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]; then + echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '$output'" >&2 + exit 1 + fi + - name: Test breaking changes action output to file + run: | + if [ ! -s breaking.txt ]; then + echo "Breaking changes file doesn't exist or is empty" + exit 1 + fi + output=$(cat breaking.txt | head -n 1) + if [[ "${output}" != "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]]; then + echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '${output}'" >&2 + exit 1 + fi + oasdiff_breaking_fail_on: + runs-on: ubuntu-latest + name: Test fail on breaking changes + env: + OASDIFF_ACTION_TEST_EXPECTED_OUTPUT: "2 changes: 0 error, 2 warning, 0 info" + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Running breaking action + id: test_breaking_changes + uses: ./breaking + with: + base: 'specs/base.yaml' + revision: 'specs/revision-breaking-warn.yaml' + output-to-file: 'breaking.txt' + fail-on: 'ERR' + - name: Test breaking changes action output + run: | + delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') + output=$(cat <<-$delimiter + ${{ steps.test_breaking_changes.outputs.breaking }} + $delimiter + ) + if [ "$output" != "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]; then + echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '$output'" >&2 + exit 1 + fi + - name: Test breaking changes action output to file + run: | + if [ ! -s breaking.txt ]; then + echo "Breaking changes file doesn't exist or is empty" + exit 1 + fi + output=$(cat breaking.txt | head -n 1) + if [[ "${output}" != "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]]; then + echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '${output}'" >&2 + exit 1 + fi + oasdiff_breaking_matching_delimiter_not_found: + runs-on: ubuntu-latest + name: Test breaking action with petsotre to validate no error of unable to process file command 'output' successfully and invalid value and matching delimiter not found + env: + OASDIFF_ACTION_TEST_EXPECTED_OUTPUT: "9 changes: 6 error, 3 warning, 0 info" + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Running breaking action with petsotre to validate no error of unable to process file command 'output' successfully and invalid value and matching delimiter not found + id: test_breaking_changes_matching_delimiter_not_found + uses: ./breaking + with: + base: 'specs/petstore-base.yaml' + revision: 'specs/petstore-revision.yaml' + - name: Test breaking changes action output + run: | + delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') + output=$(cat <<-$delimiter + ${{ steps.test_breaking_changes_matching_delimiter_not_found.outputs.breaking }} + $delimiter + ) + if [ "$output" != "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]; then + echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '$output'" >&2 + exit 1 + fi + oasdiff_breaking_composed: + runs-on: ubuntu-latest + name: Test breaking action with composed option + env: + OASDIFF_ACTION_TEST_EXPECTED_OUTPUT: "1 changes: 1 error, 0 warning, 0 info" + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Running breaking action with composed option + id: test_breaking_composed + uses: ./breaking + with: + base: 'specs/glob/base/*.yaml' + revision: 'specs/glob/revision/*.yaml' + composed: true + - name: Test breaking action output + run: | + delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') + output=$(cat <<-$delimiter + ${{ steps.test_breaking_composed.outputs.breaking }} + $delimiter + ) + if [[ ! "$output" =~ "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]]; then + echo "Expected '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT', instead got '$output'" >&2 + exit 1 + fi + oasdiff_breaking_deprecation: + runs-on: ubuntu-latest + name: Test breaking changes with deprecation + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Set date for deprecated specs + run: | + # Deprecate Beta in 14 days + sed -ie "s/{{SUNSET_DATE_BETA}}/$(date --date="14 day" "+%Y-%m-%d")/" specs/base-deprecation.yaml + # Deprecate Stable in 21 days + sed -ie "s/{{SUNSET_DATE_STABLE}}/$(date --date="21 day" "+%Y-%m-%d")/" specs/base-deprecation.yaml + - name: Running OpenAPI Spec check breaking action + id: test_breaking_deprecations + uses: ./breaking + with: + base: specs/base.yaml + revision: specs/base-deprecation.yaml + deprecation-days-beta: 14 + deprecation-days-stable: 21 + oasdiff_breaking_flatten_allof: + runs-on: ubuntu-latest + name: Test breaking action with flatten-allof option + env: + OASDIFF_ACTION_TEST_EXPECTED_OUTPUT: "1 changes: 1 error, 0 warning, 0 info" + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Running breaking action with flatten-allof option + id: test_breaking_flatten_allof + uses: ./breaking + with: + base: 'specs/base-allof.yaml' + revision: 'specs/revision-allof.yaml' + flatten-allof: true + - name: Test breaking action output + run: | + delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') + output=$(cat <<-$delimiter + ${{ steps.test_breaking_flatten_allof.outputs.breaking }} + $delimiter + ) + if [ "$output" != "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]; then + echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '$output'" >&2 + exit 1 + fi + oasdiff_breaking_err_ignore: + runs-on: ubuntu-latest + name: Test breaking action with err-ignore option + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Running breaking action with err-ignore option + id: test_breaking_err_ignore + uses: ./breaking + with: + base: 'specs/base.yaml' + revision: 'specs/revision-breaking.yaml' + err-ignore: 'specs/err-ignore.txt' + - name: Test breaking changes are suppressed + run: | + delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') + output=$(cat <<-$delimiter + ${{ steps.test_breaking_err_ignore.outputs.breaking }} + $delimiter + ) + if [ "$output" != "No breaking changes" ]; then + 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 + diff --git a/.github/workflows/test-changelog.yaml b/.github/workflows/test-changelog.yaml new file mode 100644 index 0000000..917ad49 --- /dev/null +++ b/.github/workflows/test-changelog.yaml @@ -0,0 +1,89 @@ +name: 'Test oasdiff changelog action' +on: + pull_request: + push: +jobs: + oasdiff_changelog: + runs-on: ubuntu-latest + name: Test generation of changelog + env: + OASDIFF_ACTION_TEST_EXPECTED_OUTPUT: "21 changes: 2 error, 4 warning, 15 info" + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Running changelog action + id: test_changelog + uses: ./changelog + with: + base: https://raw.githubusercontent.com/oasdiff/oasdiff/main/data/openapi-test1.yaml + revision: https://raw.githubusercontent.com/oasdiff/oasdiff/main/data/openapi-test3.yaml + output-to-file: "changelog.txt" + - name: Test changelog action output + run: | + output=$(echo "${{steps.test_changelog.outputs.changelog}}" | head -n 1) + if [[ "${output}" != "${OASDIFF_ACTION_TEST_EXPECTED_OUTPUT}" ]]; then + echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '${output}'" >&2 + exit 1 + fi + - name: Test changelog action output to file + run: | + if [ ! -s changelog.txt ]; then + echo "Changelog file doesn't exist or is empty" + exit 1 + fi + output=$(cat changelog.txt | head -n 1) + if [[ "${output}" != "${OASDIFF_ACTION_TEST_EXPECTED_OUTPUT}" ]]; then + echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '${output}'" >&2 + exit 1 + fi + oasdiff_changelog_composed: + runs-on: ubuntu-latest + name: Test changelog action with composed option + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Running changelog action with composed option + id: test_changelog_composed + uses: ./changelog + with: + base: 'specs/glob/base/*.yaml' + revision: 'specs/glob/revision/*.yaml' + composed: true + - name: Test changelog action output + run: | + delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') + output=$(cat <<-$delimiter + ${{ steps.test_changelog_composed.outputs.changelog }} + $delimiter + ) + if [[ ! "$output" =~ "1 changes: 1 error, 0 warning, 0 info" ]]; then + echo "Expected '1 changes: 1 error, 0 warning, 0 info', instead got '$output'" >&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 + diff --git a/.github/workflows/test-diff.yaml b/.github/workflows/test-diff.yaml new file mode 100644 index 0000000..0b4bd85 --- /dev/null +++ b/.github/workflows/test-diff.yaml @@ -0,0 +1,119 @@ +name: 'Test oasdiff diff action' +on: + pull_request: + push: +jobs: + oasdiff_diff: + runs-on: ubuntu-latest + name: Test diff action + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Running diff action + id: test_ete + uses: ./diff + with: + base: https://raw.githubusercontent.com/oasdiff/oasdiff/main/data/openapi-test1.yaml + revision: https://raw.githubusercontent.com/oasdiff/oasdiff/main/data/openapi-test3.yaml + format: 'text' + output-to-file: 'diff.txt' + - name: Test diff action output to file + run: | + if [ ! -s diff.txt ]; then + echo "Diff file doesn't exist or is empty" + exit 1 + fi + oasdiff_diff_exclude_elements: + runs-on: ubuntu-latest + name: Test diff action with exclude-elements option + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Running diff action with exclude-elements option + id: test_exclude_elements + uses: ./diff + with: + base: 'specs/base.yaml' + revision: 'specs/base-exclude-elements.yaml' + format: 'text' + exclude-elements: 'description,title,summary' + - name: Test diff action output + run: | + delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') + output=$(cat <<-$delimiter + ${{ steps.test_exclude_elements.outputs.diff }} + $delimiter + ) + if [ "$output" != "No changes" ]; then + echo "Expected output 'No changes' but got '$output'" >&2 + exit 1 + fi + oasdiff_diff_composed: + runs-on: ubuntu-latest + name: Test diff action with composed option + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Running diff action with composed option + id: test_composed + uses: ./diff + with: + base: 'specs/glob/base/*.yaml' + revision: 'specs/glob/revision/*.yaml' + format: 'text' + composed: true + - name: Test diff action output + run: | + delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') + output=$(cat <<-$delimiter + ${{ steps.test_composed.outputs.diff }} + $delimiter + ) + if [[ ! "$output" =~ "Deleted Endpoints: 1" ]]; then + echo "Expected 'Deleted Endpoints: 1' to be modified in diff, instead got '$output'" >&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. + # --------------------------------------------------------------------- + diff --git a/.github/workflows/test-pr-comment.yaml b/.github/workflows/test-pr-comment.yaml new file mode 100644 index 0000000..7b4ea56 --- /dev/null +++ b/.github/workflows/test-pr-comment.yaml @@ -0,0 +1,395 @@ +name: 'Test oasdiff pr-comment action' +on: + pull_request: + push: +jobs: + 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" + + pr_comment_handles_large_payload: + runs-on: ubuntu-latest + name: Test pr-comment jq payload survives a multi-MB changes array + # Regression test for the jq argv ARG_MAX limit. Real-world specs can + # produce changelogs in the multi-MB range; the original implementation + # passed `$changes` via `jq --argjson changes "$changes"` which put the + # entire JSON string on jq's command line and exceeded ARG_MAX (typical + # Linux ceiling is 2 MB), surfacing as `jq: Argument list too long` and + # aborting the action right before the service POST. The fix pipes the + # changes payload via stdin instead. This job stubs oasdiff to emit a + # ~4 MB changelog and asserts the entrypoint reaches the no-token skip + # without aborting, which proves the jq invocation processed the + # payload successfully. + steps: + - uses: actions/checkout@v6 + - name: Stub oasdiff to emit a multi-MB changelog + run: | + set -euo pipefail + mkdir -p /tmp/stub + + # Pre-build a ~2 MB filler string to embed inside two synthetic + # change entries (~4 MB total payload, well above the 2 MB Linux + # ARG_MAX ceiling). dd reads a bounded number of zero bytes and + # exits 0 cleanly, then tr converts to 'a's. We avoid `yes ... | + # head -c N` because `head` closes the pipe and `yes` exits with + # SIGPIPE, which fails under `set -o pipefail`. + dd if=/dev/zero bs=1024 count=2000 status=none | tr '\0' 'a' > /tmp/filler + filler_size=$(wc -c < /tmp/filler) + echo "filler size: ${filler_size} bytes" + + cat > /tmp/stub/oasdiff <<'STUB' + #!/bin/sh + # Emit a synthetic JSON changelog totalling >4 MB. printf is a + # POSIX builtin so neither the variable assignment via $(cat ...) + # nor the final printf '%s' "$json" goes through execve. + filler=$(cat /tmp/filler) + printf '[{"id":"big-1","text":"%s","level":3},{"id":"big-2","text":"%s","level":3}]' "$filler" "$filler" + 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 (truncated) ---" + echo "$out" | head -c 2000 + echo "--- exit code: $rc ---" + + if [ "$rc" -ne 0 ]; then + echo "FAIL: expected exit 0 (script should reach the no-token skip after building the JSON payload), got $rc" >&2 + echo "If the message contains 'jq: Argument list too long', the regression has returned: \$changes is being passed via argv (--argjson) instead of stdin." >&2 + exit 1 + fi + if ! echo "$out" | grep -q "::notice::.*View API changes"; then + echo "FAIL: script aborted before emitting the review-page notice" >&2 + exit 1 + fi + if ! echo "$out" | grep -q "No oasdiff-token provided"; then + echo "FAIL: script aborted before reaching the no-token skip; the jq payload-construction step likely blew up on the multi-MB changes array" >&2 + exit 1 + fi + echo "PASS" + + pr_comment_curl_handles_large_payload: + runs-on: ubuntu-latest + name: Test pr-comment curl POST survives a multi-MB payload + # Companion to pr_comment_handles_large_payload. The previous test + # exercises the jq payload-construction path with an empty + # oasdiff_token, which short-circuits past the curl step. This one + # exercises the curl step itself by providing a non-empty token and + # stubbing both oasdiff (multi-MB changelog source) and curl (verifies + # it received the payload via stdin, not via argv). Catches the + # regression class where `curl -d "$payload"` puts a multi-MB body on + # argv and aborts with `curl: Argument list too long` after the jq + # invocation already succeeded — the same ARG_MAX trap one layer + # down. The fix pipes the payload through stdin via `--data-binary @-`. + steps: + - uses: actions/checkout@v6 + - name: Stub oasdiff + curl, run entrypoint with a fake token + run: | + set -euo pipefail + mkdir -p /tmp/stub /tmp/run + + # Same filler strategy as the jq test: dd | tr produces a + # ~2 MB string of 'a' characters and exits 0 cleanly under + # pipefail. + dd if=/dev/zero bs=1024 count=2000 status=none | tr '\0' 'a' > /tmp/filler + + cat > /tmp/stub/oasdiff <<'STUB' + #!/bin/sh + filler=$(cat /tmp/filler) + printf '[{"id":"big-1","text":"%s","level":3},{"id":"big-2","text":"%s","level":3}]' "$filler" "$filler" + STUB + chmod +x /tmp/stub/oasdiff + + # Stub curl: consume the POST body from stdin, record its + # byte count to a side file the assertions below can read, + # and emit the success-response shape the entrypoint expects + # from `-s -w "\n%{http_code}"` (body then a newline then the + # HTTP status code). + cat > /tmp/stub/curl <<'STUB' + #!/bin/sh + body=$(cat) + printf '%s' "$body" | wc -c > /tmp/run/curl-bytes + printf '{"review_token":"stub-token-uuid"}\n200\n' + STUB + chmod +x /tmp/stub/curl + + 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 (truncated) ---" + echo "$out" | head -c 2000 + echo "--- exit code: $rc ---" + + if [ "$rc" -ne 0 ]; then + echo "FAIL: expected exit 0, got $rc" >&2 + if echo "$out" | grep -q "curl: Argument list too long"; then + echo "The regression has returned: \$payload is being passed via curl argv (-d) instead of stdin (--data-binary @-)." >&2 + fi + exit 1 + fi + if [ ! -f /tmp/run/curl-bytes ]; then + echo "FAIL: curl stub was never invoked; script aborted before reaching the POST" >&2 + exit 1 + fi + curl_bytes=$(cat /tmp/run/curl-bytes | tr -d ' ') + echo "curl received: ${curl_bytes} bytes" + if [ "$curl_bytes" -lt 4000000 ]; then + echo "FAIL: curl stub received only ${curl_bytes} bytes; expected >4 MB (proves payload made it through stdin)" >&2 + exit 1 + fi + if ! echo "$out" | grep -q "::notice::.*View API changes"; then + echo "FAIL: script aborted before emitting the review-page notice" >&2 + exit 1 + fi + echo "PASS" + + pr_comment_free_review_url_preserves_https_base: + runs-on: ubuntu-latest + name: Test pr-comment free review URL keeps the https:// scheme on URL-style base + # Regression test for a bug surfaced by oasdiff-test/test#59: when the + # workflow passes a raw.githubusercontent.com URL as the base input + # (as the integration-test repo does), the entrypoint's + # base_path=$(echo "$base" | sed 's/.*://') + # stripped the "https:" prefix and left a broken "//raw.github..." + # URL. The free /review page then tried to GET that broken URL and + # rendered "access denied" with no useful diagnostic — because the + # generic access-denied screen masked the real cause (malformed URL). + # The fix passes URL-shaped inputs through unchanged. + steps: + - uses: actions/checkout@v6 + - name: Stub oasdiff to a no-changes spec + run: | + set -euo pipefail + mkdir -p /tmp/stub + cat > /tmp/stub/oasdiff <<'STUB' + #!/bin/sh + echo '[]' + STUB + chmod +x /tmp/stub/oasdiff + + mkdir -p /tmp/run + export GITHUB_REF=refs/pull/123/merge + export GITHUB_REPOSITORY=oasdiff-test/test + export GITHUB_SHA=deadbeef + export GITHUB_BASE_REF=main + export GITHUB_STEP_SUMMARY=/tmp/run/step-summary + cat > /tmp/run/event.json <&1) + echo "--- entrypoint output ---" + echo "$out" + + if ! echo "$out" | grep -q "::notice::.*View API changes"; then + echo "FAIL: notice line missing" >&2 + exit 1 + fi + notice=$(echo "$out" | grep "::notice::.*View API changes") + # The encoded base_file= param must contain the full URL, not the + # stripped "//raw..." form. https:// URL-encodes to https%3A%2F%2F. + if echo "$notice" | grep -q 'base_file=https%3A%2F%2Fraw.githubusercontent.com'; then + echo "PASS: full https://raw... URL preserved in base_file" + elif echo "$notice" | grep -q 'base_file=%2F%2Fraw.githubusercontent.com'; then + echo "FAIL: 'https:' was stripped from base — the strip_ref_prefix URL guard is missing" >&2 + echo "notice line: $notice" >&2 + exit 1 + else + echo "FAIL: base_file= param did not contain the raw.githubusercontent.com host as expected" >&2 + echo "notice line: $notice" >&2 + exit 1 + fi + + pr_comment_free_review_url_strips_git_ref_prefix: + runs-on: ubuntu-latest + name: "Test pr-comment free review URL strips origin/main: prefix from git-ref base" + # Companion to pr_comment_free_review_url_preserves_https_base: when + # the base input is git-ref form (origin/main:openapi.yaml), the + # prefix must still be stripped so the /review page receives just the + # path. The previous sed-based implementation handled this correctly; + # this test guards the case-branch refactor against breaking it. + steps: + - uses: actions/checkout@v6 + - name: Stub oasdiff to a no-changes spec + run: | + set -euo pipefail + mkdir -p /tmp/stub + cat > /tmp/stub/oasdiff <<'STUB' + #!/bin/sh + echo '[]' + 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) + echo "--- entrypoint output ---" + echo "$out" + + notice=$(echo "$out" | grep "::notice::.*View API changes") + # base_file should be just "multi-file/openapi.yaml" (URL-encoded slash), + # not the full "origin/main:multi-file/openapi.yaml". + if echo "$notice" | grep -q 'base_file=multi-file%2Fopenapi.yaml'; then + echo "PASS: origin/main: prefix stripped from base" + else + echo "FAIL: base_file= did not have the git-ref prefix stripped" >&2 + echo "notice line: $notice" >&2 + exit 1 + fi diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml deleted file mode 100644 index b5f2ea4..0000000 --- a/.github/workflows/test.yaml +++ /dev/null @@ -1,863 +0,0 @@ -name: 'Test oasdiff actions' -on: - pull_request: - push: -jobs: - oasdiff_diff: - runs-on: ubuntu-latest - name: Test diff action - steps: - - name: checkout - uses: actions/checkout@v6 - - name: Running diff action - id: test_ete - uses: ./diff - with: - base: https://raw.githubusercontent.com/oasdiff/oasdiff/main/data/openapi-test1.yaml - revision: https://raw.githubusercontent.com/oasdiff/oasdiff/main/data/openapi-test3.yaml - format: 'text' - output-to-file: 'diff.txt' - - name: Test diff action output to file - run: | - if [ ! -s diff.txt ]; then - echo "Diff file doesn't exist or is empty" - exit 1 - fi - oasdiff_diff_exclude_elements: - runs-on: ubuntu-latest - name: Test diff action with exclude-elements option - steps: - - name: checkout - uses: actions/checkout@v6 - - name: Running diff action with exclude-elements option - id: test_exclude_elements - uses: ./diff - with: - base: 'specs/base.yaml' - revision: 'specs/base-exclude-elements.yaml' - format: 'text' - exclude-elements: 'description,title,summary' - - name: Test diff action output - run: | - delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') - output=$(cat <<-$delimiter - ${{ steps.test_exclude_elements.outputs.diff }} - $delimiter - ) - if [ "$output" != "No changes" ]; then - echo "Expected output 'No changes' but got '$output'" >&2 - exit 1 - fi - oasdiff_diff_composed: - runs-on: ubuntu-latest - name: Test diff action with composed option - steps: - - name: checkout - uses: actions/checkout@v6 - - name: Running diff action with composed option - id: test_composed - uses: ./diff - with: - base: 'specs/glob/base/*.yaml' - revision: 'specs/glob/revision/*.yaml' - format: 'text' - composed: true - - name: Test diff action output - run: | - delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') - output=$(cat <<-$delimiter - ${{ steps.test_composed.outputs.diff }} - $delimiter - ) - if [[ ! "$output" =~ "Deleted Endpoints: 1" ]]; then - echo "Expected 'Deleted Endpoints: 1' to be modified in diff, instead got '$output'" >&2 - exit 1 - fi - oasdiff_breaking: - runs-on: ubuntu-latest - name: Test breaking changes - env: - OASDIFF_ACTION_TEST_EXPECTED_OUTPUT: "1 changes: 1 error, 0 warning, 0 info" - steps: - - name: checkout - uses: actions/checkout@v6 - - name: Running breaking action - id: test_breaking_changes - uses: ./breaking - with: - base: 'specs/base.yaml' - revision: 'specs/revision-breaking.yaml' - output-to-file: 'breaking.txt' - - name: Test breaking changes action output - run: | - delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') - output=$(cat <<-$delimiter - ${{ steps.test_breaking_changes.outputs.breaking }} - $delimiter - ) - if [ "$output" != "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]; then - echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '$output'" >&2 - exit 1 - fi - - name: Test breaking changes action output to file - run: | - if [ ! -s breaking.txt ]; then - echo "Breaking changes file doesn't exist or is empty" - exit 1 - fi - output=$(cat breaking.txt | head -n 1) - if [[ "${output}" != "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]]; then - echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '${output}'" >&2 - exit 1 - fi - oasdiff_breaking_fail_on: - runs-on: ubuntu-latest - name: Test fail on breaking changes - env: - OASDIFF_ACTION_TEST_EXPECTED_OUTPUT: "2 changes: 0 error, 2 warning, 0 info" - steps: - - name: checkout - uses: actions/checkout@v6 - - name: Running breaking action - id: test_breaking_changes - uses: ./breaking - with: - base: 'specs/base.yaml' - revision: 'specs/revision-breaking-warn.yaml' - output-to-file: 'breaking.txt' - fail-on: 'ERR' - - name: Test breaking changes action output - run: | - delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') - output=$(cat <<-$delimiter - ${{ steps.test_breaking_changes.outputs.breaking }} - $delimiter - ) - if [ "$output" != "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]; then - echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '$output'" >&2 - exit 1 - fi - - name: Test breaking changes action output to file - run: | - if [ ! -s breaking.txt ]; then - echo "Breaking changes file doesn't exist or is empty" - exit 1 - fi - output=$(cat breaking.txt | head -n 1) - if [[ "${output}" != "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]]; then - echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '${output}'" >&2 - exit 1 - fi - oasdiff_breaking_matching_delimiter_not_found: - runs-on: ubuntu-latest - name: Test breaking action with petsotre to validate no error of unable to process file command 'output' successfully and invalid value and matching delimiter not found - env: - OASDIFF_ACTION_TEST_EXPECTED_OUTPUT: "9 changes: 6 error, 3 warning, 0 info" - steps: - - name: checkout - uses: actions/checkout@v6 - - name: Running breaking action with petsotre to validate no error of unable to process file command 'output' successfully and invalid value and matching delimiter not found - id: test_breaking_changes_matching_delimiter_not_found - uses: ./breaking - with: - base: 'specs/petstore-base.yaml' - revision: 'specs/petstore-revision.yaml' - - name: Test breaking changes action output - run: | - delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') - output=$(cat <<-$delimiter - ${{ steps.test_breaking_changes_matching_delimiter_not_found.outputs.breaking }} - $delimiter - ) - if [ "$output" != "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]; then - echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '$output'" >&2 - exit 1 - fi - oasdiff_breaking_composed: - runs-on: ubuntu-latest - name: Test breaking action with composed option - env: - OASDIFF_ACTION_TEST_EXPECTED_OUTPUT: "1 changes: 1 error, 0 warning, 0 info" - steps: - - name: checkout - uses: actions/checkout@v6 - - name: Running breaking action with composed option - id: test_breaking_composed - uses: ./breaking - with: - base: 'specs/glob/base/*.yaml' - revision: 'specs/glob/revision/*.yaml' - composed: true - - name: Test breaking action output - run: | - delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') - output=$(cat <<-$delimiter - ${{ steps.test_breaking_composed.outputs.breaking }} - $delimiter - ) - if [[ ! "$output" =~ "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]]; then - echo "Expected '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT', instead got '$output'" >&2 - exit 1 - fi - oasdiff_breaking_deprecation: - runs-on: ubuntu-latest - name: Test breaking changes with deprecation - steps: - - name: checkout - uses: actions/checkout@v6 - - name: Set date for deprecated specs - run: | - # Deprecate Beta in 14 days - sed -ie "s/{{SUNSET_DATE_BETA}}/$(date --date="14 day" "+%Y-%m-%d")/" specs/base-deprecation.yaml - # Deprecate Stable in 21 days - sed -ie "s/{{SUNSET_DATE_STABLE}}/$(date --date="21 day" "+%Y-%m-%d")/" specs/base-deprecation.yaml - - name: Running OpenAPI Spec check breaking action - id: test_breaking_deprecations - uses: ./breaking - with: - base: specs/base.yaml - revision: specs/base-deprecation.yaml - deprecation-days-beta: 14 - deprecation-days-stable: 21 - oasdiff_changelog: - runs-on: ubuntu-latest - name: Test generation of changelog - env: - OASDIFF_ACTION_TEST_EXPECTED_OUTPUT: "21 changes: 2 error, 4 warning, 15 info" - steps: - - name: checkout - uses: actions/checkout@v6 - - name: Running changelog action - id: test_changelog - uses: ./changelog - with: - base: https://raw.githubusercontent.com/oasdiff/oasdiff/main/data/openapi-test1.yaml - revision: https://raw.githubusercontent.com/oasdiff/oasdiff/main/data/openapi-test3.yaml - output-to-file: "changelog.txt" - - name: Test changelog action output - run: | - output=$(echo "${{steps.test_changelog.outputs.changelog}}" | head -n 1) - if [[ "${output}" != "${OASDIFF_ACTION_TEST_EXPECTED_OUTPUT}" ]]; then - echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '${output}'" >&2 - exit 1 - fi - - name: Test changelog action output to file - run: | - if [ ! -s changelog.txt ]; then - echo "Changelog file doesn't exist or is empty" - exit 1 - fi - output=$(cat changelog.txt | head -n 1) - if [[ "${output}" != "${OASDIFF_ACTION_TEST_EXPECTED_OUTPUT}" ]]; then - echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '${output}'" >&2 - exit 1 - fi - oasdiff_changelog_composed: - runs-on: ubuntu-latest - name: Test changelog action with composed option - steps: - - name: checkout - uses: actions/checkout@v6 - - name: Running changelog action with composed option - id: test_changelog_composed - uses: ./changelog - with: - base: 'specs/glob/base/*.yaml' - revision: 'specs/glob/revision/*.yaml' - composed: true - - name: Test changelog action output - run: | - delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') - output=$(cat <<-$delimiter - ${{ steps.test_changelog_composed.outputs.changelog }} - $delimiter - ) - if [[ ! "$output" =~ "1 changes: 1 error, 0 warning, 0 info" ]]; then - echo "Expected '1 changes: 1 error, 0 warning, 0 info', instead got '$output'" >&2 - exit 1 - fi - oasdiff_breaking_flatten_allof: - runs-on: ubuntu-latest - name: Test breaking action with flatten-allof option - env: - OASDIFF_ACTION_TEST_EXPECTED_OUTPUT: "1 changes: 1 error, 0 warning, 0 info" - steps: - - name: checkout - uses: actions/checkout@v6 - - name: Running breaking action with flatten-allof option - id: test_breaking_flatten_allof - uses: ./breaking - with: - base: 'specs/base-allof.yaml' - revision: 'specs/revision-allof.yaml' - flatten-allof: true - - name: Test breaking action output - run: | - delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') - output=$(cat <<-$delimiter - ${{ steps.test_breaking_flatten_allof.outputs.breaking }} - $delimiter - ) - if [ "$output" != "$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT" ]; then - echo "Expected output '$OASDIFF_ACTION_TEST_EXPECTED_OUTPUT' but got '$output'" >&2 - exit 1 - fi - oasdiff_breaking_err_ignore: - runs-on: ubuntu-latest - name: Test breaking action with err-ignore option - steps: - - name: checkout - uses: actions/checkout@v6 - - name: Running breaking action with err-ignore option - id: test_breaking_err_ignore - uses: ./breaking - with: - base: 'specs/base.yaml' - revision: 'specs/revision-breaking.yaml' - err-ignore: 'specs/err-ignore.txt' - - name: Test breaking changes are suppressed - run: | - delimiter=$(cat /proc/sys/kernel/random/uuid | tr -d '-') - output=$(cat <<-$delimiter - ${{ steps.test_breaking_err_ignore.outputs.breaking }} - $delimiter - ) - if [ "$output" != "No breaking changes" ]; then - 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" - - pr_comment_handles_large_payload: - runs-on: ubuntu-latest - name: Test pr-comment jq payload survives a multi-MB changes array - # Regression test for the jq argv ARG_MAX limit. Real-world specs can - # produce changelogs in the multi-MB range; the original implementation - # passed `$changes` via `jq --argjson changes "$changes"` which put the - # entire JSON string on jq's command line and exceeded ARG_MAX (typical - # Linux ceiling is 2 MB), surfacing as `jq: Argument list too long` and - # aborting the action right before the service POST. The fix pipes the - # changes payload via stdin instead. This job stubs oasdiff to emit a - # ~4 MB changelog and asserts the entrypoint reaches the no-token skip - # without aborting, which proves the jq invocation processed the - # payload successfully. - steps: - - uses: actions/checkout@v6 - - name: Stub oasdiff to emit a multi-MB changelog - run: | - set -euo pipefail - mkdir -p /tmp/stub - - # Pre-build a ~2 MB filler string to embed inside two synthetic - # change entries (~4 MB total payload, well above the 2 MB Linux - # ARG_MAX ceiling). dd reads a bounded number of zero bytes and - # exits 0 cleanly, then tr converts to 'a's. We avoid `yes ... | - # head -c N` because `head` closes the pipe and `yes` exits with - # SIGPIPE, which fails under `set -o pipefail`. - dd if=/dev/zero bs=1024 count=2000 status=none | tr '\0' 'a' > /tmp/filler - filler_size=$(wc -c < /tmp/filler) - echo "filler size: ${filler_size} bytes" - - cat > /tmp/stub/oasdiff <<'STUB' - #!/bin/sh - # Emit a synthetic JSON changelog totalling >4 MB. printf is a - # POSIX builtin so neither the variable assignment via $(cat ...) - # nor the final printf '%s' "$json" goes through execve. - filler=$(cat /tmp/filler) - printf '[{"id":"big-1","text":"%s","level":3},{"id":"big-2","text":"%s","level":3}]' "$filler" "$filler" - 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 (truncated) ---" - echo "$out" | head -c 2000 - echo "--- exit code: $rc ---" - - if [ "$rc" -ne 0 ]; then - echo "FAIL: expected exit 0 (script should reach the no-token skip after building the JSON payload), got $rc" >&2 - echo "If the message contains 'jq: Argument list too long', the regression has returned: \$changes is being passed via argv (--argjson) instead of stdin." >&2 - exit 1 - fi - if ! echo "$out" | grep -q "::notice::.*View API changes"; then - echo "FAIL: script aborted before emitting the review-page notice" >&2 - exit 1 - fi - if ! echo "$out" | grep -q "No oasdiff-token provided"; then - echo "FAIL: script aborted before reaching the no-token skip; the jq payload-construction step likely blew up on the multi-MB changes array" >&2 - exit 1 - fi - echo "PASS" - - pr_comment_curl_handles_large_payload: - runs-on: ubuntu-latest - name: Test pr-comment curl POST survives a multi-MB payload - # Companion to pr_comment_handles_large_payload. The previous test - # exercises the jq payload-construction path with an empty - # oasdiff_token, which short-circuits past the curl step. This one - # exercises the curl step itself by providing a non-empty token and - # stubbing both oasdiff (multi-MB changelog source) and curl (verifies - # it received the payload via stdin, not via argv). Catches the - # regression class where `curl -d "$payload"` puts a multi-MB body on - # argv and aborts with `curl: Argument list too long` after the jq - # invocation already succeeded — the same ARG_MAX trap one layer - # down. The fix pipes the payload through stdin via `--data-binary @-`. - steps: - - uses: actions/checkout@v6 - - name: Stub oasdiff + curl, run entrypoint with a fake token - run: | - set -euo pipefail - mkdir -p /tmp/stub /tmp/run - - # Same filler strategy as the jq test: dd | tr produces a - # ~2 MB string of 'a' characters and exits 0 cleanly under - # pipefail. - dd if=/dev/zero bs=1024 count=2000 status=none | tr '\0' 'a' > /tmp/filler - - cat > /tmp/stub/oasdiff <<'STUB' - #!/bin/sh - filler=$(cat /tmp/filler) - printf '[{"id":"big-1","text":"%s","level":3},{"id":"big-2","text":"%s","level":3}]' "$filler" "$filler" - STUB - chmod +x /tmp/stub/oasdiff - - # Stub curl: consume the POST body from stdin, record its - # byte count to a side file the assertions below can read, - # and emit the success-response shape the entrypoint expects - # from `-s -w "\n%{http_code}"` (body then a newline then the - # HTTP status code). - cat > /tmp/stub/curl <<'STUB' - #!/bin/sh - body=$(cat) - printf '%s' "$body" | wc -c > /tmp/run/curl-bytes - printf '{"review_token":"stub-token-uuid"}\n200\n' - STUB - chmod +x /tmp/stub/curl - - 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 (truncated) ---" - echo "$out" | head -c 2000 - echo "--- exit code: $rc ---" - - if [ "$rc" -ne 0 ]; then - echo "FAIL: expected exit 0, got $rc" >&2 - if echo "$out" | grep -q "curl: Argument list too long"; then - echo "The regression has returned: \$payload is being passed via curl argv (-d) instead of stdin (--data-binary @-)." >&2 - fi - exit 1 - fi - if [ ! -f /tmp/run/curl-bytes ]; then - echo "FAIL: curl stub was never invoked; script aborted before reaching the POST" >&2 - exit 1 - fi - curl_bytes=$(cat /tmp/run/curl-bytes | tr -d ' ') - echo "curl received: ${curl_bytes} bytes" - if [ "$curl_bytes" -lt 4000000 ]; then - echo "FAIL: curl stub received only ${curl_bytes} bytes; expected >4 MB (proves payload made it through stdin)" >&2 - exit 1 - fi - if ! echo "$out" | grep -q "::notice::.*View API changes"; then - echo "FAIL: script aborted before emitting the review-page notice" >&2 - exit 1 - fi - echo "PASS" - - pr_comment_free_review_url_preserves_https_base: - runs-on: ubuntu-latest - name: Test pr-comment free review URL keeps the https:// scheme on URL-style base - # Regression test for a bug surfaced by oasdiff-test/test#59: when the - # workflow passes a raw.githubusercontent.com URL as the base input - # (as the integration-test repo does), the entrypoint's - # base_path=$(echo "$base" | sed 's/.*://') - # stripped the "https:" prefix and left a broken "//raw.github..." - # URL. The free /review page then tried to GET that broken URL and - # rendered "access denied" with no useful diagnostic — because the - # generic access-denied screen masked the real cause (malformed URL). - # The fix passes URL-shaped inputs through unchanged. - steps: - - uses: actions/checkout@v6 - - name: Stub oasdiff to a no-changes spec - run: | - set -euo pipefail - mkdir -p /tmp/stub - cat > /tmp/stub/oasdiff <<'STUB' - #!/bin/sh - echo '[]' - STUB - chmod +x /tmp/stub/oasdiff - - mkdir -p /tmp/run - export GITHUB_REF=refs/pull/123/merge - export GITHUB_REPOSITORY=oasdiff-test/test - export GITHUB_SHA=deadbeef - export GITHUB_BASE_REF=main - export GITHUB_STEP_SUMMARY=/tmp/run/step-summary - cat > /tmp/run/event.json <&1) - echo "--- entrypoint output ---" - echo "$out" - - if ! echo "$out" | grep -q "::notice::.*View API changes"; then - echo "FAIL: notice line missing" >&2 - exit 1 - fi - notice=$(echo "$out" | grep "::notice::.*View API changes") - # The encoded base_file= param must contain the full URL, not the - # stripped "//raw..." form. https:// URL-encodes to https%3A%2F%2F. - if echo "$notice" | grep -q 'base_file=https%3A%2F%2Fraw.githubusercontent.com'; then - echo "PASS: full https://raw... URL preserved in base_file" - elif echo "$notice" | grep -q 'base_file=%2F%2Fraw.githubusercontent.com'; then - echo "FAIL: 'https:' was stripped from base — the strip_ref_prefix URL guard is missing" >&2 - echo "notice line: $notice" >&2 - exit 1 - else - echo "FAIL: base_file= param did not contain the raw.githubusercontent.com host as expected" >&2 - echo "notice line: $notice" >&2 - exit 1 - fi - - pr_comment_free_review_url_strips_git_ref_prefix: - runs-on: ubuntu-latest - name: "Test pr-comment free review URL strips origin/main: prefix from git-ref base" - # Companion to pr_comment_free_review_url_preserves_https_base: when - # the base input is git-ref form (origin/main:openapi.yaml), the - # prefix must still be stripped so the /review page receives just the - # path. The previous sed-based implementation handled this correctly; - # this test guards the case-branch refactor against breaking it. - steps: - - uses: actions/checkout@v6 - - name: Stub oasdiff to a no-changes spec - run: | - set -euo pipefail - mkdir -p /tmp/stub - cat > /tmp/stub/oasdiff <<'STUB' - #!/bin/sh - echo '[]' - 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) - echo "--- entrypoint output ---" - echo "$out" - - notice=$(echo "$out" | grep "::notice::.*View API changes") - # base_file should be just "multi-file/openapi.yaml" (URL-encoded slash), - # not the full "origin/main:multi-file/openapi.yaml". - if echo "$notice" | grep -q 'base_file=multi-file%2Fopenapi.yaml'; then - echo "PASS: origin/main: prefix stripped from base" - else - echo "FAIL: base_file= did not have the git-ref prefix stripped" >&2 - echo "notice line: $notice" >&2 - exit 1 - fi From 134472da50a1c1bedd3a84b9b92d0244cb6c1fb5 Mon Sep 17 00:00:00 2001 From: Reuven Harrison Date: Sat, 23 May 2026 12:53:45 +0300 Subject: [PATCH 3/4] test: cover the free /review URL for breaking and changelog Add the breaking/changelog equivalents of the existing pr-comment free-review-URL jobs: assert a URL-shaped base survives intact in base_file= (the regression this PR fixes) and a git-ref base is stripped to a bare path. Locks the duplicated strip_ref_prefix helper against drift in each action. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/test-breaking.yaml | 105 ++++++++++++++++++++++++++ .github/workflows/test-changelog.yaml | 101 +++++++++++++++++++++++++ 2 files changed, 206 insertions(+) diff --git a/.github/workflows/test-breaking.yaml b/.github/workflows/test-breaking.yaml index 625c5fc..08d16db 100644 --- a/.github/workflows/test-breaking.yaml +++ b/.github/workflows/test-breaking.yaml @@ -273,3 +273,108 @@ jobs: exit 1 fi + breaking_free_review_url_preserves_https_base: + runs-on: ubuntu-latest + name: Test breaking free review URL keeps the https:// scheme on URL-style base + # Regression test for the bug class fixed in pr-comment (#120): the naive + # base_path=$(echo "$base" | sed 's/.*://') strips "https:" from a + # URL-shaped base and leaves a broken "//raw.github..." in the free + # /review link, which the page renders as a misleading access-denied + # screen. The strip_ref_prefix helper passes URLs through unchanged. + steps: + - uses: actions/checkout@v6 + - name: Stub oasdiff to report a change so the notice fires + run: | + set -euo pipefail + mkdir -p /tmp/stub + cat > /tmp/stub/oasdiff <<'STUB' + #!/bin/sh + echo '1 breaking change(s)' + STUB + chmod +x /tmp/stub/oasdiff + + mkdir -p /tmp/run + export GITHUB_REPOSITORY=oasdiff-test/test + export GITHUB_SHA=deadbeef + export GITHUB_BASE_REF=main + export GITHUB_OUTPUT=/tmp/run/github-output + export GITHUB_STEP_SUMMARY=/tmp/run/step-summary + cat > /tmp/run/event.json <&1) + echo "--- entrypoint output ---" + echo "$out" + + if ! echo "$out" | grep -q "::notice::.*breaking changes"; then + echo "FAIL: review-URL notice line missing" >&2 + exit 1 + fi + notice=$(echo "$out" | grep "::notice::.*breaking changes") + if echo "$notice" | grep -q 'base_file=https%3A%2F%2Fraw.githubusercontent.com'; then + echo "PASS: full https://raw... URL preserved in base_file" + elif echo "$notice" | grep -q 'base_file=%2F%2Fraw.githubusercontent.com'; then + echo "FAIL: 'https:' was stripped from base, the strip_ref_prefix URL guard is missing" >&2 + echo "notice line: $notice" >&2 + exit 1 + else + echo "FAIL: base_file= param did not contain the raw.githubusercontent.com host as expected" >&2 + echo "notice line: $notice" >&2 + exit 1 + fi + + breaking_free_review_url_strips_git_ref_prefix: + runs-on: ubuntu-latest + name: "Test breaking free review URL strips origin/main: prefix from git-ref base" + # Companion to breaking_free_review_url_preserves_https_base: a git-ref + # base (origin/main:openapi.yaml) must still have its prefix stripped so + # the /review page receives just the path. + steps: + - uses: actions/checkout@v6 + - name: Stub oasdiff to report a change so the notice fires + run: | + set -euo pipefail + mkdir -p /tmp/stub + cat > /tmp/stub/oasdiff <<'STUB' + #!/bin/sh + echo '1 breaking change(s)' + STUB + chmod +x /tmp/stub/oasdiff + + mkdir -p /tmp/run + export GITHUB_REPOSITORY=foo/bar + export GITHUB_SHA=deadbeef + export GITHUB_BASE_REF=main + export GITHUB_OUTPUT=/tmp/run/github-output + export GITHUB_STEP_SUMMARY=/tmp/run/step-summary + cat > /tmp/run/event.json <&1) + echo "--- entrypoint output ---" + echo "$out" + + if ! echo "$out" | grep -q "::notice::.*breaking changes"; then + echo "FAIL: review-URL notice line missing" >&2 + exit 1 + fi + notice=$(echo "$out" | grep "::notice::.*breaking changes") + if echo "$notice" | grep -q 'base_file=multi-file%2Fopenapi.yaml'; then + echo "PASS: origin/main: prefix stripped from base" + else + echo "FAIL: base_file= did not have the git-ref prefix stripped" >&2 + echo "notice line: $notice" >&2 + exit 1 + fi + diff --git a/.github/workflows/test-changelog.yaml b/.github/workflows/test-changelog.yaml index 917ad49..f2cbe16 100644 --- a/.github/workflows/test-changelog.yaml +++ b/.github/workflows/test-changelog.yaml @@ -87,3 +87,104 @@ jobs: exit 1 fi + changelog_free_review_url_preserves_https_base: + runs-on: ubuntu-latest + name: Test changelog free review URL keeps the https:// scheme on URL-style base + # Regression test for the bug class fixed in pr-comment (#120): a + # URL-shaped base must survive intact in the free /review link rather + # than being mangled to "//raw.github..." by the naive ref-prefix strip. + steps: + - uses: actions/checkout@v6 + - name: Stub oasdiff to report a change so the notice fires + run: | + set -euo pipefail + mkdir -p /tmp/stub + cat > /tmp/stub/oasdiff <<'STUB' + #!/bin/sh + echo '1 change(s)' + STUB + chmod +x /tmp/stub/oasdiff + + mkdir -p /tmp/run + export GITHUB_REPOSITORY=oasdiff-test/test + export GITHUB_SHA=deadbeef + export GITHUB_BASE_REF=main + export GITHUB_OUTPUT=/tmp/run/github-output + export GITHUB_STEP_SUMMARY=/tmp/run/step-summary + cat > /tmp/run/event.json <&1) + echo "--- entrypoint output ---" + echo "$out" + + if ! echo "$out" | grep -q "::notice::.*API changes"; then + echo "FAIL: review-URL notice line missing" >&2 + exit 1 + fi + notice=$(echo "$out" | grep "::notice::.*API changes") + if echo "$notice" | grep -q 'base_file=https%3A%2F%2Fraw.githubusercontent.com'; then + echo "PASS: full https://raw... URL preserved in base_file" + elif echo "$notice" | grep -q 'base_file=%2F%2Fraw.githubusercontent.com'; then + echo "FAIL: 'https:' was stripped from base, the strip_ref_prefix URL guard is missing" >&2 + echo "notice line: $notice" >&2 + exit 1 + else + echo "FAIL: base_file= param did not contain the raw.githubusercontent.com host as expected" >&2 + echo "notice line: $notice" >&2 + exit 1 + fi + + changelog_free_review_url_strips_git_ref_prefix: + runs-on: ubuntu-latest + name: "Test changelog free review URL strips origin/main: prefix from git-ref base" + # Companion: git-ref base must have its prefix stripped to a bare path. + steps: + - uses: actions/checkout@v6 + - name: Stub oasdiff to report a change so the notice fires + run: | + set -euo pipefail + mkdir -p /tmp/stub + cat > /tmp/stub/oasdiff <<'STUB' + #!/bin/sh + echo '1 change(s)' + STUB + chmod +x /tmp/stub/oasdiff + + mkdir -p /tmp/run + export GITHUB_REPOSITORY=foo/bar + export GITHUB_SHA=deadbeef + export GITHUB_BASE_REF=main + export GITHUB_OUTPUT=/tmp/run/github-output + export GITHUB_STEP_SUMMARY=/tmp/run/step-summary + cat > /tmp/run/event.json <&1) + echo "--- entrypoint output ---" + echo "$out" + + if ! echo "$out" | grep -q "::notice::.*API changes"; then + echo "FAIL: review-URL notice line missing" >&2 + exit 1 + fi + notice=$(echo "$out" | grep "::notice::.*API changes") + if echo "$notice" | grep -q 'base_file=multi-file%2Fopenapi.yaml'; then + echo "PASS: origin/main: prefix stripped from base" + else + echo "FAIL: base_file= did not have the git-ref prefix stripped" >&2 + echo "notice line: $notice" >&2 + exit 1 + fi + From 4bcf26e564422ad739bae76e201b81472adcb08b Mon Sep 17 00:00:00 2001 From: Reuven Harrison Date: Sat, 23 May 2026 13:40:09 +0300 Subject: [PATCH 4/4] test: invoke changelog entrypoint with bash in free-review URL jobs The runner's /bin/sh is dash, which rejects `set -o pipefail` (exit 2) that changelog/entrypoint.sh sets. Running the entrypoint directly via its shebang therefore failed before producing any output. Invoke it with bash, which supports pipefail (as does the busybox ash the action uses in production). breaking/entrypoint.sh has no pipefail, so its jobs are unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/test-changelog.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-changelog.yaml b/.github/workflows/test-changelog.yaml index f2cbe16..54ded37 100644 --- a/.github/workflows/test-changelog.yaml +++ b/.github/workflows/test-changelog.yaml @@ -118,7 +118,10 @@ jobs: export PATH=/tmp/stub:$PATH base_url='https://raw.githubusercontent.com/oasdiff-test/test/main/simple.yaml' - out=$(./changelog/entrypoint.sh \ + # Invoke with bash: the entrypoint's #!/bin/sh is dash on the runner, + # which rejects `set -o pipefail` (exit 2). In production it runs under + # the container's busybox ash, which supports it. + out=$(bash ./changelog/entrypoint.sh \ "$base_url" 'simple.yaml' \ '' '' '' '' '' '' '' '' '' '' '' '' 2>&1) echo "--- entrypoint output ---" @@ -169,7 +172,9 @@ jobs: export GITHUB_EVENT_PATH=/tmp/run/event.json export PATH=/tmp/stub:$PATH - out=$(./changelog/entrypoint.sh \ + # Invoke with bash (see the companion job): dash on the runner rejects + # the entrypoint's `set -o pipefail`. + out=$(bash ./changelog/entrypoint.sh \ 'origin/main:multi-file/openapi.yaml' 'HEAD:multi-file/openapi.yaml' \ '' '' '' '' '' '' '' '' '' '' '' '' 2>&1) echo "--- entrypoint output ---"