Skip to content

Add CI - #2412

Open
elcreator wants to merge 4 commits into
evolution-cms:3.5.xfrom
elcreator:add-ci
Open

Add CI#2412
elcreator wants to merge 4 commits into
evolution-cms:3.5.xfrom
elcreator:add-ci

Conversation

@elcreator

Copy link
Copy Markdown

Summary

Adds a basic CI pipeline and nightly build pipeline for 3.5.x, plus the source
fixes needed to make both green.

There is no CI on this branch today, so this starts with the two commands the
project already defines: composer analyze (PHPStan) and composer test (Pest).

What's added

.github/workflows/ci.yml — runs on push to 3.*.x, on every PR, and manually:

  • analyze — root composer installcomposer analyze. Only root dependencies are
    needed: core/vendor is committed and covers everything phpstan.neon bootstraps.
  • testscomposer install in core/composer test.

Both run on a ['8.3', '8.4'] matrix with fail-fast: false, so the composer.json
floor (^8.3) and the recommended version are reported separately.

.github/workflows/build.yml — two independent jobs:

  • zip — installs dependencies with --no-dev, installs the CMS on sqlite via the CLI
    installer, makes the build relocatable, verifies it answers over HTTP, and packages it.
    Published as a workflow artifact (14 days) and, on push, as a nightly-<branch>
    prerelease so testers get an anonymous direct download link.
  • docker — takes the exact tree produced by zip and bakes it into an image pushed to
    GHCR. The runtime recipe comes from evolution-cms/salo2
    runtimes/8.4/Dockerfile (Salo is the Evolution CMS flavour of Sail): php:8.4-apache
    with gd/zip/pdo and mod_rewrite. Skipped on pull requests — forks get a read-only token
    and cannot push packages.

.github/docker/Dockerfile — thin layer over the Salo runtime: copies the built tree,
promotes ht.access to .htaccess, fixes ownership.

Source changes required by the above

install/cli-install.php — sqlite support. The web installer already supports sqlite
(install/src/functions.php accepts ['pgsql', 'sqlite', 'mysql'] and skips
host/user/password for it), and the runtime defaults to the sqlite driver, but the CLI
installer's whitelist was ['pgsql', 'mysql']. Passing --databaseType=sqlite sent it
into an infinite readline loop. Changes:

  • sqlite added to the database type list;
  • host/user/password prompts skipped for sqlite;
  • correct sqlite:<core/database/name.sqlite> DSN, with the directory created if missing;
    checkConnectToDatabaseWithBase() returns early since the file is the connection;
  • writeConfig() gained a sqlite case — otherwise the unreplaced [+database_port+]
    placeholder ended up in the generated config;
  • new --skipComposer=y flag: the installer otherwise runs composer update mid-install,
    which would rewrite composer.lock inside a build.

PHPStan fixes (includeOnce.fileNotFound / include.fileNotFound):

  • core/src/ManagerTheme.php — the install-flag path moved into getInstallProcFile().
    The rule ignores runtime is_file() guards entirely: it constant-folds the path and
    checks the disk at analysis time, and assets/cache/installProc.inc.php is generated at
    install time, so it never exists in a fresh checkout. Also removes four repetitions of
    the same concatenation.
  • core/src/Services/Store/PackageInstallFlowService.phpinclude "instprocessor-fast.php"
    now uses $this->modulePath . '/installer/instprocessor-fast.php". The relative literal
    only resolved because of a preceding chdir(), which static analysis cannot model.

.gitignore**/.* was silently ignoring .github; added !/.github.

Verification

Everything below was run locally against a clean checkout of this branch.

  • composer analyze: 0 errors on PHP 8.3 after the fixes (was 2). On 8.4 six
    unset.possiblyHookedProperty errors remain (5 in Core.php, 1 in MysqlDumper.php) —
    property hooks are 8.4-only, so that matrix leg is red until those are addressed.
  • CLI install on sqlite produces a working site: core/database/evolution.sqlite,
    install/ removed, config written.
  • Relocatability: the installer writes an absolute database path, so the workflow
    rewrites it to dirname(__DIR__, 3). After moving the tree, front page and manager both
    return 200. No build-machine paths remain anywhere in the tree.
  • Docker: the Salo runtime image builds, the CMS image runs, front page and /manager/
    return 200, sqlite file and cache are writable by www-data.
  • Archive: ~50 MB; dotfiles that matter (core/.install, the database) survive the
    zip/unzip round trip.
  • Tests: CliInstallTest passes, and the full suite result is identical with and without
    these changes.

Known: the suite is not green today

composer test currently reports 15 failures / 246 passing on a clean Linux checkout —
all pre-existing and unrelated to this PR (verified by reverting the changes and re-running
in the same environment; the exact count varies with environment state). Examples: the eight
CoreTest > getTagsFromContent cases, and RemoveLocksConfirmMessageTest, which expects
modx.lang.confirm_remove_locks while manager/media/style/default/js/evo.js:1818 has
evo.lang.….

So the first CI run will be red by design — it reports the branch's real state. Worth
knowing before enabling branch protection.

Relationship to the nightly branch

The nightly branch (last commit 2025-11-11) already carries a Docker pipeline:
a root Dockerfile, docker/entrypoint.sh, docker-compose.yml (postgres + adminer), and
.github/workflows/docker-nightly.yml publishing multi-arch images to Docker Hub
(dmi3yy/evolution-cms) on manual dispatch.

These two do not conflict as files — that work lives on a separate branch — but they answer
the same question differently:

nightly this PR
Model app image, external DB, installs at container start self-contained snapshot, sqlite inside
Database postgres/mysql (no sqlite; unknown values fall back to mysql) sqlite, no server
Registry Docker Hub, needs secrets GHCR, GITHUB_TOKEN is enough
Trigger manual only push to 3.*.x
Arch amd64 + arm64 (QEMU) amd64
Audience developers; data outlives the container testers; disposable stand

If nightly is ever merged into 3.5.x there would be two Dockerfiles and two Docker jobs,
which is worth untangling in advance.

Three things noticed while comparing, none addressed here:

  1. The nightly Dockerfile runs composer update (not install) at build time, so images
    are not reproducible and the lock file is ignored.
  2. entrypoint.sh passes --databasePort="${DB_PORT}", but InstallEvo has no
    databasePort property on either branch, and the argument parser only assigns to
    existing properties — so the flag is silently dropped and a non-default port never
    reaches the config.
  3. cli-install.php on nightly has the same ['pgsql', 'mysql'] whitelist, so the sqlite
    support here does not duplicate that branch — it closes the same gap.

The sqlite mode and --skipComposer would both benefit that entrypoint: a container could
then start with no database service at all.

@elcreator elcreator changed the title Add ci Add CI Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant