Skip to content

Commit f40250f

Browse files
committed
first cut at API checking
It turns out there is a consumer of cabal-install-solver, so I have added it to API generation and checking.
1 parent 1077091 commit f40250f

File tree

8 files changed

+22109
-0
lines changed

8 files changed

+22109
-0
lines changed

.github/workflows/check-api.skip.yml

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
name: Check API Skip
3+
4+
# This Workflow is special and contains a workaround for a known limitation of GitHub CI.
5+
#
6+
# The problem: We don't want to run the "check-api" jobs on PRs which contain only changes
7+
# to the docs, since these jobs take a long time to complete without providing any benefit.
8+
# We therefore use path-filtering in the workflow triggers for the bootstrap jobs, namely
9+
# "paths-ignore: doc/**". But the "Check API post job" is a required job, therefore a PR cannot
10+
# be merged unless the "Check API post job" completes succesfully, which it doesn't do if we
11+
# filter it out.
12+
#
13+
# The solution: We use a second job with the same name which always returns the exit code 0.
14+
# The logic implemented for "required" workflows accepts if 1) at least one job with that name
15+
# runs through, AND 2) If multiple jobs of that name exist, then all jobs of that name have to
16+
# finish successfully.
17+
on:
18+
push:
19+
paths:
20+
- 'doc/**'
21+
- '**/README.md'
22+
- 'CONTRIBUTING.md'
23+
- "changelog.d/**"
24+
# only top level for these, because various test packages have them too
25+
- "*/ChangeLog.md"
26+
- "*/changelog.md"
27+
- "release-notes/**"
28+
branches:
29+
- master
30+
pull_request:
31+
paths:
32+
- 'doc/**'
33+
- '**/README.md'
34+
- 'CONTRIBUTING.md'
35+
- "changelog.d/**"
36+
- "*/ChangeLog.md"
37+
- "*/changelog.md"
38+
- "release-notes/**"
39+
release:
40+
types:
41+
- created
42+
43+
jobs:
44+
check-api-post-job:
45+
if: always()
46+
name: Check API post job
47+
runs-on: ubuntu-latest
48+
steps:
49+
- run: exit 0

.github/workflows/check-api.yml

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
name: Check API
2+
3+
on:
4+
push:
5+
paths-ignore:
6+
- "doc/**"
7+
- "**/README.md"
8+
- "CONTRIBUTING.md"
9+
- "changelog.d/**"
10+
# only top level for these, because various test packages have them too
11+
- "*/ChangeLog.md"
12+
- "*/changelog.md"
13+
- "release-notes/**"
14+
branches:
15+
- master
16+
pull_request:
17+
paths-ignore:
18+
- "doc/**"
19+
- "**/README.md"
20+
- "CONTRIBUTING.md"
21+
- "changelog.d/**"
22+
- "*/ChangeLog.md"
23+
- "*/changelog.md"
24+
- "release-notes/**"
25+
release:
26+
types:
27+
- created
28+
workflow_call:
29+
30+
jobs:
31+
check-api:
32+
name: Check API using ${{ matrix.sys.os }} ghc-${{ matrix.ghc }}
33+
runs-on: ${{ matrix.sys.os }}
34+
strategy:
35+
matrix:
36+
# we check API only on one platform and ghc release, since it shouldn't
37+
# vary elsewhere and the API tracer is sensitive to both
38+
sys:
39+
- { os: ubuntu-latest }
40+
ghc:
41+
[
42+
# print-api only supports a small subset of ghc versions
43+
"9.10.1",
44+
]
45+
46+
steps:
47+
48+
- uses: actions/checkout@v4
49+
50+
- uses: haskell-actions/setup@v2
51+
id: setup-haskell
52+
with:
53+
ghc-version: ${{ matrix.ghc }}
54+
cabal-version: 3.12.1.0 # see https://github.com/haskell/cabal/pull/10251
55+
ghcup-release-channel: https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-prereleases-0.0.8.yaml
56+
57+
# I was going to use the canned action, but it only supports a single package and reinstalls the same binary each time
58+
- name: Install print-api
59+
run: |
60+
wget -q https://github.com/Kleidukos/print-api/releases/download/v0.1.0.1/print-api-0.1.0.1-Linux-static-${{ matrix.ghc }}-x86_64.tar.gz
61+
tar -xzf print-api-0.1.0.1-Linux-static-${{ matrix.ghc }}-x86_64.tar.gz
62+
mkdir -p "$HOME/.local/bin"
63+
mv print-api "$HOME/.local/bin/print-api"
64+
chmod +x "$HOME/.local/bin/print-api"
65+
echo "$HOME/.local/bin" >> $GITHUB_PATH
66+
67+
# print-api needs environment files. It also doesn't make a lot of sense to use the cached builds, sadly,
68+
# since they're special in different ways (bootstrap and validate) and we want a vanilla build. And there
69+
# isn't enough cache space to make a third cache, even though this is a very limited build.
70+
- name: Build Cabal with environment files
71+
run: cabal build Cabal-syntax Cabal cabal-install-solver --write-ghc-environment-files=always
72+
73+
- name: Generate Cabal-syntax, Cabal, and cabal-install-solver APIs
74+
run: make generate-api
75+
76+
# upload the new API records as artifacts, since there's no guarantee that a contributor could produce
77+
# them (wrong platform or ghc version). This must happen _before_ we check the API, because the
78+
# point is to have them available on API mismatch so they can be updated.
79+
- uses: actions/upload-artifact@v4
80+
with:
81+
name: Cabal-api
82+
path: '*.api'
83+
84+
- name: Check Cabal-syntax, Cabal, and cabal-install-solver APIs
85+
run: |
86+
rc=0
87+
if diff -c Cabal-syntax/Cabal-syntax-${{ matrix.ghc }}.api Cabal-syntax-${{ matrix.ghc }}.api >api.tmp; then
88+
:
89+
else
90+
echo "Cabal-syntax API changed"
91+
if [ $(wc -l < api.tmp) -lt 50 ]; then
92+
cat api.tmp
93+
else
94+
echo Diff too large for GitHub viewer
95+
fi
96+
rc=1
97+
fi
98+
if diff -c Cabal/Cabal-${{ matrix.ghc }}.api Cabal-${{ matrix.ghc }}.api >api.tmp; then
99+
:
100+
else
101+
echo "Cabal API changed"
102+
if [ $(wc -l < api.tmp) -lt 50 ]; then
103+
cat api.tmp
104+
else
105+
echo Diff too large for GitHub viewer
106+
fi
107+
rc=1
108+
fi
109+
if diff -c cabal-install-solver/cabal-install-solver-${{ matrix.ghc }}.api cabal-install-solver-${{ matrix.ghc }}.api >api.tmp; then
110+
:
111+
else
112+
echo "cabal-install-solver API changed"
113+
if [ $(wc -l < api.tmp) -lt 50 ]; then
114+
cat api.tmp
115+
else
116+
echo Diff too large for GitHub viewer
117+
fi
118+
rc=1
119+
fi
120+
if [ $rc -ne 0 ]; then
121+
echo "The new APIs are in the artifact uploaded in the previous step."
122+
exit $rc
123+
fi
124+
125+
# See check-api.skip.yml for why we need this
126+
- check-api-post-job:
127+
if: always()
128+
name: Check API post job
129+
runs-on: ubuntu-latest
130+
needs: check-api
131+
132+
steps:
133+
- run: |
134+
echo "jobs info: ${{ toJSON(needs) }}"
135+
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
136+
run: exit 1

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,6 @@ bench.html
107107

108108
# ignore the downloaded binary files
109109
scripts/release/binary-downloads/
110+
111+
# ignore generated API files
112+
/*.api

CONTRIBUTING.md

+38
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,44 @@ We use automated whitespace convention checking. Violations can be fixed by
193193
running [fix-whitespace](https://hackage.haskell.org/package/fix-whitespace). If
194194
you push a fix of a whitespace violation, please do so in a _separate commit_.
195195

196+
API Changes and Check API job
197+
-----------------------------
198+
199+
The `Check API` job tests the `Cabal`, `Cabal-syntax`, and `cabal-install-solver`
200+
packages for API changes. It's useful to indicate when a changelog is needed and
201+
which PRs aren't appropriate for backports.
202+
203+
If the `Check API` job fails, you will find in its build artifacts (at the bottom
204+
of the "upload artifacts" step, immediately before the actual API check) a ZIP file
205+
containing the new API records. You can download this and replace the existing API
206+
descriptions, which can be found in the package top level directories, with `.api`
207+
suffixes. Generating them locally is possible with the [check-api tool](https://github.com/Kleidukos/print-api), but
208+
is not guaranteed to produce the same result as the CI job does.
209+
210+
If you do wish to generate a local API record, install [`print-api`](https://github.com/Kleidukos/print-api/releases/tag/v0.1.0.1) and
211+
run it on the `Cabal`, `Cabal-syntax`, and `cabal-install-solver` packages, from
212+
the top level directory of the Cabal repo:
213+
214+
make generate-api
215+
216+
You will need `ghc-9.10.1` to be on `$PATH`; `ghcup` is the easiest way to do this.
217+
218+
The resulting `Cabal-syntax.api`, `Cabal.api`, and `cabal-install-solver` files
219+
can then be compared to the ones in the `Cabal-syntax`, `Cabal`, and
220+
`cabal-install-solver` package directories.
221+
222+
make check-api
223+
224+
If necessary, you can then install the API records:
225+
226+
make update-api
227+
228+
It is also possible to do this individually; see the `Makefile`.
229+
230+
Note that different compiler versions and different architectures will alter the
231+
output. It is not expected that different Linux distributions will, but you may
232+
need to use the static build if you aren't using Ubuntu 22.04.
233+
196234
Other Conventions
197235
-----------------
198236

0 commit comments

Comments
 (0)