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 ' ]);
22+ $ needs = ($ node ['parent ' ] ?? null ) ? GithubJobBuilder::toJobId ($ node ['parent ' ]) . '_publish ' : 'validate-automation ' ;
1523 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 ' ,
24+ $ jobId => [
25+ 'strategy ' => [
26+ 'fail-fast ' => false ,
27+ 'matrix ' => [
28+ 'include ' => [
29+ [
30+ 'arch ' => 'amd64 ' ,
31+ 'runner ' => 'ubuntu-24.04 ' ,
32+ 'platform ' => 'linux/amd64 ' ,
3633 ],
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 }} ' ,
34+ [
35+ 'arch ' => 'arm64 ' ,
36+ 'runner ' => 'ubuntu-24.04-arm ' ,
37+ 'platform ' => 'linux/arm64 ' ,
5438 ],
5539 ],
40+ ],
41+ ],
42+ 'name ' => $ node ['name ' ] . ' (${{ matrix.arch }}) ' ,
43+ 'needs ' => $ needs ,
44+ 'runs-on ' => '${{ matrix.runner }} ' ,
45+ 'container ' => 'webdevops/dockerfile-build-env ' ,
46+ 'steps ' => array_values (
47+ array_filter (
5648 [
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 }} ' ,
49+ ['uses ' => 'actions/checkout@v6 ' ],
50+ ['uses ' => 'docker/setup-buildx-action@v3 ' ],
51+ [
52+ 'name ' => 'Build (load locally) ' ,
53+ 'uses ' => 'docker/build-push-action@v6 ' ,
54+ 'with ' => [
55+ 'context ' => dirname (str_replace (__DIR__ . '/../../ ' , '' , $ node ['file ' ])),
56+ 'platforms ' => '${{ matrix.platform }} ' ,
57+ 'load ' => true ,
58+ 'tags ' => 'ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-${{ matrix.arch }} ' ,
59+ 'cache-from ' => 'type=gha ' ,
60+ 'cache-to ' => 'type=gha,mode=max ' ,
61+ ],
6462 ],
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
63+ $ serverSpec ? [
64+ 'name ' => 'run serverspec ' ,
65+ 'run ' => implode ("\n" , $ serverSpec ),
66+ ] : null ,
67+ $ structuredTests ? [
68+ 'name ' => 'run structure-test ' ,
69+ 'run ' => implode ("\n" , $ structuredTests ),
70+ ] : null ,
71+ [
72+ 'if ' => '${{github.ref == \'refs/heads/arm \'}} ' ,
73+ 'name ' => 'Login to ghcr.io ' ,
74+ 'uses ' => 'docker/login-action@v3 ' ,
75+ 'with ' => [
76+ 'registry ' => 'ghcr.io ' ,
77+ 'username ' => '${{ github.actor }} ' ,
78+ 'password ' => '${{ secrets.GITHUB_TOKEN }} ' ,
79+ ],
80+ ],
81+ [
82+ 'name ' => 'Push arch image ' ,
83+ 'if ' => '${{github.ref == \'refs/heads/arm \'}} ' ,
84+ 'run ' => 'docker push "ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-${{ matrix.arch }}" ' ,
7785 ],
7886 ],
79- ] ,
87+ ) ,
8088 ),
81- ),
89+ ],
90+ $ jobId . '_publish ' => [
91+ 'name ' => $ node ['name ' ] . ' - Publish ' ,
92+ 'runs-on ' => 'ubuntu-latest ' ,
93+ 'needs ' => $ jobId ,
94+ 'if ' => '${{github.ref == \'refs/heads/arm \'}} ' ,
95+ 'steps ' => [
96+ ['uses ' => 'docker/setup-buildx-action@v3 ' ],
97+ [
98+ 'name ' => 'Login to ghcr.io ' ,
99+ 'uses ' => 'docker/login-action@v3 ' ,
100+ 'with ' => [
101+ 'registry ' => 'ghcr.io ' ,
102+ 'username ' => '${{ github.actor }} ' ,
103+ 'password ' => '${{ secrets.GITHUB_TOKEN }} ' ,
104+ ],
105+ ],
106+ [
107+ 'name ' => 'Login to hub.docker.com ' ,
108+ 'uses ' => 'docker/login-action@v3 ' ,
109+ 'with ' => [
110+ 'username ' => '${{ secrets.DOCKERHUB_USERNAME }} ' ,
111+ 'password ' => '${{ secrets.DOCKERHUB_TOKEN }} ' ,
112+ ],
113+ ],
114+ [
115+ 'name ' => 'Create and push multi-arch manifest ' ,
116+ 'run ' => implode (" \\\n" , [
117+ 'docker buildx imagetools create ' ,
118+ '-t "webdevops/ ' . $ node ['image ' ] . ': ' . $ node ['tag ' ] . '" ' ,
119+ '-t "ghcr.io/webdevops/ ' . $ node ['image ' ] . ': ' . $ node ['tag ' ] . '" ' ,
120+ '"ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-amd64" ' ,
121+ '"ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-arm64" ' ,
122+ ]),
123+ ],
124+ ],
125+ ],
82126 ];
83127 }
84128
@@ -98,14 +142,13 @@ private function serverSpec(array $node): array
98142 return [];
99143 }
100144
101- // $testDockerfile = uniqid('Dockerfile_', true);
102145 $ testDockerfile = 'Dockerfile_test ' ;
103146 $ specConfig = $ node ['serverspec ' ];
104147 $ specConfig ['DOCKERFILE ' ] = $ testDockerfile ;
105148 $ encodedJsonConfig = base64_encode (json_encode ($ specConfig ));
106149 $ script = [
107150 'cd tests/serverspec ' ,
108- 'echo "FROM ' . $ node ['id ' ] . '" >> ' . $ testDockerfile ,
151+ 'echo "FROM ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-${{ matrix.arch }} " >> ' . $ testDockerfile ,
109152 'echo "COPY conf/ /" >> ' . $ testDockerfile ,
110153 ];
111154 $ script [] = 'bundle install ' ;
@@ -119,9 +162,9 @@ private function structuredTests(array $node): array
119162 if (file_exists (__DIR__ . '/../../tests/structure-test/ ' . $ node ['image ' ] . '/test.yaml ' )) {
120163 $ script [] = 'cd tests/structure-test ' ;
121164 if (file_exists (__DIR__ . '/../../tests/structure-test/ ' . $ node ['image ' ] . '/ ' . $ node ['tag ' ] . '/test.yaml ' )) {
122- $ script [] = '/usr/local/bin/container-structure-test test --image ' . $ node ['name ' ] . ' --config ' . $ node ['image ' ] . '/test.yaml --config ' . $ node ['image ' ] . '/ ' . $ node ['tag ' ] . '/test.yaml ' ;
165+ $ script [] = '/usr/local/bin/container-structure-test test --image ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-${{ matrix.arch }} --config ' . $ node ['image ' ] . '/test.yaml --config ' . $ node ['image ' ] . '/ ' . $ node ['tag ' ] . '/test.yaml ' ;
123166 } else {
124- $ script [] = '/usr/local/bin/container-structure-test test --image ' . $ node ['name ' ] . ' --config ' . $ node ['image ' ] . '/test.yaml ' ;
167+ $ script [] = '/usr/local/bin/container-structure-test test --image ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-${{ matrix.arch }} --config ' . $ node ['image ' ] . '/test.yaml ' ;
125168 }
126169 }
127170 return $ script ;
@@ -133,7 +176,7 @@ public function getValidationConfig(): array
133176 'name ' => 'Validate Automation ' ,
134177 'runs-on ' => 'ubuntu-latest ' ,
135178 'steps ' => [
136- ['uses ' => 'actions/checkout@v4 ' ],
179+ ['uses ' => 'actions/checkout@v6 ' ],
137180 [
138181 'name ' => 'Validate that template/* are used to generate Dockerfiles ' ,
139182 'run ' => implode ("\n" , [
0 commit comments