22
33namespace Webdevops \Build ;
44
5+ use function array_filter ;
6+ use function array_values ;
7+ use function dirname ;
8+ use function implode ;
59use function str_replace ;
610
711class GithubJobBuilder
812{
9-
10- public function getJobDescription (array $ node ): array
13+ /**
14+ * @return array<string, array<string, mixed>>
15+ */
16+ public function getJobsDescription (array $ node ): array
1117 {
1218 $ serverSpec = $ this ->serverSpec ($ node );
1319 $ structuredTests = $ this ->structuredTests ($ node );
1420
21+ $ jobId = GithubJobBuilder::toJobId ($ node ['name ' ]);
1522 return [
16- 'name ' => $ node ['name ' ],
17- 'needs ' => [
18- ($ node ['parent ' ] ?? null ) ? GithubJobBuilder::toJobId ($ node ['parent ' ]) : 'validate-automation ' ,
19- ],
20- 'runs-on ' => 'ubuntu-latest ' ,
21- 'container ' => 'webdevops/dockerfile-build-env ' ,
22- 'steps ' => array_values (
23- array_filter (
24- [
25- ['uses ' => 'actions/checkout@v4 ' ],
26- // ['uses' => 'docker/setup-qemu-action@v3'], // only needed for ARM builds
27- ['uses ' => 'docker/setup-buildx-action@v3 ' ],
28- [
29- 'name ' => 'Build x64 ' ,
30- 'uses ' => 'docker/build-push-action@v6 ' ,
31- 'with ' => [
32- 'context ' => dirname (str_replace (__DIR__ . '/../../ ' , '' , $ node ['file ' ])),
33- 'load ' => true ,
34- 'tags ' => 'ghcr.io/webdevops/ ' . $ node ['image ' ] . ': ' . $ node ['tag ' ] . ',webdevops/ ' . $ node ['image ' ] . ': ' . $ node ['tag ' ],
35- 'platforms ' => 'linux/amd64 ' ,
23+ $ jobId => [
24+ 'strategy ' => [
25+ 'fail-fast ' => false ,
26+ 'matrix ' => [
27+ 'include ' => [
28+ [
29+ 'arch ' => 'amd64 ' ,
30+ 'runner ' => 'ubuntu-latest ' ,
31+ 'platform ' => 'linux/amd64 ' ,
3632 ],
37- ],
38- $ serverSpec ? [
39- 'name ' => 'run serverspec ' ,
40- 'run ' => implode ("\n" , $ serverSpec ),
41- ] : null ,
42- $ structuredTests ? [
43- 'name ' => 'run structure-test ' ,
44- 'run ' => implode ("\n" , $ structuredTests ),
45- ] : null ,
46- [
47- 'if ' => '${{github.ref == \'refs/heads/master \'}} ' ,
48- 'name ' => 'Login to ghcr.io ' ,
49- 'uses ' => 'docker/login-action@v3 ' ,
50- 'with ' => [
51- 'registry ' => 'ghcr.io ' ,
52- 'username ' => '${{ github.actor }} ' ,
53- 'password ' => '${{ secrets.GITHUB_TOKEN }} ' ,
33+ [
34+ 'arch ' => 'arm64 ' ,
35+ 'runner ' => 'ubuntu-latest ' , // 'ubuntu-24.04-arm' // only needed for ARM builds
36+ 'platform ' => 'linux/arm64 ' ,
5437 ],
5538 ],
39+ ],
40+ ],
41+ 'name ' => $ node ['name ' ] . ' (${{ matrix.arch }}) ' ,
42+ 'needs ' => ($ node ['parent ' ] ?? null ) ? GithubJobBuilder::toJobId ($ node ['parent ' ]) : 'validate-automation ' ,
43+ 'runs-on ' => '${{ matrix.runner }} ' ,
44+ 'container ' => 'webdevops/dockerfile-build-env ' ,
45+ 'steps ' => array_values (
46+ array_filter (
5647 [
57- // login after the build so the rate limit of github is used and not from our login Token.
58- 'if ' => '${{github.ref == \'refs/heads/master \'}} ' ,
59- 'name ' => 'Login to hub.docker.com ' ,
60- 'uses ' => 'docker/login-action@v3 ' ,
61- 'with ' => [
62- 'username ' => '${{ secrets.DOCKERHUB_USERNAME }} ' ,
63- 'password ' => '${{ secrets.DOCKERHUB_TOKEN }} ' ,
48+ ['uses ' => 'actions/checkout@v6 ' ],
49+ [
50+ 'name ' => 'Build (load locally) ' ,
51+ 'uses ' => 'docker/build-push-action@v6 ' ,
52+ 'with ' => [
53+ 'context ' => dirname (str_replace (__DIR__ . '/../../ ' , '' , $ node ['file ' ])),
54+ 'platforms ' => '${{ matrix.platform }} ' ,
55+ 'load ' => true ,
56+ 'tags ' => 'ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-${{ matrix.arch }} ' ,
57+ 'cache-from ' => 'type=gha ' ,
58+ 'cache-to ' => 'type=gha,mode=max ' ,
59+ ],
6460 ],
65- ],
66- [
67- 'if ' => '${{github.ref == \'refs/heads/master \'}} ' ,
68- 'name ' => 'Push ' ,
69- // 'name' => 'Build ARM + Push',
70- 'uses ' => 'docker/build-push-action@v6 ' ,
71- 'with ' => [
72- 'context ' => dirname (str_replace (__DIR__ . '/../../ ' , '' , $ node ['file ' ])),
73- 'push ' => true ,
74- 'tags ' => 'ghcr.io/webdevops/ ' . $ node ['image ' ] . ': ' . $ node ['tag ' ] . ',webdevops/ ' . $ node ['image ' ] . ': ' . $ node ['tag ' ],
75- 'platforms ' => 'linux/amd64 ' ,
76- // 'platforms' => 'linux/amd64,linux/arm64', // ARM not ready yet
61+ $ serverSpec ? [
62+ 'name ' => 'run serverspec ' ,
63+ 'run ' => implode ("\n" , $ serverSpec ),
64+ ] : null ,
65+ $ structuredTests ? [
66+ 'name ' => 'run structure-test ' ,
67+ 'run ' => implode ("\n" , $ structuredTests ),
68+ ] : null ,
69+ [
70+ 'if ' => '${{github.ref == \'refs/heads/arm \'}} ' ,
71+ 'name ' => 'Login to ghcr.io ' ,
72+ 'uses ' => 'docker/login-action@v3 ' ,
73+ 'with ' => [
74+ 'registry ' => 'ghcr.io ' ,
75+ 'username ' => '${{ github.actor }} ' ,
76+ 'password ' => '${{ secrets.GITHUB_TOKEN }} ' ,
77+ ],
78+ ],
79+ [
80+ 'name ' => 'Push arch image ' ,
81+ 'if ' => '${{github.ref == \'refs/heads/arm \'}} ' ,
82+ 'run ' => 'docker push "ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-${{ matrix.arch }}" ' ,
7783 ],
7884 ],
79- ] ,
85+ ) ,
8086 ),
81- ),
87+ ],
88+ $ jobId . '_push ' => [
89+ 'name ' => $ node ['name ' ] . ' - Publish ' ,
90+ 'runs-on ' => 'ubuntu-latest ' ,
91+ 'needs ' => $ jobId ,
92+ 'if ' => '${{github.ref == \'refs/heads/arm \'}} ' ,
93+ 'steps ' => [
94+ ['uses ' => 'docker/setup-buildx-action@v3 ' ],
95+ [
96+ 'name ' => 'Login to ghcr.io ' ,
97+ 'uses ' => 'docker/login-action@v3 ' ,
98+ 'with ' => [
99+ 'registry ' => 'ghcr.io ' ,
100+ 'username ' => '${{ github.actor }} ' ,
101+ 'password ' => '${{ secrets.GITHUB_TOKEN }} ' ,
102+ ],
103+ ],
104+ [
105+ 'name ' => 'Login to hub.docker.com ' ,
106+ 'uses ' => 'docker/login-action@v3 ' ,
107+ 'with ' => [
108+ 'username ' => '${{ secrets.DOCKERHUB_USERNAME }} ' ,
109+ 'password ' => '${{ secrets.DOCKERHUB_TOKEN }} ' ,
110+ ],
111+ ],
112+ [
113+ 'name ' => 'Create and push multi-arch manifest ' ,
114+ 'run ' => implode (" \\\n" , [
115+ 'docker buildx imagetools create ' ,
116+ '-t "webdevops/ ' . $ node ['image ' ] . ': ' . $ node ['tag ' ] . '" ' ,
117+ '-t "ghcr.io/webdevops/ ' . $ node ['image ' ] . ': ' . $ node ['tag ' ] . '" ' ,
118+ '"ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-amd64" ' ,
119+ '"ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-arm64" ' ,
120+ ]),
121+ ],
122+ ],
123+ ],
82124 ];
83125 }
84126
@@ -98,14 +140,13 @@ private function serverSpec(array $node): array
98140 return [];
99141 }
100142
101- // $testDockerfile = uniqid('Dockerfile_', true);
102143 $ testDockerfile = 'Dockerfile_test ' ;
103144 $ specConfig = $ node ['serverspec ' ];
104145 $ specConfig ['DOCKERFILE ' ] = $ testDockerfile ;
105146 $ encodedJsonConfig = base64_encode (json_encode ($ specConfig ));
106147 $ script = [
107148 'cd tests/serverspec ' ,
108- 'echo "FROM ' . $ node ['id ' ] . '" >> ' . $ testDockerfile ,
149+ 'echo "FROM ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-${{ matrix.arch }} " >> ' . $ testDockerfile ,
109150 'echo "COPY conf/ /" >> ' . $ testDockerfile ,
110151 ];
111152 $ script [] = 'bundle install ' ;
@@ -133,7 +174,7 @@ public function getValidationConfig(): array
133174 'name ' => 'Validate Automation ' ,
134175 'runs-on ' => 'ubuntu-latest ' ,
135176 'steps ' => [
136- ['uses ' => 'actions/checkout@v4 ' ],
177+ ['uses ' => 'actions/checkout@v6 ' ],
137178 [
138179 'name ' => 'Validate that template/* are used to generate Dockerfiles ' ,
139180 'run ' => implode ("\n" , [
0 commit comments