Skip to content
Merged

Add CI #2412

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: 18 additions & 0 deletions .github/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Runs the pre-installed sqlite build produced by .github/workflows/build.yml.
#
# The base image is built from evolution-cms/salo2 runtimes/8.4 (Salo is the
# Evolution CMS flavour of Laravel Sail): php:8.4-apache with gd, zip, pdo and
# mod_rewrite. sqlite3/pdo_sqlite come enabled in the official php images.
ARG BASE_IMAGE=evo-salo-runtime:8.4
FROM ${BASE_IMAGE}

COPY build/ /var/www/html/

# Friendly URLs: the repository ships the rules as ht.access.
RUN if [ -f /var/www/html/ht.access ] && [ ! -f /var/www/html/.htaccess ]; then \
cp /var/www/html/ht.access /var/www/html/.htaccess; \
fi \
&& chown -R www-data:www-data /var/www/html \
&& a2enmod rewrite

EXPOSE 80
200 changes: 200 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
name: Build

on:
push:
branches:
- '3.*.x'
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: build-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
# Credentials of the pre-installed demo build. They are published together
# with the artifact, so this is a testing account and nothing else.
DEMO_ADMIN: admin
DEMO_EMAIL: admin@evo.local
DEMO_PASSWORD: Passw0rd123
DEMO_DATABASE: evolution
DEMO_PREFIX: evo_

jobs:
zip:
name: Installed CMS (zip, sqlite)
runs-on: ubuntu-latest
permissions:
contents: write # required to publish the nightly release
outputs:
zip-name: ${{ steps.package.outputs.zip-name }}
steps:
- uses: actions/checkout@v7

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
# Built on the lowest supported version so the vendor tree stays
# usable on 8.3 and 8.4 alike.
php-version: '8.3'
extensions: ctype, dom, fileinfo, filter, iconv, intl, json, mbstring, openssl, pdo, pdo_sqlite, readline, simplexml, sqlite3, tokenizer, xml, xmlreader, zip
coverage: none
tools: composer:v2

- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"

- name: Cache composer downloads
uses: actions/cache@v6
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: composer-${{ runner.os }}-php8.3-build-${{ hashFiles('core/composer.lock') }}
restore-keys: composer-${{ runner.os }}-php8.3-build-

- name: Install core dependencies
working-directory: core
run: composer install --no-dev --no-interaction --no-progress --prefer-dist

# --removeInstall=y drops install/ from the build, --skipComposer=y keeps
# the dependencies installed in the previous step untouched.
- name: Install Evolution CMS on sqlite
working-directory: install
run: |
php cli-install.php \
--typeInstall=1 \
--databaseType=sqlite \
--database="$DEMO_DATABASE" \
--tablePrefix="$DEMO_PREFIX" \
--cmsAdmin="$DEMO_ADMIN" \
--cmsAdminEmail="$DEMO_EMAIL" \
--cmsPassword="$DEMO_PASSWORD" \
--language=en \
--removeInstall=y \
--skipComposer=y < /dev/null

# The installer writes the absolute path of the build machine into the
# connection config. Resolve it at runtime instead, so the archive keeps
# working wherever it is unpacked.
- name: Make the sqlite path portable
run: |
config=core/config/database/connections/default.php
sed -i "s#'database' => env('DB_DATABASE', '.*'),#'database' => env('DB_DATABASE', dirname(__DIR__, 3) . '/database/${DEMO_DATABASE}.sqlite'),#" "$config"
grep -q "dirname(__DIR__, 3)" "$config"
php -l "$config"

- name: Verify the build answers over HTTP
run: |
php -S 127.0.0.1:8899 -t . > /tmp/server.log 2>&1 &
for i in $(seq 1 15); do
sleep 1
curl -sf -o /dev/null http://127.0.0.1:8899/ && break
done
front=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8899/)
# The manager answers 404 without an Accept-Language header by design.
manager=$(curl -s -o /dev/null -w '%{http_code}' -H 'Accept-Language: en-US,en;q=0.9' http://127.0.0.1:8899/manager/index.php)
echo "front=$front manager=$manager"
test "$front" = "200" && test "$manager" = "200"

- name: Package
id: package
run: |
version=$(php -r '$c = json_decode(file_get_contents("core/composer.json"), true); echo $c["version"] ?? "0.0.0";')
name="evolution-cms-${version}-${GITHUB_REF_NAME//\//-}-$(git rev-parse --short HEAD)-sqlite.zip"
# install/ is left empty by --removeInstall=y; the rest is repository
# tooling that has no place in a distributable build.
rm -rf .git .github install core/tests core/phpunit.xml AGENTS.md
# A leading "." keeps dotfiles such as core/.install, which the
# manager requires to consider the CMS installed.
zip -qr "/tmp/${name}" .
mv "/tmp/${name}" .
ls -lh "${name}"
echo "zip-name=${name}" >> "$GITHUB_OUTPUT"

- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: evolution-cms-sqlite
path: ${{ steps.package.outputs.zip-name }}
retention-days: 14
compression-level: 0 # already a zip

# Nightly release: anonymous direct download, unlike workflow artifacts.
# The tag is recreated on every push, so the link always serves the
# latest build of the branch.
- name: Publish nightly release
if: github.event_name != 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
ZIP: ${{ steps.package.outputs.zip-name }}
run: |
tag="nightly-${GITHUB_REF_NAME}"
# .git is gone by now, so every call is repo-qualified explicitly.
gh release delete "$tag" --repo "$GITHUB_REPOSITORY" --cleanup-tag --yes 2>/dev/null || true
gh release create "$tag" "$ZIP" \
--repo "$GITHUB_REPOSITORY" \
--prerelease \
--target "$GITHUB_SHA" \
--title "Nightly ${GITHUB_REF_NAME} ($(date -u +%Y-%m-%d))" \
--notes "Automated build of \`${GITHUB_REF_NAME}\` at ${GITHUB_SHA}.

Pre-installed Evolution CMS on sqlite — unpack into a docroot and open it, no database server needed.

Manager: \`${DEMO_ADMIN}\` / \`${DEMO_PASSWORD}\`

A testing build with published credentials. Not for production."

docker:
name: Docker image (GHCR)
needs: zip
# Forked pull requests get a read-only token and cannot push packages.
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v7

- name: Download the build
uses: actions/download-artifact@v8
with:
name: evolution-cms-sqlite
path: /tmp/artifact

- name: Unpack the build
run: |
mkdir -p build
unzip -q "/tmp/artifact/${{ needs.zip.outputs.zip-name }}" -d build

# The runtime recipe comes from Salo, the Evolution CMS flavour of Sail:
# php:8.4-apache with gd/zip/pdo and mod_rewrite enabled.
- name: Build the Salo runtime image
run: |
git clone --depth 1 https://github.com/evolution-cms/salo2 /tmp/salo2
docker build -t evo-salo-runtime:8.4 /tmp/salo2/runtimes/8.4

- name: Log in to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
run: |
owner=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
image="ghcr.io/${owner}/evolution"
docker build \
-f .github/docker/Dockerfile \
-t "${image}:${GITHUB_REF_NAME}" \
-t "${image}:${GITHUB_REF_NAME}-$(git rev-parse --short HEAD)" \
.
docker push --all-tags "${image}"
echo "### Image" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
echo "docker run --rm -p 8080:80 ${image}:${GITHUB_REF_NAME}" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
93 changes: 93 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: CI

on:
push:
branches:
- '3.*.x'
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
PHP_EXTENSIONS: ctype, dom, fileinfo, filter, iconv, intl, json, mbstring, openssl, pdo, pdo_sqlite, readline, simplexml, tokenizer, xml, xmlreader, zip

jobs:
analyze:
name: Static analysis (PHP ${{ matrix.php }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ['8.3', '8.4']
steps:
- uses: actions/checkout@v7

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: ${{ env.PHP_EXTENSIONS }}
coverage: none
tools: composer:v2

- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"

- name: Cache composer downloads
uses: actions/cache@v6
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: composer-${{ runner.os }}-php${{ matrix.php }}-analyze-${{ hashFiles('composer.json') }}
restore-keys: composer-${{ runner.os }}-php${{ matrix.php }}-analyze-

# Only the root dependencies (PHPStan) are installed here:
# core/vendor is committed and provides everything phpstan.neon bootstraps.
- name: Install root dependencies
run: composer install --no-interaction --no-progress --prefer-dist

- name: Run PHPStan
run: composer analyze

tests:
name: Unit tests (PHP ${{ matrix.php }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ['8.3', '8.4']
steps:
- uses: actions/checkout@v7

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: ${{ env.PHP_EXTENSIONS }}
coverage: none
tools: composer:v2

- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"

- name: Cache composer downloads
uses: actions/cache@v6
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: composer-${{ runner.os }}-php${{ matrix.php }}-core-${{ hashFiles('core/composer.lock') }}
restore-keys: composer-${{ runner.os }}-php${{ matrix.php }}-core-

- name: Install core dependencies
working-directory: core
run: composer install --no-interaction --no-progress --prefer-dist

- name: Run Pest
working-directory: core
run: composer test -- --compact
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@ Desktop.ini
# all dotfiles and dotfolders except do not ignore .gitignore
**/.*
!.gitignore
!/.github
5 changes: 4 additions & 1 deletion core/includes/aliases.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ class Lang extends \Illuminate\Support\Facades\Lang {}
class View extends \Illuminate\Support\Facades\View {}
class Response extends \Illuminate\Support\Facades\Response {}
class Redirect extends \Illuminate\Support\Facades\Redirect {}
class Redis extends \Illuminate\Support\Facades\Redis {}
// ext-redis already provides a global Redis class, alias the facade only without it.
if (!class_exists('Redis', false)) {
class Redis extends \Illuminate\Support\Facades\Redis {}
}
class Route extends \Illuminate\Support\Facades\Route {}
class Str extends \Illuminate\Support\Str {}
class Config extends \EvolutionCMS\Facades\ConfigService {}
Expand Down
20 changes: 16 additions & 4 deletions core/src/ManagerTheme.php
Original file line number Diff line number Diff line change
Expand Up @@ -563,13 +563,15 @@ public function isAuthManager()
@session_destroy();
}

if (is_file(EVO_BASE_PATH . 'assets/cache/installProc.inc.php')) {
include_once(EVO_BASE_PATH . 'assets/cache/installProc.inc.php');
$installProcFile = $this->getInstallProcFile();

if (is_file($installProcFile)) {
include_once($installProcFile);
if (isset($installStartTime)) {
if ((time() - $installStartTime) > 5 * 60) { // if install flag older than 5 minutes, discard
unset($installStartTime);
@ chmod(EVO_BASE_PATH . 'assets/cache/installProc.inc.php', 0755);
unlink(EVO_BASE_PATH . 'assets/cache/installProc.inc.php');
@ chmod($installProcFile, 0755);
unlink($installProcFile);
} else {
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
if (isset($_COOKIE[session_name()])) {
Expand Down Expand Up @@ -601,6 +603,16 @@ public function isAuthManager()
return isset($_SESSION['mgrValidated']);
}

/**
* Path to the install flag file generated by the installer.
*
* @return string
*/
protected function getInstallProcFile(): string
{
return EVO_BASE_PATH . 'assets/cache/installProc.inc.php';
}

public function hasManagerAccess()
{
// check if user is allowed to access manager interface
Expand Down
9 changes: 6 additions & 3 deletions core/src/Services/Store/PackageInstallFlowService.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,12 @@ public function handleLegacyInstall(string $action, array $request, array $files

$cwd = getcwd();
chdir($this->modulePath . '/installer/');
ob_start();
require "instprocessor-fast.php";
ob_end_clean();
$fastProcessor = $this->modulePath . '/installer/instprocessor-fast.php';
if (is_file($fastProcessor)) {
ob_start();
include $fastProcessor; // @todo [remove@3.7] Remove in Evolution CMS 3.7
ob_end_clean();
}
chdir($cwd);

if (is_dir(EVO_BASE_PATH . 'assets/cache/store/')) {
Expand Down
Loading
Loading