diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ddc2409f0..dbfe54ba7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,10 +67,39 @@ jobs: 10.0.201 - name: Run build pipeline script - run: bash ./build/build.sh + shell: bash + run: | + set -euo pipefail + LEGACY_VERSION="${NBGV_SemVer2}" + UMBRACO18_VERSION="7.${LEGACY_VERSION#*.}" + + ARTICULATE_PACKAGE_LANE=legacy \ + ARTICULATE_PACKAGE_VERSION="$LEGACY_VERSION" \ + PACK_SAMPLE_THEME=true \ + bash ./build/build.sh + + ARTICULATE_PACKAGE_LANE=umbraco18 \ + ARTICULATE_PACKAGE_VERSION="$UMBRACO18_VERSION" \ + PACK_SAMPLE_THEME=true \ + bash ./build/build.sh + + - name: Export package versions + shell: bash + run: | + set -euo pipefail + LEGACY_VERSION="${NBGV_SemVer2}" + UMBRACO18_VERSION="7.${LEGACY_VERSION#*.}" + echo "LEGACY_VERSION=$LEGACY_VERSION" >> "$GITHUB_ENV" + echo "UMBRACO18_VERSION=$UMBRACO18_VERSION" >> "$GITHUB_ENV" + + - name: Upload v6 packages + uses: actions/upload-artifact@v7.0.1 + with: + name: Articulate-v6-${{ env.LEGACY_VERSION }} + path: build/Release/legacy/Articulate.* - - name: Upload packages + - name: Upload v7 packages uses: actions/upload-artifact@v7.0.1 with: - name: Articulate.${{ env.NBGV_SemVer2 }} - path: ${{ env.RELEASE_FOLDER }}/Articulate.* + name: Articulate-v7-${{ env.UMBRACO18_VERSION }} + path: build/Release/umbraco18/Articulate.* diff --git a/.gitignore b/.gitignore index af2fc7aeb..b28f17fbf 100644 --- a/.gitignore +++ b/.gitignore @@ -529,3 +529,4 @@ scratch/ /src/Articulate.Tests/umbraco-package-schema.json /src/Articulate.Tests/appsettings-schema.Umbraco.Cms.json src/Articulate.Tests/appsettings-schema.json +/build/test-site-publish diff --git a/DEVELOP.md b/DEVELOP.md index a80f680cc..c52578cab 100644 --- a/DEVELOP.md +++ b/DEVELOP.md @@ -56,22 +56,213 @@ pnpm run generate:api - `BUILD_CONFIGURATION=Debug` is the default for local builds; Release is the default in packaging flows. - `ENABLE_CLIENT_BUILD=true` enables local TypeScript Back Office client builds. - `PACK_SAMPLE_THEME=true` forces packing `Articulate.Theme.Sample`; local builds pack it by default, but CI skips it unless explicitly enabled. -- The scripts clean, restore, build, and pack the current Articulate projects for .NET 9 and .NET 10. -- The packable NuGet package is produced by `src/Articulate.Web/Articulate.Web.csproj` (`PackageId=Articulate`). Packages are written under `build/$(Configuration)` by default. -- If you change packaged runtime dependencies or client/static assets, regenerate the Docker inputs before validating source-built or Docker-based installs: - - `dotnet pack src/Articulate.Web/Articulate.Web.csproj -c Release` - - `dotnet pack src/Articulate.Theme.Sample/Articulate.Theme.Sample.csproj -c Release` -- Keep `Articulate` and `Articulate.Theme.Sample` at the same package version in `build/Release`; the Docker site restores both using the selected Articulate package version. -- The Dockerfile selects the newest `Articulate.[0-9]*.nupkg` in `build/Release` by modified time and ignores `.snupkg` files and theme packages when choosing the version. -- Rebuilding the image is not enough on its own. A running Compose service can remain on an older image/container. Use `docker compose up -d --build --force-recreate articulate`, or run both steps explicitly: - - `docker compose build articulate` - - `docker compose up -d --force-recreate --no-deps articulate` -- The expected image tag is `articulate-local:net10`; the Compose container name will still be project/service based, for example `articulate-pr-articulate-1`. -- If the Docker back office still appears stale after a rebuild, check the running container, not just the image: - - `docker compose ps` - - `docker exec articulate-pr-articulate-1 /bin/sh -c "find /app -path '*App_Plugins/Articulate/BackOffice/articulate-backoffice.js' -o -path '*App_Plugins/Articulate/umbraco-package.json'"` - - `Invoke-WebRequest https://localhost:18443/App_Plugins/Articulate/BackOffice/articulate-backoffice.js -SkipCertificateCheck` -- The default unattended Docker backoffice user is `admin@localhost` with password `@rticulate` and display name `Jane Doe`. Override with `UMBRACO_USER_NAME`, `UMBRACO_USER_EMAIL`, and `UMBRACO_USER_PASSWORD` when needed. +- The scripts clean, restore, build, and pack one package lane at a time. The default lane is `legacy`. +- Running the local build script once does not produce both lanes; run it once with `ARTICULATE_PACKAGE_LANE=legacy` and once with `ARTICULATE_PACKAGE_LANE=umbraco18` when you need both NuGet package sets locally. +- The packable NuGet package is produced by `src/Articulate.Web/Articulate.Web.csproj` (`PackageId=Articulate`). +- Packages are written under `build/$(Configuration)/$(ARTICULATE_PACKAGE_LANE)`. + +### Package Lanes + +The source tree supports two package lanes: + +| Lane | Package line | Umbraco support | Target frameworks | Output folder | +| --- | --- | --- | --- | --- | +| `legacy` | Articulate 6.x | Umbraco 16/17 | `net9.0`, `net10.0` | `build/Release/legacy` | +| `umbraco18` | Articulate 7.x | Umbraco 18 | `net10.0` | `build/Release/umbraco18` | + +The lanes produce separate NuGet packages because the compiled Umbraco 17 and Umbraco 18 extension points are not binary-compatible. Do not install an Articulate 6 package into Umbraco 18, or an Articulate 7 package into Umbraco 16/17. + +Build the Articulate 6 lane: + +PowerShell: + +```powershell +$env:ARTICULATE_PACKAGE_LANE='legacy' +$env:ARTICULATE_PACKAGE_VERSION='6.0.0-rc.2' +$env:PACK_SAMPLE_THEME='true' +./build/build.ps1 +``` + +Bash: + +```bash +ARTICULATE_PACKAGE_LANE=legacy \ +ARTICULATE_PACKAGE_VERSION=6.0.0-rc.2 \ +PACK_SAMPLE_THEME=true \ +./build/build.sh +``` + +Build the Articulate 7 / Umbraco 18 lane: + +PowerShell: + +```powershell +$env:ARTICULATE_PACKAGE_LANE='umbraco18' +$env:ARTICULATE_PACKAGE_VERSION='7.0.0-rc.2' +$env:PACK_SAMPLE_THEME='true' +./build/build.ps1 +``` + +Bash: + +```bash +ARTICULATE_PACKAGE_LANE=umbraco18 \ +ARTICULATE_PACKAGE_VERSION=7.0.0-rc.2 \ +PACK_SAMPLE_THEME=true \ +./build/build.sh +``` + +The build scripts temporarily patch `version.json` when `ARTICULATE_PACKAGE_VERSION` is set, then restore it before exiting. This keeps Nerdbank.GitVersioning as the source of package metadata while allowing one checkout to produce the Articulate 6 and 7 package lines. + +## Local Docker Validation + +Docker is a local validation tool. GitHub Actions builds package artifacts but does not run Docker. + +### Quick smoke test + +Build and smoke-test Docker images for any Umbraco target version using the provided script: + +```powershell +# Windows PowerShell +.\scripts\docker-test.ps1 -Target umbraco16 +.\scripts\docker-test.ps1 -Target umbraco17 +.\scripts\docker-test.ps1 -Target umbraco18 +.\scripts\docker-test.ps1 -Target all -Keep # leaves containers running for manual browsing +``` + +```bash +# Bash +./scripts/docker-test.sh umbraco16 +./scripts/docker-test.sh umbraco17 +./scripts/docker-test.sh umbraco18 +./scripts/docker-test.sh all --keep # leaves containers running for manual browsing +``` + +The PowerShell and Bash Docker test scripts support the same targets: + +| Target | Package lane | TFM | Umbraco version | Image tag | HTTPS port | +| --- | --- | --- | --- | --- | --- | +| `umbraco16` | `legacy` | `net9.0` | `[16.5.1,17.0.0)` | `articulate-local:umbraco16` | 16016 | +| `umbraco17` | `legacy` | `net10.0` | `[17.2.2,18.0.0)` | `articulate-local:umbraco17` | 17017 | +| `umbraco18` | `umbraco18` | `net10.0` | `18.0.0-rc*` | `articulate-local:umbraco18` | 18018 | + +Each target builds a Docker image with the correct SDK, ASP.NET runtime, TFM, and Umbraco version range. The smoke test starts Umbraco behind a Caddy sidecar for TLS termination (required by Umbraco Production mode) and verifies both `/` and `/umbraco` respond with HTTP 200. Use `-Keep` in PowerShell or `--keep` in Bash to leave the scripted containers running for manual browser testing. + +### Package prerequisites + +The Docker test scripts check for both `Articulate.*.nupkg` and `Articulate.Theme.Sample.*.nupkg` in the required package lane. If either is missing, the script builds that lane with `PACK_SAMPLE_THEME=true`. + +The package build scripts also assert that the main `Articulate` package contains the Material theme static web assets that previously regressed in the Umbraco 18 lane: + +- `staticwebassets/App_Plugins/Articulate/Themes/Material/assets/dist/css/material.min.css` +- `staticwebassets/App_Plugins/Articulate/Themes/Material/assets/dist/js/material.min.js` +- `staticwebassets/App_Plugins/Articulate/Themes/Material/assets/vendor/mdl/material.min.js` +- `staticwebassets/App_Plugins/Articulate/Themes/Material/assets/vendor/mdl/material.pink-blue.min.css` + +You can still build both lanes explicitly: + +```powershell +$env:ARTICULATE_PACKAGE_LANE='legacy' +./build/build.ps1 + +$env:ARTICULATE_PACKAGE_LANE='umbraco18' +./build/build.ps1 +``` + +```bash +ARTICULATE_PACKAGE_LANE=legacy ./build/build.sh +ARTICULATE_PACKAGE_LANE=umbraco18 ./build/build.sh +``` + +### HTTPS / Caddy + +The smoke test runs each target behind Caddy for TLS. The default `Caddyfile` uses `tls internal`, which generates a local CA and server certificate. Browsers on Windows will show a certificate warning until the local CA is trusted. + +To trust Caddy's root CA: + +```powershell +powershell -ExecutionPolicy Bypass -File .\build\docker-site\Trust-CaddyRootCA.ps1 +``` + +### HMAC image signing (Umbraco 17+) + +Umbraco 17+ auto-generates an HMAC secret key on first boot. Image resize/crop URLs generated by `GetCropUrl()` include an `hmac` query parameter signed with this key. The server validates the HMAC before processing the image — requests without a valid HMAC return 400. + +When rendering image URLs in `