Skip to content

Commit a2a2476

Browse files
committed
add workflow & skeleton generator
1 parent 1ed80ba commit a2a2476

File tree

3 files changed

+184
-2
lines changed

3 files changed

+184
-2
lines changed

.github/actions/verify-generated-files/action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ runs:
1313
ext/tokenizer/tokenizer_data_gen.php
1414
build/gen_stub.php -f --generate-optimizer-info --verify
1515
ext/phar/makestub.php
16+
.github/scripts/download-bundled/make-workflow-file.php
1617
.github/scripts/test-directory-unchanged.sh .
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Phpsrc\Ci\DownloadBundled;
6+
7+
$bundles = [
8+
new Bundle('PCRE2', ['ext/pcre/pcre2lib']),
9+
];
10+
11+
class Bundle
12+
{
13+
/**
14+
* @param list<string> $directories
15+
*/
16+
public function __construct(
17+
public string $name,
18+
public array $directories
19+
) {}
20+
21+
public function getNameForPath(): string
22+
{
23+
return preg_replace('~\W+~', '-', strtolower($this->name));
24+
}
25+
}
26+
27+
class Generator
28+
{
29+
/**
30+
* @param list<Bundle> $bundles
31+
*/
32+
public function __construct(
33+
public array $bundles
34+
) {}
35+
36+
protected function getRepoDirectory(): string
37+
{
38+
return dirname(__DIR__, 3);
39+
}
40+
41+
protected function indentString(string $value, int $levels, bool $inclFirstLine): string
42+
{
43+
return preg_replace(
44+
'~' . ($inclFirstLine ? '^|' : '') . '(?<=\n)~',
45+
str_repeat(' ', $levels),
46+
$value
47+
);
48+
}
49+
50+
/**
51+
* @param mixed $data
52+
*/
53+
protected function encodeYml($data): string
54+
{
55+
if (is_array($data)) {
56+
$isList = array_is_list($data);
57+
$resParts = [];
58+
foreach ($data as $k => $v) {
59+
$kEncoded = $isList
60+
? '-'
61+
: $this->encodeYml($k) . ':';
62+
$vEncoded = $this->encodeYml($v);
63+
64+
$resParts[] = $kEncoded
65+
. (!$isList && is_array($v) && $v !== [] ? "\n " : ' ')
66+
. (is_array($v) ? $this->indentString($vEncoded, 1, false) : $vEncoded);
67+
}
68+
69+
return implode("\n", $resParts);
70+
}
71+
72+
if (preg_match('~^(\w+|\$\{\{[^\}]+\}\})$~', $data)) {
73+
return $data;
74+
}
75+
76+
return strpos($data, "\n") !== false
77+
? '|' . "\n" . $this->indentString($data, 1, true)
78+
: '\'' . str_replace('\'', '\'\'', $data) . '\'';
79+
}
80+
81+
public function makeWorkflowFile(): void
82+
{
83+
$content = <<<'EOD'
84+
name: Verify Bundled Files
85+
86+
on:
87+
push: ~
88+
pull_request: ~
89+
schedule:
90+
- cron: "0 1 * * *"
91+
workflow_dispatch: ~
92+
93+
permissions:
94+
contents: read
95+
96+
jobs:
97+
VERIFY_BUNDLED_FILES:
98+
name: Verify Bundled Files
99+
runs-on: ubuntu-24.04
100+
steps:
101+
- name: git checkout
102+
uses: actions/checkout@v5
103+
104+
- name: Detect changed files
105+
uses: dorny/paths-filter@v3
106+
id: changes
107+
with:
108+
base: master
109+
filters: %filters%
110+
111+
%steps%
112+
113+
EOD;
114+
115+
$filters = [];
116+
foreach ($this->bundles as $bundle) {
117+
$filters[$bundle->getNameForPath()] = $this->makeDornyPathsFilterFilters($bundle);
118+
}
119+
$content = str_replace('%filters%', $this->indentString($this->encodeYml($this->encodeYml($filters)), 5, false), $content);
120+
121+
$steps = [];
122+
foreach ($this->bundles as $bundle) {
123+
$steps[] = [
124+
'name' => $bundle->name,
125+
'if' => '${{ !cancelled() && (steps.changes.outputs.pcre2 == \'true\' || github.event_name == \'schedule\' || github.event_name == \'workflow_dispatch\') }}',
126+
'run' => implode("\n", [
127+
'echo "::group::Download"',
128+
'.github/scripts/download-bundled/' . $bundle->getNameForPath() . '.sh',
129+
'echo "::endgroup::"',
130+
'echo "::group::Verify files"',
131+
...array_map(static fn ($v) => '.github/scripts/test-directory-unchanged.sh ' . escapeshellarg($v), $bundle->directories),
132+
'echo "::endgroup::"',
133+
]),
134+
];
135+
}
136+
$content = str_replace('%steps%', $this->indentString($this->encodeYml($steps), 3, false), $content);
137+
138+
file_put_contents($this->getRepoDirectory() . '/.github/workflows/verify-bundled-files.yml', $content);
139+
}
140+
141+
protected function makeDornyPathsFilterFilters(Bundle $bundle): array
142+
{
143+
return [
144+
'.github/scripts/download-bundled/' . $bundle->getNameForPath() . '.*',
145+
...array_map(static fn ($v) => $v . '/**', $bundle->directories),
146+
];
147+
}
148+
149+
public function makeDownloadScriptHeaders(): void
150+
{
151+
foreach ($this->bundles as $bundle) {
152+
$this->makeDownloadScriptHeader($bundle);
153+
}
154+
}
155+
156+
protected function makeDownloadScriptHeader(Bundle $bundle): void
157+
{
158+
$scriptPath = $this->getRepoDirectory() . '/.github/scripts/download-bundled/' . $bundle->getNameForPath() . '.sh';
159+
160+
$content = !file_exists($scriptPath)
161+
? "# TODO\n"
162+
: file_get_contents($scriptPath);
163+
164+
$header = <<<'EOD'
165+
#!/bin/sh
166+
set -ex
167+
cd "$(dirname "$0")/../../.."
168+
169+
170+
EOD;
171+
if (!str_starts_with($content, $header)) {
172+
$content = $header . $content;
173+
}
174+
175+
file_put_contents($scriptPath, $content);
176+
}
177+
}
178+
179+
$generator = new Generator($bundles);
180+
$generator->makeWorkflowFile();
181+
$generator->makeDownloadScriptHeaders();

.github/workflows/verify-bundled-files.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
base: master
2626
filters: |
2727
pcre2:
28-
- '.github/scripts/download-bundled/pcre2.sh'
28+
- '.github/scripts/download-bundled/pcre2.*'
2929
- 'ext/pcre/pcre2lib/**'
3030
3131
- name: PCRE2
@@ -35,5 +35,5 @@ jobs:
3535
.github/scripts/download-bundled/pcre2.sh
3636
echo "::endgroup::"
3737
echo "::group::Verify files"
38-
.github/scripts/test-directory-unchanged.sh ext/pcre/pcre2lib
38+
.github/scripts/test-directory-unchanged.sh "ext/pcre/pcre2lib"
3939
echo "::endgroup::"

0 commit comments

Comments
 (0)