Skip to content

Commit 219d3e1

Browse files
authored
Merge pull request #31 from KingPin/ci-optimizations
perf: optimize CI workflow and Dockerfiles for faster builds
2 parents 59fdc4e + c11ad23 commit 219d3e1

5 files changed

Lines changed: 108 additions & 150 deletions

File tree

.github/workflows/docker-ci.yml

Lines changed: 61 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -30,76 +30,75 @@ concurrency:
3030
cancel-in-progress: true
3131

3232
jobs:
33+
setup:
34+
runs-on: ubuntu-latest
35+
outputs:
36+
matrix: ${{ steps.matrix.outputs.matrix }}
37+
s6_version: ${{ steps.s6.outputs.version }}
38+
steps:
39+
- name: Compute build matrix
40+
id: matrix
41+
run: |
42+
if [ "${{ github.event_name }}" = "pull_request" ]; then
43+
PHP_VERSIONS='["8.4","8.2"]'
44+
echo "::notice::PR detected — testing PHP 8.4 + 8.2 only (skipping 8.3)"
45+
else
46+
PHP_VERSIONS='["8.4","8.3","8.2"]'
47+
fi
48+
49+
# Build trixie include list for v2 based on selected PHP versions
50+
INCLUDES="[]"
51+
for ver in $(echo "$PHP_VERSIONS" | jq -r '.[]'); do
52+
for type in fpm cli apache; do
53+
INCLUDES=$(echo "$INCLUDES" | jq -c ". + [{\"variant\":\"v2\",\"php-version\":\"$ver\",\"php-type\":\"$type\",\"php-base\":\"trixie\"}]")
54+
done
55+
done
56+
57+
MATRIX=$(jq -n -c \
58+
--argjson versions "$PHP_VERSIONS" \
59+
--argjson includes "$INCLUDES" \
60+
'{
61+
"variant": ["v1","v2"],
62+
"php-version": $versions,
63+
"php-type": ["fpm","cli","apache"],
64+
"php-base": ["alpine","bookworm"],
65+
"exclude": [
66+
{"php-type":"apache","php-base":"alpine"},
67+
{"variant":"v2","php-base":"bookworm"}
68+
],
69+
"include": $includes
70+
}')
71+
72+
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
73+
74+
- name: Get latest s6-overlay version
75+
id: s6
76+
run: |
77+
set -euo pipefail
78+
RESPONSE="$(curl -fSLs \
79+
-H "Authorization: Bearer ${{ github.token }}" \
80+
https://api.github.com/repos/just-containers/s6-overlay/releases/latest)"
81+
S6_OVERLAY_VERSION="$(echo "$RESPONSE" | jq -r .tag_name)"
82+
if [ -z "$S6_OVERLAY_VERSION" ] || [ "$S6_OVERLAY_VERSION" = "null" ]; then
83+
echo "::error::Failed to determine s6-overlay version"
84+
exit 1
85+
fi
86+
echo "version=${S6_OVERLAY_VERSION}" >> $GITHUB_OUTPUT
87+
echo "✅ Latest s6-overlay version: ${S6_OVERLAY_VERSION}"
88+
3389
build-and-test:
90+
needs: setup
3491
runs-on: ubuntu-latest
3592
strategy:
3693
fail-fast: false
37-
matrix:
38-
variant: [v1, v2]
39-
php-version: ['8.4', '8.3', '8.2']
40-
php-type: [fpm, cli, apache]
41-
php-base: [alpine, bookworm]
42-
exclude:
43-
- php-type: apache
44-
php-base: alpine
45-
- variant: v2
46-
php-base: bookworm
47-
include:
48-
- variant: v2
49-
php-version: '8.4'
50-
php-type: fpm
51-
php-base: trixie
52-
- variant: v2
53-
php-version: '8.4'
54-
php-type: cli
55-
php-base: trixie
56-
- variant: v2
57-
php-version: '8.4'
58-
php-type: apache
59-
php-base: trixie
60-
- variant: v2
61-
php-version: '8.3'
62-
php-type: fpm
63-
php-base: trixie
64-
- variant: v2
65-
php-version: '8.3'
66-
php-type: cli
67-
php-base: trixie
68-
- variant: v2
69-
php-version: '8.3'
70-
php-type: apache
71-
php-base: trixie
72-
- variant: v2
73-
php-version: '8.2'
74-
php-type: fpm
75-
php-base: trixie
76-
- variant: v2
77-
php-version: '8.2'
78-
php-type: cli
79-
php-base: trixie
80-
- variant: v2
81-
php-version: '8.2'
82-
php-type: apache
83-
php-base: trixie
94+
matrix: ${{ fromJson(needs.setup.outputs.matrix) }}
8495

8596
name: ${{ matrix.variant }}-${{ matrix.php-version }}-${{ matrix.php-type }}-${{ matrix.php-base }}
8697

8798
steps:
8899
- name: Checkout
89100
uses: actions/checkout@v4
90101

91-
- name: Get latest s6-overlay version
92-
id: s6-version
93-
run: |
94-
S6_OVERLAY_VERSION="$(curl -s https://api.github.com/repos/just-containers/s6-overlay/releases/latest | jq -r .tag_name)"
95-
echo "version=${S6_OVERLAY_VERSION}" >> $GITHUB_OUTPUT
96-
echo "✅ Latest s6-overlay version: ${S6_OVERLAY_VERSION}"
97-
98-
- name: Setup QEMU
99-
uses: docker/setup-qemu-action@v3
100-
with:
101-
platforms: amd64,arm64,arm
102-
103102
- name: Setup Docker Buildx
104103
uses: docker/setup-buildx-action@v3
105104

@@ -138,7 +137,7 @@ jobs:
138137
VERSION=${{ steps.vars.outputs.VERSION }}
139138
PHPVERSION=${{ matrix.php-version }}
140139
BASEOS=${{ matrix.php-base }}
141-
S6_OVERLAY_VERSION=${{ steps.s6-version.outputs.version }}
140+
S6_OVERLAY_VERSION=${{ needs.setup.outputs.s6_version }}
142141
BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }}
143142
VCS_REF=${{ github.sha }}
144143
tags: test-${{ steps.vars.outputs.TAG }}
@@ -295,7 +294,7 @@ jobs:
295294
echo "::notice::✅ Build and tests passed for ${{ matrix.variant }} - ${{ steps.vars.outputs.TAG }}"
296295
297296
publish:
298-
needs: build-and-test
297+
needs: [setup, build-and-test]
299298
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'schedule')
300299
runs-on: ubuntu-latest
301300
strategy:
@@ -356,13 +355,6 @@ jobs:
356355
- name: Checkout
357356
uses: actions/checkout@v4
358357

359-
- name: Get latest s6-overlay version
360-
id: s6-version
361-
run: |
362-
S6_OVERLAY_VERSION="$(curl -s https://api.github.com/repos/just-containers/s6-overlay/releases/latest | jq -r .tag_name)"
363-
echo "version=${S6_OVERLAY_VERSION}" >> $GITHUB_OUTPUT
364-
echo "✅ Latest s6-overlay version: ${S6_OVERLAY_VERSION}"
365-
366358
- name: Setup QEMU
367359
uses: docker/setup-qemu-action@v3
368360
with:
@@ -429,7 +421,7 @@ jobs:
429421
VERSION=${{ steps.vars.outputs.VERSION }}
430422
PHPVERSION=${{ matrix.php-version }}
431423
BASEOS=${{ matrix.php-base }}
432-
S6_OVERLAY_VERSION=${{ steps.s6-version.outputs.version }}
424+
S6_OVERLAY_VERSION=${{ needs.setup.outputs.s6_version }}
433425
BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }}
434426
VCS_REF=${{ github.sha }}
435427
tags: |

Dockerfile.v1

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,57 +6,29 @@ ARG BASEOS
66
# Set environment variables
77
ENV DEBIAN_FRONTEND=noninteractive
88

9+
COPY extras/retry.sh /usr/local/bin/retry
10+
RUN chmod +x /usr/local/bin/retry
11+
912
# Install dependencies based on the base OS
10-
RUN if [ "$BASEOS" = "bookworm" ]; then \
13+
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-$BASEOS \
14+
--mount=type=cache,target=/var/lib/apt/lists,sharing=locked,id=aptlists-$BASEOS \
15+
if [ "$BASEOS" = "bookworm" ]; then \
1116
echo 'deb http://deb.debian.org/debian bookworm main' > /etc/apt/sources.list && \
1217
apt-get update && \
1318
apt-get -y upgrade && \
14-
apt-get install -y --no-install-recommends curl git zip unzip ghostscript imagemagick optipng gifsicle pngcrush jpegoptim libjpeg-turbo-progs pngquant webp && \
15-
rm -rf /var/lib/apt/lists/*; \
19+
apt-get install -y --no-install-recommends curl git zip unzip ghostscript imagemagick optipng gifsicle pngcrush jpegoptim libjpeg-turbo-progs pngquant webp; \
1620
elif [ "$BASEOS" = "alpine" ]; then \
1721
apk update && \
1822
apk add --no-cache curl git zip unzip ghostscript imagemagick optipng gifsicle pngcrush jpegoptim libjpeg-turbo libjpeg-turbo-utils pngquant libwebp-tools; \
1923
fi
2024

21-
# Add all needed PHP extensions with retry logic for transient network failures
22-
RUN for ATTEMPT in 1 2 3; do \
23-
if curl -sSLf -o /usr/local/bin/install-php-extensions \
24-
https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions; then \
25-
break; \
26-
else \
27-
if [ $ATTEMPT -lt 3 ]; then \
28-
case $ATTEMPT in \
29-
1) SLEEP_TIME=5 ;; \
30-
2) SLEEP_TIME=10 ;; \
31-
esac; \
32-
echo "Download attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \
33-
sleep $SLEEP_TIME; \
34-
rm -f /usr/local/bin/install-php-extensions; \
35-
else \
36-
echo "Failed to download install-php-extensions after 3 attempts"; \
37-
exit 1; \
38-
fi; \
39-
fi; \
40-
done && \
25+
# Download and install PHP extensions with retry for transient failures
26+
RUN retry 3 curl -sSLf -o /usr/local/bin/install-php-extensions \
27+
https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions && \
4128
chmod +x /usr/local/bin/install-php-extensions && \
42-
for ATTEMPT in 1 2 3; do \
43-
if install-php-extensions amqp bcmath bz2 calendar ctype exif intl imagick imap json mbstring ldap mcrypt memcached mongodb \
44-
mysqli opcache pdo_mysql pdo_pgsql pgsql redis snmp soap sockets tidy timezonedb uuid vips xsl yaml zip zstd @composer; then \
45-
break; \
46-
else \
47-
if [ $ATTEMPT -lt 3 ]; then \
48-
case $ATTEMPT in \
49-
1) SLEEP_TIME=5 ;; \
50-
2) SLEEP_TIME=10 ;; \
51-
esac; \
52-
echo "Extension installation attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \
53-
sleep $SLEEP_TIME; \
54-
else \
55-
echo "Failed to install PHP extensions after 3 attempts"; \
56-
exit 1; \
57-
fi; \
58-
fi; \
59-
done
29+
retry 3 install-php-extensions \
30+
amqp bcmath bz2 calendar ctype exif intl imagick imap json mbstring ldap mcrypt memcached mongodb \
31+
mysqli opcache pdo_mysql pdo_pgsql pgsql redis snmp soap sockets tidy timezonedb uuid vips xsl yaml zip zstd @composer
6032

6133
# Enable Apache rewrite mod, if applicable
6234
RUN if command -v a2enmod; then a2enmod rewrite; fi

Dockerfile.v2

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ ARG VERSION
1212
# Set environment variables
1313
ENV DEBIAN_FRONTEND=noninteractive
1414

15+
COPY extras/retry.sh /usr/local/bin/retry
16+
RUN chmod +x /usr/local/bin/retry
17+
1518
# OCI standard labels
1619
LABEL org.opencontainers.image.title="php-docker" \
1720
org.opencontainers.image.description="PHP runtime with s6-overlay and curated extensions" \
@@ -26,7 +29,9 @@ LABEL org.opencontainers.image.title="php-docker" \
2629

2730
# Install build dependencies, PHP extensions, runtime libraries, and s6-overlay
2831
# Then clean up build-only packages in a single layer to minimize image size
29-
RUN if [ "$BASEOS" = "trixie" ] || [ "$BASEOS" = "bookworm" ]; then \
32+
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-$BASEOS \
33+
--mount=type=cache,target=/var/lib/apt/lists,sharing=locked,id=aptlists-$BASEOS \
34+
if [ "$BASEOS" = "trixie" ] || [ "$BASEOS" = "bookworm" ]; then \
3035
apt-get update && \
3136
apt-get -y upgrade && \
3237
# Install build tools, dev packages, and runtime libraries together
@@ -129,25 +134,8 @@ RUN if [ "$BASEOS" = "trixie" ] || [ "$BASEOS" = "bookworm" ]; then \
129134
libxpm libxpm-dev; \
130135
fi && \
131136
# Download PHP extension installer with retry
132-
for ATTEMPT in 1 2 3; do \
133-
if curl -sSLf -o /usr/local/bin/install-php-extensions \
134-
https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions; then \
135-
break; \
136-
else \
137-
if [ $ATTEMPT -lt 3 ]; then \
138-
case $ATTEMPT in \
139-
1) SLEEP_TIME=5 ;; \
140-
2) SLEEP_TIME=10 ;; \
141-
esac; \
142-
echo "Download attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \
143-
sleep $SLEEP_TIME; \
144-
rm -f /usr/local/bin/install-php-extensions; \
145-
else \
146-
echo "Failed to download install-php-extensions after 3 attempts"; \
147-
exit 1; \
148-
fi; \
149-
fi; \
150-
done && \
137+
retry 3 curl -sSLf -o /usr/local/bin/install-php-extensions \
138+
https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions && \
151139
chmod +x /usr/local/bin/install-php-extensions && \
152140
# Install PHP extensions
153141
install-php-extensions \
@@ -173,25 +161,10 @@ RUN if [ "$BASEOS" = "trixie" ] || [ "$BASEOS" = "bookworm" ]; then \
173161
*) S6_ARCH="x86_64" ;; \
174162
esac && \
175163
echo "Downloading s6-overlay ${S6_OVERLAY_VERSION} for ${S6_ARCH}" && \
176-
for ATTEMPT in 1 2 3; do \
177-
if wget -O /tmp/s6-overlay-noarch.tar.xz https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz && \
178-
wget -O /tmp/s6-overlay-${S6_ARCH}.tar.xz https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz; then \
179-
break; \
180-
else \
181-
if [ $ATTEMPT -lt 3 ]; then \
182-
case $ATTEMPT in \
183-
1) SLEEP_TIME=5 ;; \
184-
2) SLEEP_TIME=10 ;; \
185-
esac; \
186-
echo "Download attempt $ATTEMPT failed, retrying in ${SLEEP_TIME}s..."; \
187-
sleep $SLEEP_TIME; \
188-
rm -f /tmp/s6-overlay-*.tar.xz; \
189-
else \
190-
echo "Failed to download s6-overlay after 3 attempts"; \
191-
exit 1; \
192-
fi; \
193-
fi; \
194-
done && \
164+
retry 3 wget -O /tmp/s6-overlay-noarch.tar.xz \
165+
https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz && \
166+
retry 3 wget -O /tmp/s6-overlay-${S6_ARCH}.tar.xz \
167+
https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz && \
195168
tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz && \
196169
tar -C / -Jxpf /tmp/s6-overlay-${S6_ARCH}.tar.xz && \
197170
rm /tmp/s6-overlay-noarch.tar.xz /tmp/s6-overlay-${S6_ARCH}.tar.xz && \

extras/retry.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/sh
2+
# Retry a command with linear backoff
3+
# Usage: retry <max_attempts> <command...>
4+
# Example: retry 3 curl -sSLf -o /tmp/file https://example.com/file
5+
6+
MAX=$1
7+
shift
8+
9+
for ATTEMPT in $(seq 1 "$MAX"); do
10+
if "$@"; then
11+
exit 0
12+
fi
13+
if [ "$ATTEMPT" -lt "$MAX" ]; then
14+
SLEEP_TIME=$((5 * ATTEMPT))
15+
echo "Attempt $ATTEMPT/$MAX failed, retrying in ${SLEEP_TIME}s..."
16+
sleep "$SLEEP_TIME"
17+
fi
18+
done
19+
20+
echo "Failed after $MAX attempts: $*"
21+
exit 1

extras/test-build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ build_v1() {
5151
echo "Building v1: ${IMAGE_NAME}:${tag}"
5252
echo " VERSION=${version}, PHPVERSION=${phpversion}, BASEOS=${baseos}"
5353

54-
docker build -f Dockerfile.v1 \
54+
DOCKER_BUILDKIT=1 docker build -f Dockerfile.v1 \
5555
--build-arg VERSION="${version}" \
5656
--build-arg PHPVERSION="${phpversion}" \
5757
--build-arg BASEOS="${baseos}" \

0 commit comments

Comments
 (0)