Skip to content

Commit cba5161

Browse files
Merge pull request #553 from liberusoftware/copilot/fix-docker-setup-multi-stage
Implement multi-stage Docker builds and GitHub Actions caching
2 parents f1c90e9 + c6c1738 commit cba5161

File tree

6 files changed

+216
-52
lines changed

6 files changed

+216
-52
lines changed

.dockerignore

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Git files
2+
.git
3+
.github
4+
.gitignore
5+
.gitattributes
6+
7+
# Documentation
8+
*.md
9+
docs/
10+
DEVELOPER_NOTES.md
11+
ARCHITECTURE.md
12+
13+
# CI/CD
14+
.circleci
15+
16+
# IDE
17+
.vscode
18+
.idea
19+
*.swp
20+
*.swo
21+
*~
22+
23+
# OS files
24+
.DS_Store
25+
Thumbs.db
26+
27+
# Development files
28+
.env
29+
.env.*
30+
!.env.example
31+
.env.testing
32+
33+
# Dependencies (will be installed in container)
34+
vendor/
35+
node_modules/
36+
37+
# Build artifacts
38+
public/hot
39+
public/storage
40+
public/build/
41+
storage/framework/cache/*
42+
storage/framework/sessions/*
43+
storage/framework/views/*
44+
storage/logs/*
45+
bootstrap/cache/*
46+
!storage/framework/cache/.gitkeep
47+
!storage/framework/sessions/.gitkeep
48+
!storage/framework/views/.gitkeep
49+
!storage/logs/.gitkeep
50+
!bootstrap/cache/.gitkeep
51+
52+
# Test files
53+
tests/
54+
phpunit.xml
55+
.phpunit.result.cache
56+
coverage/
57+
.coverage
58+
59+
# Package manager files (excluding lock files needed for builds)
60+
npm-debug.log
61+
yarn-error.log
62+
63+
# Laravel specific
64+
Homestead.yaml
65+
Homestead.json
66+
/.vagrant
67+
rr
68+
.rr.yaml
69+
70+
# Backup files
71+
*.bak
72+
*.backup
73+
*.sql
74+
*.dump
75+
76+
# Temporary files
77+
tmp/
78+
temp/
79+
*.tmp

.github/workflows/install.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,26 @@ jobs:
1818
uses: shivammathur/setup-php@v2
1919
with:
2020
php-version: '8.5'
21+
extensions: mbstring, bcmath, pdo, mysql, dom, curl
2122

2223
- name: Setup Node.js
2324
uses: actions/setup-node@v4
2425
with:
2526
node-version: '24'
27+
cache: 'npm'
28+
29+
- name: Get Composer cache directory
30+
id: composer-cache
31+
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
32+
33+
- name: Cache Composer dependencies
34+
uses: actions/cache@v4
35+
with:
36+
path: ${{ steps.composer-cache.outputs.dir }}
37+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
38+
restore-keys: |
39+
${{ runner.os }}-composer-
2640
27-
2841
- name: Start database
2942
run: sudo /etc/init.d/mysql start
3043

.github/workflows/main.yml

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,34 @@ jobs:
1818
if: github.event_name == 'push'
1919
runs-on: ubuntu-latest
2020
steps:
21-
-
22-
name: Login to Docker Hub
21+
- name: Checkout code
22+
uses: actions/checkout@v4
23+
24+
- name: Login to Docker Hub
2325
uses: docker/login-action@v3
2426
with:
2527
username: ${{ secrets.DOCKERHUB_USERNAME }}
2628
password: ${{ secrets.DOCKERHUB_TOKEN }}
2729

28-
-
29-
name: Extract metadata (tags, labels) for Docker
30+
- name: Extract metadata (tags, labels) for Docker
3031
id: meta
31-
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f
32+
uses: docker/metadata-action@v5
3233
with:
3334
images: liberu/boilerplate
3435

35-
-
36-
# Setting up Docker Buildx with docker-container driver is required
37-
# at the moment to be able to use a subdirectory with Git context
38-
name: Set up Docker Buildx
36+
# Setting up Docker Buildx with docker-container driver is required
37+
# at the moment to be able to use a subdirectory with Git context
38+
- name: Set up Docker Buildx
3939
uses: docker/setup-buildx-action@v3
4040

4141
- name: Build and push Docker image
42-
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83
42+
uses: docker/build-push-action@v6
4343
with:
44-
# context: "{{defaultContext}}:.docker/prod/app/"
44+
context: .
4545
file: Dockerfile
4646
push: true
4747
tags: ${{ steps.meta.outputs.tags }}
48-
labels: ${{ steps.meta.outputs.labels }}
48+
labels: ${{ steps.meta.outputs.labels }}
49+
cache-from: type=registry,ref=liberu/boilerplate:buildcache
50+
cache-to: type=registry,ref=liberu/boilerplate:buildcache,mode=max
51+

.github/workflows/security.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ jobs:
2222
- uses: shivammathur/setup-php@v2
2323
with:
2424
php-version: '8.5'
25+
extensions: mbstring, bcmath, pdo, mysql, dom, curl
26+
27+
- name: Get Composer cache directory
28+
id: composer-cache
29+
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
30+
31+
- name: Cache Composer dependencies
32+
uses: actions/cache@v4
33+
with:
34+
path: ${{ steps.composer-cache.outputs.dir }}
35+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
36+
restore-keys: |
37+
${{ runner.os }}-composer-
38+
2539
- name: 'Run Phpcpd'
2640
run: |
2741
sudo composer install
@@ -35,6 +49,20 @@ jobs:
3549
- uses: shivammathur/setup-php@v2
3650
with:
3751
php-version: '8.5'
52+
extensions: mbstring, bcmath, pdo, mysql, dom, curl
53+
54+
- name: Get Composer cache directory
55+
id: composer-cache
56+
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
57+
58+
- name: Cache Composer dependencies
59+
uses: actions/cache@v4
60+
with:
61+
path: ${{ steps.composer-cache.outputs.dir }}
62+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
63+
restore-keys: |
64+
${{ runner.os }}-composer-
65+
3866
- name: 'Run php-insight'
3967
run: |
4068
sudo composer install
@@ -47,6 +75,20 @@ jobs:
4775
- uses: shivammathur/setup-php@v2
4876
with:
4977
php-version: '8.5'
78+
extensions: mbstring, bcmath, pdo, mysql, dom, curl
79+
80+
- name: Get Composer cache directory
81+
id: composer-cache
82+
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
83+
84+
- name: Cache Composer dependencies
85+
uses: actions/cache@v4
86+
with:
87+
path: ${{ steps.composer-cache.outputs.dir }}
88+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
89+
restore-keys: |
90+
${{ runner.os }}-composer-
91+
5092
- name: 'Run php-insight'
5193
run: |
5294
PHP_SC_VERSION=$(curl -s "https://api.github.com/repos/fabpot/local-php-security-checker/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/;s/^v//')

.github/workflows/tests.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ jobs:
1818
with:
1919
php-version: '8.5'
2020
extensions: mbstring, bcmath, pdo, mysql, dom, curl
21+
coverage: xdebug
22+
23+
- name: Get Composer cache directory
24+
id: composer-cache
25+
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
26+
27+
- name: Cache Composer dependencies
28+
uses: actions/cache@v4
29+
with:
30+
path: ${{ steps.composer-cache.outputs.dir }}
31+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
32+
restore-keys: |
33+
${{ runner.os }}-composer-
2134
2235
- name: Start database
2336
run: sudo /etc/init.d/mysql start

Dockerfile

Lines changed: 53 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
11
# Accepted values: 8.3 - 8.2
22
ARG PHP_VERSION=8.3
33

4-
ARG COMPOSER_VERSION=latest
4+
###########################################
5+
# Composer dependencies stage
6+
###########################################
7+
FROM composer:latest AS composer-deps
58

6-
ARG NODE_VERSION=20-alpine
9+
WORKDIR /app
710

8-
###########################################
11+
# Copy composer files
12+
COPY composer.json composer.lock ./
913

10-
FROM composer:${COMPOSER_VERSION} AS vendor
14+
# Install composer dependencies (no autoloader yet, will optimize in final stage)
15+
RUN composer install \
16+
--no-dev \
17+
--no-interaction \
18+
--no-autoloader \
19+
--no-ansi \
20+
--no-scripts \
21+
--prefer-dist
1122

23+
###########################################
24+
# Main application stage
25+
###########################################
1226
FROM php:${PHP_VERSION}-cli-alpine
1327

1428
LABEL maintainer="SMortexa <seyed.me720@gmail.com>"
@@ -39,8 +53,9 @@ RUN ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime \
3953

4054
ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
4155

42-
RUN apk update; \
43-
apk upgrade; \
56+
# Install system dependencies and PHP extensions in one layer
57+
RUN apk update && \
58+
apk upgrade && \
4459
apk add --no-cache \
4560
curl \
4661
wget \
@@ -49,9 +64,8 @@ RUN apk update; \
4964
procps \
5065
ca-certificates \
5166
supervisor \
52-
libsodium-dev \
53-
# Install PHP extensions
54-
&& install-php-extensions \
67+
libsodium-dev && \
68+
install-php-extensions \
5569
bz2 \
5670
pcntl \
5771
mbstring \
@@ -70,9 +84,9 @@ RUN apk update; \
7084
memcached \
7185
igbinary \
7286
ldap \
73-
swoole \
74-
&& docker-php-source delete \
75-
&& rm -rf /var/cache/apk/* /tmp/* /var/tmp/*
87+
swoole && \
88+
docker-php-source delete && \
89+
rm -rf /var/cache/apk/* /tmp/* /var/tmp/*
7690

7791
RUN arch="$(apk --print-arch)" \
7892
&& case "$arch" in \
@@ -99,48 +113,48 @@ RUN cp ${PHP_INI_DIR}/php.ini-production ${PHP_INI_DIR}/php.ini
99113

100114
USER ${USER}
101115

102-
COPY --chown=${USER}:${USER} --from=vendor /usr/bin/composer /usr/bin/composer
103-
COPY --chown=${USER}:${USER} composer.json composer.lock ./
116+
# Install Composer from official image
117+
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
104118

105-
RUN composer install \
106-
--no-dev \
107-
--no-interaction \
108-
--no-autoloader \
109-
--no-ansi \
110-
--no-scripts \
111-
--audit
119+
# Copy vendor from composer-deps stage for better caching
120+
COPY --chown=${USER}:${USER} --from=composer-deps /app/vendor ./vendor
112121

113-
COPY --chown=${USER}:${USER} . .
122+
# Copy composer files (needed for autoloader generation)
123+
COPY --chown=${USER}:${USER} composer.json composer.lock ./
114124

125+
# Generate optimized autoloader with vendor already in place
126+
RUN composer dump-autoload --classmap-authoritative --no-dev && \
127+
composer clear-cache
128+
129+
# Copy application code
130+
COPY --chown=${USER}:${USER} . .
131+
132+
# Create necessary Laravel directories
115133
RUN mkdir -p \
116134
storage/framework/sessions \
117135
storage/framework/views \
118136
storage/framework/cache \
119137
storage/framework/testing \
120138
storage/logs \
121-
bootstrap/cache && chmod -R a+rw storage
139+
bootstrap/cache && \
140+
chmod -R a+rw storage
122141

123-
COPY --chown=${USER}:${USER} .docker/supervisord.conf /etc/supervisor/
124-
COPY --chown=${USER}:${USER} .docker/octane/Swoole/supervisord.swoole.conf /etc/supervisor/conf.d/
125-
COPY --chown=${USER}:${USER} .docker/supervisord.*.conf /etc/supervisor/conf.d/
126-
COPY --chown=${USER}:${USER} .docker/php.ini ${PHP_INI_DIR}/conf.d/99-octane.ini
127-
COPY --chown=${USER}:${USER} .docker/start-container /usr/local/bin/start-container
142+
# Copy configuration files
143+
COPY --chown=${USER}:${USER} .docker/supervisord.conf /etc/supervisor/
144+
COPY --chown=${USER}:${USER} .docker/octane/Swoole/supervisord.swoole.conf /etc/supervisor/conf.d/
145+
COPY --chown=${USER}:${USER} .docker/supervisord.*.conf /etc/supervisor/conf.d/
146+
COPY --chown=${USER}:${USER} .docker/php.ini ${PHP_INI_DIR}/conf.d/99-octane.ini
147+
COPY --chown=${USER}:${USER} .docker/start-container /usr/local/bin/start-container
128148

129-
RUN composer install \
130-
--classmap-authoritative \
131-
--no-interaction \
132-
--no-ansi \
133-
--no-dev \
134-
&& composer clear-cache
149+
# Copy environment file
150+
COPY --chown=${USER}:${USER} .env.example ./.env
135151

136-
COPY .env.example ./.env
137-
138-
RUN chmod +x /usr/local/bin/start-container
139-
140-
RUN cat .docker/utilities.sh >> ~/.bashrc
152+
RUN chmod +x /usr/local/bin/start-container && \
153+
cat .docker/utilities.sh >> ~/.bashrc
141154

142155
EXPOSE 8000
143156

144157
ENTRYPOINT ["start-container"]
145158

146159
HEALTHCHECK --start-period=5s --interval=2s --timeout=5s --retries=8 CMD php artisan octane:status || exit 1
160+

0 commit comments

Comments
 (0)