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
81 changes: 79 additions & 2 deletions .docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,57 @@ forum.

## Installing the forum

```sh
.docker/install-forum.sh --engine mysql
.docker/install-forum.sh --engine postgresql
.docker/install-forum.sh --engine both
```

That resets the engine's database and installs a forum into it, with no browser
involved. It takes about a minute. Log in at http://localhost:8080 as
`admin` / `password`.

SMF 3.0's installer is CLI-native: `Maintenance::parseCliArguments()` turns
`--name=value` into `$_POST`, and `Maintenance::execute()` then runs every step
in one process, stopping at the first that still needs input. The script makes
two passes, because `databasePopulation()` always stops the first time even
though it succeeded — it pauses so a human can read its "N duplicate tables
ignored" report, and the form's `pop_done` field is the short-circuit past it.
Passing `pop_done` on the first pass would skip building the schema entirely.

Two flags worth knowing:

- `--force` reinstalls even when a forum is already there. Without it the
script leaves an existing install alone.
- `--pin-secrets` fixes `auth_secret` and `image_proxy_secret` to known values
instead of the random ones `ForumSettings()` generates. Both installs then
differ only in their database, so a login cookie survives `use-engine.sh`.
Dev-only values for a throwaway forum: never reuse them.

### Two forums at once

`--engine both` installs MySQL first and PostgreSQL second, one after the other.
It has to be sequential: `Settings.php` pins a single `$db_type`, and
`Db::load()` hands back the connection it already made, so only one engine can
ever be live in a process.

Both installs are kept. Switch between them with:

```sh
.docker/use-engine.sh postgresql
```

That puts the saved `Settings.php` back and clears `cache/`. No restart is
needed — the entrypoint only writes `Settings.php` when there is not one, so it
leaves whatever is in place alone. The copies live in `.docker/settings/` and
are gitignored.

`reset.sh` is the other half: it empties one engine's database and restages the
installer, discarding that forum. `use-engine.sh` switches between forums,
`reset.sh` throws one away.

### Installing in a browser instead

On first boot the entrypoint writes a `Settings.php` pre-filled for the chosen
engine and copies `other/install.php` to the web root, so
http://localhost:8080 redirects into the installer.
Expand All @@ -77,6 +128,32 @@ compose network.
When the installer finishes, delete `install.php` from the repo root — while it
exists, `Settings.php` redirects every request back into the installer.

## Running CI locally

```sh
.docker/ci.sh # everything CI checks
.docker/ci.sh --full # style check over the whole tree, not just changes
.docker/ci.sh --fix # apply the style fixes rather than reporting them
```

Mirrors `php.yml` (sign-off, the four file integrity checks, phplint) and
`php-cs-fixer.yml`, and runs the test suite when the branch has one. Every check
runs even after one fails, because finding out about the second problem on the
next push is the thing this is meant to stop.

`--full` is worth knowing about: the style workflow normally only looks at the
files a pull request changed, but switches to the whole tree when `composer.lock`
or the fixer config is in the diff. So a branch that touches a dependency
inherits every pre-existing violation in the repository. `--full` tells you that
before you push rather than after.

Two things it cannot do for you:

- **The other PHP version.** CI lints and tests on 8.4 *and* 8.5; the container
is whichever built it. To cover the other:
`PHP_VERSION=8.5 docker compose up -d --build web`.
- **The integration tests on both engines.** Use `.docker/test.sh` for that.

## Everyday use

```sh
Expand All @@ -102,8 +179,8 @@ The repository is bind-mounted at `/var/www/html`, so edits on the host are
live on the next request. Opcache is on but revalidates every request, so you
never need to restart for a PHP change.

To reinstall from scratch: `docker compose down -v`, delete `Settings.php` and
`Settings_bak.php`, then `docker compose up -d`.
To reinstall from scratch: `.docker/install-forum.sh --engine mysql --force`.
To wipe everything including the volumes: `docker compose down -v`.

## Debugging SQL with the PostgreSQL log

Expand Down
130 changes: 130 additions & 0 deletions .docker/ci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#!/usr/bin/env bash
# Runs what CI runs, before you push instead of after.
#
# .docker/ci.sh every check
# .docker/ci.sh --full style check over the whole tree, not just changes
# .docker/ci.sh --fix apply the style fixes rather than reporting them
#
# The workflows this mirrors are php.yml (sign-off, the file integrity checks,
# phplint) and php-cs-fixer.yml. phpunit.yml is included when the branch has a
# test suite on it.
#
# Every check runs even after one fails, because finding out about the second
# problem on the next push is the thing this script exists to stop.
#
# One difference worth knowing: CI lints and tests on PHP 8.4 *and* 8.5, and the
# web container is whichever PHP_VERSION built it (8.4 by default). To cover the
# other one, rebuild against it:
#
# PHP_VERSION=8.5 docker compose up -d --build web
#
# Runs on the host.
set -uo pipefail

. "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh"

FULL=0
FIX=0

while [ $# -gt 0 ]; do
case "$1" in
--full) FULL=1; shift ;;
--fix) FIX=1; shift ;;
-h|--help) sed -n '2,21p' "${BASH_SOURCE[0]}"; exit 0 ;;
*) die "unknown argument: $1" ;;
esac
done

# Unlike the other scripts here this one does not set -e, so that a failing
# check does not stop the ones after it. That means cd has to be checked.
cd "$BOARD_DIR" || die "cannot enter $BOARD_DIR"

docker compose ps --status running --services 2>/dev/null | grep -qx web \
|| die 'the web container is not running -- docker compose up -d'

FAILED=''

# $1 label, rest: the command to run in the web container.
check() {
local label="$1"
shift

printf '\n[smf-dev] --- %s ---\n' "$label"

if docker compose exec -T web "$@"; then
return 0
fi

FAILED="${FAILED}\n - ${label}"

return 1
}

# ------------------------------------------------------------------- php.yml
check 'sign-off (DCO)' php ./vendor/simplemachines/build-tools/check-signed-off.php

check 'file integrity' sh -c '
set -e
php ./vendor/simplemachines/build-tools/check-smf-license.php
php ./vendor/simplemachines/build-tools/check-smf-languages.php
php ./vendor/simplemachines/build-tools/check-smf-index.php
php ./vendor/simplemachines/build-tools/check-version.php
echo "all four integrity checks passed"
'

check "syntax ($(docker compose exec -T web php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;' 2>/dev/null))" \
vendor/bin/phplint --no-progress --exclude .git --exclude vendor .

# ------------------------------------------------------------ php-cs-fixer.yml
# CI checks only the files a pull request changed, and switches to the whole
# tree when composer.lock or the fixer config is part of the diff. --full asks
# for that second behaviour, which is worth doing before touching a dependency:
# it surfaces anything already non-compliant on release-3.0.
FIXER_ARGS=(--config .php-cs-fixer.dist.php --allow-risky=yes --using-cache=no --show-progress=none)

if [ "$FIX" -eq 1 ]; then
FIXER_MODE='fix'
else
FIXER_MODE='check'
FIXER_ARGS+=(--diff)
fi

if [ "$FULL" -eq 1 ]; then
check 'code style (whole tree)' vendor/bin/php-cs-fixer "$FIXER_MODE" "${FIXER_ARGS[@]}"
else
# Same intersection CI builds, from the files this branch actually touches:
# committed since release-3.0, staged, unstaged, and - the one CI never has
# to think about - new files that are not in the index yet.
CHANGED=$(
{
git diff --name-only --diff-filter=d release-3.0...HEAD -- '*.php'
git diff --name-only --diff-filter=d HEAD -- '*.php'
git ls-files --others --exclude-standard -- '*.php'
} 2>/dev/null | sort -u | grep -v '^$'
)

if [ -z "$CHANGED" ]; then
printf '\n[smf-dev] --- code style --- no changed PHP files\n'
else
# shellcheck disable=SC2086
check 'code style (changed files)' vendor/bin/php-cs-fixer "$FIXER_MODE" "${FIXER_ARGS[@]}" --path-mode=intersection $CHANGED
fi
fi

# ---------------------------------------------------------------- phpunit.yml
if [ -f phpunit.xml.dist ]; then
check 'tests' vendor/bin/phpunit --no-coverage --colors=always
else
printf '\n[smf-dev] --- tests --- no phpunit.xml.dist on this branch, skipping\n'
fi

# ---------------------------------------------------------------------- result
printf '\n'

if [ -n "$FAILED" ]; then
# shellcheck disable=SC2059
printf "[smf-dev] failed:${FAILED}\n" >&2
exit 1
fi

log 'everything CI checks passes'
Loading
Loading