11# ## ### Api Spec
22# ##
33# ## Re-exports the OpenAPI spec (`public/api-spec-v2.{yaml,json}`) from the
4- # ## API Platform configuration and fails if the result differs from the
5- # ## committed copy.
4+ # ## API Platform configuration. Posts a PR comment **only** when something
5+ # ## needs attention — spec drift, non-breaking changes, or breaking changes —
6+ # ## and stays quiet otherwise. The comment is edited in place across runs
7+ # ## so a PR carries at most one comment from this workflow.
68# ##
79# ## #### Assumptions
810# ##
911# ## 1. A docker compose service named `phpfpm` can be run and `bin/console`
1012# ## can be run inside the `phpfpm` service.
1113# ## 2. The committed `public/api-spec-v2.{yaml,json}` are kept in sync with
12- # ## the API Platform config .
14+ # ## the API Platform configuration via `composer update-api-spec` .
1315
14- on : pull_request
16+ on :
17+ pull_request :
18+ paths :
19+ - " src/**/*.php"
20+ - " config/**"
21+ - " composer.json"
22+ - " composer.lock"
23+ - " public/api-spec-v2.yaml"
24+ - " public/api-spec-v2.json"
25+ - " docker-compose.yml"
1526
1627name : Api Spec
1728
1829env :
1930 COMPOSE_USER : runner
2031
2132jobs :
22- apispec :
33+ api-spec-export :
34+ name : Ensure API specification is up to date
2335 runs-on : ubuntu-latest
24- name : API Specification validation
36+ permissions :
37+ contents : read
38+ pull-requests : write
2539 steps :
2640 - name : Checkout
2741 uses : actions/checkout@v6
28- with :
29- fetch-depth : 2
3042
3143 - name : Cache vendor
3244 uses : actions/cache@v5
@@ -41,14 +53,108 @@ jobs:
4153 - name : Install Dependencies
4254 run : docker compose run --rm phpfpm composer install
4355
44- - name : Export specifications (yaml)
45- run : docker compose run --rm phpfpm bin/console api:openapi:export --yaml --output=public/api-spec-v2.yaml --no-interaction
56+ - name : Export specifications
57+ run : |
58+ docker compose run --rm phpfpm bin/console api:openapi:export --yaml --output=public/api-spec-v2.yaml --no-interaction
59+ docker compose run --rm phpfpm bin/console api:openapi:export --output=public/api-spec-v2.json --no-interaction
60+
61+ - name : Check for uncommitted changes
62+ id : git-diff-spec
63+ continue-on-error : true
64+ run : git diff --diff-filter=ACMRT --exit-code public/api-spec-v2.yaml public/api-spec-v2.json
65+
66+ - name : Comment PR if spec is outdated
67+ if : steps.git-diff-spec.outcome == 'failure'
68+ env :
69+ GH_TOKEN : ${{ github.token }}
70+ run : |
71+ gh pr comment ${{ github.event.pull_request.number }} \
72+ --body "$(cat <<'EOF'
73+ ## API specification not up to date
74+
75+ The committed API specification files do not match the exported output.
76+
77+ Please run the following command, then commit and push the changes:
78+
79+ ```shell
80+ docker compose exec phpfpm composer update-api-spec
81+ ```
82+ EOF
83+ )" \
84+ --create-if-none --edit-last
85+
86+ - name : Fail if spec is outdated
87+ if : steps.git-diff-spec.outcome == 'failure'
88+ run : exit 1
89+
90+ api-spec-breaking-changes :
91+ name : Detect breaking changes in API specification
92+ runs-on : ubuntu-latest
93+ needs : [api-spec-export]
94+ permissions :
95+ contents : read
96+ pull-requests : write
97+ steps :
98+ - name : Checkout
99+ uses : actions/checkout@v6
100+
101+ - name : Fetch base branch for comparison
102+ run : git fetch --depth=1 origin ${{ github.base_ref }}
103+
104+ - name : Detect breaking changes
105+ id : breaking
106+ continue-on-error : true
107+ uses : oasdiff/oasdiff-action/breaking@v0.0.44
108+ with :
109+ base : " origin/${{ github.base_ref }}:public/api-spec-v2.yaml"
110+ revision : " public/api-spec-v2.yaml"
111+ fail-on : ERR
112+
113+ - name : Generate changelog
114+ id : changelog
115+ continue-on-error : true
116+ uses : oasdiff/oasdiff-action/changelog@v0.0.44
117+ with :
118+ base : " origin/${{ github.base_ref }}:public/api-spec-v2.yaml"
119+ revision : " public/api-spec-v2.yaml"
120+ format : markdown
121+ output-to-file : changelog.md
122+
123+ # Stay quiet when nothing changed — neither breaking nor non-breaking.
124+ # Comments only fire on the two branches below.
46125
47- - name : Check for changes in specifications (yaml)
48- run : git diff --diff-filter=ACMRT --exit-code public/api-spec-v2.yaml
126+ - name : Comment PR - non-breaking changes
127+ if : steps.breaking.outcome == 'success' && hashFiles('changelog.md') != ''
128+ env :
129+ GH_TOKEN : ${{ github.token }}
130+ run : |
131+ {
132+ echo "## API Specification - Non-breaking changes"
133+ echo ""
134+ cat changelog.md
135+ } > comment.md
136+ gh pr comment ${{ github.event.pull_request.number }} \
137+ --body-file comment.md \
138+ --create-if-none --edit-last
49139
50- - name : Export specifications (json)
51- run : docker compose run --rm phpfpm bin/console api:openapi:export --output=public/api-spec-v2.json --no-interaction
140+ - name : Comment PR - breaking changes
141+ if : steps.breaking.outcome == 'failure'
142+ env :
143+ GH_TOKEN : ${{ github.token }}
144+ run : |
145+ {
146+ echo "## API Specification - Breaking changes detected"
147+ echo ""
148+ if [ -s changelog.md ]; then
149+ cat changelog.md
150+ else
151+ echo "The breaking changes action detected incompatible changes. Review the action logs for details."
152+ fi
153+ } > comment.md
154+ gh pr comment ${{ github.event.pull_request.number }} \
155+ --body-file comment.md \
156+ --create-if-none --edit-last
52157
53- - name : Check for changes in specifications (json)
54- run : git diff --diff-filter=ACMRT --exit-code public/api-spec-v2.json
158+ - name : Fail if breaking changes detected
159+ if : steps.breaking.outcome == 'failure'
160+ run : exit 1
0 commit comments