diff --git a/CLAUDE.md b/CLAUDE.md index 63f2d98..49a59d3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,6 +19,7 @@ make # builds dist/{deck-engine,interpreter-cli}, dist/img/, dist/i18n/ # dist/run-engine.sh, dist/run-interpreter.sh, dist/user.properties make test # builds and runs engine/tests/smoke_test.c and interpreter/tests/*_test.c make clean # removes build/ and the generated binaries/img/i18n (never touches dist/user.properties) +make package # builds a versioned Linux CLI tarball at dist/ - see "Packaging" below ``` A single `Makefile` at the repo root builds both `engine/` and @@ -63,6 +64,36 @@ Run a single smoke test by editing `engine/tests/smoke_test.c`'s `main()` temporarily, or just read its assertions — there's no test filter flag, the whole suite runs in milliseconds. +## Packaging + +`make package` (a `Makefile` target, not a separate script) builds a +fresh `all` and bundles it into a versioned, self-contained Linux CLI +tarball at `dist/deck-in-a-dash--linux-.tar.gz` - +`deck-engine`, `interpreter-cli`, `run-engine.sh`/`run-interpreter.sh`, +`img/`, `i18n/`, `LICENSE`, `README.md`, `docs/reading.md` (renamed +`READING.md`), and a placeholder `user.properties` (from +`engine/scripts/user.properties.template`, **never** +`dist/user.properties` itself - see below). Extracting the tarball +anywhere and running the scripts/binaries from inside it works exactly +like running them from `dist/` in place, since `run-engine.sh`/ +`run-interpreter.sh` already resolve every path relative to their own +location (`SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"`), not the +caller's working directory. + +**Version**: defaults to the current commit's git tag, matching +`vX.Y.Z` (the `v` is stripped); if `HEAD` isn't exactly on such a tag, +it falls back to a `0.0.0-dev+` placeholder with a warning on +stderr. Override either with `make package VERSION=1.2.3`. Push a +`vX.Y.Z` tag to drive a real release version. + +**Never packages `dist/user.properties`**: the staging step copies +files into the package by explicit name only - never a wildcard or +recursive copy of `dist/` itself - specifically so a filled-in +`dist/user.properties` (real birth data, gitignored, private) can never +end up in a distributable package. The package always gets the +placeholder template instead, identical to what a first-time `make` +seeds `dist/user.properties` with. + ## Architecture ``` @@ -91,8 +122,10 @@ deck_in_a_dash/ tests/smoke_test.c scripts/run-engine.sh, scripts/run-interpreter.sh, scripts/user.properties.template interpreter/ Separate module + binary; see "Interpretation" below. - dist/ Build output (gitignored-style; see Build & run). - Makefile Single root Makefile, builds both engine/ and interpreter/. + dist/ Build output (gitignored-style; see Build & run) and, + after `make package`, versioned release tarballs. + Makefile Single root Makefile, builds both engine/ and + interpreter/, and packages a release (see Packaging). ``` ### Data flow / the real API diff --git a/Makefile b/Makefile index 89d7daf..2a2841b 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,11 @@ JSON_TEST_BIN := $(BUILD_DIR)/json_test NARRATIVE_TEST_BIN := $(BUILD_DIR)/narrative_test GUIDANCE_TEST_BIN := $(BUILD_DIR)/guidance_test -.PHONY: all test clean images i18n scripts +# Linux CLI release tarball - see the `package` target below. +ARCH := $(shell uname -m) +PACKAGE_DIR := $(BUILD_DIR)/package + +.PHONY: all test clean images i18n scripts package all: $(ENGINE_BINARY) $(INTERP_BINARY) images i18n scripts @@ -152,6 +156,47 @@ $(OBJ_DIR)/%.o: %.c @mkdir -p $(dir $@) $(CC) $(CFLAGS) -c -o $@ $< +# Builds a versioned Linux CLI release tarball at +# dist/deck-in-a-dash--linux-.tar.gz. Version defaults to +# the current git tag (vX.Y.Z, tag prefix stripped) - push a tag to drive +# a release. Override with `make package VERSION=1.2.3`, or just run it +# untagged for a local dev build (gets a 0.0.0-dev+ placeholder +# version, with a warning). +# +# Copies files into the staging dir by explicit name only - never a +# wildcard/recursive copy of dist/ itself - so a real, filled-in +# dist/user.properties (birth data, gitignored, private) can never +# accidentally end up in a package. The package gets the placeholder +# template instead, same as a first-time `make` produces. +package: all + @V="$(VERSION)"; \ + if [ -z "$$V" ]; then \ + if TAG=$$(git describe --tags --exact-match --match 'v[0-9]*.[0-9]*.[0-9]*' 2>/dev/null); then \ + V=$${TAG#v}; \ + else \ + V="0.0.0-dev+$$(git rev-parse --short HEAD)"; \ + echo "WARNING: HEAD is not on a vX.Y.Z tag - building placeholder version $$V (push a tag to drive a real release version)" >&2; \ + fi; \ + fi; \ + case "$$V" in \ + [0-9]*.[0-9]*.[0-9]*) ;; \ + *) echo "PREFLIGHT FAIL: VERSION '$$V' is not a semantic version (expected X.Y.Z, optionally with a -pre+meta suffix)" >&2; exit 1;; \ + esac; \ + PKG_NAME="deck-in-a-dash-$$V-linux-$(ARCH)"; \ + PKG_STAGE="$(PACKAGE_DIR)/$$PKG_NAME"; \ + echo "Packaging $$PKG_NAME"; \ + rm -rf "$$PKG_STAGE"; \ + mkdir -p "$$PKG_STAGE"; \ + cp $(ENGINE_BINARY) $(INTERP_BINARY) "$$PKG_STAGE/"; \ + cp $(DIST_DIR)/run-engine.sh $(DIST_DIR)/run-interpreter.sh "$$PKG_STAGE/"; \ + cp -r $(DIST_IMG_DIR) $(DIST_I18N_DIR) "$$PKG_STAGE/"; \ + cp $(ENGINE_SCRIPTS_DIR)/user.properties.template "$$PKG_STAGE/user.properties"; \ + cp LICENSE README.md "$$PKG_STAGE/"; \ + cp docs/reading.md "$$PKG_STAGE/READING.md"; \ + tar -czf "$(DIST_DIR)/$$PKG_NAME.tar.gz" -C "$(PACKAGE_DIR)" "$$PKG_NAME"; \ + rm -rf "$(PACKAGE_DIR)"; \ + echo "Wrote $(DIST_DIR)/$$PKG_NAME.tar.gz" + clean: rm -rf $(BUILD_DIR) $(ENGINE_BINARY) $(INTERP_BINARY) $(DIST_IMG_DIR) $(DIST_I18N_DIR) \ $(DIST_DIR)/run-engine.sh $(DIST_DIR)/run-interpreter.sh diff --git a/README.md b/README.md index 1b7b293..9e1fc33 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,8 @@ interpreter/ Separate module + binary (dist/interpreter-cli) --format text|html|json and --lang en|de for its own output too (see CLAUDE.md). docs/ Architecture/dataflow diagrams, input/output format reference. -Makefile Single root Makefile, builds both engine/ and interpreter/. +Makefile Single root Makefile, builds both engine/ and + interpreter/, and packages a release (`make package`). ``` See [`CLAUDE.md`](CLAUDE.md) for the detailed architecture and the sharp @@ -69,6 +70,10 @@ struct format reference make # -> dist/deck-engine, dist/interpreter-cli, dist/img/, dist/i18n/, # dist/run-engine.sh, dist/run-interpreter.sh, dist/user.properties make test # builds and runs engine/tests/smoke_test.c and interpreter/tests/*_test.c +make package # -> dist/deck-in-a-dash--linux-.tar.gz, a + # self-contained release bundle (binaries, scripts, img/, i18n/, + # LICENSE, docs) - version from the current git tag, or + # VERSION=1.2.3 make package to override ``` Requires only a C99 compiler and `make` — no other dependencies.