diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index c08a88e..f740d5c 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -1,9 +1,11 @@ name: build on: - push: - branches: [dev, main] - pull_request: + # Manual only for now. Select the branch to build when dispatching: + # main -> APP_VERSION is the current git tag + # dev -> APP_VERSION is the current commit hash + # Both branches bake the portal API key from secrets. + workflow_dispatch: jobs: nix-build: @@ -11,11 +13,13 @@ jobs: steps: # actions/checkout@v4 is unreliable on host-mode runners (its files are # copied into the workspace instead of the repo), so clone explicitly. + # A full clone (no --depth) is required so `git describe --tags` on main + # resolves the real nearest tag instead of falling back to a hash. - name: Checkout run: | rm -rf ./* ./.[!.]* 2>/dev/null || true git config --global http.extraheader "Authorization: token ${{ github.token }}" - git clone --depth 1 --branch "${{ gitea.ref_name }}" \ + git clone --branch "${{ gitea.ref_name }}" \ "${{ gitea.server_url }}/${{ gitea.repository }}.git" . test -f flake.nix git log --oneline -1 @@ -26,12 +30,37 @@ jobs: ls -la git rev-parse --show-toplevel 2>&1 || true git remote -v 2>&1 || true + git describe --tags --always 2>&1 || true test -f flake.nix && echo "flake.nix present" || echo "NO flake.nix" + # Compute the version string baked into the binary's Home footer: + # main reports the git tag, dev reports the commit hash fingerprint. + # The key stays in the build step's environment (never echoed). + - name: Compute version + id: version + run: | + if [ "${{ gitea.ref_name }}" = "main" ]; then + APP_VERSION="$(git describe --tags --always)" + else + APP_VERSION="$(git rev-parse --short HEAD)" + fi + echo "APP_VERSION=$APP_VERSION" >> "$GITHUB_ENV" + echo "Branch: ${{ gitea.ref_name }} -> APP_VERSION=$APP_VERSION" + if [ -n "${{ secrets.RWT_API_KEY }}" ]; then + echo "API key: provided via secret RWT_API_KEY" + else + echo "API key: NOT set (build proceeds without an Authorization header)" + fi + # checks is keyed per-system (checks..default); nix build's # #attr shorthand can't resolve it, so resolve the current system inline. + # --impure lets the flake read APP_VERSION/API_KEY via builtins.getEnv + # (pure evals always get the package defaults). The offline test suite + # runs in the package's checkPhase. - name: Build + offline test suite - run: nix build -L ".#checks.$(nix eval --impure --raw --expr 'builtins.currentSystem').default" + env: + API_KEY: ${{ secrets.RWT_API_KEY }} + run: nix build -L ".#checks.$(nix eval --impure --raw --expr 'builtins.currentSystem').default" --impure - name: Live portal tests if: ${{ secrets.RWT_LIVE_API_KEY != '' }} diff --git a/AGENTS.md b/AGENTS.md index f3b05b9..a8aa045 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,8 +30,9 @@ the project version, or pass your own at configure time: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DAPP_VERSION="1.2.3" ``` -The Nix package bakes `-DAPP_VERSION=` via `cmakeFlags` (version is -hardcoded `0.1.0` in `nix/package.nix`, not read from the flake). `nix develop` +The Nix package bakes `-DAPP_VERSION=` via `cmakeFlags` (`version` +defaults to `0.1.0` in `nix/package.nix`; the flake overrides it from the +`APP_VERSION` env var on impure evals). `nix develop` sets `APP_VERSION=dev` and exports `API_KEY` from the ambient environment (falling back to `RWT_LIVE_API_KEY`). CMake seeds a *fresh* configure from the `APP_VERSION` / `API_KEY` env vars; `-D` wins on reconfigure. @@ -230,11 +231,16 @@ cached. - Do not modify files in `archive/` — reference only. - Build needs SDL3 + SDL3_ttf/image/mixer/net + GTest installed; CI uses Gitea Actions on a self-hosted host runner (`build-server`, no docker). - The workflow lives in `.gitea/workflows/build.yml` and runs on both `dev` - and `main`. It runs `nix build -L .#checks..default` (build + the - offline test suite; `checks` is keyed per-system, so the workflow resolves - the current system via `nix eval --impure --expr 'builtins.currentSystem'`); - when the `RWT_LIVE_API_KEY` secret is set it also runs + The workflow lives in `.gitea/workflows/build.yml` and is **manual-only** + (`workflow_dispatch`); pick the branch when dispatching. It runs + `nix build -L .#checks..default` (build + the offline test suite; + `checks` is keyed per-system, so the workflow resolves the current system + via `nix eval --impure --expr 'builtins.currentSystem'`). The build bakes + in a per-branch `APP_VERSION` — the current git tag on `main`, the current + commit hash on `dev` — plus the portal API key from the `RWT_API_KEY` + secret. The flake reads `APP_VERSION`/`API_KEY` from the environment via + `builtins.getEnv`, so the build must pass `--impure` (pure evals get the + package defaults). When the `RWT_LIVE_API_KEY` secret is set it also runs `test_api_client` / `test_license_service` against the portal via `nix develop` (outside the sandbox, like a dev machine). Those live tests also need `RWT_LIVE_ACCOUNT_EMAIL`, `RWT_LIVE_LICENSE_EMAIL`, and diff --git a/README.md b/README.md index 7556fcd..f8f510c 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,12 @@ cmake --build build If `APP_VERSION` is left unset the footer falls back to the project version from `CMakeLists.txt` (`project(... VERSION ...)`); a build that defines no version at all shows `unknown`. The Nix build sets `APP_VERSION` automatically -to the flake package version, so `nix build` needs no extra flags. +to the flake package version, so a plain `nix build` needs no extra flags. +With `--impure` the flake instead reads the `APP_VERSION` / `API_KEY` +environment variables — this is how CI injects the version and portal key +(see the CI note under *Common Pitfalls*). Pure evals always get the package +defaults, because `builtins.getEnv` cannot see the environment in pure +evaluation mode. #### Portal API key @@ -151,7 +156,8 @@ cmake --build build Rotate the key by building with a new value and shipping the new binary. The Nix package accepts the same value via `callPackage ./nix/package.nix { -apiKey = "…"; }`; the flake itself never embeds one. +apiKey = "…"; }`, or via the `API_KEY` environment variable with +`nix build --impure`; the flake itself never embeds one. #### Version + API key in one configure @@ -179,6 +185,17 @@ nix build .#packages.x86_64-linux.default # needs a Linux machine/builders nix build .#packages.aarch64-linux.default ``` +To bake in a custom version and/or portal key, build impurely — the flake +reads `APP_VERSION` and `API_KEY` from the environment on impure evals: + +```sh +APP_VERSION="1.2.3" API_KEY="$PORTAL_API_KEY" nix build --impure .#default +``` + +A pure `nix build` always uses the package defaults (`0.1.0`, no auth header) +even if those variables are set in your shell, because pure evaluation cannot +see the environment. CI uses `--impure` to inject them. + `nix build` runs the full offline test suite in its `checkPhase`; the live-portal test binaries (`test_api_client`, `test_license_service`) are excluded. `nix flake check` builds the package and therefore also runs the @@ -610,6 +627,12 @@ status to decide which page to show. - `build/` is gitignored. Do not commit build artifacts. - Use the Nix dev shell or ensure SDL3 is installed for building. - CI uses Gitea Actions on a self-hosted host runner (`build-server`); the - workflow is `.gitea/workflows/build.yml`. + workflow is `.gitea/workflows/build.yml`. It is **manual-only** + (`workflow_dispatch`) — pick the branch when dispatching. `main` builds bake + the current git tag as `APP_VERSION`; `dev` builds bake the current commit + hash. Both pass the portal key from the `RWT_API_KEY` secret and build with + `nix build --impure` (which lets the flake's env injection work). The + offline test suite runs in the package's `checkPhase`; the live portal tests + run from `nix develop` when the `RWT_LIVE_*` secrets are set. - Asset tests (`test_assets`) must pass before committing -- they catch missing fonts and images that would break the app at runtime. diff --git a/flake.nix b/flake.nix index 73a0718..b2abc88 100644 --- a/flake.nix +++ b/flake.nix @@ -34,7 +34,20 @@ }; in { - packages.default = pkgs.callPackage ./nix/package.nix { }; + # Version and API key are injected from the ambient environment on + # impure evals (CI sets APP_VERSION/API_KEY from the git ref and a + # secret). Pure evals (`nix flake check`, local `nix build`) can't + # see the environment, so builtins.getEnv returns "" and the + # package falls back to its 0.1.0 default / no auth header. + packages.default = + let + envVersion = builtins.getEnv "APP_VERSION"; + envApiKey = builtins.getEnv "API_KEY"; + packageArgs = + { apiKey = envApiKey; } + // (nixpkgs.lib.optionalAttrs (envVersion != "") { version = envVersion; }); + in + pkgs.callPackage ./nix/package.nix packageArgs; devShells.default = pkgs.mkShell { inputsFrom = [ self.packages.${system}.default ]; @@ -55,6 +68,7 @@ echo "RadiantWave dev shell (APP_VERSION=dev, API_KEY=$([[ -n "$API_KEY" ]] && echo set || echo unset))" echo " Configure: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug" echo " Bake version+key: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DAPP_VERSION=\"1.2.3\" -DAPI_KEY=\"\"" + echo " Nix build (env version+key, impure): APP_VERSION=... API_KEY=... nix build --impure .#default" echo " Build: cmake --build build" echo " Run: ./build/radiantwave" echo " Tests: ./build/test_assets && ./build/test_" diff --git a/nix/package.nix b/nix/package.nix index e30be24..d4e4c0f 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -18,11 +18,15 @@ # Portal API key baked into the binary. Keep it out of the flake; pass it # when calling callPackage from CI/your own config (see README "App version"). apiKey ? "", + # App version baked into the Home footer's Version row. CI overrides this + # per branch (git tag on main, commit hash on dev); local/pure builds keep + # the 0.1.0 default. + version ? "0.1.0", }: stdenv.mkDerivation (finalAttrs: { pname = "radiantwave"; - version = "0.1.0"; + inherit version; src = lib.cleanSourceWith { filter =