# Engineering Versioned Java standards, formatting, coverage checks, and the Gitea Actions / Maven Java 25 / npm / Docker Compose recipe. Architecture and specifications belong to [landscape](http://192.168.2.28:3000/wanjakrah/landscape). Shared agent skills are maintained in landscape under skills/. Source attribution is in SOURCE.md. ## Start a project Clone this repository and check out a released tag. Use JDK 25, Maven, Python 3, Git, Docker with Compose, and a supported Node version for a frontend. ```sh python3 create-project.py my-project ../my-project --version v2.0.0 --components both ``` Choose `backend`, `frontend`, or `both`. The command refuses existing destinations and creates local files only. The backend starter is Quarkus; the frontend starter uses npm and a minimal static page. Replace these starters with application code while preserving the configured build commands and container contracts. Initialize the resulting Git repository and commit its generated resources. Create the remote repository separately, configure its runner and secrets, then push. New projects default to the shared **host** runner label `mini-pc`. Register it once at Gitea user scope for the owner of the projects; it then serves existing and future repositories of that owner. `--runner` overrides the label for another workstation. A label alone does not grant a repository access to a runner. Do not install another runner service for every project. ## Consumer layout and configuration `engineering.lock.json` selects a repository, immutable semantic version tag and full commit SHA. `scripts/engineering.py` is the independent bootstrap, and `generated/` is the committed release snapshot. No sibling checkout or global skill installation is needed for ordinary use. Regeneration uses Git credentials or a Git credential helper; never place credentials in the lockfile URL. ```sh mvn -N -Pregenerate-engineering validate python3 scripts/engineering.py --check mvn -Dformat.mode=check clean verify java generated/ci/Ci.java verify java generated/ci/Ci.java test java generated/ci/Ci.java release-test ``` The Maven regeneration profile skips formatting and works without `generated/`. CI checks regeneration in a temporary checkout before running the pipeline. Added, removed, modified and executable-bit changes fail the check. Runtime `target/` and Python cache output are not release resources. Updates remove obsolete generated files and never overwrite project-owned files outside `generated/`. `engineering.properties` supplies project defaults; environment variables take precedence. Required settings are `PROJECT_NAME` and at least one of `BACKEND_MODULE` / `FRONTEND_MODULE`. Modules must be relative paths within the project. Image names are `-backend` and `-frontend`; changing PROJECT_NAME configures that namespace without changing shared code. `BACKEND_CONTEXT` defaults to `.`, and `FRONTEND_CONTEXT` to its module path. Both Dockerfiles provide a `prebuilt` target packaging verified output and a default standalone source build. `FORMAT_DIRECTORIES` lists comma-separated handwritten Java directories. Backend verification prepares a standards classpath, runs the shared standards checker and requires 80% line coverage. Application waivers remain in `tooling/standards/waivers.tsv`; no waiver is supplied by default. `FRONTEND_TEST_COMMAND` is a JSON argument array, defaulting to the established Vitest JUnit command; the starter sets `["npm","test"]`. Frontend verification runs npm ci, lint, tests, and build. `BROWSER_TESTS=true` enables the recipe's Playwright/JaCoCo integration for a backend and frontend. Configure `CI_READINESS_URL`, `PLAYWRIGHT_IMAGE`, `CI_DATABASE`, `DATABASE_NAME` and `DATABASE_USER` as appropriate. The browser recipe expects backend port 8080, frontend port 8080, a backend JaCoCo agent and CLI prepared by Maven, and npm's `e2e` script. Projects without that integration leave BROWSER_TESTS=false. ## Gitea setup Build workflows need a host runner, `TARGET_PLATFORM` (for example linux/amd64), and `REGISTRY_USER` / `REGISTRY_TOKEN` secrets with the repository/package access needed for registry publication. The runner must also be able to read the pinned engineering repository during the regeneration check. For private engineering, install a read-only credential helper on the runner. Application registry and release tokens are not used as implicit bootstrap credentials. Publication is restricted to main after verification. Immutable commit images are pulled back and checked before moving main tags. A host lock serializes publication; a newer main prevents an older run from moving those tags. Manual deployment and rollback workflows require variables `DEPLOY_HOST`, `DEPLOY_USER`, `DEPLOY_SSH_PORT`, `DEPLOY_DIRECTORY`, `DEPLOY_REGISTRY`, and secrets `RELEASE_TOKEN`, `DEPLOY_SSH_KEY`, `DEPLOY_KNOWN_HOSTS`. Release eligibility requires a successful main-push build.yaml run. Keep build.yaml's filename when adapting the recipe. Configure Gitea access, registry access and SSH host trust explicitly. ### Shared workstation runner Set up the physical mini-pc once, using a consumer generated from this release: ```sh export GITEA_SERVER_URL=http://192.168.2.28:3000 export RUNNER_NAME=mini-pc export RUNNER_LABEL=mini-pc export RUNNER_SCOPE=user # Owner-only PAT file with permissions for the selected runner scope; never a workflow secret. export GITEA_ADMIN_TOKEN_FILE="$HOME/.config/mini-pc-runner/admin-token" java generated/ci/Ci.java configure-runner java generated/ci/Ci.java install-runner systemctl --user status mini-pc-runner ``` User scope uses `/api/v1/user/actions/runners/registration-token`, granting the runner access to that user's current and future repositories. Organization and instance scopes are optional alternatives (`RUNNER_ORGANIZATION` is required for organization scope); instance scope requires a Gitea administrator. Repository scope is available only for intentionally isolated runners. For this landscape, use user scope for `wanjakrah` and the label `mini-pc`. The runner has one slot shared by all projects. Its identity, service and state are based on RUNNER_NAME, independent of PROJECT_NAME: `~/.local/share/mini-pc-runner`, `~/.config/mini-pc-runner` and `mini-pc-runner.service`. Application credentials remain in each application's secrets; never copy deployment credentials into runner defaults. The host runner executes trusted project code as the workstation user; use it for repositories maintained by the trusted owner. A new project's first push needs no new workstation registration. Ensure the workstation is awake and Docker is ready. For each new repository enable Actions, configure its own registry secrets and TARGET_PLATFORM, then verify availability before pushing: ```sh export CI_REPOSITORY=wanjakrah/my-project # Owner PAT with read access to the repository, user profile and user runners. java generated/ci/Ci.java check-runner ``` `configure-gitea` configures the application and does not create runner registrations. `configure-runner` obtains the one-time shared registration token; `install-runner` installs and registers the workstation. `--prepare-only` installs without registering or starting. Installation supports Linux x86_64. `configure-docker` uses INSECURE_REGISTRY only for a deliberately trusted internal HTTP registry. Runner configuration and service templates are under ci/runner/. When migrating a repository-scoped runner, keep the old registration and service available until the new shared runner is online and has run a test workflow for both an existing and a new consumer. Change all workflow labels to `mini-pc`, then stop/disable the old service. Preserve its state outside Git for recovery. Do not change application-specific deployment credential paths as part of this migration. ## Deployment and recovery Projects own deploy/compose.yaml, deploy/health.sh and production.env. Set COMPOSE_PROJECT, DEPENDENCY_SERVICES and REQUIRED_DEPLOY_SECRETS in project configuration. Backend/frontend services use BACKEND_IMAGE / FRONTEND_IMAGE, RELEASE_SHA and RELEASE_VERSION. Missing roles are represented by `-` in release state; at least one image is required. Dependency services are started without recreating their storage. Application containers alone are recreated on releases. The remote mechanism requires a dedicated directory with owner-only production.env containing BIND_IP, FRONTEND_PORT, DOCKER_BIN, DOCKER_CONFIG and DOCKER_SUDO. The bind IP must be private, and the port 1024..65535. Provision a pull-only registry login. `generated/deploy/provision.sh` accepts directory, registry user, registry, bind IP, port and Docker executable. Optional `generated/deploy/enable-docker.sh` takes deployment account and Docker executable and must be run by an administrator after reviewing the Docker permission grant. deploy/health.sh defines `application_health`, returning nonzero until the app is ready. It can use the shared `compose` and `docker_cmd` functions. Health scripts, Compose configuration, digests and deployment settings are retained per release. For pre-S19 retained releases lacking health.sh/settings, the current project health function/settings provide compatibility; verify them before rolling back across this migration. Existing release-state JSON remains compatible. Locking, a pending journal, digest/revision checks, bounded readiness checks and automatic recovery protect replacement. An ambiguous state write or failed recovery retains the journal and lock for manual inspection. Inspect retained state, pending.json, history and diagnostics before repairing a deployment; never blindly delete a lock. Each application must document its own recovery procedure and database-schema rollback limits. Production deployment remains manual. ## Releases and upgrades Release the resources, mechanisms and templates together. Use semantic version tags `vMAJOR.MINOR.PATCH`; never move or reuse a published tag. Breaking changes increase MAJOR. Record required project-file changes in UPGRADING.md before tagging. Consumers update both tag and commit, regenerate, review and commit the diff. The bootstrap rejects a tag resolving to a different commit and never upgrades automatically. Templates create project-owned files once; upgrading only replaces generated/. Bootstrap/profile/workflow changes require deliberate consumer edits. ## Validation Run `python3 -m unittest discover -s tests`. The example definition is examples/recipe-demo.json. Generate it with create-project.py and use the same pipeline as any consumer. `tests/acceptance.py` exercises generated projects and isolated deployment/rollback without touching production. Validation evidence is recorded in VALIDATION.md.