From b0ddfdb810f27944c3de80e49f848ccfccec7faf Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 4 Aug 2026 18:24:41 +0300 Subject: [PATCH 1/3] Report exact installed YiiPress version --- .github/workflows/package-static.yml | 12 +++++-- .github/workflows/release.yml | 14 ++++++-- Makefile | 8 +++-- build/package-phar.php | 17 ++++++++++ docker/Dockerfile | 6 ++-- docs/binaries-phar-docker.md | 3 ++ install.sh | 9 ++++- src/ApplicationInfo.php | 9 ++--- tests/Console/ConsoleRunnerTest.php | 2 +- tests/Unit/ApplicationInfoTest.php | 3 +- .../Packaging/ConfigurationPackagingTest.php | 33 ++++++++++++++++--- tests/Unit/Packaging/InstallerTest.php | 13 +++++--- 12 files changed, 103 insertions(+), 26 deletions(-) diff --git a/.github/workflows/package-static.yml b/.github/workflows/package-static.yml index 029b27b7..372edab8 100644 --- a/.github/workflows/package-static.yml +++ b/.github/workflows/package-static.yml @@ -16,6 +16,7 @@ concurrency: env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }}-static + YIIPRESS_COMMIT: ${{ github.sha }} jobs: linux: @@ -61,9 +62,11 @@ jobs: cache-from: type=gha,scope=package-linux-amd64 cache-to: type=gha,scope=package-linux-amd64,mode=max platforms: linux/amd64 + build-args: | + YIIPRESS_COMMIT=${{ github.sha }} - name: Smoke test Linux binary - run: ./dist/linux-amd64/yiipress --help + run: test "$(./dist/linux-amd64/yiipress --version)" = "YiiPress ${GITHUB_SHA}" - name: Upload Linux artifacts uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 @@ -136,7 +139,10 @@ jobs: - name: Smoke test Windows binary shell: pwsh - run: ./dist/windows-amd64/yiipress.exe --help + run: | + if ((./dist/windows-amd64/yiipress.exe --version) -ne "YiiPress $env:GITHUB_SHA") { + throw "Packaged YiiPress version does not match $env:GITHUB_SHA." + } - name: Upload Windows artifacts uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 @@ -191,7 +197,7 @@ jobs: 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 + run: test "$(./dist/macos-arm64/yiipress --version)" = "YiiPress ${GITHUB_SHA}" - name: Pack macOS artifact run: tar -C dist/macos-arm64 -czf dist/yiipress-macos-arm64.tar.gz yiipress diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eaf56a25..49afe887 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,8 @@ concurrency: env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }}-static + COMPOSER_ROOT_VERSION: ${{ github.ref_name }} + YIIPRESS_COMMIT: ${{ github.sha }} jobs: linux: @@ -41,9 +43,12 @@ jobs: cache-from: type=gha,scope=release-linux-amd64 cache-to: type=gha,scope=release-linux-amd64,mode=max platforms: linux/amd64 + build-args: | + COMPOSER_ROOT_VERSION=${{ github.ref_name }} + YIIPRESS_COMMIT=${{ github.sha }} - name: Smoke test Linux binary - run: ./dist/linux-amd64/yiipress --help + run: test "$(./dist/linux-amd64/yiipress --version)" = "YiiPress ${GITHUB_REF_NAME}" - name: Upload Linux artifact uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 @@ -105,7 +110,10 @@ jobs: - name: Smoke test Windows binary shell: pwsh - run: ./dist/windows-amd64/yiipress.exe --help + run: | + if ((./dist/windows-amd64/yiipress.exe --version) -ne "YiiPress $env:GITHUB_REF_NAME") { + throw "Packaged YiiPress version does not match $env:GITHUB_REF_NAME." + } - name: Upload Windows artifact uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 @@ -159,7 +167,7 @@ jobs: 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 + run: test "$(./dist/macos-arm64/yiipress --version)" = "YiiPress ${GITHUB_REF_NAME}" - name: Pack macOS artifact run: tar -C dist/macos-arm64 -czf dist/yiipress-macos-arm64.tar.gz yiipress diff --git a/Makefile b/Makefile index ece5636a..db307235 100644 --- a/Makefile +++ b/Makefile @@ -28,8 +28,10 @@ PACKAGE_MACOS_DIST ?= dist/macos-$(PACKAGE_MACOS_ARCH) PACKAGE_WINDOWS_DIST ?= dist/windows-amd64 PACKAGE_PHAR_DIST ?= dist/phar PACKAGE_IMAGE ?= ${IMAGE}-static +YIIPRESS_COMMIT ?= $(shell git rev-parse HEAD) MACOS_PACKAGE_ARGS ?= WINDOWS_PACKAGE_ARGS ?= +export YIIPRESS_COMMIT .PHONY: build up open down stop clear shell yii composer rector cs-fix test test-coverage test-coverage-clover 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 @@ -207,19 +209,19 @@ ifeq ($(PRIMARY_GOAL),package) package: ## Build Linux static binary into dist/linux-amd64/ @mkdir -p $(PACKAGE_LINUX_DIST) @rm -f $(PACKAGE_LINUX_DIST)/yiipress.phar - docker buildx build --file docker/Dockerfile --target package-linux-artifacts --platform $(PACKAGE_PLATFORM) --output type=local,dest=$(PACKAGE_LINUX_DIST) . + docker buildx build --file docker/Dockerfile --target package-linux-artifacts --build-arg YIIPRESS_COMMIT=$(YIIPRESS_COMMIT) --platform $(PACKAGE_PLATFORM) --output type=local,dest=$(PACKAGE_LINUX_DIST) . endif ifeq ($(PRIMARY_GOAL),package-phar) package-phar: ## Build YiiPress PHAR into dist/phar/ - docker buildx build --file docker/Dockerfile --target package-phar-artifacts --platform $(PACKAGE_PLATFORM) --output type=local,dest=$(PACKAGE_PHAR_DIST) . + docker buildx build --file docker/Dockerfile --target package-phar-artifacts --build-arg YIIPRESS_COMMIT=$(YIIPRESS_COMMIT) --platform $(PACKAGE_PLATFORM) --output type=local,dest=$(PACKAGE_PHAR_DIST) . endif ifeq ($(PRIMARY_GOAL),package-linux) package-linux: ## Build Linux static binary into dist/linux-amd64/ @mkdir -p $(PACKAGE_LINUX_DIST) @rm -f $(PACKAGE_LINUX_DIST)/yiipress.phar - docker buildx build --file docker/Dockerfile --target package-linux-artifacts --platform $(PACKAGE_PLATFORM) --output type=local,dest=$(PACKAGE_LINUX_DIST) . + docker buildx build --file docker/Dockerfile --target package-linux-artifacts --build-arg YIIPRESS_COMMIT=$(YIIPRESS_COMMIT) --platform $(PACKAGE_PLATFORM) --output type=local,dest=$(PACKAGE_LINUX_DIST) . endif ifeq ($(PRIMARY_GOAL),package-macos) diff --git a/build/package-phar.php b/build/package-phar.php index a8270045..a7a4eff3 100644 --- a/build/package-phar.php +++ b/build/package-phar.php @@ -16,6 +16,12 @@ $target = $argv[1] ?? $root . '/dist/yiipress.phar'; $targetDirectory = dirname($target); +$commit = getenv('YIIPRESS_COMMIT') ?: ''; + +if ($commit !== '' && preg_match('/^[0-9a-f]{40}$/', $commit) !== 1) { + fwrite(STDERR, "YIIPRESS_COMMIT must be a full 40-character commit SHA.\n"); + exit(1); +} if (!is_dir($targetDirectory) && !mkdir($targetDirectory, 0775, true) && !is_dir($targetDirectory)) { fwrite(STDERR, "Failed to create target directory: {$targetDirectory}\n"); @@ -61,6 +67,17 @@ continue; } + if ($localPath === 'src/ApplicationInfo.php' && $commit !== '') { + $contents = file_get_contents($fullPath); + if ($contents === false) { + fwrite(STDERR, "Failed to read file: {$localPath}\n"); + exit(1); + } + $contents = str_replace("public const string COMMIT = '';", "public const string COMMIT = '{$commit}';", $contents); + $phar->addFromString($localPath, $contents); + continue; + } + if (PhpDocStripper::shouldStrip($localPath)) { $contents = file_get_contents($fullPath); if ($contents === false) { diff --git a/docker/Dockerfile b/docker/Dockerfile index fd81e6ac..9d1ab187 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -119,6 +119,8 @@ CMD ["./yii", "serve", "0.0.0.0", "--port=19777", "--workers=1"] # FROM app-base AS phar-builder +ARG COMPOSER_ROOT_VERSION=dev-master +ARG YIIPRESS_COMMIT ENV APP_ENV=prod COPY --from=composer /composer /usr/bin/composer WORKDIR /app @@ -129,8 +131,8 @@ COPY themes /app/themes COPY build/package-phar.php build/PharArchiveFilter.php build/PhpDocStripper.php /app/build/ COPY yii composer.json composer.lock /app/ RUN --mount=type=cache,target=/tmp/composer-cache \ - COMPOSER_CACHE_DIR=/tmp/composer-cache composer install --no-dev --no-progress --no-interaction --classmap-authoritative && \ - php -d phar.readonly=0 build/package-phar.php dist/yiipress.phar + COMPOSER_CACHE_DIR=/tmp/composer-cache COMPOSER_ROOT_VERSION="${COMPOSER_ROOT_VERSION}" composer install --no-dev --no-progress --no-interaction --classmap-authoritative && \ + YIIPRESS_COMMIT="${YIIPRESS_COMMIT}" php -d phar.readonly=0 build/package-phar.php dist/yiipress.phar FROM app-base AS static-package ARG PHP_VERSION=8.5 diff --git a/docs/binaries-phar-docker.md b/docs/binaries-phar-docker.md index 2fffaa46..b4a73a20 100644 --- a/docs/binaries-phar-docker.md +++ b/docs/binaries-phar-docker.md @@ -15,6 +15,9 @@ irm https://raw.githubusercontent.com/yiipress/engine/master/install.ps1 | iex ``` The installers download the latest release archive and `SHA256SUMS`, verify the archive, and atomically install the executable. +The shell installer prints the exact resolved release version before downloading its archive. +The installed binary reports that same release tag with `yiipress --version`. +Development artifacts report the full commit SHA from which they were built instead of a placeholder version. The shell installer uses `/usr/local/bin/yiipress`; the PowerShell installer uses the current user's local application directory and adds it to the user `PATH`. Re-run the same command to update an existing installation. To use another directory or a fixed release, set environment variables before invoking the installer. For example, with the shell installer: diff --git a/install.sh b/install.sh index 847113bb..fd18fc4f 100755 --- a/install.sh +++ b/install.sh @@ -59,9 +59,16 @@ fi work_dir="$(mktemp -d)" trap 'rm -rf "$work_dir"' EXIT HUP INT TERM +if [ "$version" = "latest" ]; then + effective_url="$(curl -fsSL --retry 3 --retry-delay 2 -w '%{url_effective}' "${release_url}/SHA256SUMS" -o "${work_dir}/SHA256SUMS")" + release_url="${effective_url%/SHA256SUMS}" + version="${release_url##*/}" +else + curl -fsSL --retry 3 --retry-delay 2 "${release_url}/SHA256SUMS" -o "${work_dir}/SHA256SUMS" +fi + echo "Downloading YiiPress ${version} for ${platform}/${architecture}..." curl -fsSL --retry 3 --retry-delay 2 "${release_url}/${asset}" -o "${work_dir}/${asset}" -curl -fsSL --retry 3 --retry-delay 2 "${release_url}/SHA256SUMS" -o "${work_dir}/SHA256SUMS" expected_checksum="$(awk -v asset="$asset" '$2 == asset || $2 == "*" asset || $2 == "assets/" asset { print $1; exit }' "${work_dir}/SHA256SUMS")" if [ -z "$expected_checksum" ]; then diff --git a/src/ApplicationInfo.php b/src/ApplicationInfo.php index 0aab9bf4..dc9bafed 100644 --- a/src/ApplicationInfo.php +++ b/src/ApplicationInfo.php @@ -9,16 +9,17 @@ final class ApplicationInfo { public const string NAME = 'YiiPress'; - public const string VERSION = '1.0.0'; + public const string COMMIT = ''; + public const string VERSION = 'unknown'; public static function version(): string { $version = InstalledVersions::getPrettyVersion('yiipress/engine'); - if ($version === null || str_ends_with($version, '+no-version-set')) { - return self::VERSION; + if ($version !== null && !str_ends_with($version, '+no-version-set') && !str_starts_with($version, 'dev-')) { + return $version; } - return $version; + return self::COMMIT !== '' ? self::COMMIT : (InstalledVersions::getReference('yiipress/engine') ?? self::VERSION); } } diff --git a/tests/Console/ConsoleRunnerTest.php b/tests/Console/ConsoleRunnerTest.php index 8a195e2a..786f7505 100644 --- a/tests/Console/ConsoleRunnerTest.php +++ b/tests/Console/ConsoleRunnerTest.php @@ -20,7 +20,7 @@ public function testConsoleRunsWithApplicationNameAndVersion(): void exec($yii . ' 2>&1', $output, $exitCode); assertSame(0, $exitCode); - assertStringContainsString('YiiPress 1.0.0', implode("\n", $output)); + assertStringContainsString('YiiPress unknown', implode("\n", $output)); self::assertStringNotContainsString('Yii Console', implode("\n", $output)); assertStringNotContainsString('Runs an internal portable worker job', implode("\n", $output)); } diff --git a/tests/Unit/ApplicationInfoTest.php b/tests/Unit/ApplicationInfoTest.php index 8275bb3a..43491a04 100644 --- a/tests/Unit/ApplicationInfoTest.php +++ b/tests/Unit/ApplicationInfoTest.php @@ -14,7 +14,8 @@ final class ApplicationInfoTest extends TestCase public function versionIsUserFacingReleaseVersion(): void { self::assertSame('YiiPress', ApplicationInfo::NAME); - self::assertMatchesRegularExpression('/^\d+\.\d+\.\d+/', ApplicationInfo::version()); + self::assertMatchesRegularExpression('/^(?:\d+\.\d+\.\d+|[0-9a-f]{40}|unknown)/', ApplicationInfo::version()); + self::assertSame('', ApplicationInfo::COMMIT); self::assertStringNotContainsString('no-version-set', ApplicationInfo::version()); } } diff --git a/tests/Unit/Packaging/ConfigurationPackagingTest.php b/tests/Unit/Packaging/ConfigurationPackagingTest.php index 7dd89935..cd192872 100644 --- a/tests/Unit/Packaging/ConfigurationPackagingTest.php +++ b/tests/Unit/Packaging/ConfigurationPackagingTest.php @@ -281,7 +281,8 @@ public function packageWorkflowPublishesNightlyBuilds(): void self::assertStringContainsString('name: yiipress-phar', $workflow); self::assertStringContainsString('path: dist/linux-amd64/yiipress.phar', $workflow); self::assertStringContainsString('Smoke test Linux binary', $workflow); - self::assertStringContainsString('./dist/linux-amd64/yiipress --help', $workflow); + self::assertStringContainsString('YiiPress ${GITHUB_SHA}', $workflow); + self::assertStringContainsString('YIIPRESS_COMMIT=${{ github.sha }}', $workflow); self::assertStringContainsString('path: dist/linux-amd64/yiipress', $workflow); self::assertStringContainsString('build/package-windows.ps1 -DistDir dist/windows-amd64', $workflow); self::assertStringContainsString( @@ -293,7 +294,7 @@ public function packageWorkflowPublishesNightlyBuilds(): void self::assertStringNotContainsString('runtime\package-windows\yiipress-highlighter', $workflow); self::assertStringContainsString('runs-on: windows-2022', $workflow); self::assertStringContainsString('Smoke test Windows binary', $workflow); - self::assertStringContainsString('./dist/windows-amd64/yiipress.exe --help', $workflow); + self::assertStringContainsString('./dist/windows-amd64/yiipress.exe --version', $workflow); self::assertStringContainsString('path: dist/windows-amd64/yiipress.exe', $workflow); self::assertStringContainsString('runs-on: macos-14', $workflow); self::assertStringContainsString('targets: aarch64-apple-darwin', $workflow); @@ -302,7 +303,7 @@ public function packageWorkflowPublishesNightlyBuilds(): void self::assertStringNotContainsString('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('./dist/macos-arm64/yiipress --version', $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); @@ -455,6 +456,18 @@ public function releaseWorkflowBuildsNotesFromPreviousStableReleaseTag(): void self::assertIsString($workflow); self::assertStringContainsString("tags:\n - '*.*.*'", $workflow); + self::assertStringContainsString('COMPOSER_ROOT_VERSION: ${{ github.ref_name }}', $workflow); + self::assertStringContainsString('YIIPRESS_COMMIT: ${{ github.sha }}', $workflow); + self::assertStringContainsString('COMPOSER_ROOT_VERSION=${{ github.ref_name }}', $workflow); + self::assertStringContainsString( + 'test "$(./dist/linux-amd64/yiipress --version)" = "YiiPress ${GITHUB_REF_NAME}"', + $workflow, + ); + self::assertStringContainsString('./dist/windows-amd64/yiipress.exe --version', $workflow); + self::assertStringContainsString( + 'test "$(./dist/macos-arm64/yiipress --version)" = "YiiPress ${GITHUB_REF_NAME}"', + $workflow, + ); self::assertStringContainsString( "git describe --tags --abbrev=0 --match '[0-9]*.[0-9]*.[0-9]*' --exclude '*[!0-9.]*' \"\${tag}^\"", $workflow, @@ -646,6 +659,9 @@ public function pharBuilderCopiesOnlyRuntimeInputs(): void self::assertStringNotContainsString("'content'", $packageScript); self::assertStringNotContainsString("'runtime'", $packageScript); self::assertStringContainsString('PharArchiveFilter::shouldExclude($localPath)', $packageScript); + self::assertStringContainsString("getenv('YIIPRESS_COMMIT')", $packageScript); + self::assertStringContainsString("\$localPath === 'src/ApplicationInfo.php'", $packageScript); + self::assertStringContainsString("public const string COMMIT = '{\$commit}';", $packageScript); self::assertStringNotContainsString('packages/highlighter-extension/php', $stage); self::assertStringNotContainsString("'packages/highlighter-extension/php'", $packageScript); self::assertStringContainsString('COPY config /app/config', $stage); @@ -755,7 +771,16 @@ public function dockerPackageBuildUsesComposerAndCargoCaches(): void self::assertIsString($dockerfile); self::assertStringContainsString('--mount=type=cache,target=/tmp/composer-cache', $dockerfile); - self::assertStringContainsString('COMPOSER_CACHE_DIR=/tmp/composer-cache composer install', $dockerfile); + self::assertStringContainsString('ARG COMPOSER_ROOT_VERSION=dev-master', $dockerfile); + self::assertStringContainsString('ARG YIIPRESS_COMMIT', $dockerfile); + self::assertStringContainsString( + 'COMPOSER_CACHE_DIR=/tmp/composer-cache COMPOSER_ROOT_VERSION="${COMPOSER_ROOT_VERSION}" composer install', + $dockerfile, + ); + self::assertStringContainsString( + 'YIIPRESS_COMMIT="${YIIPRESS_COMMIT}" php -d phar.readonly=0 build/package-phar.php', + $dockerfile, + ); self::assertStringContainsString('COMPOSER_CACHE_DIR=/tmp/composer-cache composer create-project', $dockerfile); self::assertStringNotContainsString('ARG HIGHLIGHTER_VERSION', $dockerfile); self::assertStringNotContainsString('ARG MARKDOWN_VERSION', $dockerfile); diff --git a/tests/Unit/Packaging/InstallerTest.php b/tests/Unit/Packaging/InstallerTest.php index 0ff3b322..1fb29804 100644 --- a/tests/Unit/Packaging/InstallerTest.php +++ b/tests/Unit/Packaging/InstallerTest.php @@ -43,15 +43,20 @@ protected function setUp(): void set -eu url="" output="" +write_out="" while [ "$#" -gt 0 ]; do case "$1" in -o) output="$2"; shift 2 ;; + -w) write_out="$2"; shift 2 ;; http*) url="$1"; shift ;; *) shift ;; esac done cp "${YIIPRESS_TEST_RELEASE_DIR}/${url##*/}" "$output" printf '%s\n' "$url" >> "${YIIPRESS_TEST_CURL_LOG}" +if [ "$write_out" = '%{url_effective}' ]; then + printf '%s\n' "https://github.com/test/engine/releases/download/${YIIPRESS_TEST_LATEST_VERSION}/${url##*/}" +fi SH); file_put_contents($this->root . '/bin/curl', $curl); chmod($this->root . '/bin/curl', 0755); @@ -82,6 +87,7 @@ public function installsAndUpdatesTheLatestLinuxBinary(): void [$exitCode, $output] = $this->runInstaller(); self::assertSame(0, $exitCode, $output); + self::assertStringContainsString('Downloading YiiPress 1.2.3 for linux/amd64...', $output); self::assertSame('version-one', file_get_contents($this->root . '/install/yiipress')); self::assertSame(0755, fileperms($this->root . '/install/yiipress') & 0777); @@ -183,8 +189,7 @@ private function createRelease( string $contents, string $asset = 'yiipress-linux-amd64.tar.gz', bool $symbolicLink = false, - ): void - { + ): void { $archiveRoot = $this->root . '/archive'; if (!is_dir($archiveRoot)) { mkdir($archiveRoot); @@ -219,8 +224,7 @@ private function runInstaller( string $machine = 'x86_64', ?string $installDirectory = null, string $version = 'latest', - ): array - { + ): array { $systemPath = getenv('PATH'); self::assertIsString($systemPath); $environment = [ @@ -231,6 +235,7 @@ private function runInstaller( 'YIIPRESS_TEST_CURL_LOG' => $this->root . '/curl.log', 'YIIPRESS_TEST_SYSTEM' => $system, 'YIIPRESS_TEST_MACHINE' => $machine, + 'YIIPRESS_TEST_LATEST_VERSION' => '1.2.3', 'YIIPRESS_TEST_SUDO_LOG' => $this->root . '/sudo.log', 'YIIPRESS_VERSION' => $version, ]; From 701661c0c469c23ea1ac5270c13cb3568fd229b2 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 4 Aug 2026 19:04:52 +0300 Subject: [PATCH 2/3] Address version reporting review feedback --- .github/workflows/package-static.yml | 2 +- .github/workflows/release.yml | 2 +- build/package-phar.php | 13 ++++++-- install.sh | 8 ++--- src/ApplicationInfo.php | 19 ++++++++--- tests/Console/ConsoleRunnerTest.php | 3 +- tests/Unit/ApplicationInfoTest.php | 23 ++++++++++++- .../Packaging/ConfigurationPackagingTest.php | 33 ++++++++++++++++--- tests/Unit/Packaging/InstallerTest.php | 9 +++-- 9 files changed, 91 insertions(+), 21 deletions(-) diff --git a/.github/workflows/package-static.yml b/.github/workflows/package-static.yml index 372edab8..729e82d0 100644 --- a/.github/workflows/package-static.yml +++ b/.github/workflows/package-static.yml @@ -194,7 +194,7 @@ jobs: macos-package-${{ runner.os }}-${{ runner.arch }}- - name: Build macOS artifacts - run: make package-macos PACKAGE_MACOS_ARCH=arm64 PACKAGE_MACOS_DIST=dist/macos-arm64 + run: make package-macos PACKAGE_MACOS_ARCH=arm64 PACKAGE_MACOS_DIST=dist/macos-arm64 YIIPRESS_COMMIT=${GITHUB_SHA} - name: Smoke test macOS binary run: test "$(./dist/macos-arm64/yiipress --version)" = "YiiPress ${GITHUB_SHA}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 49afe887..a1bcb537 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -164,7 +164,7 @@ jobs: release-macos-package-${{ runner.os }}-${{ runner.arch }}- - name: Build macOS artifact - run: make package-macos PACKAGE_MACOS_ARCH=arm64 PACKAGE_MACOS_DIST=dist/macos-arm64 + run: make package-macos PACKAGE_MACOS_ARCH=arm64 PACKAGE_MACOS_DIST=dist/macos-arm64 YIIPRESS_COMMIT=${GITHUB_SHA} - name: Smoke test macOS binary run: test "$(./dist/macos-arm64/yiipress --version)" = "YiiPress ${GITHUB_REF_NAME}" diff --git a/build/package-phar.php b/build/package-phar.php index a7a4eff3..1c5cd83e 100644 --- a/build/package-phar.php +++ b/build/package-phar.php @@ -16,7 +16,7 @@ $target = $argv[1] ?? $root . '/dist/yiipress.phar'; $targetDirectory = dirname($target); -$commit = getenv('YIIPRESS_COMMIT') ?: ''; +$commit = strtolower(getenv('YIIPRESS_COMMIT') ?: ''); if ($commit !== '' && preg_match('/^[0-9a-f]{40}$/', $commit) !== 1) { fwrite(STDERR, "YIIPRESS_COMMIT must be a full 40-character commit SHA.\n"); @@ -73,7 +73,16 @@ fwrite(STDERR, "Failed to read file: {$localPath}\n"); exit(1); } - $contents = str_replace("public const string COMMIT = '';", "public const string COMMIT = '{$commit}';", $contents); + $contents = str_replace( + "public const string COMMIT = '';", + "public const string COMMIT = '{$commit}';", + $contents, + $replacementCount, + ); + if ($replacementCount !== 1) { + fwrite(STDERR, "Failed to embed the YiiPress commit in {$localPath}.\n"); + exit(1); + } $phar->addFromString($localPath, $contents); continue; } diff --git a/install.sh b/install.sh index fd18fc4f..7228687d 100755 --- a/install.sh +++ b/install.sh @@ -60,14 +60,14 @@ work_dir="$(mktemp -d)" trap 'rm -rf "$work_dir"' EXIT HUP INT TERM if [ "$version" = "latest" ]; then - effective_url="$(curl -fsSL --retry 3 --retry-delay 2 -w '%{url_effective}' "${release_url}/SHA256SUMS" -o "${work_dir}/SHA256SUMS")" - release_url="${effective_url%/SHA256SUMS}" + redirect_url="$(curl -fsS --retry 3 --retry-delay 2 -w '%{redirect_url}' "${release_url}/SHA256SUMS" -o /dev/null)" + redirect_url="${redirect_url%%\?*}" + release_url="${redirect_url%/SHA256SUMS}" version="${release_url##*/}" -else - curl -fsSL --retry 3 --retry-delay 2 "${release_url}/SHA256SUMS" -o "${work_dir}/SHA256SUMS" fi echo "Downloading YiiPress ${version} for ${platform}/${architecture}..." +curl -fsSL --retry 3 --retry-delay 2 "${release_url}/SHA256SUMS" -o "${work_dir}/SHA256SUMS" curl -fsSL --retry 3 --retry-delay 2 "${release_url}/${asset}" -o "${work_dir}/${asset}" expected_checksum="$(awk -v asset="$asset" '$2 == asset || $2 == "*" asset || $2 == "assets/" asset { print $1; exit }' "${work_dir}/SHA256SUMS")" diff --git a/src/ApplicationInfo.php b/src/ApplicationInfo.php index dc9bafed..f161637c 100644 --- a/src/ApplicationInfo.php +++ b/src/ApplicationInfo.php @@ -5,7 +5,6 @@ namespace YiiPress; use Composer\InstalledVersions; - final class ApplicationInfo { public const string NAME = 'YiiPress'; @@ -14,12 +13,24 @@ final class ApplicationInfo public static function version(): string { - $version = InstalledVersions::getPrettyVersion('yiipress/engine'); + $package = InstalledVersions::getRootPackage(); + $version = $package['name'] === 'yiipress/engine' ? $package['pretty_version'] : null; + $reference = $package['name'] === 'yiipress/engine' ? $package['reference'] : null; + + return self::resolveVersion($version, $reference); + } - if ($version !== null && !str_ends_with($version, '+no-version-set') && !str_starts_with($version, 'dev-')) { + private static function resolveVersion(?string $version, ?string $reference): string + { + if ( + $version !== null + && !str_ends_with($version, '+no-version-set') + && !str_starts_with($version, 'dev-') + && !str_ends_with($version, '-dev') + ) { return $version; } - return self::COMMIT !== '' ? self::COMMIT : (InstalledVersions::getReference('yiipress/engine') ?? self::VERSION); + return self::COMMIT !== '' ? self::COMMIT : ($reference ?? self::VERSION); } } diff --git a/tests/Console/ConsoleRunnerTest.php b/tests/Console/ConsoleRunnerTest.php index 786f7505..dbbab2d0 100644 --- a/tests/Console/ConsoleRunnerTest.php +++ b/tests/Console/ConsoleRunnerTest.php @@ -5,6 +5,7 @@ namespace YiiPress\Tests\Console; use PHPUnit\Framework\TestCase; +use YiiPress\ApplicationInfo; use function escapeshellarg; use function PHPUnit\Framework\assertSame; @@ -20,7 +21,7 @@ public function testConsoleRunsWithApplicationNameAndVersion(): void exec($yii . ' 2>&1', $output, $exitCode); assertSame(0, $exitCode); - assertStringContainsString('YiiPress unknown', implode("\n", $output)); + assertStringContainsString('YiiPress ' . ApplicationInfo::version(), implode("\n", $output)); self::assertStringNotContainsString('Yii Console', implode("\n", $output)); assertStringNotContainsString('Runs an internal portable worker job', implode("\n", $output)); } diff --git a/tests/Unit/ApplicationInfoTest.php b/tests/Unit/ApplicationInfoTest.php index 43491a04..077b8786 100644 --- a/tests/Unit/ApplicationInfoTest.php +++ b/tests/Unit/ApplicationInfoTest.php @@ -4,6 +4,7 @@ namespace YiiPress\Tests\Unit; +use ReflectionMethod; use YiiPress\ApplicationInfo; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; @@ -14,8 +15,28 @@ final class ApplicationInfoTest extends TestCase public function versionIsUserFacingReleaseVersion(): void { self::assertSame('YiiPress', ApplicationInfo::NAME); - self::assertMatchesRegularExpression('/^(?:\d+\.\d+\.\d+|[0-9a-f]{40}|unknown)/', ApplicationInfo::version()); + self::assertMatchesRegularExpression('/^(?:\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?|[0-9a-f]{40}|unknown)$/', ApplicationInfo::version()); self::assertSame('', ApplicationInfo::COMMIT); + self::assertSame('unknown', ApplicationInfo::VERSION); self::assertStringNotContainsString('no-version-set', ApplicationInfo::version()); } + + #[Test] + public function developmentAliasFallsBackToExactReference(): void + { + $method = new ReflectionMethod(ApplicationInfo::class, 'resolveVersion'); + $reference = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; + + self::assertSame($reference, $method->invoke(null, '1.0.x-dev', $reference)); + self::assertSame($reference, $method->invoke(null, 'dev-master', $reference)); + self::assertSame('1.2.3', $method->invoke(null, '1.2.3', $reference)); + } + + #[Test] + public function missingComposerMetadataUsesSafeFallback(): void + { + $method = new ReflectionMethod(ApplicationInfo::class, 'resolveVersion'); + + self::assertSame(ApplicationInfo::VERSION, $method->invoke(null, null, null)); + } } diff --git a/tests/Unit/Packaging/ConfigurationPackagingTest.php b/tests/Unit/Packaging/ConfigurationPackagingTest.php index cd192872..8131ef20 100644 --- a/tests/Unit/Packaging/ConfigurationPackagingTest.php +++ b/tests/Unit/Packaging/ConfigurationPackagingTest.php @@ -281,8 +281,12 @@ public function packageWorkflowPublishesNightlyBuilds(): void self::assertStringContainsString('name: yiipress-phar', $workflow); self::assertStringContainsString('path: dist/linux-amd64/yiipress.phar', $workflow); self::assertStringContainsString('Smoke test Linux binary', $workflow); - self::assertStringContainsString('YiiPress ${GITHUB_SHA}', $workflow); + self::assertStringContainsString('YIIPRESS_COMMIT: ${{ github.sha }}', $workflow); self::assertStringContainsString('YIIPRESS_COMMIT=${{ github.sha }}', $workflow); + self::assertStringContainsString( + 'test "$(./dist/linux-amd64/yiipress --version)" = "YiiPress ${GITHUB_SHA}"', + $workflow, + ); self::assertStringContainsString('path: dist/linux-amd64/yiipress', $workflow); self::assertStringContainsString('build/package-windows.ps1 -DistDir dist/windows-amd64', $workflow); self::assertStringContainsString( @@ -294,16 +298,25 @@ public function packageWorkflowPublishesNightlyBuilds(): void self::assertStringNotContainsString('runtime\package-windows\yiipress-highlighter', $workflow); self::assertStringContainsString('runs-on: windows-2022', $workflow); self::assertStringContainsString('Smoke test Windows binary', $workflow); - self::assertStringContainsString('./dist/windows-amd64/yiipress.exe --version', $workflow); + self::assertStringContainsString( + 'if ((./dist/windows-amd64/yiipress.exe --version) -ne "YiiPress $env:GITHUB_SHA")', + $workflow, + ); self::assertStringContainsString('path: dist/windows-amd64/yiipress.exe', $workflow); self::assertStringContainsString('runs-on: macos-14', $workflow); self::assertStringContainsString('targets: aarch64-apple-darwin', $workflow); self::assertStringContainsString('Cache macOS package dependencies', $workflow); self::assertStringNotContainsString('runtime/package-macos/yiipress-markdown', $workflow); self::assertStringNotContainsString('runtime/package-macos/yiipress-highlighter', $workflow); - self::assertStringContainsString('make package-macos PACKAGE_MACOS_ARCH=arm64 PACKAGE_MACOS_DIST=dist/macos-arm64', $workflow); + self::assertStringContainsString( + 'make package-macos PACKAGE_MACOS_ARCH=arm64 PACKAGE_MACOS_DIST=dist/macos-arm64 YIIPRESS_COMMIT=${GITHUB_SHA}', + $workflow, + ); self::assertStringContainsString('Smoke test macOS binary', $workflow); - self::assertStringContainsString('./dist/macos-arm64/yiipress --version', $workflow); + self::assertStringContainsString( + 'test "$(./dist/macos-arm64/yiipress --version)" = "YiiPress ${GITHUB_SHA}"', + $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); @@ -459,15 +472,23 @@ public function releaseWorkflowBuildsNotesFromPreviousStableReleaseTag(): void self::assertStringContainsString('COMPOSER_ROOT_VERSION: ${{ github.ref_name }}', $workflow); self::assertStringContainsString('YIIPRESS_COMMIT: ${{ github.sha }}', $workflow); self::assertStringContainsString('COMPOSER_ROOT_VERSION=${{ github.ref_name }}', $workflow); + self::assertStringContainsString('YIIPRESS_COMMIT=${{ github.sha }}', $workflow); self::assertStringContainsString( 'test "$(./dist/linux-amd64/yiipress --version)" = "YiiPress ${GITHUB_REF_NAME}"', $workflow, ); - self::assertStringContainsString('./dist/windows-amd64/yiipress.exe --version', $workflow); + self::assertStringContainsString( + 'if ((./dist/windows-amd64/yiipress.exe --version) -ne "YiiPress $env:GITHUB_REF_NAME")', + $workflow, + ); self::assertStringContainsString( 'test "$(./dist/macos-arm64/yiipress --version)" = "YiiPress ${GITHUB_REF_NAME}"', $workflow, ); + self::assertStringContainsString( + 'make package-macos PACKAGE_MACOS_ARCH=arm64 PACKAGE_MACOS_DIST=dist/macos-arm64 YIIPRESS_COMMIT=${GITHUB_SHA}', + $workflow, + ); self::assertStringContainsString( "git describe --tags --abbrev=0 --match '[0-9]*.[0-9]*.[0-9]*' --exclude '*[!0-9.]*' \"\${tag}^\"", $workflow, @@ -660,8 +681,10 @@ public function pharBuilderCopiesOnlyRuntimeInputs(): void self::assertStringNotContainsString("'runtime'", $packageScript); self::assertStringContainsString('PharArchiveFilter::shouldExclude($localPath)', $packageScript); self::assertStringContainsString("getenv('YIIPRESS_COMMIT')", $packageScript); + self::assertStringContainsString("strtolower(getenv('YIIPRESS_COMMIT')", $packageScript); self::assertStringContainsString("\$localPath === 'src/ApplicationInfo.php'", $packageScript); self::assertStringContainsString("public const string COMMIT = '{\$commit}';", $packageScript); + self::assertStringContainsString('$replacementCount !== 1', $packageScript); self::assertStringNotContainsString('packages/highlighter-extension/php', $stage); self::assertStringNotContainsString("'packages/highlighter-extension/php'", $packageScript); self::assertStringContainsString('COPY config /app/config', $stage); diff --git a/tests/Unit/Packaging/InstallerTest.php b/tests/Unit/Packaging/InstallerTest.php index 1fb29804..5d86d9c1 100644 --- a/tests/Unit/Packaging/InstallerTest.php +++ b/tests/Unit/Packaging/InstallerTest.php @@ -54,8 +54,8 @@ protected function setUp(): void done cp "${YIIPRESS_TEST_RELEASE_DIR}/${url##*/}" "$output" printf '%s\n' "$url" >> "${YIIPRESS_TEST_CURL_LOG}" -if [ "$write_out" = '%{url_effective}' ]; then - printf '%s\n' "https://github.com/test/engine/releases/download/${YIIPRESS_TEST_LATEST_VERSION}/${url##*/}" +if [ "$write_out" = '%{redirect_url}' ]; then + printf '%s\n' "https://github.com/test/engine/releases/download/${YIIPRESS_TEST_LATEST_VERSION}/${url##*/}?sig=test&expires=1" fi SH); file_put_contents($this->root . '/bin/curl', $curl); @@ -88,6 +88,11 @@ public function installsAndUpdatesTheLatestLinuxBinary(): void self::assertSame(0, $exitCode, $output); self::assertStringContainsString('Downloading YiiPress 1.2.3 for linux/amd64...', $output); + $curlLog = file_get_contents($this->root . '/curl.log'); + self::assertIsString($curlLog); + self::assertStringContainsString('/releases/download/1.2.3/SHA256SUMS', $curlLog); + self::assertStringContainsString('/releases/download/1.2.3/yiipress-linux-amd64.tar.gz', $curlLog); + self::assertStringNotContainsString('?sig=test', $curlLog); self::assertSame('version-one', file_get_contents($this->root . '/install/yiipress')); self::assertSame(0755, fileperms($this->root . '/install/yiipress') & 0777); From 79858aed5a90017e1822011105f49cfe0ddea8de Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 4 Aug 2026 19:14:48 +0300 Subject: [PATCH 3/3] Handle wrapped macOS version output --- .github/workflows/package-static.yml | 2 +- .github/workflows/release.yml | 2 +- tests/Unit/Packaging/ConfigurationPackagingTest.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/package-static.yml b/.github/workflows/package-static.yml index 729e82d0..d3176230 100644 --- a/.github/workflows/package-static.yml +++ b/.github/workflows/package-static.yml @@ -197,7 +197,7 @@ jobs: run: make package-macos PACKAGE_MACOS_ARCH=arm64 PACKAGE_MACOS_DIST=dist/macos-arm64 YIIPRESS_COMMIT=${GITHUB_SHA} - name: Smoke test macOS binary - run: test "$(./dist/macos-arm64/yiipress --version)" = "YiiPress ${GITHUB_SHA}" + run: ./dist/macos-arm64/yiipress --version | grep -Fqx "YiiPress ${GITHUB_SHA}" - name: Pack macOS artifact run: tar -C dist/macos-arm64 -czf dist/yiipress-macos-arm64.tar.gz yiipress diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a1bcb537..5ac3ba2a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -167,7 +167,7 @@ jobs: run: make package-macos PACKAGE_MACOS_ARCH=arm64 PACKAGE_MACOS_DIST=dist/macos-arm64 YIIPRESS_COMMIT=${GITHUB_SHA} - name: Smoke test macOS binary - run: test "$(./dist/macos-arm64/yiipress --version)" = "YiiPress ${GITHUB_REF_NAME}" + run: ./dist/macos-arm64/yiipress --version | grep -Fqx "YiiPress ${GITHUB_REF_NAME}" - name: Pack macOS artifact run: tar -C dist/macos-arm64 -czf dist/yiipress-macos-arm64.tar.gz yiipress diff --git a/tests/Unit/Packaging/ConfigurationPackagingTest.php b/tests/Unit/Packaging/ConfigurationPackagingTest.php index 8131ef20..7c0c34e2 100644 --- a/tests/Unit/Packaging/ConfigurationPackagingTest.php +++ b/tests/Unit/Packaging/ConfigurationPackagingTest.php @@ -314,7 +314,7 @@ public function packageWorkflowPublishesNightlyBuilds(): void ); self::assertStringContainsString('Smoke test macOS binary', $workflow); self::assertStringContainsString( - 'test "$(./dist/macos-arm64/yiipress --version)" = "YiiPress ${GITHUB_SHA}"', + './dist/macos-arm64/yiipress --version | grep -Fqx "YiiPress ${GITHUB_SHA}"', $workflow, ); self::assertStringContainsString('Pack macOS artifact', $workflow); @@ -482,7 +482,7 @@ public function releaseWorkflowBuildsNotesFromPreviousStableReleaseTag(): void $workflow, ); self::assertStringContainsString( - 'test "$(./dist/macos-arm64/yiipress --version)" = "YiiPress ${GITHUB_REF_NAME}"', + './dist/macos-arm64/yiipress --version | grep -Fqx "YiiPress ${GITHUB_REF_NAME}"', $workflow, ); self::assertStringContainsString(