diff --git a/.github/Taskfile.yml b/.github/Taskfile.yml deleted file mode 100644 index 2cb169d4..00000000 --- a/.github/Taskfile.yml +++ /dev/null @@ -1,46 +0,0 @@ -# Task file for GitHub Actions, https://taskfile.dev/ - -version: "3" - -# https://taskfile.dev/usage/#env-files -dotenv: [".env.local", ".env"] - -tasks: - default: - desc: "List all tasks" - cmds: - - task --list-all - silent: true - - build-prod: - desc: "Build application for production" - cmds: - - task setup-network - - task composer-install - - task npm-install - - task install-cleanup - - setup-network: - desc: "Setup docker frontend network" - cmds: - - docker network create frontend - - composer-install: - desc: "Install dependencies with composer." - cmds: - - docker compose run --rm --env APP_ENV=prod phpfpm composer install --no-dev -o --classmap-authoritative - - docker compose run --rm --env APP_ENV=prod phpfpm composer clear-cache - - npm-install: - desc: "Installs node dependencies with npm." - cmds: - - docker compose run --rm node npm install - - docker compose run --rm node npm run build - - install-cleanup: - desc: "Cleanup after install" - cmds: - - rm -rf infrastructure - - rm -rf fixtures - - rm -rf tests - - rm -rf node_modules diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml index 91bdc9ac..3b99f142 100644 --- a/.github/workflows/build-images.yml +++ b/.github/workflows/build-images.yml @@ -30,6 +30,11 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Set release timestamp + run: | + echo "APP_RELEASE_TIMESTAMP=$(date +%s)" >> "$GITHUB_ENV" + echo "APP_RELEASE_TIME=$(date -u)" >> "$GITHUB_ENV" + - name: Docker meta (API) id: meta-api uses: docker/metadata-action@v6 @@ -61,8 +66,8 @@ jobs: # '3.0.0-RC1') without bespoke string handling per trigger. build-args: | APP_VERSION=${{ steps.meta-api.outputs.version }} - APP_RELEASE_VERSION=${{ steps.meta-api.outputs.version }} - APP_RELEASE_TIMESTAMP=${{ github.run_number }} + APP_RELEASE_TIMESTAMP=${{ env.APP_RELEASE_TIMESTAMP }} + APP_RELEASE_TIME=${{ env.APP_RELEASE_TIME }} push: true tags: ${{ steps.meta-api.outputs.tags }} labels: ${{ steps.meta-api.outputs.labels }} @@ -101,8 +106,8 @@ jobs: repository-root=. build-args: | APP_VERSION=${{ steps.meta-nginx.outputs.version }} - APP_RELEASE_VERSION=${{ steps.meta-nginx.outputs.version }} - APP_RELEASE_TIMESTAMP=${{ github.run_number }} + APP_RELEASE_TIMESTAMP=${{ env.APP_RELEASE_TIMESTAMP }} + APP_RELEASE_TIME=${{ env.APP_RELEASE_TIME }} push: true tags: ${{ steps.meta-nginx.outputs.tags }} labels: ${{ steps.meta-nginx.outputs.labels }} diff --git a/.github/workflows/github_build_release.yml b/.github/workflows/github_build_release.yml index 482cbdc2..210bf1b3 100644 --- a/.github/workflows/github_build_release.yml +++ b/.github/workflows/github_build_release.yml @@ -18,12 +18,58 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Install Task task runner - uses: go-task/setup-task@v2 + - name: Setup network + run: docker network create frontend - - name: Install, Build, Cleanup + - name: Composer install (prod) run: | - task --taskfile=.github/Taskfile.yaml build-prod + docker compose run --rm --env APP_ENV=prod phpfpm \ + composer install --no-dev -o --classmap-authoritative + docker compose run --rm --env APP_ENV=prod phpfpm composer clear-cache + + - name: NPM install and build + run: | + docker compose run --rm node npm install + docker compose run --rm node npm run build + + - name: Cleanup before packaging + run: | + # Mirrors .dockerignore for tarball hygiene, minus vendor/ and + # public/build/ which we just produced and need to ship. + rm -rf \ + infrastructure \ + fixtures \ + tests \ + node_modules \ + docs \ + scripts \ + var \ + test-results \ + playwright-report \ + blob-report \ + playwright \ + public/fixtures + rm -f \ + docker-compose*.yml \ + Taskfile.yml \ + playwright.config.ts \ + phpstan.dist.neon \ + phpunit.xml \ + phpunit.xml.dist \ + psalm.xml \ + psalm-baseline.xml \ + rector.php \ + xdebug.ini \ + launch.json + + - name: Write release.json + run: | + printf '{"releaseTimestamp": %s, "releaseTime": "%s", "releaseVersion": "%s"}\n' \ + "$(date +%s)" \ + "$(date -u)" \ + "${{ github.ref_name }}" \ + > public/release.json + cat public/release.json - name: Make assets dir run: | @@ -37,6 +83,19 @@ jobs: - name: Create checksum run: sha256sum ../assets/${{ github.event.repository.name }}-${{ github.ref_name }}.tar.gz > ../assets/checksum.txt + # Mirror docker/metadata-action's 'latest=auto' rule used by build-images.yml: + # any semver tag with a pre-release identifier ('3.0.0-RC1', '3.0.0-beta') + # is published as a GitHub pre-release; final tags ('3.0.0') are not. + - name: Detect pre-release + id: prerelease + shell: bash + run: | + if [[ "$GITHUB_REF_NAME" == *-* ]]; then + echo "flag=--prerelease" >> "$GITHUB_OUTPUT" + else + echo "flag=" >> "$GITHUB_OUTPUT" + fi + - name: Create a release in GitHub and uploads assets run: | gh release create ${{ github.ref_name }} --verify-tag --generate-notes ${{ steps.prerelease.outputs.flag }} ../assets/*.* diff --git a/CHANGELOG.md b/CHANGELOG.md index 66b714a3..8cc13c83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,9 @@ All notable changes to this project will be documented in this file. - Switched image build pipeline to GHCR with multi-arch layer caching. - Aligned the nginx image env-var contract: split `NGINX_FPM_SERVICE` and `NGINX_FPM_PORT`, raised upload cap and trusted-proxy CIDR defaults. +- Image build now writes `public/release.json` so the client's + release-loader can fetch it. The same file is included in the GitHub + Release tarball. ### NB! Prior to 3.x the project was split into separate repositories diff --git a/infrastructure/build-n-push.sh b/infrastructure/build-n-push.sh index 0f64eee7..2a0fc645 100755 --- a/infrastructure/build-n-push.sh +++ b/infrastructure/build-n-push.sh @@ -4,6 +4,7 @@ set -eux APP_VERSION="${APP_VERSION:-develop}" REGISTRY="${REGISTRY:-ghcr.io/os2display}" RELEASE_TIMESTAMP="$(date +%s)" +RELEASE_TIME="$(date -u)" # Set BUILD_LOAD=1 to build for the host platform and load into the local # docker daemon instead of pushing. Useful for local smoke tests; production @@ -25,8 +26,8 @@ docker buildx build \ --pull ${OUTPUT} \ --build-context repository-root=. \ --build-arg APP_VERSION="${APP_VERSION}" \ - --build-arg APP_RELEASE_VERSION="${APP_VERSION}" \ --build-arg APP_RELEASE_TIMESTAMP="${RELEASE_TIMESTAMP}" \ + --build-arg APP_RELEASE_TIME="${RELEASE_TIME}" \ --tag "${REGISTRY}/display-api-service:${APP_VERSION}" \ --file infrastructure/display-api-service/Dockerfile \ infrastructure/display-api-service @@ -38,8 +39,8 @@ docker buildx build \ --pull ${OUTPUT} \ --build-context repository-root=. \ --build-arg APP_VERSION="${APP_VERSION}" \ - --build-arg APP_RELEASE_VERSION="${APP_VERSION}" \ --build-arg APP_RELEASE_TIMESTAMP="${RELEASE_TIMESTAMP}" \ + --build-arg APP_RELEASE_TIME="${RELEASE_TIME}" \ --tag "${REGISTRY}/display-api-service-nginx:${APP_VERSION}" \ --file infrastructure/nginx/Dockerfile \ infrastructure/nginx diff --git a/infrastructure/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile index be3e5b4b..762bae1e 100644 --- a/infrastructure/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -28,6 +28,8 @@ FROM itkdev/php8.4-fpm:alpine AS api_app_builder LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" +ARG APP_RELEASE_TIMESTAMP=0 +ARG APP_RELEASE_TIME="" ENV APP_CLIENT_PATH=/app \ APP_API_PATH=/var/www/html @@ -57,6 +59,14 @@ COPY --chown=deploy:deploy --from=client_app_builder ${APP_CLIENT_PATH}/public/b # Re-run composer install after application code copied to image to complete install RUN APP_ENV=prod composer install --no-dev --optimize-autoloader --classmap-authoritative +# Write release.json into the public root so the client (assets/shared/release-loader.js) +# can fetch it at /release.json. Shape matches docs/release-example.json. +RUN printf '{"releaseTimestamp": %s, "releaseTime": "%s", "releaseVersion": "%s"}\n' \ + "${APP_RELEASE_TIMESTAMP}" \ + "${APP_RELEASE_TIME}" \ + "${APP_VERSION}" \ + > public/release.json + ######## PHP-FPM (API) production image ######## FROM itkdev/php8.4-fpm:alpine diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index 92cabe37..3a76f857 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -27,6 +27,8 @@ FROM itkdev/php8.4-fpm:alpine AS api_app_builder LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" +ARG APP_RELEASE_TIMESTAMP=0 +ARG APP_RELEASE_TIME="" ENV APP_CLIENT_PATH=/app \ APP_API_PATH=/var/www/html @@ -56,6 +58,14 @@ COPY --chown=deploy:deploy --from=client_app_builder ${APP_CLIENT_PATH}/public/b # Re-run composer install after application code copied to image to complete install RUN APP_ENV=prod composer install --no-dev --optimize-autoloader --classmap-authoritative +# Write release.json into the public root so the client (assets/shared/release-loader.js) +# can fetch it at /release.json. Shape matches docs/release-example.json. +RUN printf '{"releaseTimestamp": %s, "releaseTime": "%s", "releaseVersion": "%s"}\n' \ + "${APP_RELEASE_TIMESTAMP}" \ + "${APP_RELEASE_TIME}" \ + "${APP_VERSION}" \ + > public/release.json + ######## Nginx production image ########