Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions .castor/init.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use Castor\Attribute\AsTask;

use function Castor\finder;
use function Castor\fs;
use function Castor\io;
use function Castor\variable;
Expand All @@ -11,12 +12,15 @@
#[AsTask(description: 'Initialize the project')]
function init(): void
{
// The CI of docker-starter itself goes away, the production images workflow stays
$buildPushWorkflow = '.github/workflows/build-push.yml';
$buildPushWorkflowContent = file_get_contents($buildPushWorkflow);
// The CI is kept for the project, without what only tests docker-starter itself
foreach (finder()->files()->in('.github')->name('*.yml') as $file) {
$content = preg_replace('{^[ \t]*# >>> docker-starter only.*?^[ \t]*# <<< docker-starter only\n}ms', '', $file->getContents());
// The PHP versions matrix is gone
$content = str_replace(' with PHP ${{ matrix.php-version }}', '', (string) $content);
fs()->dumpFile($file->getPathname(), rtrim($content) . "\n");
}

fs()->remove([
'.github/',
'.castor/docker-push-test.php',
'README.md',
'CHANGELOG.md',
Expand All @@ -26,12 +30,6 @@ function init(): void
]);
fs()->rename('README.dist.md', 'README.md');

if (false !== $buildPushWorkflowContent) {
// Drop the "disabled" notice (first paragraph), then uncomment the workflow
$buildPushWorkflowContent = explode("\n\n", $buildPushWorkflowContent, 2)[1] ?? '';
fs()->dumpFile($buildPushWorkflow, (string) preg_replace('{^# ?}m', '', $buildPushWorkflowContent));
}

$readMeContent = file_get_contents('README.md');

if (false === $readMeContent) {
Expand Down
36 changes: 36 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,39 @@ updates:
- '*'
cooldown:
default-days: 7

- package-ecosystem: 'composer'
directories:
# Add '/application' once the application has a composer.json
- '/tools/php-cs-fixer'
- '/tools/phpstan'
- '/tools/twig-cs-fixer'
schedule:
interval: 'monthly'
groups:
composer:
patterns:
- '*'
composer-security:
applies-to: security-updates
patterns:
- '*'
cooldown:
default-days: 7

- package-ecosystem: 'docker'
directories:
- '/infrastructure/docker/services/php'
- '/infrastructure/docker/services/router'
schedule:
interval: 'monthly'
groups:
docker:
patterns:
- '*'
docker-security:
applies-to: security-updates
patterns:
- '*'
cooldown:
default-days: 7
96 changes: 48 additions & 48 deletions .github/workflows/build-push.yml
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
# Disabled in docker-starter itself: there is nothing to deploy. `castor init`
# enables it in your project, by removing this paragraph and uncommenting the rest.

# name: Build and push production images
#
# "on":
# push:
# branches: ["main"]
# tags: ["*"]
#
# permissions:
# contents: read
# packages: write
#
# env:
# # Fix for symfony/color detection. We know GitHub Actions can handle it
# ANSICON: 1
# CASTOR_CONTEXT: prod
# REGISTRY: "ghcr.io/${{ github.repository }}"
#
# jobs:
# build-push:
# name: Build and push production images
# runs-on: ubuntu-latest
# steps:
# - name: Set up Docker Buildx
# uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
#
# - name: Log in to registry
# shell: bash
# run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
#
# - name: setup-castor
# uses: castor-php/setup-castor@2a495b8c91f00be6768ad8a040ba8634c797d386 # v1.1.0
#
# - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# with:
# persist-credentials: false
#
# # Images are tagged with the short commit sha (what a deployment should
# # reference), "latest" on main, and the git tag when there is one
# - name: "Build and push the production images"
# shell: bash
# run: |
# tags=(--tag="${GITHUB_SHA::7}")
# if [ "${GITHUB_REF_NAME}" = main ]; then tags+=(--tag=latest); fi
# if [ "${GITHUB_REF_TYPE}" = tag ]; then tags+=(--tag="${GITHUB_REF_NAME}"); fi
# castor docker:push "${tags[@]}"
name: Build and push production images

"on":
push:
branches: ["main"]
tags: ["*"]

permissions:
contents: read
packages: write

env:
# Fix for symfony/color detection. We know GitHub Actions can handle it
ANSICON: 1
CASTOR_CONTEXT: prod
REGISTRY: "ghcr.io/${{ github.repository }}"

jobs:
build-push:
name: Build and push production images
# Set the PUSH_PRODUCTION_IMAGES repository variable to "true"
# (Settings > Secrets and variables > Actions > Variables) to enable it
if: vars.PUSH_PRODUCTION_IMAGES == 'true'
runs-on: ubuntu-latest
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1

- name: Log in to registry
shell: bash
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin

- name: setup-castor
uses: castor-php/setup-castor@2a495b8c91f00be6768ad8a040ba8634c797d386 # v1.1.0

- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

# Images are tagged with the short commit sha (what a deployment should
# reference), "latest" on main, and the git tag when there is one
- name: "Build and push the production images"
shell: bash
run: |
tags=(--tag="${GITHUB_SHA::7}")
if [ "${GITHUB_REF_NAME}" = main ]; then tags+=(--tag=latest); fi
if [ "${GITHUB_REF_TYPE}" = tag ]; then tags+=(--tag="${GITHUB_REF_NAME}"); fi
castor docker:push "${tags[@]}"
54 changes: 26 additions & 28 deletions .github/workflows/cache.yml
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
name: Push docker image to registry

"on":
push:
# Only run this job when pushing to the main branch
branches: ["main"]
push:
# Only run this job when pushing to the main branch
branches: ["main"]

permissions:
contents: read
packages: write
contents: read
packages: write

env:
REGISTRY: "ghcr.io/jolicode/docker-starter"
DS_PHP_VERSION: "8.5"
REGISTRY: "ghcr.io/${{ github.repository }}"

jobs:
push-images:
name: Push image to registry
runs-on: ubuntu-latest
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1

- name: Log in to registry
shell: bash
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin

- name: setup-castor
uses: castor-php/setup-castor@2a495b8c91f00be6768ad8a040ba8634c797d386 # v1.1.0

- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false


- name: "Build and start the infrastructure"
run: "castor docker:push"
push-images:
name: Push image to registry
runs-on: ubuntu-latest
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1

- name: Log in to registry
shell: bash
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin

- name: setup-castor
uses: castor-php/setup-castor@2a495b8c91f00be6768ad8a040ba8634c797d386 # v1.1.0

- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: "Push the images cache to the registry"
run: "castor docker:push"
18 changes: 15 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ env:
# Fix for symfony/color detection. We know GitHub Actions can handle it
ANSICON: 1
CASTOR_CONTEXT: ci
REGISTRY: "ghcr.io/jolicode/docker-starter"
REGISTRY: "ghcr.io/${{ github.repository }}"

# >>> docker-starter only
# The "docker-starter only" blocks test docker-starter itself: `castor init`
# removes them, the rest is the CI of your project.
# <<< docker-starter only
jobs:
check-dockerfiles:
name: Check Dockerfile
Expand Down Expand Up @@ -60,27 +64,33 @@ jobs:
- name: "Build and start the production images"
run: "castor start"

# Add some checks of your application, e.g. the status code of the homepage
- name: "Test HTTP server"
run: |
set -e
set -o pipefail

# >>> docker-starter only
curl --fail --silent http://127.0.0.1:8000 | grep "Hello world"
curl --fail --silent http://127.0.0.1:8000 | grep "Environment: prod"
# <<< docker-starter only
test "$(curl --silent -o /dev/null -w '%{http_code}' http://127.0.0.1:8000/index.php)" = 404

- name: "Test the php image runs as a non-root user"
run: "castor builder -- id -u | grep -x 1000"

ci:
name: Test with PHP ${{ matrix.php-version }}
# >>> docker-starter only: every supported PHP version is tested (`castor
# init` also removes the PHP version from the job name)
strategy:
fail-fast: false
matrix:
php-version: ["8.3", "8.4", "8.5"]
runs-on: ubuntu-latest
env:
DS_PHP_VERSION: ${{ matrix.php-version }}
# <<< docker-starter only
name: Test with PHP ${{ matrix.php-version }}
runs-on: ubuntu-latest
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
Expand Down Expand Up @@ -114,6 +124,7 @@ jobs:
- name: "Run PHPUnit"
run: "castor qa:phpunit"

# >>> docker-starter only
- name: "Test docker:push"
run: "castor docker:test-push"

Expand Down Expand Up @@ -170,3 +181,4 @@ jobs:
sleep 3

curl --fail --insecure --silent -H "Host: app.test" https://127.0.0.1 | grep "database OK"
# <<< docker-starter only
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Migrate from Invoke to Castor
* Add `castor symfony` to install a Symfony application
* Add `castor init` command to initialize a new project
* `castor init` keeps the CI (GitHub Actions) for the project, without what only tests docker-starter
* Add a `test` context
* Add a `prod` context to build, run and push production images
* `castor docker:push --tag=...` also pushes the images
Expand All @@ -30,6 +31,7 @@
* Add git worktree support (auto-isolated project name, ports, volumes, networks)
* Add support for caching image cache in a registry
* Add production images (`php` and `nginx`): code baked in, non-root, php-fpm on a unix socket
* Push the production images from the CI only when the `PUSH_PRODUCTION_IMAGES` repository variable is `true`
* Share php-fpm and nginx configuration between the dev `frontend` container and the production images
* The `frontend` container now listens on port 8080 and php-fpm on a unix socket
* Upgrade base to Debian Bookworm (12.5)
Expand Down
14 changes: 13 additions & 1 deletion README.dist.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,26 @@ castor builder

The application ships as two images, `php` and `nginx`, built from the
"Production stages" of `infrastructure/docker/services/php/Dockerfile` and
pushed to the registry by `.github/workflows/build-push.yml`. To test them
pushed to the registry by `.github/workflows/build-push.yml`, when the
`PUSH_PRODUCTION_IMAGES` repository variable is `true` (Settings > Secrets and
variables > Actions > Variables, disabled by default). To test them
locally, on a stack independent from the development one:

```bash
castor start -c prod # -> http://127.0.0.1:8000
castor destroy -c prod
```

### Continuous integration

The GitHub Actions workflows live in `.github/workflows/`:

* `ci.yml` checks the Dockerfile, runs the QA tools and the tests, and checks
the production images: add there some checks of your application
* `cache.yml` pushes the Docker build cache to the registry, on `main`
* `build-push.yml` builds and pushes the production images, on `main` and on
tags, when the `PUSH_PRODUCTION_IMAGES` repository variable is `true`

### Other tasks

Checkout `castor` to have the list of available tasks.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ castor init
> [!NOTE]
> This command can be run only once

The GitHub Actions workflows of `.github/` are kept as the CI of your project:
`castor init` only removes their "docker-starter only" blocks, which test
docker-starter itself (PHP versions matrix, demo application...).

Also, in order to improve your usage of castor scripts, you can install console
autocompletion script.

Expand Down Expand Up @@ -1125,11 +1129,12 @@ container).

`castor docker:push --tag=...` also pushes the images themselves (not only
their build cache). On every push to `main` (and on every git tag), the
`.github/workflows/build-push.yml` workflow (commented in docker-starter,
enabled by `castor init`) pushes both images to
`.github/workflows/build-push.yml` workflow pushes both images to
`ghcr.io/<repository>/php` and `ghcr.io/<repository>/nginx`, tagged with the
short commit sha, `latest` on `main`, and the tag name when there is one. To
push from your machine (you need to be logged in to the registry, and a buildx
short commit sha, `latest` on `main`, and the tag name when there is one. This
workflow is disabled by default (and in docker-starter itself): set the
`PUSH_PRODUCTION_IMAGES` repository variable to `true` to enable it (Settings >
Secrets and variables > Actions > Variables). To push from your machine (you need to be logged in to the registry, and a buildx
builder able to export a registry cache, e.g. `docker buildx create --use`):

```bash
Expand Down
Loading