Skip to content

Commit ed03eb5

Browse files
authored
Merge pull request #557 from webdevops/arm
✨ add ARM into build pipeline
2 parents 113714e + a6843b6 commit ed03eb5

File tree

22 files changed

+4336
-1582
lines changed

22 files changed

+4336
-1582
lines changed

.github/workflows/build.yaml

Lines changed: 4151 additions & 1461 deletions
Large diffs are not rendered by default.

build-local.sh

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,42 +11,30 @@ PHP_VERSION=8.5
1111

1212
if [ "$#" -gt 0 ]; then
1313
PHP_VERSION=$1
14-
if [ ! -d "$ROOT_DIR/docker/php/$PHP_VERSION" ]; then
14+
if [ ! -d "docker/php/$PHP_VERSION" ]; then
1515
echo "PHP version $PHP_VERSION not found"
1616
exit 1
1717
fi
1818
fi
1919

2020
# alpine
21-
cd $ROOT_DIR/docker/php/$PHP_VERSION-alpine
22-
docker build -t webdevops/php:$PHP_VERSION-alpine .
23-
cd $ROOT_DIR/docker/php-dev/$PHP_VERSION-alpine
24-
docker build -t webdevops/php-dev:$PHP_VERSION-alpine .
21+
docker build -t webdevops/php:$PHP_VERSION-alpine docker/php/$PHP_VERSION-alpine
22+
docker build -t webdevops/php-dev:$PHP_VERSION-alpine docker/php-dev/$PHP_VERSION-alpine
2523

26-
cd $ROOT_DIR/docker/php-nginx/$PHP_VERSION-alpine
27-
docker build -t webdevops/php-nginx:$PHP_VERSION-alpine .
28-
cd $ROOT_DIR/docker/php-nginx-dev/$PHP_VERSION-alpine
29-
docker build -t webdevops/php-nginx-dev:$PHP_VERSION-alpine .
24+
docker build -t webdevops/php-nginx:$PHP_VERSION-alpine docker/php-nginx/$PHP_VERSION-alpine
25+
docker build -t webdevops/php-nginx-dev:$PHP_VERSION-alpine docker/php-nginx-dev/$PHP_VERSION-alpine
3026

31-
cd $ROOT_DIR/docker/php-apache/$PHP_VERSION-alpine
32-
docker build -t webdevops/php-apache:$PHP_VERSION-alpine .
33-
cd $ROOT_DIR/docker/php-apache-dev/$PHP_VERSION-alpine
34-
docker build -t webdevops/php-apache-dev:$PHP_VERSION-alpine .
27+
docker build -t webdevops/php-apache:$PHP_VERSION-alpine docker/php-apache/$PHP_VERSION-alpine
28+
docker build -t webdevops/php-apache-dev:$PHP_VERSION-alpine docker/php-apache-dev/$PHP_VERSION-alpine
3529

3630
# debian
37-
cd $ROOT_DIR/docker/php/$PHP_VERSION
38-
docker build -t webdevops/php:$PHP_VERSION .
39-
cd $ROOT_DIR/docker/php-dev/$PHP_VERSION
40-
docker build -t webdevops/php-dev:$PHP_VERSION .
41-
42-
cd $ROOT_DIR/docker/php-nginx/$PHP_VERSION
43-
docker build -t webdevops/php-nginx:$PHP_VERSION .
44-
cd $ROOT_DIR/docker/php-nginx-dev/$PHP_VERSION
45-
docker build -t webdevops/php-nginx-dev:$PHP_VERSION .
46-
47-
cd $ROOT_DIR/docker/php-apache/$PHP_VERSION
48-
docker build -t webdevops/php-apache:$PHP_VERSION .
49-
cd $ROOT_DIR/docker/php-apache-dev/$PHP_VERSION
50-
docker build -t webdevops/php-apache-dev:$PHP_VERSION .
31+
docker build -t webdevops/php:$PHP_VERSION docker/php/$PHP_VERSION
32+
docker build -t webdevops/php-dev:$PHP_VERSION docker/php-dev/$PHP_VERSION
33+
34+
docker build -t webdevops/php-nginx:$PHP_VERSION docker/php-nginx/$PHP_VERSION
35+
docker build -t webdevops/php-nginx-dev:$PHP_VERSION docker/php-nginx-dev/$PHP_VERSION
36+
37+
docker build -t webdevops/php-apache:$PHP_VERSION docker/php-apache/$PHP_VERSION
38+
docker build -t webdevops/php-apache-dev:$PHP_VERSION docker/php-apache-dev/$PHP_VERSION
5139

5240
docker images | grep webdevops | grep $PHP_VERSION

ci/src/Commands/GithubCommand.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,16 @@ private function traverse(Node $node): void
8585
if ($node->getLevel() > $this->deepestLevel) {
8686
$this->deepestLevel = $node->getLevel();
8787
}
88-
$this->jobs[GithubJobBuilder::toJobId($node->getId())] = $this->jobBuilder->getJobDescription($nodeAr);
88+
$this->jobs = [
89+
...$this->jobs,
90+
...$this->jobBuilder->getJobsDescription($nodeAr),
91+
];
8992
if ($this->isNameBlacklisted($nodeAr['id'])) {
9093
// $this->jobs[GithubJobBuilder::toJobId($node->getId())] = array_merge($this->jobs[GithubJobBuilder::toJobId($node->getId())], ['when' => 'manual']);
9194
$line .= ' *blacklisted*';
9295
if ($node->get('tag') !== $this->_settings['docker']['autoLatestTag']) {
9396
unset($this->jobs[GithubJobBuilder::toJobId($node->getId())]);
97+
unset($this->jobs[GithubJobBuilder::toJobId($node->getId()) . '_publish']);
9498
}
9599
}
96100
$this->output->write([str_pad('', $node->getLevel() - 1, "\t", STR_PAD_LEFT), $line, PHP_EOL]);

ci/src/GithubJobBuilder.php

Lines changed: 129 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,83 +2,148 @@
22

33
namespace Webdevops\Build;
44

5+
use function array_filter;
6+
use function array_values;
7+
use function dirname;
8+
use function implode;
59
use function str_replace;
610

711
class 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';
23+
24+
$pushTags = [];
25+
$pushTags[] = '-t "' . $node['id'] . '"';
26+
$pushTags[] = '-t "ghcr.io/' . $node['id'] . '"';
27+
foreach ($node['aliases'] as $alias) {
28+
$pushTags[] = '-t "' . $alias . '"';
29+
$pushTags[] = '-t "ghcr.io/' . $alias . '"';
30+
}
1531
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',
32+
$jobId => [
33+
'strategy' => [
34+
'fail-fast' => false,
35+
'matrix' => [
36+
'include' => [
37+
[
38+
'arch' => 'amd64',
39+
'runner' => 'ubuntu-24.04',
40+
'platform' => 'linux/amd64',
3641
],
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 }}',
42+
[
43+
'arch' => 'arm64',
44+
'runner' => 'ubuntu-24.04-arm',
45+
'platform' => 'linux/arm64',
5446
],
5547
],
48+
],
49+
],
50+
'name' => $node['name'] . ' (${{ matrix.arch }})',
51+
'needs' => $needs,
52+
// even run if previous job skipped
53+
'if' => '${{ !failure() }}',
54+
'runs-on' => '${{ matrix.runner }}',
55+
'container' => 'webdevops/dockerfile-build-env',
56+
'steps' => array_values(
57+
array_filter(
5658
[
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 }}',
59+
['uses' => 'actions/checkout@v6'],
60+
['uses' => 'docker/setup-buildx-action@v3'],
61+
[
62+
'name' => 'Build (load locally)',
63+
'uses' => 'docker/build-push-action@v6',
64+
'with' => [
65+
'context' => dirname(str_replace(__DIR__ . '/../../', '', $node['file'])),
66+
'platforms' => '${{ matrix.platform }}',
67+
'load' => true,
68+
'tags' => 'ghcr.io/webdevops/' . $node['image'] . ':sha-${{ github.sha }}-${{ matrix.arch }}-' . $node['tag'],
69+
'cache-from' => 'type=gha',
70+
'cache-to' => 'type=gha,mode=max',
71+
'build-args' => implode("\n", [
72+
'TARGETARCH=${{ matrix.arch }}',
73+
]),
74+
],
6475
],
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
76+
$serverSpec ? [
77+
'name' => 'run serverspec',
78+
'run' => implode("\n", $serverSpec),
79+
] : null,
80+
$structuredTests ? [
81+
'name' => 'run structure-test',
82+
'run' => implode("\n", $structuredTests),
83+
] : null,
84+
[
85+
'if' => '${{github.ref == \'refs/heads/master\'}}',
86+
'name' => 'Login to ghcr.io',
87+
'uses' => 'docker/login-action@v3',
88+
'with' => [
89+
'registry' => 'ghcr.io',
90+
'username' => '${{ github.actor }}',
91+
'password' => '${{ secrets.GITHUB_TOKEN }}',
92+
],
93+
],
94+
[
95+
'name' => 'Push arch image',
96+
'if' => '${{github.ref == \'refs/heads/master\'}}',
97+
'run' => 'docker push "ghcr.io/webdevops/' . $node['image'] . ':sha-${{ github.sha }}-${{ matrix.arch }}"-' . $node['tag'],
7798
],
7899
],
79-
],
100+
),
80101
),
81-
),
102+
],
103+
$jobId . '_publish' => [
104+
'name' => $node['name'] . ' - Publish',
105+
'runs-on' => 'ubuntu-latest',
106+
'needs' => $jobId,
107+
'if' => '${{github.ref == \'refs/heads/master\'}}',
108+
'steps' => [
109+
['uses' => 'docker/setup-buildx-action@v3'],
110+
[
111+
'name' => 'Login to ghcr.io',
112+
'uses' => 'docker/login-action@v3',
113+
'with' => [
114+
'registry' => 'ghcr.io',
115+
'username' => '${{ github.actor }}',
116+
'password' => '${{ secrets.GITHUB_TOKEN }}',
117+
],
118+
],
119+
[
120+
'name' => 'Login to hub.docker.com',
121+
'uses' => 'docker/login-action@v3',
122+
'with' => [
123+
'username' => '${{ secrets.DOCKERHUB_USERNAME }}',
124+
'password' => '${{ secrets.DOCKERHUB_TOKEN }}',
125+
],
126+
],
127+
[
128+
'name' => 'Create and push multi-arch manifest',
129+
'run' =>
130+
// we need the retry loop here because sometimes docker hub returns errors when pushing manifests (especially if pushed to the same image multiple times in a short time frame)
131+
implode("\n", [
132+
'set -euo pipefail',
133+
'for i in 1 2 3 4 5 6 7 8 9 10; do',
134+
' ' . implode(" \\\n ", [
135+
'docker buildx imagetools create',
136+
...$pushTags,
137+
'"ghcr.io/webdevops/' . $node['image'] . ':sha-${{ github.sha }}-amd64-' . $node['tag'] . '"',
138+
'"ghcr.io/webdevops/' . $node['image'] . ':sha-${{ github.sha }}-arm64-' . $node['tag'] . '" && exit 0',
139+
]),
140+
' sleep $((i*i))',
141+
'done',
142+
'exit 1',
143+
]),
144+
],
145+
],
146+
],
82147
];
83148
}
84149

@@ -98,14 +163,13 @@ private function serverSpec(array $node): array
98163
return [];
99164
}
100165

101-
// $testDockerfile = uniqid('Dockerfile_', true);
102166
$testDockerfile = 'Dockerfile_test';
103167
$specConfig = $node['serverspec'];
104168
$specConfig['DOCKERFILE'] = $testDockerfile;
105169
$encodedJsonConfig = base64_encode(json_encode($specConfig));
106170
$script = [
107171
'cd tests/serverspec',
108-
'echo "FROM ' . $node['id'] . '" >> ' . $testDockerfile,
172+
'echo "FROM ghcr.io/webdevops/' . $node['image'] . ':sha-${{ github.sha }}-${{ matrix.arch }}"-' . $node['tag'] . ' >> ' . $testDockerfile,
109173
'echo "COPY conf/ /" >> ' . $testDockerfile,
110174
];
111175
$script[] = 'bundle install';
@@ -119,9 +183,9 @@ private function structuredTests(array $node): array
119183
if (file_exists(__DIR__ . '/../../tests/structure-test/' . $node['image'] . '/test.yaml')) {
120184
$script[] = 'cd tests/structure-test';
121185
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';
186+
$script[] = '/usr/local/bin/container-structure-test test --image ghcr.io/webdevops/' . $node['image'] . ':sha-${{ github.sha }}-${{ matrix.arch }}-' . $node['tag'] . ' --config ' . $node['image'] . '/test.yaml --config ' . $node['image'] . '/' . $node['tag'] . '/test.yaml';
123187
} else {
124-
$script[] = '/usr/local/bin/container-structure-test test --image ' . $node['name'] . ' --config ' . $node['image'] . '/test.yaml';
188+
$script[] = '/usr/local/bin/container-structure-test test --image ghcr.io/webdevops/' . $node['image'] . ':sha-${{ github.sha }}-${{ matrix.arch }}-' . $node['tag'] . ' --config ' . $node['image'] . '/test.yaml';
125189
}
126190
}
127191
return $script;
@@ -133,7 +197,7 @@ public function getValidationConfig(): array
133197
'name' => 'Validate Automation',
134198
'runs-on' => 'ubuntu-latest',
135199
'steps' => [
136-
['uses' => 'actions/checkout@v4'],
200+
['uses' => 'actions/checkout@v6'],
137201
[
138202
'name' => 'Validate that template/* are used to generate Dockerfiles',
139203
'run' => implode("\n", [

docker/dockerfile-build-env/latest/Dockerfile

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#+++++++++++++++++++++++++++++++++++++++
55

66
FROM webdevops/base-app:ubuntu-22.04
7+
ARG TARGETARCH
78

89
RUN apt-install \
910
build-essential \
@@ -19,18 +20,20 @@ RUN apt-install \
1920
ruby \
2021
ruby-dev \
2122
ruby-bundler \
23+
libyaml-dev \
2224
&& curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py \
2325
&& python2 get-pip.py \
2426
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - \
25-
&& add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
27+
&& add-apt-repository "deb [arch=$TARGETARCH] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
2628
&& apt-install docker-ce \
2729
&& usermod -aG docker application \
28-
&& curl -LO https://storage.googleapis.com/container-structure-test/latest/container-structure-test-linux-amd64 \
29-
&& chmod +x container-structure-test-linux-amd64 \
30-
&& mv container-structure-test-linux-amd64 /usr/local/bin/container-structure-test \
30+
&& curl -LO https://storage.googleapis.com/container-structure-test/latest/container-structure-test-linux-$TARGETARCH \
31+
&& chmod +x container-structure-test-linux-$TARGETARCH \
32+
&& mv container-structure-test-linux-$TARGETARCH /usr/local/bin/container-structure-test \
3133
&& pip install --upgrade pip \
3234
&& hash -r pip \
3335
&& pip install --upgrade setuptools \
36+
&& pip install Cython==0.29.21 wheel \
3437
&& git clone --depth 1 https://github.com/webdevops/Dockerfile.git /tmp/Dockerfile \
3538
&& gem install bundler -v 2.1.4 --no-document \
3639
&& cd /tmp/Dockerfile/tests/serverspec \

docker/dockerfile-build-env/latest/Dockerfile.jinja2

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{{ docker.from("base-app","ubuntu-22.04") }}
2+
ARG TARGETARCH
23

34
RUN apt-install \
45
build-essential \
@@ -14,18 +15,20 @@ RUN apt-install \
1415
ruby \
1516
ruby-dev \
1617
ruby-bundler \
18+
libyaml-dev \
1719
&& curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py \
1820
&& python2 get-pip.py \
1921
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - \
20-
&& add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
22+
&& add-apt-repository "deb [arch=$TARGETARCH] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
2123
&& apt-install docker-ce \
2224
&& usermod -aG docker application \
23-
&& curl -LO https://storage.googleapis.com/container-structure-test/latest/container-structure-test-linux-amd64 \
24-
&& chmod +x container-structure-test-linux-amd64 \
25-
&& mv container-structure-test-linux-amd64 /usr/local/bin/container-structure-test \
25+
&& curl -LO https://storage.googleapis.com/container-structure-test/latest/container-structure-test-linux-$TARGETARCH \
26+
&& chmod +x container-structure-test-linux-$TARGETARCH \
27+
&& mv container-structure-test-linux-$TARGETARCH /usr/local/bin/container-structure-test \
2628
&& pip install --upgrade pip \
2729
&& hash -r pip \
2830
&& pip install --upgrade setuptools \
31+
&& pip install Cython==0.29.21 wheel \
2932
&& git clone --depth 1 https://github.com/webdevops/Dockerfile.git /tmp/Dockerfile \
3033
&& gem install bundler -v 2.1.4 --no-document \
3134
&& cd /tmp/Dockerfile/tests/serverspec \

0 commit comments

Comments
 (0)