diff --git a/.github/workflows/package-static.yml b/.github/workflows/package-static.yml index 3c14db9a..324d8b81 100644 --- a/.github/workflows/package-static.yml +++ b/.github/workflows/package-static.yml @@ -180,6 +180,66 @@ jobs: path: dist/windows-amd64/yiipress.exe if-no-files-found: error + macos: + name: macOS static binary + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + runs-on: macos-latest + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + with: + persist-credentials: false + + - name: Set up PHP + uses: shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc + with: + php-version: '8.5' + coverage: none + + - name: Set up Rust + uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 + with: + targets: aarch64-apple-darwin + + - name: Install macOS package dependencies + run: | + brew install autoconf automake bison re2c pkgconf + echo "$(brew --prefix bison)/bin" >> "$GITHUB_PATH" + + - name: Cache macOS package dependencies + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 + with: + path: | + ~/.composer/cache/files + ~/.cargo/git + ~/.cargo/registry + runtime/package-macos/app/vendor + runtime/package-macos/static-php-cli + runtime/package-macos/md4c-extension + runtime/package-macos/yiipress-highlighter + key: macos-package-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('composer.lock', 'build/package-macos.sh', 'build/static-php/*.php') }} + restore-keys: | + macos-package-${{ runner.os }}-${{ runner.arch }}- + + - name: Build macOS artifacts + run: make package-macos PACKAGE_MACOS_ARCH=arm64 PACKAGE_MACOS_DIST=dist/macos-arm64 + + - name: Smoke test macOS binary + run: ./dist/macos-arm64/yiipress --help + + - name: Pack macOS artifact + run: tar -C dist/macos-arm64 -czf dist/yiipress-macos-arm64.tar.gz yiipress + + - name: Upload macOS artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: yiipress-macos-arm64 + path: dist/yiipress-macos-arm64.tar.gz + if-no-files-found: error + release: name: Publish release artifacts if: startsWith(github.ref, 'refs/tags/') @@ -187,6 +247,7 @@ jobs: - phar - linux - windows + - macos runs-on: ubuntu-latest permissions: contents: write @@ -200,6 +261,7 @@ jobs: - name: Pack release archives run: | tar -C release/yiipress-linux-amd64 -czf yiipress-linux-amd64.tar.gz . + cp release/yiipress-macos-arm64/yiipress-macos-arm64.tar.gz yiipress-macos-arm64.tar.gz zip -j yiipress-windows-amd64.zip release/yiipress-windows-amd64/* cp release/yiipress-phar/yiipress.phar yiipress.phar @@ -209,4 +271,5 @@ jobs: files: | yiipress.phar yiipress-linux-amd64.tar.gz + yiipress-macos-arm64.tar.gz yiipress-windows-amd64.zip diff --git a/Makefile b/Makefile index d59d7075..bb72e007 100644 --- a/Makefile +++ b/Makefile @@ -23,12 +23,15 @@ DOCKER_COMPOSE_DEV := docker compose -f docker/compose.yml -f docker/dev/compose DOCKER_COMPOSE_TEST := docker compose -f docker/compose.yml -f docker/test/compose.yml PACKAGE_PLATFORM ?= linux/amd64 PACKAGE_LINUX_DIST ?= dist/linux-amd64 +PACKAGE_MACOS_ARCH ?= $(shell uname -m | sed -e 's/x86_64/amd64/' -e 's/aarch64/arm64/') +PACKAGE_MACOS_DIST ?= dist/macos-$(PACKAGE_MACOS_ARCH) PACKAGE_WINDOWS_DIST ?= dist/windows-amd64 PACKAGE_PHAR_DIST ?= dist/phar PACKAGE_IMAGE ?= ${IMAGE}-static +MACOS_PACKAGE_ARGS ?= WINDOWS_PACKAGE_ARGS ?= -.PHONY: build up open down stop clear shell yii composer rector cs-fix test test-coverage psalm composer-dependency-analyser bench-generate bench-generate-realistic bench bench-baseline bench-compare bench-profile profile-build php build-docs package package-phar package-linux package-windows package-distroless package-distroless-push prod-build prod-push prod-deploy help +.PHONY: build up open down stop clear shell yii composer rector cs-fix test test-coverage psalm composer-dependency-analyser bench-generate bench-generate-realistic bench bench-baseline bench-compare bench-profile profile-build php build-docs package package-phar package-linux package-macos package-windows package-distroless package-distroless-push prod-build prod-push prod-deploy help # # Development @@ -214,6 +217,12 @@ package-linux: ## Build Linux static binary into dist/linux-amd64/ docker buildx build --file docker/Dockerfile --target package-linux-artifacts --platform $(PACKAGE_PLATFORM) --output type=local,dest=$(PACKAGE_LINUX_DIST) . endif +ifeq ($(PRIMARY_GOAL),package-macos) +package-macos: ## Build macOS static executable into dist/macos-/ + @command -v bash >/dev/null 2>&1 || { echo 'Bash is required for package-macos.' >&2; exit 127; } + build/package-macos.sh --dist-dir $(PACKAGE_MACOS_DIST) --arch $(PACKAGE_MACOS_ARCH) $(MACOS_PACKAGE_ARGS) +endif + ifeq ($(PRIMARY_GOAL),package-windows) package-windows: ## Build Windows static executable into dist/windows-amd64/ @command -v pwsh >/dev/null 2>&1 || { echo 'PowerShell 7 (pwsh) is required for package-windows.' >&2; exit 127; } diff --git a/README.md b/README.md index 21a2b28e..7fddeb41 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ or a database on the machine that builds or previews the site. ## Features -- Static Linux and Windows binaries for simple installs with no PHP runtime. +- Static Linux, macOS, and Windows binaries for simple installs with no PHP runtime. - Docker images for CI and container-based workflows. - Incremental and parallel builds with native Markdown, YAML, and syntax highlighting. - Plain Markdown/YAML content that works well with Git. @@ -127,6 +127,7 @@ Engine packaging commands, used by maintainers: ```shell make package-phar make package +make package-macos make package-distroless ``` diff --git a/build/package-macos.sh b/build/package-macos.sh new file mode 100755 index 00000000..660029d9 --- /dev/null +++ b/build/package-macos.sh @@ -0,0 +1,206 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PHP_VERSION="${PHP_VERSION:-8.5}" +MD4C_VERSION="${MD4C_VERSION:-1.1}" +HIGHLIGHTER_VERSION="${HIGHLIGHTER_VERSION:-1.0.1}" +STATIC_PHP_CLI_REF="${STATIC_PHP_CLI_REF:-5b5861c366a0d94bc84002db7b3f46144b388fbb}" +STATIC_PHP_EXTENSIONS="${STATIC_PHP_EXTENSIONS:-ctype,dom,filter,highlighter,mbstring,md4c,opcache,pcntl,phar,posix,xml,xmlwriter,yaml}" + +detect_arch() { + case "$(uname -m)" in + arm64|aarch64) + printf 'arm64' + ;; + x86_64|amd64) + printf 'amd64' + ;; + *) + printf 'Unsupported macOS architecture: %s\n' "$(uname -m)" >&2 + exit 1 + ;; + esac +} + +HOST_ARCH="$(detect_arch)" +ARCH="$HOST_ARCH" +DIST_DIR="dist/macos-${ARCH}" + +while [ "$#" -gt 0 ]; do + case "$1" in + --arch) + ARCH="$2" + shift 2 + ;; + --dist-dir) + DIST_DIR="$2" + shift 2 + ;; + *) + printf 'Unknown argument: %s\n' "$1" >&2 + exit 1 + ;; + esac +done + +if [ "$(uname -s)" != "Darwin" ]; then + printf 'package-macos must run on macOS.\n' >&2 + exit 1 +fi + +if [ "$ARCH" != "$HOST_ARCH" ]; then + printf 'package-macos does not support cross-compilation: requested %s on %s host.\n' "$ARCH" "$HOST_ARCH" >&2 + exit 1 +fi + +case "$ARCH" in + arm64) + CARGO_BUILD_TARGET="aarch64-apple-darwin" + ;; + amd64) + CARGO_BUILD_TARGET="x86_64-apple-darwin" + ;; + *) + printf 'Unsupported macOS package architecture: %s\n' "$ARCH" >&2 + exit 1 + ;; +esac + +DIST_PATH="${ROOT}/${DIST_DIR}" +WORK_PATH="${ROOT}/runtime/package-macos" +APP_PATH="${WORK_PATH}/app" +STATIC_PHP_PATH="${WORK_PATH}/static-php-cli" +MD4C_PATH="${WORK_PATH}/md4c-extension" +HIGHLIGHTER_PATH="${WORK_PATH}/yiipress-highlighter" +PHAR_PATH="${WORK_PATH}/yiipress.phar" +BIN_PATH="${DIST_PATH}/yiipress" + +invoke() { + "$@" +} + +require_command() { + if ! command -v "$1" >/dev/null 2>&1; then + printf 'Required command was not found in PATH: %s\n' "$1" >&2 + exit 127 + fi +} + +write_log_tail() { + local path="$1" + if [ -f "$path" ]; then + printf '===== %s =====\n' "$path" + tail -n 200 "$path" + fi +} + +expand_tar_gz_archive() { + local url="$1" + local destination="$2" + local archive + + archive="$(mktemp "${WORK_PATH}/archive.XXXXXX")" + curl -fsSL --max-time 300 --retry 3 --retry-delay 5 "$url" -o "$archive" + mkdir -p "$destination" + tar -xzf "$archive" --strip-components=1 -C "$destination" + rm -f "$archive" +} + +copy_clean_path() { + local source="$1" + local destination="$2" + + rm -rf "$destination" + cp -R "$source" "$destination" +} + +for command in php composer tar curl rustup cargo make; do + require_command "$command" +done + +mkdir -p "$DIST_PATH" "$WORK_PATH" +rm -f "${DIST_PATH}/yiipress.phar" + +mkdir -p "$APP_PATH" +for directory in config public src themes; do + copy_clean_path "${ROOT}/${directory}" "${APP_PATH}/${directory}" +done +mkdir -p "${APP_PATH}/build" +cp "${ROOT}/build/package-phar.php" "${APP_PATH}/build/package-phar.php" +cp "${ROOT}/build/PharArchiveFilter.php" "${APP_PATH}/build/PharArchiveFilter.php" +cp "${ROOT}/yii" "${APP_PATH}/yii" +cp "${ROOT}/composer.json" "${APP_PATH}/composer.json" +cp "${ROOT}/composer.lock" "${APP_PATH}/composer.lock" + +pushd "$APP_PATH" >/dev/null +invoke composer install \ + --no-dev \ + --no-progress \ + --no-interaction \ + --classmap-authoritative \ + --ignore-platform-req=ext-inotify \ + --ignore-platform-req=ext-md4c \ + --ignore-platform-req=ext-yaml \ + --ignore-platform-req=ext-highlighter +invoke php -d phar.readonly=0 build/package-phar.php "$PHAR_PATH" +popd >/dev/null + +if [ ! -d "$STATIC_PHP_PATH" ]; then + expand_tar_gz_archive \ + "https://github.com/crazywhalecc/static-php-cli/archive/${STATIC_PHP_CLI_REF}.tar.gz" \ + "$STATIC_PHP_PATH" +fi + +if [ ! -d "$MD4C_PATH" ]; then + expand_tar_gz_archive "https://pecl.php.net/get/md4c-${MD4C_VERSION}.tgz" "$MD4C_PATH" +fi + +if [ ! -d "$HIGHLIGHTER_PATH" ]; then + invoke composer create-project \ + --no-dev \ + --no-progress \ + --no-interaction \ + yiipress/highlighter \ + "$HIGHLIGHTER_PATH" \ + "$HIGHLIGHTER_VERSION" +fi + +pushd "$STATIC_PHP_PATH" >/dev/null +invoke composer install \ + --no-dev \ + --no-progress \ + --no-interaction \ + --classmap-authoritative + +export CARGO_BUILD_TARGET +export HIGHLIGHTER_SOURCE="$HIGHLIGHTER_PATH" +export YIIPRESS_MD4C_SOURCE="$MD4C_PATH" +export YIIPRESS_STATIC_INCLUDE_PROCESS_EXTENSIONS=1 + +pushd "$HIGHLIGHTER_PATH" >/dev/null +invoke rustup target add "$CARGO_BUILD_TARGET" +invoke cargo build --release --target "$CARGO_BUILD_TARGET" +popd >/dev/null + +invoke php "${ROOT}/build/static-php/patch-extension-config.php" config/ext.json +invoke php "${ROOT}/build/static-php/patch-source-config.php" config/source.json config/lib.json +invoke php bin/spc download \ + --for-extensions="${STATIC_PHP_EXTENSIONS}" \ + --with-php="${PHP_VERSION}" \ + --without-suggestions +invoke php bin/spc doctor --auto-fix +if ! invoke php bin/spc build \ + "$STATIC_PHP_EXTENSIONS" \ + --build-micro \ + -P "${ROOT}/build/static-php/register-yiipress-highlighter.php"; then + write_log_tail "${STATIC_PHP_PATH}/log/spc.output.log" + write_log_tail "${STATIC_PHP_PATH}/log/spc.shell.log" + exit 1 +fi +invoke php bin/spc micro:combine "$PHAR_PATH" -O "$BIN_PATH" +popd >/dev/null + +chmod +x "$BIN_PATH" +printf 'Built %s\n' "$BIN_PATH" diff --git a/build/static-php/register-yiipress-highlighter.php b/build/static-php/register-yiipress-highlighter.php index a8c79ba5..9c73823a 100644 --- a/build/static-php/register-yiipress-highlighter.php +++ b/build/static-php/register-yiipress-highlighter.php @@ -132,7 +132,9 @@ ); if ($highlighterWindowsConfigReplacementCount !== 1) { - throw new RuntimeException('Unable to patch highlighter config.w32 extension mode.'); + if (!str_contains($highlighterWindowsConfigContents, 'EXTENSION("highlighter", "highlighter.c", false);')) { + throw new RuntimeException('Unable to patch highlighter config.w32 extension mode.'); + } } file_put_contents($highlighterWindowsConfig, $highlighterWindowsConfigContents); diff --git a/docs/deployment.md b/docs/deployment.md index 4582caee..6934e26b 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -160,12 +160,14 @@ Additional package targets are available: ```bash make package-phar make package-linux +make package-macos make package-windows make package-distroless make package-distroless-push ``` - `make package-linux` is the explicit Linux static executable target. Set `PACKAGE_PLATFORM=linux/amd64` and `PACKAGE_LINUX_DIST=...` to override the defaults. +- `make package-macos` builds the native macOS static executable into `dist/macos-/yiipress` with the shell packaging script. It is intended for macOS hosts with PHP, Composer, Rust, and the Xcode command-line build toolchain available. `PACKAGE_MACOS_ARCH` must match the host architecture (`arm64` or `amd64`); set `PACKAGE_MACOS_DIST=...` to override the output directory. The intermediate PHAR used by `static-php-cli` is kept under `runtime/package-macos/` and is not part of the macOS artifact. - `make package-windows` builds only `dist/windows-amd64/yiipress.exe` with the PowerShell packaging script. It is intended for Windows hosts with PowerShell 7 (`pwsh`), PHP, Composer, Rust, and the Visual Studio C++ toolchain available. The intermediate PHAR used by `static-php-cli` is kept under `runtime/package-windows/` and is not part of the Windows artifact. - `make package-phar` builds `dist/phar/yiipress.phar` separately for environments that already have PHP 8.5 and required extensions. Set `PACKAGE_PHAR_DIST=...` to override the output directory. - `make package-distroless` builds a local `${IMAGE}-static:${IMAGE_TAG}` image from the `distroless` Docker target. The image copies only the static `yiipress` binary into a distroless base and runs it as the entrypoint. @@ -176,4 +178,4 @@ The static executable includes `ext-highlighter`, so syntax highlighting does no Relative `content-dir`, `output-dir`, `new`, `clean`, `serve`, and `import` paths are resolved from the directory where you run `yiipress`, not from the packaged executable location. PHAR and static binary runs keep build cache and incremental manifests under the OS temp directory, keyed by the current project directory, instead of writing to `runtime/` in the site checkout. `yiipress clean` removes that packaged cache as well as the configured output directory. -GitHub Actions builds the same outputs in the `Package Static Builds` workflow. Commits to `master` publish separate nightly PHAR, Linux, and Windows workflow artifacts and push the distroless image as `ghcr.io//-static:nightly` plus a commit-specific `nightly-` tag. Version tags publish the standalone `yiipress.phar`, Linux archive, and Windows archive to the GitHub release and push semver tags for the distroless image. +GitHub Actions builds the same outputs in the `Package Static Builds` workflow. Commits to `master` publish separate nightly PHAR, Linux, macOS, and Windows workflow artifacts and push the distroless image as `ghcr.io//-static:nightly` plus a commit-specific `nightly-` tag. Version tags publish the standalone `yiipress.phar`, Linux archive, macOS archive, and Windows archive to the GitHub release and push semver tags for the distroless image. diff --git a/roadmap.md b/roadmap.md index 635670c8..43804a83 100644 --- a/roadmap.md +++ b/roadmap.md @@ -48,7 +48,7 @@ - [x] Live reload / file watching during `yiipress serve` - [x] PHAR and static PHP binary packaging - [x] Worker-mode serving for PHAR and static PHP binary packages -- [x] Linux, Windows, and distroless Docker release packaging +- [x] Linux, macOS, Windows, and distroless Docker release packaging - [x] `yiipress new` command — scaffold new entries from archetypes/templates - [x] `yiipress init` command — scaffold initial content structure - [x] Incremental builds (only rebuild changed files) diff --git a/tests/Unit/Packaging/ConfigurationPackagingTest.php b/tests/Unit/Packaging/ConfigurationPackagingTest.php index 8db1ead9..4216ae32 100644 --- a/tests/Unit/Packaging/ConfigurationPackagingTest.php +++ b/tests/Unit/Packaging/ConfigurationPackagingTest.php @@ -100,6 +100,10 @@ public function makefileExposesAllPackageBuilds(): void self::assertStringContainsString('package-linux:', $makefile); self::assertStringContainsString('--target package-linux-artifacts', $makefile); self::assertStringContainsString('rm -f $(PACKAGE_LINUX_DIST)/yiipress.phar', $makefile); + self::assertStringContainsString('PACKAGE_MACOS_DIST ?= dist/macos-$(PACKAGE_MACOS_ARCH)', $makefile); + self::assertStringContainsString('package-macos:', $makefile); + self::assertStringContainsString('build/package-macos.sh --dist-dir $(PACKAGE_MACOS_DIST) --arch $(PACKAGE_MACOS_ARCH)', $makefile); + self::assertStringContainsString('Bash is required for package-macos.', $makefile); self::assertStringContainsString('package-windows:', $makefile); self::assertStringContainsString('build/package-windows.ps1', $makefile); self::assertStringContainsString('PowerShell 7 (pwsh) is required for package-windows.', $makefile); @@ -137,8 +141,10 @@ public function platformArtifactsDoNotIncludeStandalonePhar(): void { $dockerfile = file_get_contents(dirname(__DIR__, 3) . '/docker/Dockerfile'); $script = file_get_contents(dirname(__DIR__, 3) . '/build/package-windows.ps1'); + $macosScript = file_get_contents(dirname(__DIR__, 3) . '/build/package-macos.sh'); self::assertIsString($dockerfile); self::assertIsString($script); + self::assertIsString($macosScript); $pharStart = strpos($dockerfile, 'FROM scratch AS package-phar-artifacts'); $linuxStart = strpos($dockerfile, 'FROM scratch AS package-linux-artifacts'); @@ -158,6 +164,9 @@ public function platformArtifactsDoNotIncludeStandalonePhar(): void self::assertStringNotContainsString('$pharPath = Join-Path $distPath "yiipress.phar"', $script); self::assertStringContainsString('$legacyDistPharPath = Join-Path $distPath "yiipress.phar"', $script); self::assertStringContainsString('Remove-Item $legacyDistPharPath -Force', $script); + self::assertStringContainsString('PHAR_PATH="${WORK_PATH}/yiipress.phar"', $macosScript); + self::assertStringNotContainsString('PHAR_PATH="${DIST_PATH}/yiipress.phar"', $macosScript); + self::assertStringContainsString('rm -f "${DIST_PATH}/yiipress.phar"', $macosScript); } #[Test] @@ -192,6 +201,38 @@ public function windowsPackageScriptBuildsPharAndExecutable(): void self::assertStringContainsString('$env:RUSTFLAGS = "-C target-feature=+crt-static"', $script); } + #[Test] + public function macosPackageScriptBuildsPharAndExecutable(): void + { + $script = file_get_contents(dirname(__DIR__, 3) . '/build/package-macos.sh'); + self::assertIsString($script); + + self::assertStringContainsString('build/package-phar.php', $script); + self::assertStringContainsString('BIN_PATH="${DIST_PATH}/yiipress"', $script); + self::assertStringContainsString('HOST_ARCH="$(detect_arch)"', $script); + self::assertStringContainsString('package-macos does not support cross-compilation', $script); + self::assertStringContainsString('aarch64-apple-darwin', $script); + self::assertStringContainsString('x86_64-apple-darwin', $script); + self::assertStringContainsString('micro:combine', $script); + self::assertStringContainsString('APP_PATH="${WORK_PATH}/app"', $script); + self::assertStringContainsString('pushd "$APP_PATH"', $script); + self::assertStringContainsString('require_command "$command"', $script); + self::assertStringContainsString('for command in php composer tar curl rustup cargo make; do', $script); + self::assertStringContainsString('curl -fsSL --max-time 300 --retry 3 --retry-delay 5 "$url" -o "$archive"', $script); + self::assertStringContainsString('HIGHLIGHTER_VERSION="${HIGHLIGHTER_VERSION:-1.0.1}"', $script); + self::assertStringNotContainsString('dev-master', $script); + self::assertStringContainsString('--ignore-platform-req=ext-inotify', $script); + self::assertStringContainsString('--ignore-platform-req=ext-md4c', $script); + self::assertStringContainsString('--ignore-platform-req=ext-yaml', $script); + self::assertStringContainsString('--ignore-platform-req=ext-highlighter', $script); + self::assertStringContainsString('write_log_tail "${STATIC_PHP_PATH}/log/spc.output.log"', $script); + self::assertStringContainsString('write_log_tail "${STATIC_PHP_PATH}/log/spc.shell.log"', $script); + self::assertStringNotContainsString('--with-micro-fake-cli', $script); + self::assertStringContainsString('rustup target add "$CARGO_BUILD_TARGET"', $script); + self::assertStringContainsString('cargo build --release --target "$CARGO_BUILD_TARGET"', $script); + self::assertStringContainsString('chmod +x "$BIN_PATH"', $script); + } + #[Test] public function packageWorkflowPublishesNightlyAndReleaseBuilds(): void { @@ -215,15 +256,31 @@ public function packageWorkflowPublishesNightlyAndReleaseBuilds(): void self::assertStringContainsString('Smoke test Windows binary', $workflow); self::assertStringContainsString('./dist/windows-amd64/yiipress.exe --help', $workflow); self::assertStringContainsString('path: dist/windows-amd64/yiipress.exe', $workflow); + self::assertStringContainsString('runs-on: macos-latest', $workflow); + self::assertStringContainsString('targets: aarch64-apple-darwin', $workflow); + self::assertStringContainsString('Cache macOS package dependencies', $workflow); + self::assertStringContainsString('runtime/package-macos/yiipress-highlighter', $workflow); + self::assertStringContainsString('make package-macos PACKAGE_MACOS_ARCH=arm64 PACKAGE_MACOS_DIST=dist/macos-arm64', $workflow); + self::assertStringContainsString('Smoke test macOS binary', $workflow); + self::assertStringContainsString('./dist/macos-arm64/yiipress --help', $workflow); + self::assertStringContainsString('Pack macOS artifact', $workflow); + self::assertStringContainsString('tar -C dist/macos-arm64 -czf dist/yiipress-macos-arm64.tar.gz yiipress', $workflow); + self::assertStringContainsString('name: yiipress-macos-arm64', $workflow); + self::assertStringContainsString('path: dist/yiipress-macos-arm64.tar.gz', $workflow); self::assertStringContainsString('target: distroless', $workflow); self::assertStringContainsString("github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')", $workflow); self::assertStringContainsString('type=raw,value=nightly', $workflow); self::assertStringContainsString('type=semver,pattern={{version}}', $workflow); self::assertStringContainsString('cp release/yiipress-phar/yiipress.phar yiipress.phar', $workflow); + self::assertStringContainsString( + 'cp release/yiipress-macos-arm64/yiipress-macos-arm64.tar.gz yiipress-macos-arm64.tar.gz', + $workflow, + ); + self::assertStringContainsString('yiipress-macos-arm64.tar.gz', $workflow); self::assertStringContainsString('softprops/action-gh-release', $workflow); self::assertDoesNotMatchRegularExpression('/uses:\s+[^@\s]+@v\d+/', $workflow); self::assertStringNotContainsString('dtolnay/rust-toolchain@stable', $workflow); - self::assertSame(3, substr_count($workflow, 'persist-credentials: false')); + self::assertSame(4, substr_count($workflow, 'persist-credentials: false')); } #[Test] @@ -339,8 +396,10 @@ public function staticPackageInstallsRustTargetForHighlighterToolchain(): void { $dockerfile = file_get_contents(dirname(__DIR__, 3) . '/docker/Dockerfile'); $windowsScript = file_get_contents(dirname(__DIR__, 3) . '/build/package-windows.ps1'); + $macosScript = file_get_contents(dirname(__DIR__, 3) . '/build/package-macos.sh'); self::assertIsString($dockerfile); self::assertIsString($windowsScript); + self::assertIsString($macosScript); self::assertStringContainsString('RUN rustup target add x86_64-unknown-linux-musl', $dockerfile); self::assertStringContainsString( @@ -348,6 +407,7 @@ public function staticPackageInstallsRustTargetForHighlighterToolchain(): void $dockerfile, ); self::assertStringContainsString('Invoke-NativeCommand "rustup" @("target", "add", $env:CARGO_BUILD_TARGET)', $windowsScript); + self::assertStringContainsString('rustup target add "$CARGO_BUILD_TARGET"', $macosScript); } #[Test] @@ -382,6 +442,10 @@ public function customStaticExtensionsAreAvailableForWindowsBuilds(): void self::assertStringContainsString('ARG_ENABLE("md4c"', $registrationPatch); self::assertStringContainsString('EXTENSION("highlighter", "highlighter.c", false);', $registrationPatch); self::assertStringContainsString('$highlighterWindowsConfigReplacementCount !== 1', $registrationPatch); + self::assertStringContainsString( + 'str_contains($highlighterWindowsConfigContents, \'EXTENSION("highlighter", "highlighter.c", false);\')', + $registrationPatch, + ); self::assertStringContainsString('EXTENSION("md4c", "md4c.c", false);', $registrationPatch); self::assertStringContainsString('md4c config.w32 was not found.', $registrationPatch); self::assertStringContainsString('php_md4c.h', $registrationPatch);