Skip to content

Commit debd696

Browse files
Merge branch '7.2' into 7.3
* 7.2: Turn fabbot into a reusable github action
2 parents 106eb2a + 64feae6 commit debd696

File tree

2 files changed

+255
-0
lines changed

2 files changed

+255
-0
lines changed

.github/workflows/callable-fabbot.yml

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
name: Fabbot
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
package:
7+
required: true
8+
type: string
9+
10+
env:
11+
GH_TOKEN: ${{ github.token }}
12+
13+
jobs:
14+
check:
15+
name: Checks
16+
runs-on: ubuntu-24.04
17+
steps:
18+
- name: Checkout code
19+
run: |
20+
# Checkout patched files using the REST API and install dependencies concurrently
21+
PR_NUMBER="${{ github.event.pull_request.number }}"
22+
PR_HEAD_SHA="${{ github.event.pull_request.head.sha }}"
23+
REPO_OWNER="${{ github.repository_owner }}"
24+
REPO_NAME="${{ github.event.repository.name }}"
25+
26+
pip install codespell &
27+
composer global require -q friendsofphp/php-cs-fixer seld/jsonlint symfony/yaml &
28+
29+
mkdir a
30+
31+
gh api -H "Accept: application/vnd.github.v3.raw" \
32+
"/repos/$REPO_OWNER/$REPO_NAME/contents/.php-cs-fixer.dist.php?ref=$PR_HEAD_SHA" \
33+
> a/.php-cs-fixer.dist.php || rm a/.php-cs-fixer.dist.php &
34+
35+
gh api --paginate "/repos/$REPO_OWNER/$REPO_NAME/pulls/$PR_NUMBER/files" \
36+
| jq -c '.[] | select(.status != "removed") | {filename, sha}' \
37+
| while read -r FILE_OBJ; do
38+
FILENAME=$(echo "$FILE_OBJ" | jq -r '.filename')
39+
FILE_SHA=$(echo "$FILE_OBJ" | jq -r '.sha')
40+
41+
mkdir -p "a/$(dirname "$FILENAME")"
42+
gh api -H "Accept: application/vnd.github.raw" \
43+
"/repos/$REPO_OWNER/$REPO_NAME/git/blobs/$FILE_SHA" \
44+
> "a/$FILENAME" &
45+
done
46+
47+
wait
48+
49+
- name: Check code style
50+
if: always()
51+
run: |
52+
# Run PHP-CS-Fixer
53+
cp -a a b && cd b
54+
~/.composer/vendor/bin/php-cs-fixer fix --using-cache no --show-progress none
55+
cd ..
56+
57+
if ! diff -qr --no-dereference a/ b/ >/dev/null; then
58+
echo "::error::PHP-CS-Fixer found style issues. Please apply the patch below."
59+
echo -e "\n \n git apply - <<'EOF_PATCH'"
60+
diff -pru2 --no-dereference --color=always a/ b/ || true
61+
echo -e "EOF_PATCH\n \n"
62+
echo "Then commit the changes and push to your PR branch."
63+
exit 1
64+
fi
65+
66+
- name: Check for common typos
67+
if: always()
68+
run: |
69+
# Run codespell
70+
rm -rf b && cp -a a b && cd b
71+
codespell -L invokable --check-filenames -w || true
72+
cd ..
73+
74+
if ! diff -qr --no-dereference a/ b/ >/dev/null; then
75+
echo "::error::PHP-CS-Fixer found typos. Please apply the patch below."
76+
echo -e "\n \n git apply - <<'EOF_PATCH'"
77+
diff -pru2 --no-dereference --color=always a/ b/ || true
78+
echo -e "EOF_PATCH\n \n"
79+
echo "Then commit the changes and push to your PR branch."
80+
exit 1
81+
fi
82+
83+
- name: Check for merge commits
84+
if: always()
85+
run: |
86+
# If a PR contains merge commits, fail the job
87+
gh api -H "Accept: application/vnd.github.v3+json" \
88+
"/repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }}/commits" \
89+
| jq -r '.[].parents | length > 1' | grep true > /dev/null && {
90+
echo "::error::Merge commits are not allowed in pull requests."
91+
echo "Please rebase your branch."
92+
exit 1
93+
} || true
94+
95+
- name: Check test-case methods
96+
if: always()
97+
run: |
98+
# Test method names should not have a return type
99+
rm -rf b && cp -a a b && cd b
100+
find -wholename '**/Tests/**.php' \
101+
| while read -r FILE; do
102+
sed -i -E 's/^( public function test.*): void$/\1/' "$FILE"
103+
done
104+
cd ..
105+
106+
if ! diff -qr --no-dereference a/ b/ >/dev/null; then
107+
echo "::error::Test case methods should not have a return type. Please apply the patch below."
108+
echo -e "\n \n git apply - <<'EOF_PATCH'"
109+
diff -pru2 --no-dereference --color=always a/ b/ || true
110+
echo -e "EOF_PATCH\n \n"
111+
echo "Then commit the changes and push to your PR branch."
112+
exit 1
113+
fi
114+
115+
- name: Check @deprecated annotations
116+
if: always()
117+
run: |
118+
# @deprecated annotations should mention ${{ inputs.package }}
119+
rm -rf b && cp -a a b && cd b
120+
find -name '*.php' \
121+
| while read -r FILE; do
122+
sed -i -E 's/(@deprecated since )([0-9])/\1${{ inputs.package }} \2/' "$FILE"
123+
done
124+
cd ..
125+
126+
if ! diff -qr --no-dereference a/ b/ >/dev/null; then
127+
echo "::error::@deprecated annotations should mention ${{ inputs.package }}. Please apply the patch below."
128+
echo -e "\n \n git apply - <<'EOF_PATCH'"
129+
diff -pru2 --no-dereference --color=always a/ b/ || true
130+
echo -e "EOF_PATCH\n \n"
131+
echo "Then commit the changes and push to your PR branch."
132+
exit 1
133+
fi
134+
135+
- name: Check PR header
136+
if: always()
137+
run: |
138+
# Check if the PR title and body follow the Symfony contribution guidelines
139+
PR_TITLE="${{ github.event.pull_request.title }}"
140+
PR_BODY="${{ github.event.pull_request.body }}"
141+
142+
if [[ ! "$PR_BODY" =~ \|\ License[\ ]+\|\ MIT ]]; then
143+
echo "::error::You must add the standard contribution header in the PR description"
144+
echo "See https://symfony.com/doc/current/contributing/code/patches.html#make-a-pull-request"
145+
exit 1
146+
fi
147+
148+
if [[ "$PR_TITLE" =~ (feat|fix|docs|style|refactor|perf|test|chore|revert|build|ci|types?|wip)[:()] ]]; then
149+
echo "::error::Don't use conventional commits in PR titles."
150+
echo "We'll add the appropriate prefix while merging."
151+
echo "Use the component name instead, e.g., [Component] Description."
152+
exit 1
153+
fi
154+
155+
- name: Check YAML files
156+
if: always()
157+
run: |
158+
# Check YAML files for syntax errors
159+
rm -rf b && cp -a a b && cd b
160+
find . -name '*.yml' -o -name '*.yaml' \
161+
| while read -r FILE; do php -r '
162+
use Symfony\Component\Yaml\{Parser,Yaml};
163+
require $_SERVER["HOME"]."/.composer/vendor/autoload.php";
164+
try { (new Parser())->parse(file_get_contents($argv[1]), Yaml::PARSE_CUSTOM_TAGS); }
165+
catch (Exception $e) { echo "::error::in $argv[1]:\n{$e->getMessage()}\n"; exit(1); }
166+
' "$FILE"
167+
done
168+
cd ..
169+
170+
- name: Check JSON files
171+
if: always()
172+
run: |
173+
# Check JSON files for syntax errors
174+
rm -rf b && cp -a a b && cd b
175+
find . -name '*.json' \
176+
| while read -r FILE; do php -r '
177+
use Seld\JsonLint\JsonParser;
178+
require $_SERVER["HOME"]."/.composer/vendor/autoload.php";
179+
try { (new JsonParser())->parse(file_get_contents($argv[1])); }
180+
catch (Exception $e) { echo "::error:: in $argv[1]: {$e->getMessage()}\n"; exit(1); }
181+
' "$FILE"
182+
done
183+
cd ..
184+
185+
- name: Check exception messages
186+
if: always()
187+
run: |
188+
# Placeholders should be enclosed in double-quotes and messages should end with a dot
189+
rm -rf b && cp -a a b && cd b
190+
find -name '*.php' \
191+
| while read -r FILE; do php -r "$(cat <<'EOF'
192+
$new = preg_replace_callback('{throw new ([^\(]+)\((.+?)\);}', function ($match) {
193+
$contents = $match[2];
194+
195+
// %s::%s() -> "%s::%s()"
196+
$contents = preg_replace('{(?<= )%s\:\:%s(\(\))?}', '"%s::%s()"', $contents);
197+
$contents = preg_replace('{\(\'%s\:\:%s(\(\))?}', '(\'"%s::%s()"', $contents);
198+
199+
// %s() -> "%s()"
200+
$contents = preg_replace('{(?<= )%s(\(\))}', '"%s$1"', $contents);
201+
$contents = preg_replace('{\(\'%s(\(\))}', '(\'"%s$1"', $contents);
202+
203+
// %s -> "%s" after a space
204+
$contents = preg_replace('{(?<= )%s}', '"%s"', $contents);
205+
$contents = preg_replace('{\(\'%s}', '(\'"%s"', $contents);
206+
207+
return sprintf('throw new %s(%s);', $match[1], $contents);
208+
}, $old = file_get_contents($argv[1]));
209+
210+
// ensure there is a dot at the end of the exception message
211+
// except for files under Tests/
212+
if (false === strpos($argv[1], '/Tests/')) {
213+
$new = preg_replace_callback('{throw new ([^\(]+)\((sprintf\()?(\'|")(.+?)(?<!\\\)(\3)}', function ($match) {
214+
if ('UnexpectedTypeException' === $match[1]) {
215+
return $match[0];
216+
}
217+
218+
return sprintf('throw new %s(%s%s%s%s%s', $match[1], $match[2], $match[3], $match[4], \in_array($match[4][\strlen($match[4]) - 1], ['.', '!', '?', ' ']) ? '' : '.', $match[5]);
219+
}, $new);
220+
}
221+
222+
if ($new !== $old) {
223+
file_put_contents($argv[1], $new);
224+
}
225+
EOF
226+
)" "$FILE"
227+
done
228+
cd ..
229+
230+
if ! diff -qr --no-dereference a/ b/ >/dev/null; then
231+
echo "::error::Some exception messages might need a tweak. Please consider the patch below."
232+
echo -e "\n \n git apply - <<'EOF_PATCH'"
233+
diff -pru2 --no-dereference --color=always a/ b/ || true
234+
echo -e "EOF_PATCH\n \n"
235+
echo "Then commit the changes and push to your PR branch."
236+
exit 1
237+
fi
238+
239+
- name: 🧠 Fabbot can generate false-positives. Cherry-pick as fits 🍒. Reviewers will help.
240+
if: always()
241+
run: exit 0

.github/workflows/fabbot.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: CS
2+
3+
on:
4+
pull_request:
5+
6+
permissions:
7+
contents: read
8+
9+
jobs:
10+
call-fabbot:
11+
name: Fabbot
12+
uses: ./.github/workflows/callable-fabbot.yml
13+
with:
14+
package: Symfony

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy