|
| 1 | +name: Check API |
| 2 | + |
| 3 | +# Informational API diff job: it shows which public API of the Cabal |
| 4 | +# packages changes with respect to the PR base, but it is not a required |
| 5 | +# check and does not block merging. |
| 6 | + |
| 7 | +on: |
| 8 | + pull_request: |
| 9 | + paths-ignore: |
| 10 | + - 'doc/**' |
| 11 | + - '**/*.md' |
| 12 | + - 'changelog.d/**' |
| 13 | + - 'release-notes/**' |
| 14 | + push: |
| 15 | + branches: |
| 16 | + - '3.*' |
| 17 | + paths-ignore: |
| 18 | + - 'doc/**' |
| 19 | + - '**/*.md' |
| 20 | + - 'changelog.d/**' |
| 21 | + - 'release-notes/**' |
| 22 | + workflow_dispatch: |
| 23 | + |
| 24 | +permissions: |
| 25 | + contents: read |
| 26 | + |
| 27 | +env: |
| 28 | + # The API depends on the GHC version, so both revisions are always built |
| 29 | + # with a single pinned GHC. Use the GHC used for releases, see GHC_FOR_RELEASE |
| 30 | + # in .github/workflows/validate.yml, and bump both together. |
| 31 | + GHC_VERSION: "9.10.3" |
| 32 | + |
| 33 | +jobs: |
| 34 | + check-api: |
| 35 | + name: API diff ${{ matrix.package }} |
| 36 | + runs-on: ubuntu-latest |
| 37 | + timeout-minutes: 60 |
| 38 | + strategy: |
| 39 | + fail-fast: false |
| 40 | + matrix: |
| 41 | + package: |
| 42 | + - Cabal-syntax |
| 43 | + - Cabal |
| 44 | + - cabal-install-solver |
| 45 | + - Cabal-hooks |
| 46 | + |
| 47 | + steps: |
| 48 | + # fetch-depth: 0 is needed because packdiff checks out the base |
| 49 | + # revision itself and builds it. |
| 50 | + - uses: actions/checkout@v7 |
| 51 | + with: |
| 52 | + fetch-depth: 0 |
| 53 | + ref: ${{ github.event.pull_request.head.sha }} |
| 54 | + |
| 55 | + - uses: haskell-actions/setup@v2 |
| 56 | + id: setup-haskell |
| 57 | + with: |
| 58 | + ghc-version: ${{ env.GHC_VERSION }} |
| 59 | + cabal-version: latest |
| 60 | + |
| 61 | + # runner.os isn't sufficient for binary compatible caches |
| 62 | + - name: Get runner OS/version for cache keys |
| 63 | + id: get-osver |
| 64 | + run: echo "osver=$ImageOS" >> "$GITHUB_OUTPUT" |
| 65 | + |
| 66 | + - uses: actions/cache@v6 |
| 67 | + with: |
| 68 | + path: ${{ steps.setup-haskell.outputs.cabal-store }} |
| 69 | + key: ${{ steps.get-osver.outputs.osver }}-check-api-${{ env.GHC_VERSION }}-${{ github.sha }} |
| 70 | + restore-keys: ${{ steps.get-osver.outputs.osver }}-check-api-${{ env.GHC_VERSION }}- |
| 71 | + |
| 72 | + - name: "Work around git problem https://bugs.launchpad.net/ubuntu/+source/git/+bug/1993586 (cabal PR #8546)" |
| 73 | + run: git config --global protocol.file.allow always |
| 74 | + |
| 75 | + - name: Install packdiff |
| 76 | + run: | |
| 77 | + mkdir -p "$HOME/.local/bin" |
| 78 | + cabal install packdiff --project-file=cabal.project.api --installdir="$HOME/.local/bin" --overwrite-policy=always |
| 79 | +
|
| 80 | + - name: Run packdiff |
| 81 | + run: | |
| 82 | + set -o pipefail |
| 83 | + case "${{ github.event_name }}" in |
| 84 | + pull_request) base="${{ github.event.pull_request.base.sha }}" ;; |
| 85 | + push) base="${{ github.event.before }}" ;; |
| 86 | + *) base="" ;; |
| 87 | + esac |
| 88 | + if [ -z "$base" ] || [ "$base" = "0000000000000000000000000000000000000000" ]; then |
| 89 | + base="$(git merge-base origin/master HEAD)" |
| 90 | + fi |
| 91 | + head="$(git rev-parse HEAD)" |
| 92 | + echo "Diffing ${{ matrix.package }} API: $base -> $head" |
| 93 | + packdiff diff ${{ matrix.package }} "$base" ${{ matrix.package }} "$head" | tee api-diff.txt |
| 94 | +
|
| 95 | + sed -n '/API Annotations/,$p' api-diff.txt > api-diff-summary.txt |
| 96 | + { |
| 97 | + echo "## API diff \`${{ matrix.package }}\`: \`$(git rev-parse --short "$base")\` -> \`$(git rev-parse --short "$head")\`" |
| 98 | + echo '```' |
| 99 | + cat api-diff-summary.txt |
| 100 | + echo '```' |
| 101 | + } >> "$GITHUB_STEP_SUMMARY" |
| 102 | +
|
| 103 | + # Diff entries look like "[C] Module.Name"; the annotation legend |
| 104 | + # ("[A] : Added") has a colon right after the marker and must not match. |
| 105 | + if grep -qE '^\[[ARC]\] [^ :]' api-diff.txt; then |
| 106 | + echo "::error::The API of ${{ matrix.package }} changed with respect to the base revision. See the job summary for the diff. Consider a changelog entry, a PVP version bump, and whether the change should be backported." |
| 107 | + exit 1 |
| 108 | + else |
| 109 | + echo "No API changes detected in ${{ matrix.package }}." |
| 110 | + fi |
0 commit comments