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+ 'build-args ' => implode ("\n" , [
62+ 'TARGETARCH=${{ matrix.arch }} ' ,
63+ ]),
64+ ],
6465 ],
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
66+ $ serverSpec ? [
67+ 'name ' => 'run serverspec ' ,
68+ 'run ' => implode ("\n" , $ serverSpec ),
69+ ] : null ,
70+ $ structuredTests ? [
71+ 'name ' => 'run structure-test ' ,
72+ 'run ' => implode ("\n" , $ structuredTests ),
73+ ] : null ,
74+ [
75+ 'if ' => '${{github.ref == \'refs/heads/arm \'}} ' ,
76+ 'name ' => 'Login to ghcr.io ' ,
77+ 'uses ' => 'docker/login-action@v3 ' ,
78+ 'with ' => [
79+ 'registry ' => 'ghcr.io ' ,
80+ 'username ' => '${{ github.actor }} ' ,
81+ 'password ' => '${{ secrets.GITHUB_TOKEN }} ' ,
82+ ],
83+ ],
84+ [
85+ 'name ' => 'Push arch image ' ,
86+ 'if ' => '${{github.ref == \'refs/heads/arm \'}} ' ,
87+ 'run ' => 'docker push "ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-${{ matrix.arch }}" ' ,
7788 ],
7889 ],
79- ] ,
90+ ) ,
8091 ),
81- ),
92+ ],
93+ $ jobId . '_publish ' => [
94+ 'name ' => $ node ['name ' ] . ' - Publish ' ,
95+ 'runs-on ' => 'ubuntu-latest ' ,
96+ 'needs ' => $ jobId ,
97+ 'if ' => '${{github.ref == \'refs/heads/arm \'}} ' ,
98+ 'steps ' => [
99+ ['uses ' => 'docker/setup-buildx-action@v3 ' ],
100+ [
101+ 'name ' => 'Login to ghcr.io ' ,
102+ 'uses ' => 'docker/login-action@v3 ' ,
103+ 'with ' => [
104+ 'registry ' => 'ghcr.io ' ,
105+ 'username ' => '${{ github.actor }} ' ,
106+ 'password ' => '${{ secrets.GITHUB_TOKEN }} ' ,
107+ ],
108+ ],
109+ [
110+ 'name ' => 'Login to hub.docker.com ' ,
111+ 'uses ' => 'docker/login-action@v3 ' ,
112+ 'with ' => [
113+ 'username ' => '${{ secrets.DOCKERHUB_USERNAME }} ' ,
114+ 'password ' => '${{ secrets.DOCKERHUB_TOKEN }} ' ,
115+ ],
116+ ],
117+ [
118+ 'name ' => 'Create and push multi-arch manifest ' ,
119+ 'run ' => implode (" \\\n" , [
120+ 'docker buildx imagetools create ' ,
121+ '-t "webdevops/ ' . $ node ['image ' ] . ': ' . $ node ['tag ' ] . '" ' ,
122+ '-t "ghcr.io/webdevops/ ' . $ node ['image ' ] . ': ' . $ node ['tag ' ] . '" ' ,
123+ '"ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-amd64" ' ,
124+ '"ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-arm64" ' ,
125+ ]),
126+ ],
127+ ],
128+ ],
82129 ];
83130 }
84131
@@ -98,14 +145,13 @@ private function serverSpec(array $node): array
98145 return [];
99146 }
100147
101- // $testDockerfile = uniqid('Dockerfile_', true);
102148 $ testDockerfile = 'Dockerfile_test ' ;
103149 $ specConfig = $ node ['serverspec ' ];
104150 $ specConfig ['DOCKERFILE ' ] = $ testDockerfile ;
105151 $ encodedJsonConfig = base64_encode (json_encode ($ specConfig ));
106152 $ script = [
107153 'cd tests/serverspec ' ,
108- 'echo "FROM ' . $ node ['id ' ] . '" >> ' . $ testDockerfile ,
154+ 'echo "FROM ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-${{ matrix.arch }} " >> ' . $ testDockerfile ,
109155 'echo "COPY conf/ /" >> ' . $ testDockerfile ,
110156 ];
111157 $ script [] = 'bundle install ' ;
@@ -119,9 +165,9 @@ private function structuredTests(array $node): array
119165 if (file_exists (__DIR__ . '/../../tests/structure-test/ ' . $ node ['image ' ] . '/test.yaml ' )) {
120166 $ script [] = 'cd tests/structure-test ' ;
121167 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 ' ;
168+ $ 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 ' ;
123169 } else {
124- $ script [] = '/usr/local/bin/container-structure-test test --image ' . $ node ['name ' ] . ' --config ' . $ node ['image ' ] . '/test.yaml ' ;
170+ $ script [] = '/usr/local/bin/container-structure-test test --image ghcr.io/webdevops/ ' . $ node ['image ' ] . ':sha-${{ github.sha }}-${{ matrix.arch }} --config ' . $ node ['image ' ] . '/test.yaml ' ;
125171 }
126172 }
127173 return $ script ;
@@ -133,7 +179,7 @@ public function getValidationConfig(): array
133179 'name ' => 'Validate Automation ' ,
134180 'runs-on ' => 'ubuntu-latest ' ,
135181 'steps ' => [
136- ['uses ' => 'actions/checkout@v4 ' ],
182+ ['uses ' => 'actions/checkout@v6 ' ],
137183 [
138184 'name ' => 'Validate that template/* are used to generate Dockerfiles ' ,
139185 'run ' => implode ("\n" , [
0 commit comments