-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaction.yml
More file actions
201 lines (191 loc) · 8.29 KB
/
action.yml
File metadata and controls
201 lines (191 loc) · 8.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
name: 'API Surface Check for PHP'
description: 'Comment on a pull request with the public API surface changes (added, removed, modified symbols) detected.'
author: 'Composer'
branding:
icon: 'eye'
color: 'blue'
inputs:
paths:
description: 'Pathspec patterns of files to analyze (whitespace or newline separated).'
required: false
default: 'src/**/*.php'
source-roots:
description: 'Directories used to resolve parent classes/interfaces, comma or whitespace separated.'
required: false
default: 'src'
output-dir:
description: 'Directory inside the runner where the analysis output is written.'
required: false
default: 'api-surface-result'
include-internal:
description: 'Include symbols marked with @internal or @private.'
required: false
default: 'false'
types:
description: 'Comma-separated list of symbol kinds to report (class,interface,trait,enum,method,property,constant).'
required: false
default: 'class,interface,trait,enum,method,property,constant'
visibility:
description: 'Comma-separated visibilities to report (public,protected,private).'
required: false
default: 'public,protected'
show-removed:
description: 'Include removed symbols in the report.'
required: false
default: 'true'
show-modified:
description: 'Include symbols whose signature changed.'
required: false
default: 'true'
comment-marker:
description: 'HTML comment marker used to identify previous bot comments.'
required: false
default: '<!-- api-surface-bot -->'
heading:
description: 'Markdown heading for the comment body.'
required: false
default: '## API Surface Changes'
working-directory:
description: 'Directory the analysis runs from (must be a git repo with the analyzed files). Defaults to the workflow''s working directory.'
required: false
default: '.'
install-dependencies:
description: 'Run composer install in the analyzed project so vendor classes are resolvable when walking parent hierarchies. Composer is invoked with --no-scripts and --no-plugins so PR-controlled code never executes.'
required: false
default: 'false'
composer-working-directory:
description: 'Directory containing the analyzed project''s composer.json. Empty (default) means use the value of working-directory.'
required: false
default: ''
runs:
using: 'composite'
steps:
- name: Resolve PR via API
id: pr
shell: bash
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
HEAD_OWNER: ${{ github.event.workflow_run.head_repository.owner.login }}
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
run: |
# Resolve by head ref rather than commit SHA: the commits/{sha}/pulls
# endpoint returns empty for fork PRs even when the SHA is the live
# head of an open PR. The head-ref query handles same-repo and fork
# PRs uniformly.
response=$(gh api "repos/${REPO}/pulls?state=all&head=${HEAD_OWNER}:${HEAD_BRANCH}&per_page=1")
number=$(echo "$response" | jq -r '.[0].number // empty')
base_ref=$(echo "$response" | jq -r '.[0].base.ref // empty')
if [[ -z "$number" || -z "$base_ref" ]]; then
echo "Could not resolve PR for ${HEAD_OWNER}:${HEAD_BRANCH}; nothing to do."
exit 0
fi
echo "number=${number}" >> "$GITHUB_OUTPUT"
echo "base-ref=${base_ref}" >> "$GITHUB_OUTPUT"
- name: Checkout PR head (data only)
if: steps.pr.outputs.number
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: refs/pull/${{ steps.pr.outputs.number }}/head
fetch-depth: 1
persist-credentials: false
- name: Fetch merge base
id: base
if: steps.pr.outputs.number
shell: bash
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
BASE_REF: ${{ steps.pr.outputs.base-ref }}
run: |
# Resolve the merge base server-side: shallow fetches mean the local
# repo can't compute it, and diffing against the BASE_REF tip produces
# garbage when the PR is rebased onto an older commit (everything main
# has gained since the rebase point shows up as "removed in PR").
head_sha=$(git rev-parse HEAD)
merge_base=$(gh api "repos/${REPO}/compare/${BASE_REF}...${head_sha}" --jq '.merge_base_commit.sha')
if [[ -z "${merge_base}" || "${merge_base}" == "null" ]]; then
echo "::error::Could not determine merge base for ${BASE_REF}...${head_sha}"
exit 1
fi
git fetch --no-tags --depth=1 origin "${merge_base}"
echo "merge-base=${merge_base}" >> "$GITHUB_OUTPUT"
- name: Preflight scan
id: preflight
if: steps.pr.outputs.number
shell: bash
working-directory: ${{ inputs.working-directory }}
env:
BASE_REF: ${{ steps.base.outputs.merge-base }}
PATHS_GLOB: ${{ inputs.paths }}
OUTPUT_FILE: api-surface-preflight.txt
ACTION_PATH: ${{ github.action_path }}
run: |
bash "${ACTION_PATH}/scripts/preflight.sh"
echo "should-run=$(cat api-surface-preflight.txt)" >> "$GITHUB_OUTPUT"
- name: Setup PHP
if: steps.pr.outputs.number && steps.preflight.outputs.should-run == 'true'
uses: shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc # 2.37.1
with:
coverage: 'none'
php-version: 8.5
tools: composer:v2
- name: Install action dependencies
if: steps.pr.outputs.number && steps.preflight.outputs.should-run == 'true'
shell: bash
working-directory: ${{ github.action_path }}
run: composer install --no-dev --no-interaction --no-progress
- name: Install project dependencies
id: project-deps
if: steps.pr.outputs.number && steps.preflight.outputs.should-run == 'true' && inputs.install-dependencies == 'true'
shell: bash
working-directory: ${{ inputs.composer-working-directory || inputs.working-directory }}
run: |
if [[ ! -f composer.json ]]; then
echo "::warning::install-dependencies=true but no composer.json at $(pwd); proceeding without vendor resolution."
exit 0
fi
# Safety: --no-scripts blocks composer.json scripts; --no-plugins blocks
# installed composer-plugin code. Together they reduce composer install
# to "fetch tarballs and write autoload" — no PR-controlled code runs.
if ! composer install --no-scripts --no-plugins --no-dev --prefer-dist --no-interaction --no-progress --ignore-platform-reqs; then
echo "::warning::composer install failed; proceeding without vendor resolution."
exit 0
fi
if [[ -d vendor ]]; then
echo "project-path=$(pwd)" >> "$GITHUB_OUTPUT"
fi
- name: Run analysis
id: detect
if: steps.pr.outputs.number && steps.preflight.outputs.should-run == 'true'
shell: bash
working-directory: ${{ inputs.working-directory }}
env:
BASE_REF: ${{ steps.base.outputs.merge-base }}
PATHS_GLOB: ${{ inputs.paths }}
SOURCE_ROOTS: ${{ inputs.source-roots }}
COMPOSER_PROJECT_PATH: ${{ steps.project-deps.outputs.project-path }}
OUTPUT_DIR: ${{ inputs.output-dir }}
INCLUDE_INTERNAL: ${{ inputs.include-internal }}
TYPES: ${{ inputs.types }}
VISIBILITY: ${{ inputs.visibility }}
SHOW_REMOVED: ${{ inputs.show-removed }}
SHOW_MODIFIED: ${{ inputs.show-modified }}
COMMENT_MARKER: ${{ inputs.comment-marker }}
HEADING: ${{ inputs.heading }}
PR_NUMBER: ${{ steps.pr.outputs.number }}
ACTION_PATH: ${{ github.action_path }}
run: bash "${ACTION_PATH}/scripts/detect.sh"
# Always run post-comment when we have a PR. With analysis skipped,
# comment-body.txt is missing/empty and post-comment deletes any
# previously-posted bot comment.
- name: Post or update comment
if: steps.pr.outputs.number
shell: bash
env:
GH_TOKEN: ${{ github.token }}
OUTPUT_DIR: ${{ inputs.output-dir }}
COMMENT_MARKER: ${{ inputs.comment-marker }}
PR_NUMBER: ${{ steps.pr.outputs.number }}
ACTION_PATH: ${{ github.action_path }}
run: bash "${ACTION_PATH}/scripts/post-comment.sh"