Skip to content

Commit 5c94873

Browse files
Turn fabbot into a github action
1 parent 47a5a5e commit 5c94873

File tree

2 files changed

+256
-0
lines changed

2 files changed

+256
-0
lines changed

.github/workflows/callable-fabbot.yml

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
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+
# Check out 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+
116+
- name: Check @deprecated annotations
117+
if: always()
118+
run: |
119+
# @deprecated annotations should mention ${{ inputs.package }}
120+
rm -rf b && cp -a a b && cd b
121+
find -name '*.php' \
122+
| while read -r FILE; do
123+
sed -i -E 's/(@deprecated since )([0-9])/\1${{ inputs.package }} \2/' "$FILE"
124+
done
125+
cd ..
126+
127+
if ! diff -qr --no-dereference a/ b/ >/dev/null; then
128+
echo "::error::@deprecated annotations should mention ${{ inputs.package }}. Please apply the patch below."
129+
echo -e "\n \n git apply - <<'EOF_PATCH'"
130+
diff -pru2 --no-dereference --color=always a/ b/ || true
131+
echo -e "EOF_PATCH\n \n"
132+
echo "Then commit the changes and push to your PR branch."
133+
exit 1
134+
fi
135+
136+
- name: Check PR header
137+
if: always()
138+
run: |
139+
# Check if the PR title and body follow the Symfony contribution guidelines
140+
PR_TITLE="${{ github.event.pull_request.title }}"
141+
PR_BODY="${{ github.event.pull_request.body }}"
142+
143+
if [[ ! "$PR_BODY" =~ \|\ License[\ ]+\|\ MIT ]]; then
144+
echo "::error::You must add the standard contribution header in the PR description"
145+
echo "See https://symfony.com/doc/current/contributing/code/patches.html#make-a-pull-request"
146+
exit 1
147+
fi
148+
149+
if [[ "$PR_TITLE" =~ (feat|fix|docs|style|refactor|perf|test|chore|revert|build|ci|types?|wip)[:()] ]]; then
150+
echo "::error::Don't use conventional commits in PR titles."
151+
echo "We'll add the appropriate prefix while merging."
152+
echo "Use the component name instead, e.g., [Component] Description."
153+
exit 1
154+
fi
155+
156+
- name: Check YAML files
157+
if: always()
158+
run: |
159+
# Check YAML files for syntax errors
160+
rm -rf b && cp -a a b && cd b
161+
find . -name '*.yml' -o -name '*.yaml' \
162+
| while read -r FILE; do php -r '
163+
use Symfony\Component\Yaml\{Parser,Yaml};
164+
require $_SERVER["HOME"]."/.composer/vendor/autoload.php";
165+
try { (new Parser())->parse(file_get_contents($argv[1]), Yaml::PARSE_CUSTOM_TAGS); }
166+
catch (Exception $e) { echo "::error::in $argv[1]:\n{$e->getMessage()}\n"; exit(1); }
167+
' "$FILE"
168+
done
169+
cd ..
170+
171+
- name: Check JSON files
172+
if: always()
173+
run: |
174+
# Check JSON files for syntax errors
175+
rm -rf b && cp -a a b && cd b
176+
find . -name '*.json' \
177+
| while read -r FILE; do php -r '
178+
use Seld\JsonLint\JsonParser;
179+
require $_SERVER["HOME"]."/.composer/vendor/autoload.php";
180+
try { (new JsonParser())->parse(file_get_contents($argv[1])); }
181+
catch (Exception $e) { echo "::error:: in $argv[1]: {$e->getMessage()}\n"; exit(1); }
182+
' "$FILE"
183+
done
184+
cd ..
185+
186+
- name: Check exception messages
187+
if: always()
188+
run: |
189+
# Placeholders should be enclosed in double-quotes and messages should end with a dot
190+
rm -rf b && cp -a a b && cd b
191+
find -name '*.php' \
192+
| while read -r FILE; do php -r "$(cat <<'EOF'
193+
$new = preg_replace_callback('{throw new ([^\(]+)\((.+?)\);}', function ($match) {
194+
$contents = $match[2];
195+
196+
// %s::%s() -> "%s::%s()"
197+
$contents = preg_replace('{(?<= )%s\:\:%s(\(\))?}', '"%s::%s()"', $contents);
198+
$contents = preg_replace('{\(\'%s\:\:%s(\(\))?}', '(\'"%s::%s()"', $contents);
199+
200+
// %s() -> "%s()"
201+
$contents = preg_replace('{(?<= )%s(\(\))}', '"%s$1"', $contents);
202+
$contents = preg_replace('{\(\'%s(\(\))}', '(\'"%s$1"', $contents);
203+
204+
// %s -> "%s" after a space
205+
$contents = preg_replace('{(?<= )%s}', '"%s"', $contents);
206+
$contents = preg_replace('{\(\'%s}', '(\'"%s"', $contents);
207+
208+
return sprintf('throw new %s(%s);', $match[1], $contents);
209+
}, $old = file_get_contents($argv[1]));
210+
211+
// ensure there is a dot at the end of the exception message
212+
// except for files under Tests/
213+
if (false === strpos($argv[1], '/Tests/')) {
214+
$new = preg_replace_callback('{throw new ([^\(]+)\((sprintf\()?(\'|")(.+?)(?<!\\\)(\3)}', function ($match) {
215+
if ('UnexpectedTypeException' === $match[1]) {
216+
return $match[0];
217+
}
218+
219+
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]);
220+
}, $new);
221+
}
222+
223+
if ($new !== $old) {
224+
file_put_contents($argv[1], $new);
225+
}
226+
EOF
227+
)" "$FILE"
228+
done
229+
cd ..
230+
231+
if ! diff -qr --no-dereference a/ b/ >/dev/null; then
232+
echo "::error::Some exception messages might need a tweak. Please consider the patch below."
233+
echo -e "\n \n git apply - <<'EOF_PATCH'"
234+
diff -pru2 --no-dereference --color=always a/ b/ || true
235+
echo -e "EOF_PATCH\n \n"
236+
echo "Then commit the changes and push to your PR branch."
237+
exit 1
238+
fi
239+
240+
- name: 🧠 Fabbot can generate false-positives. Cherry-pick as fits 🍒. Reviewers will help.
241+
if: always()
242+
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