explodorama / commit
Version number, build commit and source link in the menu
CHANGELOG.md | 3 +++ CONTRIBUTING.md | 3 ++- README.md | 7 +++++++ decisions.md | 17 +++++++++++++++++ src/main.ts | 15 ++++++++++++++- test/version.test.ts | 11 +++++++++++ vite.config.ts | 15 +++++++++++++++ 7 files changed, 69 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc91ad3..4093cbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ saved before the change is dropped and the next visit starts a new run. ### Added +- **A version number**, at the bottom of the menu and near the top of the README. It is the + rules version (`SIM_VERSION`), so it changes exactly when old saved runs stop resuming. The + menu also shows the commit the game was built from and links to the source code. - **A high-score board** in this browser: every finished run, ranked by how far it got, then coins, with enemies killed, turns, how it ended, the power-ups held and the seed. A global board and a daily dungeon are planned. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5c7cea0..e311d67 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,7 +54,8 @@ The README's *How it's built* section describes the layout. The rules that matte the same state. - **Bump `SIM_VERSION`** in [src/sim/record.ts](src/sim/record.ts) whenever a rule change would make an old record replay differently. Saved runs from another version are dropped - rather than replayed wrongly. Note the bump in the changelog. + rather than replayed wrongly. Note the bump in the changelog, and update the version near + the top of the README: it is the game's version, also shown in the menu. - **Rendering** (`src/render/`) and the **HTML UI** (`src/ui/`) only read the state and the events that `step()` returns; they never change the rules. - **Movement on screen comes from the change of state, not from events.** The player's and the diff --git a/README.md b/README.md index 93166f1..3cb681b 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,13 @@ seven charges that work when picked up, a hidden gem on every level that offers mid-run work. The shop, the Keep / Fresh choice on death, curses and more enemies are planned — see [PLAN.md](PLAN.md). +Version: **22**, shown at the bottom of the menu with the commit the game was built from (a link +to it on GitLab, plus "plus local changes" in a build with uncommitted changes) and a link to the +[source code](https://gitlab.com/PavelSafronov/explodorama). The version is the rules version +(`SIM_VERSION` in [src/sim/record.ts](src/sim/record.ts)): it goes up by one whenever a rule +change would make an old run replay differently. A run saved under another version is not +resumed, and a seed gives the same dungeon only within one version. +  ## How to play diff --git a/decisions.md b/decisions.md index 45d0d60..3a25af2 100644 --- a/decisions.md +++ b/decisions.md @@ -1073,3 +1073,20 @@ start thinking about a daily dungeon with a seed from the date. Checked in a browser: two game overs (the second a Developer run with more coins) each said "New high score: #1!"; the board showed both, the Developer one first, red-bordered. + +### 2026-10-24 — The version number is the rules version + +User request: a version number in the menu and the README, using the `SIM_VERSION` counter. + +- **One number:** the game's version is `SIM_VERSION` (now 22), not `package.json`'s 0.1.0, + which nothing bumps. It goes up exactly when a rule change makes old runs replay differently, + so it also says which saved runs and seeds carry over. +- **Where:** a small line at the bottom of the menu, under the art credit, and a "Version:" line + near the top of the README. A test (`test/version.test.ts`) fails if the README's number and + `SIM_VERSION` disagree, so a bump can't leave the README behind. +- A change that alters only the look (no rule change) keeps the same version. +- **The commit and the repository** (user request, same day): the menu also shows the short + commit hash the build came from, linked to it on GitLab, and a "Source code" link. The hash + is read from git when Vite starts (`vite.config.ts`, as `VITE_COMMIT`), so the deployed build + shows the commit CI built. A build with uncommitted changes says "plus local changes", since + the hash alone would claim code it doesn't contain; with no git, the commit is left out. diff --git a/src/main.ts b/src/main.ts index 4a9424b..36a42f1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -341,6 +341,16 @@ function showInfo(info: Info, extra: DialogButton[] = []): void { }) } +const REPO_URL = 'https://gitlab.com/PavelSafronov/explodorama' + +/** ", commit 0580e85" linked to it on GitLab, with a note if built with uncommitted changes. */ +function commitLine(): string { + const commit = import.meta.env.VITE_COMMIT + if (!commit) return '' + const link = `<a href="${REPO_URL}/-/commit/${commit}" target="_blank" rel="noopener">${commit}</a>` + return `, commit ${link}${import.meta.env.VITE_COMMIT_DIRTY ? ' plus local changes' : ''}` +} + /** The menu: resume, or abandon this run for a new one (confirmed first, since it can't be undone). */ function showMenu(): void { const where = state.status === 'playing' ? `Level ${state.level}, turn ${state.turn}. ` : '' @@ -350,7 +360,10 @@ function showMenu(): void { `<p>${where}Seed ${state.seed}. The game saves after every move, so you can close it and come back.</p>` + modeLines(state).map((l) => `<p class="mode">${l}</p>`).join('') + `<p class="credit">Art by <a href="https://www.kenney.nl" target="_blank" rel="noopener">Kenney</a>: ` + - `New Platformer Pack, UI Pack, UI Pack: RPG Expansion, Game Icons and Board Game Icons (CC0). Thank you!</p>`, + `New Platformer Pack, UI Pack, UI Pack: RPG Expansion, Game Icons and Board Game Icons (CC0). Thank you!</p>` + + // The version is the rules version: it changes exactly when old saves stop resuming. + `<p class="credit">Version ${SIM_VERSION}${commitLine()}. ` + + `<a href="${REPO_URL}" target="_blank" rel="noopener">Source code</a></p>`, buttons: [ { label: 'Resume', kind: 'primary', icon: playIcon }, { label: 'Almanac', kind: 'plain', onClick: () => openAlmanac() }, diff --git a/test/version.test.ts b/test/version.test.ts new file mode 100644 index 0000000..01d1a0a --- /dev/null +++ b/test/version.test.ts @@ -0,0 +1,11 @@ +import { readFileSync } from 'node:fs' +import { describe, expect, it } from 'vitest' +import { SIM_VERSION } from '../src/sim/record' + +describe('version', () => { + // The game's version is SIM_VERSION (shown in the menu); the README states it by hand. + it('the README states the current SIM_VERSION', () => { + const stated = readFileSync('README.md', 'utf8').match(/^Version: \*\*(\d+)\*\*/m)?.[1] + expect(Number(stated)).toBe(SIM_VERSION) + }) +}) diff --git a/vite.config.ts b/vite.config.ts index a22b448..eea955f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,5 +1,6 @@ import { defineConfig } from 'vite' import { qrcode } from 'vite-plugin-qrcode' +import { execSync } from 'node:child_process' /** * Relative asset paths by default, so the same build works wherever Pages puts it: @@ -8,6 +9,20 @@ import { qrcode } from 'vite-plugin-qrcode' */ const base = process.env.BASE || './' +/** A git command's output, or '' where there is no git (the menu then leaves the commit out). */ +function git(args: string): string { + try { + return execSync(`git ${args}`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() + } catch { + return '' + } +} + +// The commit the game was built from, shown in the menu with the version. Vite hands VITE_* +// variables to the game as import.meta.env. DIRTY: the build has uncommitted changes (in dev). +process.env.VITE_COMMIT = git('rev-parse --short HEAD') +process.env.VITE_COMMIT_DIRTY = git('status --porcelain') ? '1' : '' + export default defineConfig({ base, // `npm run dev` and `npm run preview` bind to every interface (--host) so a phone on the same