S19: establish engineering repository

This commit is contained in:
2026-11-08 15:15:50 +01:00
commit c25ce11a1b
84 changed files with 6599 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
name: Build, test and publish
on:
push:
branches: ['**']
jobs:
verify:
runs-on: @RUNNER@
env:
CI: 'true'
TARGET_PLATFORM: ${{ vars.TARGET_PLATFORM }}
GITEA_SERVER_URL: ${{ gitea.server_url }}
CI_REPOSITORY: ${{ gitea.repository }}
CI_COMMIT: ${{ gitea.sha }}
CI_REF: ${{ gitea.ref }}
CI_RUN_ID: ${{ gitea.run_id }}
steps:
- name: Check out pushed commit
uses: https://github.com/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
- name: Verify pinned engineering resources
run: python3 scripts/engineering.py --check
- name: Identify workflow attempt
# Gitea 1.25 does not populate gitea.run_attempt. Use a unique value for
# container names and diagnostic artifacts, including reruns of one SHA.
run: java generated/ci/Ci.java attempt
- name: Build and test both images
run: java generated/ci/Ci.java verify
- name: Publish verified main images
if: success() && gitea.ref == 'refs/heads/main'
env:
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: java generated/ci/Ci.java publish
- name: Collect diagnostics and clean up test containers
if: always()
run: java generated/ci/Ci.java cleanup
- name: Retain diagnostics for fourteen days
if: always()
uses: https://github.com/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5
with:
name: diagnostics-${{ gitea.run_id }}-${{ env.CI_RUN_ATTEMPT }}
path: ci/artifacts/
if-no-files-found: error
retention-days: 14

View File

@@ -0,0 +1,70 @@
name: Deploy production
on:
workflow_dispatch:
inputs:
mode:
description: 'Release mode'
required: true
type: choice
default: new
options:
- new
- retry
increment:
description: 'New release only: semantic version increment (first release is v1.0.0)'
required: true
type: choice
default: patch
options:
- patch
- minor
- major
version:
description: 'Retry only: existing version tag, e.g. v1.2.3; leave empty for new release'
required: false
type: string
# Gitea 1.25 ignores concurrency. The helper acquires a NAS-wide mkdir lock.
jobs:
deploy:
if: gitea.ref == 'refs/heads/main'
runs-on: @RUNNER@
env:
CI: 'true'
GITEA_SERVER_URL: ${{ gitea.server_url }}
CI_REPOSITORY: ${{ gitea.repository }}
CI_COMMIT: ${{ gitea.sha }}
CI_REF: ${{ gitea.ref }}
CI_EVENT: ${{ gitea.event_name }}
CI_RUN_ID: ${{ gitea.run_id }}
steps:
- name: Check out trusted main workflow revision
uses: https://github.com/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
- name: Verify pinned engineering resources
run: python3 scripts/engineering.py --check
- name: Identify workflow attempt
run: java generated/ci/Ci.java attempt
- name: Release verified images
env:
RELEASE_MODE: ${{ inputs.mode }}
RELEASE_INCREMENT: ${{ inputs.increment }}
RELEASE_VERSION: ${{ inputs.version }}
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
DEPLOY_HOST: ${{ vars.DEPLOY_HOST }}
DEPLOY_USER: ${{ vars.DEPLOY_USER }}
DEPLOY_SSH_PORT: ${{ vars.DEPLOY_SSH_PORT }}
DEPLOY_DIRECTORY: ${{ vars.DEPLOY_DIRECTORY }}
DEPLOY_REGISTRY: ${{ vars.DEPLOY_REGISTRY }}
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
DEPLOY_KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
run: java generated/ci/Ci.java deploy
- name: Retain deployment diagnostics for fourteen days
if: always()
uses: https://github.com/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5
with:
name: deployment-${{ gitea.run_id }}-${{ env.CI_RUN_ATTEMPT }}
path: ci/artifacts/deployment/
if-no-files-found: warn
retention-days: 14

View File

@@ -0,0 +1,44 @@
name: Rollback production
on:
workflow_dispatch:
jobs:
rollback:
if: gitea.ref == 'refs/heads/main'
runs-on: @RUNNER@
env:
CI: 'true'
GITEA_SERVER_URL: ${{ gitea.server_url }}
CI_REPOSITORY: ${{ gitea.repository }}
CI_COMMIT: ${{ gitea.sha }}
CI_REF: ${{ gitea.ref }}
CI_EVENT: ${{ gitea.event_name }}
CI_RUN_ID: ${{ gitea.run_id }}
steps:
- name: Check out trusted main workflow revision
uses: https://github.com/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
- name: Verify pinned engineering resources
run: python3 scripts/engineering.py --check
- name: Identify workflow attempt
run: java generated/ci/Ci.java attempt
- name: Restore previous successful release
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
DEPLOY_HOST: ${{ vars.DEPLOY_HOST }}
DEPLOY_USER: ${{ vars.DEPLOY_USER }}
DEPLOY_SSH_PORT: ${{ vars.DEPLOY_SSH_PORT }}
DEPLOY_DIRECTORY: ${{ vars.DEPLOY_DIRECTORY }}
DEPLOY_REGISTRY: ${{ vars.DEPLOY_REGISTRY }}
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
DEPLOY_KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
run: java generated/ci/Ci.java rollback
- name: Retain deployment diagnostics for fourteen days
if: always()
uses: https://github.com/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5
with:
name: rollback-${{ gitea.run_id }}-${{ env.CI_RUN_ATTEMPT }}
path: ci/artifacts/deployment/
if-no-files-found: warn
retention-days: 14

6
templates/common/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
target
**/node_modules/
**/dist/
**/__pycache__/
ci/artifacts/
.env

View File

@@ -0,0 +1,8 @@
# Project guidance
Read `generated/CODING_STANDARDS.md` before changing Java.
For reviews use `generated/skills/code-review/SKILL.md` with
`generated/docs/backend-review.md`.
Specifications and ADRs: @LANDSCAPE@.
Do not edit generated resources; use `mvn -N -Pregenerate-engineering validate`.
See README.md for setup and engineering upgrades. Skills are project-local.

View File

@@ -0,0 +1,20 @@
# @PROJECT@
Generated from the version recorded in engineering.lock.json.
Use JDK 25, Maven, Python 3, Git, Docker with Compose, and Node/npm for a frontend.
Run `mvn verify` for a backend and `java generated/ci/Ci.java verify` for the full recipe.
Local builds use committed generated files. Run
`mvn -N -Pregenerate-engineering validate` to regenerate, or
`python3 scripts/engineering.py --check` to check drift.
Edit engineering.properties for module paths, images (derived from PROJECT_NAME),
deployment project, required secrets and dependencies. Edit deploy/health.sh for
application readiness. Configure the workflow runner label and Gitea variables
and secrets as described in the engineering README. Repository creation and
secret installation are manual setup steps; the generator creates local files.
ADRs, specifications and onboarding: @LANDSCAPE@.
Engineering usage and upgrades: @ENGINEERING@.
Update engineering.lock.json's tag and commit together, regenerate and review
the diff and that release's upgrade instructions. Project-owned files outside
generated/ are never overwritten by an upgrade.

View File

@@ -0,0 +1,11 @@
log:
level: info
runner:
file: .runner
capacity: 1
timeout: 2h
shutdown_timeout: 2m
labels:
- @RUNNER@:host
cache:
enabled: false

View File

@@ -0,0 +1,3 @@
{
"insecure-registries": ["192.168.2.28:3000"]
}

View File

@@ -0,0 +1,9 @@
# Desktop-specific override: start after graphical login, alongside Docker Desktop.
[Unit]
Requires=docker-desktop.service
After=graphical-session.target docker-desktop.service
PartOf=graphical-session.target docker-desktop.service
[Install]
WantedBy=
WantedBy=graphical-session.target

View File

@@ -0,0 +1,19 @@
[Unit]
Description=@PROJECT@ Gitea Actions runner
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=%h/.local/share/@PROJECT@-runner
EnvironmentFile=%h/.config/@PROJECT@-runner/environment
ExecStartPre=/usr/bin/env java %h/.local/share/@PROJECT@-runner/WaitForDocker.java 120
TimeoutStartSec=150
ExecStart=%h/.local/bin/act_runner daemon --config %h/.local/share/@PROJECT@-runner/config.yaml
Restart=on-failure
RestartSec=10
KillSignal=SIGTERM
TimeoutStopSec=150
[Install]
WantedBy=default.target

View File

@@ -0,0 +1,4 @@
# Sourced by the shared deployment mechanism, retained with each release.
application_health() {
curl --noproxy '*' --fail --silent --show-error --max-time 5 "http://$BIND_IP:$FRONTEND_PORT/" -o /dev/null
}

View File

@@ -0,0 +1,6 @@
# Copy to the deployment directory as production.env, chmod 600; never commit real secrets.
BIND_IP=127.0.0.1
FRONTEND_PORT=8088
DOCKER_BIN=/usr/bin/docker
DOCKER_SUDO=0
DOCKER_CONFIG=/absolute/deployment/path/docker-auth

177
templates/common/pom.xml Normal file
View File

@@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example.@PROJECT@</groupId>
<artifactId>@PROJECT@</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>@PROJECT@</name>
<description>Generated engineering starter</description>
<modules>@MODULES@</modules>
<!-- TODO Add SCM ??? -->
<properties>
<maven.compiler.release>25</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.version>3.40.1</quarkus.platform.version>
<format.mode>apply</format.mode>
<format.skip>false</format.skip>
<palantir-java-format.version>2.101.0</palantir-java-format.version>
<rewrite-core.version>8.63.4</rewrite-core.version>
<ortools-java.version>9.15.6755</ortools-java.version>
<mapstruct.version>1.6.3</mapstruct.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
<lombok.version>1.18.48</lombok.version>
<!-- test dependencies -->
<archunit.version>1.5.1</archunit.version>
<datafaker.version>2.7.0</datafaker.version>
</properties>
<!-- dependencies that can be included by child-project - without stating the version -->
<dependencyManagement><dependencies><dependency>
<groupId>io.quarkus.platform</groupId><artifactId>quarkus-bom</artifactId><version>${quarkus.platform.version}</version><type>pom</type><scope>import</scope>
</dependency></dependencies></dependencyManagement>
<!-- dependencies that will always be included by the child-project -->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.6.3</version>
<inherited>false</inherited>
<executions>
<execution>
<id>format</id>
<phase>validate</phase>
<goals><goal>exec</goal></goals>
</execution>
</executions>
<configuration>
<skip>${format.skip}</skip>
<executable>${java.home}/bin/java</executable>
<includeProjectDependencies>false</includeProjectDependencies>
<includePluginDependencies>true</includePluginDependencies>
<arguments>
<argument>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</argument>
<argument>--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</argument>
<argument>--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</argument>
<argument>--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</argument>
<argument>--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</argument>
<argument>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</argument>
<argument>--class-path</argument><classpath/>
<argument>${maven.multiModuleProjectDirectory}/generated/tooling/format/Format.java</argument>
<argument>${maven.multiModuleProjectDirectory}</argument>
<argument>${format.mode}</argument>
</arguments>
</configuration>
<dependencies>
<dependency>
<groupId>com.palantir.javaformat</groupId>
<artifactId>palantir-java-format</artifactId>
<version>${palantir-java-format.version}</version>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-core</artifactId>
<version>${rewrite-core.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.16.0</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemPropertyVariables>
<includes>
<include>**/Test*.java</include>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
<include>**/*TestCase.java</include>
<include>**/*Sit.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.5.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.2.0</version>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>regenerate-engineering</id>
<properties><format.skip>true</format.skip></properties>
<build><plugins><plugin>
<groupId>org.codehaus.mojo</groupId><artifactId>exec-maven-plugin</artifactId><version>3.6.3</version>
<inherited>false</inherited>
<executions><execution><id>regenerate-engineering</id><phase>validate</phase><goals><goal>exec</goal></goals>
<configuration combine.self="override"><executable>python3</executable>
<arguments><argument>${maven.multiModuleProjectDirectory}/scripts/engineering.py</argument></arguments>
</configuration>
</execution></executions>
</plugin></plugins></build>
</profile>
</profiles>
</project>