Add self-contained runtime bundle distribution (jlink) for hosts without Java

./gradlew runtimeBundle packs a jlink-trimmed JRE, gittally.jar, and a
launcher script into one tarball, unpacked to ~/opt/gittally on the target
host; init --systemd works from the bundle unchanged because java.home and
the running-jar path resolve into it. Chosen over a GraalVM native image
(Spring AOT evaluates bean conditions at build time, which cannot represent
the dual-context CLI/server wiring) and over a containerized runtime — see
ADR 0006 and docs/plan/15-runtime-bundle-distribution.md, which also records
the full vm2176-to-vm4006 migration walkthrough.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-10 15:35:35 +02:00
co-authored by Claude Fable 5
parent 8b71d0db8e
commit 9e7982ce34
9 changed files with 308 additions and 12 deletions
+1
View File
@@ -74,6 +74,7 @@ All major decisions are in `docs/adrs/`. Run `adr-status` (after `source .envrc`
- **Spring Boot**: 4.0.6 (ADR 0003)
- **Rewrite architecture**: JSON-file persistence behind a repository interface, server-rendered UI with JSON polling, no managed nginx — systemd unit behind the host's reverse proxy (ADR 0004)
- **Managed nginx/TLS**: revises ADR 0004 — an opt-in nginx+certbot container for hosts without a reverse proxy (e.g. Hostsharing), planned as `docs/plan/13-nginx-tls.md` (ADR 0005)
- **Runtime bundle distribution**: `./gradlew runtimeBundle` builds a jlink-trimmed JRE + jar tarball for hosts without a Java runtime; GraalVM native image and a containerized runtime were rejected (ADR 0006)
## Skills
+90
View File
@@ -76,6 +76,96 @@ kotlin {
}
}
// Self-contained runtime bundle for hosts without a Java runtime (plan step 15, ADR 0006):
// a jlink-trimmed JRE plus gittally.jar plus the packaging/gittally launcher, packed as a tarball.
// The JDK module list below was computed from the exploded boot jar via
// jdeps -q --ignore-missing-deps --multi-release 21 --print-module-deps \
// --class-path 'BOOT-INF/lib/*' BOOT-INF/classes BOOT-INF/lib/*.jar
// plus runtime-only modules jdeps cannot detect: java.logging (Tomcat JULI),
// jdk.crypto.ec (TLS ECDHE), jdk.management (extended OS MXBeans), jdk.zipfs (nested jar access).
// Re-check the jdeps output when dependencies change.
val runtimeBundleModules =
listOf(
"java.base",
"java.compiler",
"java.desktop",
"java.instrument",
"java.logging",
"java.management",
"java.naming",
"java.net.http",
"java.prefs",
"java.scripting",
"java.security.jgss",
"java.sql",
"jdk.crypto.ec",
"jdk.jfr",
"jdk.management",
"jdk.unsupported",
"jdk.zipfs",
).joinToString(",")
val runtimeBundle by tasks.registering {
group = "distribution"
description = "Builds the self-contained runtime bundle (jlink JRE + jar + launcher) as a tar.gz"
dependsOn(tasks.bootJar)
val jdkHome = javaToolchains.launcherFor(java.toolchain).map { it.metadata.installationPath.asFile }
val jarFile = tasks.bootJar.flatMap { it.archiveFile }
val launcherFile = layout.projectDirectory.file("packaging/gittally").asFile
val stagingDir =
layout.buildDirectory
.dir("runtime-bundle")
.get()
.asFile
val tarballFile =
layout.buildDirectory
.file("distributions/gittally-runtime-linux-x64.tar.gz")
.get()
.asFile
inputs.files(tasks.bootJar.map { it.outputs.files })
inputs.file(launcherFile)
inputs.property("modules", runtimeBundleModules)
outputs.file(tarballFile)
doLast {
val bundleRoot = stagingDir.resolve("gittally")
bundleRoot.deleteRecursively()
bundleRoot.parentFile.mkdirs()
val jlink = jdkHome.get().resolve("bin/jlink")
val jlinkProcess =
ProcessBuilder(
jlink.absolutePath,
"--add-modules",
runtimeBundleModules,
"--strip-debug",
"--no-header-files",
"--no-man-pages",
"--compress",
"zip-6",
"--output",
bundleRoot.resolve("jre").absolutePath,
).redirectErrorStream(true).start()
val jlinkOutput = jlinkProcess.inputStream.bufferedReader().readText()
check(jlinkProcess.waitFor() == 0) { "jlink failed:\n$jlinkOutput" }
jarFile.get().asFile.copyTo(bundleRoot.resolve("lib/gittally.jar").also { it.parentFile.mkdirs() })
val launcher = launcherFile.copyTo(bundleRoot.resolve("bin/gittally").also { it.parentFile.mkdirs() })
check(launcher.setExecutable(true, false)) { "cannot make $launcher executable" }
tarballFile.parentFile.mkdirs()
// system tar preserves the execute bits of jre/bin/* and jre/lib/jspawnhelper
val tarProcess =
ProcessBuilder("tar", "-czf", tarballFile.absolutePath, "-C", stagingDir.absolutePath, "gittally")
.redirectErrorStream(true)
.start()
val tarOutput = tarProcess.inputStream.bufferedReader().readText()
check(tarProcess.waitFor() == 0) { "tar failed:\n$tarOutput" }
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
@@ -0,0 +1,57 @@
# Self-Contained Runtime Bundle Distribution
**Status:**
- proposed: 2026-08-10
- accepted: 2026-08-10
- rejected: -
- superseded: -
**Decision [accepted]:** GitTally is distributed for hosts without a Java runtime as a self-contained runtime bundle — a jlink-trimmed JRE plus `gittally.jar` plus a launcher script in one tarball, built by `./gradlew runtimeBundle`.
The JAR stays the primary artifact; a GraalVM native image and a containerized GitTally runtime were rejected.
## Context and Problem Statement
GitTally must run on Hostsharing container servers (the primary target, see ADR 0005).
These hosts provide git, Docker, make, and systemd user sessions, but no Java runtime, and nothing may be installed system-wide.
`docs/bootstrapping.md` sketched a containerized GitTally runtime as the future answer; that sketch was never validated against the operational details.
## Considered Options
* jlink runtime bundle (trimmed JRE + jar + launcher, one tarball)
* GraalVM native image (single executable)
* Containerized GitTally runtime (the original `docs/bootstrapping.md` sketch)
### jlink Runtime Bundle
A `jlink`-generated JRE with the pinned module list, the boot jar, and a `bin/gittally` launcher script, packed as `gittally-runtime-linux-x64.tar.gz` (~66 MB) and unpacked to `~/opt/gittally/` on the target host.
Good:
- No production-code changes and plain JVM semantics — no new failure modes.
- git and docker CLIs are used from the host; build worktree paths stay host paths.
- `init --systemd` works unchanged: `java.home` and the running-jar path resolve into the bundle, so the generated unit points at `<bundle>/jre/bin/java` and `<bundle>/lib/gittally.jar` (verified).
- Every JDK 21 ships jlink — no new build-toolchain requirement.
Bad:
- A directory tree, not a single file (still one tarball to copy).
- JVM startup (~1-2 s) instead of native-image startup — irrelevant for a long-running server.
- The jlink image links glibc dynamically: build on glibc ≤ target (Ubuntu 24.04 dev machine: 2.39; vm4006 Debian 13: 2.41 — compatible), same architecture.
### GraalVM Native Image
Rejected because Spring AOT evaluates bean conditions at build time, and GitTally's dual-mode wiring cannot be represented in a single AOT arrangement:
the CLI context runs without web and with `@Profile("!server")` `CliRunner`, while the `server` subcommand starts a second `SpringApplication` with `WebApplicationType.SERVLET` and the `server` profile gating the watcher/metrics/nginx lifecycles.
Whichever profile and web type the AOT processing fixes, the other mode's beans are missing from the binary.
Supporting both would require replacing the profile wiring with runtime guards and collapsing the two context shapes — an invasive rewrite with regression risk for the JVM path, on top of the usual native-image reflection work (Jackson-bound config and persistence classes, picocli).
### Containerized GitTally Runtime
Rejected for operational complexity: the image must bundle git and docker CLIs; the container needs a same-path `$HOME` mount plus a docker-socket mount and uid/gid mapping so that `DockerBuildRunner`'s `--volume $workspace:$workspace` sibling mounts keep working; and the systemd unit must be hand-edited to a `docker run` invocation.
This remains the documented fallback if the runtime bundle ever becomes unworkable.
## Consequences
- `docs/deployment.md` documents the bundle path; the "Future: Docker-based Deployment" section in `docs/bootstrapping.md` is replaced.
- The pinned JDK module list in `build.gradle.kts` must be re-checked (via the documented jdeps command) when dependencies change.
- Deployment and migration for vm4006 follow plan step 15.
+4 -10
View File
@@ -102,17 +102,11 @@ Or, when files already exist:
.gittally.yml already exists — not overwritten
```
## Future: Docker-based Deployment
## Hosts Without a Java Runtime
GitTally is intended to run on Hostsharing Container Server environments, which provide Docker
but no Java runtime. A later development step will add a Docker image distribution where:
- GitTally itself runs as a Docker container (image bundles the JRE + JAR)
- Builds are spawned by mounting the host Docker socket (`/var/run/docker.sock`)
- `init` then optionally generates a `docker-compose.yml`, a secrets env file, and a systemd unit
that starts the Compose stack at boot
Until then, a Java runtime must be available on the host.
GitTally is intended to run on Hostsharing Container Server environments, which provide Docker and git but no Java runtime.
For these hosts, `./gradlew runtimeBundle` builds a self-contained runtime bundle (jlink-trimmed JRE + JAR + launcher) — see [deployment.md](deployment.md) and ADR 0006.
A containerized GitTally runtime was considered and rejected there.
## Next Steps After `init`
+33 -1
View File
@@ -8,7 +8,7 @@ For hosts without one, an opt-in managed nginx/TLS container is available, see [
## Prerequisites
- Linux with systemd.
- Java runtime (JRE 21).
- Java runtime (JRE 21) — or none, when using the self-contained runtime bundle, see [Hosts Without a Java Runtime](#hosts-without-a-java-runtime-runtime-bundle).
- `git` CLI on the `PATH`.
- `docker` CLI on the `PATH`, only if any branch uses `docker.enabled` (see [configuration.md](configuration.md)).
- A checked-out working tree of the repository to watch, with a remote named `origin`.
@@ -124,6 +124,38 @@ sudo certbot --nginx -d ci.example.org
This replaces the legacy script's managed nginx/Let's Encrypt Docker container for hosts that have their own web server.
## Hosts Without a Java Runtime (Runtime Bundle)
Some hosts provide git and Docker but no Java runtime and no way to install one, e.g. Hostsharing container servers.
For these, GitTally ships as a self-contained runtime bundle: a jlink-trimmed JRE, `gittally.jar`, and a launcher script in one tarball (ADR 0006).
Build the bundle on a Linux x86_64 machine whose glibc is not newer than the target's:
```bash
./gradlew runtimeBundle
ls build/distributions/gittally-runtime-linux-x64.tar.gz
```
Copy and unpack it on the target host, by convention to `~/opt/gittally`:
```bash
scp build/distributions/gittally-runtime-linux-x64.tar.gz user@host:/tmp/
ssh user@host 'mkdir -p ~/opt && tar -xzf /tmp/gittally-runtime-linux-x64.tar.gz -C ~/opt'
```
Then use `~/opt/gittally/bin/gittally` wherever this document says `java -jar ~/bin/gittally.jar`:
```bash
cd /path/to/repo
~/opt/gittally/bin/gittally init
~/opt/gittally/bin/gittally init --systemd
```
`init --systemd` detects the bundle automatically: the generated unit's `ExecStart` points at the bundle's `jre/bin/java` and `lib/gittally.jar`, so the install commands printed by `init --systemd` work unchanged.
`JAVA_OPTS` from the environment file applies as usual.
To update GitTally, stop the service, unpack the new bundle over `~/opt/gittally`, and restart the service.
## Hosts Without a Reverse Proxy (Managed nginx/TLS)
Some hosts provide Docker but no root access and no host web server, e.g. Hostsharing managed container environments.
+3
View File
@@ -63,6 +63,9 @@ Old artifacts under the legacy artifact root remain readable on disk until you d
## Manual Migration Steps
When migrating to a **different host**, the legacy instance can keep running in parallel until the new one is verified — then skip step 1 here and stop the legacy service on the old host last.
During parallel operation, give the new instance a distinct `gitea.statusContext`, so the two instances do not overwrite each other's commit statuses in Gitea.
1. Stop and remove the legacy service:
```bash
+107
View File
@@ -0,0 +1,107 @@
# Step 15: Self-Contained Runtime Bundle Distribution
Prerequisites: steps 12, 13.
Read `README.md` first.
This step revises the "Future: Docker-based Deployment" section of `docs/bootstrapping.md` — see the ADR requirement below.
## Goal
Deploy GitTally on hosts that provide Docker and git but no Java runtime (Hostsharing container servers, e.g. `tallyman@vm4006`).
GitTally is distributed as a self-contained runtime bundle: a jlink-trimmed JRE plus `gittally.jar` plus a launcher script, packed as one tarball.
The JAR stays the primary artifact for development and for hosts that already have a JRE.
## Distribution Format Decision (ADR 0006)
Three formats were considered; write ADR 0006 recording the decision and this rationale:
- **jlink runtime bundle (chosen)** — no production-code changes, plain JVM semantics, one tarball to `scp`.
git and docker CLIs are used from the host, worktree paths stay host paths, and the `init --systemd` unit works unchanged because `java.home` and the running-jar path resolve into the bundle.
- **GraalVM native image (rejected)** — Spring AOT evaluates bean conditions at build time.
GitTally's dual-context design (CLI context without web, second `SpringApplication` with the `server` profile and `WebApplicationType.SERVLET`, `@Profile("!server")` `CliRunner`, `@Profile("server")` lifecycles) cannot be represented in a single AOT arrangement.
Supporting it would require replacing the profile wiring with runtime guards and collapsing the two context shapes — an invasive rewrite with regression risk for the JVM path.
- **Containerized GitTally runtime (rejected, was the `docs/bootstrapping.md` sketch)** — needs git and docker CLIs inside the image, a same-path `$HOME` mount plus docker-socket mount and uid/gid mapping so that `DockerBuildRunner`'s `--volume $workspace:$workspace` sibling mounts keep working, and a hand-edited systemd unit.
Kept as the documented fallback if the bundle approach ever becomes unworkable.
## Target Host Facts (verified 2026-08-10)
- `vm4006.hostsharing.net`: Debian 13, x86_64, glibc 2.41, git 2.47.3, docker 26.1.5, GNU make, systemd user session running with `Linger=yes`, no Java runtime.
- The jlink image contains natively linked JVM libs, so it must be built on glibc ≤ 2.41 for the same architecture; the Ubuntu 24.04 dev machine (glibc 2.39, x86_64) qualifies.
For reproducible builds elsewhere, the bundle can be built inside an `eclipse-temurin:21-jdk` container (glibc 2.39 base).
## Design
Gradle:
- Add a `runtimeBundle` task (depends on `bootJar`); the normal `./gradlew build` stays unchanged.
- The task runs `jlink` from the configured Java toolchain (every JDK 21 ships jlink; no new toolchain requirement).
- The JDK module list is pinned in the build script, computed once via `jdeps` on the exploded boot jar and its `BOOT-INF/lib`; document the jdeps command next to the list and re-check it when dependencies change.
- Bundle layout: `gittally/jre/` (jlink image), `gittally/lib/gittally.jar`, `gittally/bin/gittally` (sh launcher: `exec "$DIR/../jre/bin/java" $JAVA_OPTS -jar "$DIR/../lib/gittally.jar" "$@"`).
- Output: `build/distributions/gittally-runtime-linux-x64.tar.gz` with preserved execute permissions.
Deployment (no code changes expected):
- Unpack to `~/opt/gittally/` on the target host; run everything via `~/opt/gittally/bin/gittally`.
- `init --systemd` already generates `ExecStart=<java> $JAVA_OPTS -jar <jar> server` from `java.home` and the running jar path — from the bundle both resolve into `~/opt/gittally/`, so the unit points at the bundle without changes.
Verify this instead of adapting code; adapt only if the resolution fails.
- Updating GitTally = unpack a new bundle over `~/opt/gittally/` (or switch a symlink) and restart the service.
Documentation:
- `docs/deployment.md`: prerequisites become "JRE 21 **or** the runtime bundle"; add a section "Hosts Without a Java Runtime (Runtime Bundle)" with build, `scp`, unpack, and systemd setup.
- `docs/bootstrapping.md`: replace the "Future: Docker-based Deployment" section with the runtime bundle and a pointer to ADR 0006.
- `docs/adrs/0006-…`: the distribution-format decision (see above).
- `docs/migration-from-legacy.md`: add a note that migrating to a different host allows parallel operation, with a distinct `gitea.statusContext` per instance so the two CIs do not overwrite each other's commit statuses.
## Tests
- No production code changes are expected, so no new unit tests; existing tests must stay green.
- Smoke-verify the bundle manually: `bin/gittally --help`, `init` in a scratch repo, `config:print --full`, a short `server` run, and `init --systemd` unit content pointing into the bundle; document the results in this file.
- Verify on vm4006 (which has no Java): copy the bundle, run `bin/gittally --help` and `config:print`; document the results in this file.
## Acceptance Criteria
- `./gradlew ktlintFormat` then `./gradlew build` is green, with unchanged toolchain requirements.
- `./gradlew runtimeBundle` produces a tarball whose `bin/gittally` runs `--help`, `init`, and `server` on a machine without any Java runtime.
- A fresh deployment to vm4006 following `docs/deployment.md` and `docs/migration-from-legacy.md` reaches a running service: web UI reachable, a Docker build succeeds, commit status arrives in Gitea, managed nginx/TLS works (`server.nginx.enabled: true`, DNS for `serverName` pointing at vm4006).
- vm2176 (legacy) keeps running in parallel during the migration; the legacy service is only retired after vm4006 is verified.
- Docs and ADR 0006 written as described; document deviations in this file.
## Result (2026-08-10)
Implemented as designed; no production-code change was needed.
The step was originally drafted for a GraalVM native image; it was re-planned to the jlink bundle after the Spring-AOT build-time condition evaluation turned out to be incompatible with the dual-context CLI/server wiring (see ADR 0006).
- `runtimeBundle` task in `build.gradle.kts` with the pinned module list (jdeps output plus java.logging, jdk.crypto.ec, jdk.management, jdk.zipfs); launcher script in `packaging/gittally`; tarball ~66 MB.
- Smoke tests on the dev machine (with `JAVA_HOME` unset and a stripped `PATH`): `--help`, `init --systemd` in a scratch repo, `config:print --full`, and a `server` run all passed; `/` served HTTP 200 and `/api/branches` returned JSON.
- The `init --systemd` unit generated from the bundle points at `<bundle>/jre/bin/java` and `<bundle>/lib/gittally.jar` as predicted — no detection code needed.
- Verified on vm4006 (no Java installed): bundle unpacked to `~/opt/gittally`, `--version`, `--help`, and `status` in a scratch repo (host git via `GitCommandRunner`) all worked.
Production deployment to vm4006 (2026-08-10, same session):
- `hs.hsadmin.ng` cloned to `~/hs.hsadmin.ng` on vm4006; legacy configuration from vm2176 (repo `.gitTally` + `gitTally.env`) migrated to `.gittally.yml` per `docs/migration-from-legacy.md`; Gitea token moved (the token in vm2176's `gitTally.env` file was stale — the valid one came from the running legacy process environment).
- `statusContext: GitTally@vm4006` for the parallel phase; rename to `GitTally` after vm2176 is retired.
- systemd user service installed via `init --systemd` from the bundle and running; watcher fetches origin branches with the migrated credentials.
- Managed nginx/TLS live: Let's Encrypt certificate for `vm4006.hostsharing.net` obtained, `https://vm4006.hostsharing.net/` serves the UI with a valid chain, HTTP 301s to HTTPS (Hostsharing routes public 80/443 to `httpPort`/`httpsPort`, same as on vm2176).
- Fix discovered during rollout: certbot removed `ssl-dhparams.pem` from its repository, so the first nginx start failed with HTTP 404.
The DH parameters (RFC 7919 ffdhe2048) are now bundled as the classpath resource `nginx/ssl-dhparams.pem` instead of downloaded; the download seam and `NginxConfigFiles.DH_PARAMS_URL` were removed (revises the step-13 note about the download).
Legacy on vm2176 only kept working because its state dir cached the file.
Second fix discovered by the first real build: under a **rootless** Docker daemon, `DockerBuildRunner` ran the build container as `--user <host-uid>` (ported from legacy).
With rootless identity mapping the host user is container root, and the host uid inside the container falls into the subuid range — the container could not create `.gradle` in the freshly created worktree ("Failed to create parent directory").
Legacy on vm2176 only worked because its ownership-repair chown had (unintentionally) moved `build/` and `.gradle/` into subuid ownership on the host (verified: owned by uid 166536 there) — a stable but wrong equilibrium tied to its reused primary checkout.
The rewrite now always runs the build container as `--user 0`: under rootless that IS the unprivileged host user (files stay host-owned, the repair chown degenerates to `0:0`); under rootful daemons the behavior is unchanged (root + chown to the host ids).
Third finding (config, not code): the hsadmin-ng Liquibase migration tests (`LiquibaseCompatibilityIntegrationTest`, `ImportHostingAssets.liquibaseMigrationForBookingAndHosting`) failed on vm4006 with "environment variable HSADMINNG_POSTGRES_ADMIN_USERNAME not set".
These tests run Liquibase programmatically without Spring's `spring.liquibase.parameters`, so the changelog parameters resolve only via Liquibase's env-var substitution; the legacy script had a host-env passthrough for exactly these variables.
The values `admin`/`restricted` are hsadmin-ng's committed defaults, but only for the other execution paths: `.tc-environment` for the documented dev workflow (`. .tc-environment; ./gradlew …`) and the `${…:admin}` fallbacks in `src/test/resources/application.yml` for the Spring-managed Liquibase path.
The programmatic path deliberately has no fallback — `009-check-environment.sql` exists to verify the environment is configured — so a CI environment must export the variables itself.
Fix: `HSADMINNG_POSTGRES_ADMIN_USERNAME=admin` and `HSADMINNG_POSTGRES_RESTRICTED_USERNAME=restricted` in `branches.default.docker.env` on vm4006 — the mechanism `docs/migration-from-legacy.md` prescribes for the legacy passthrough list; alternatively the build command could source `.tc-environment` like the dev workflow.
Verified by running both test classes in the build container with the variables set: green.
Open oddity: the same commit passed on vm2176 although neither its daemon environment, build image, Gradle volume, nor any build-script mechanism supplies these variables there (an unused git-ignored `.environment` file exists in its primary checkout, but nothing in the build reads it); the loading path on vm2176 remains unidentified.
Fourth finding (GitTally limitation, worked around in config): with all tests green, the build then failed in hsadmin-ng's `:prQuickCheck` — "fatal: not a git repository".
GitTally builds in a git worktree whose `.git` is a pointer file into the primary repository's `.git/worktrees/…`, and the Docker build container (deliberately, credentials live under `.git/gittally/`) only mounts the worktree — so build steps that call git fail; the legacy script avoided this by building in the primary checkout.
Workaround: `prQuickCheck` removed from the vm4006 build command — it is a PR quality gate against a base branch and has no meaning in a post-merge master build (on vm2176 it only passed as an accidental no-op).
The underlying question (safe git availability inside Docker build containers without exposing `.git/gittally/` secrets) is left as a follow-up design task.
Still open: green completion of the first real build with its Gitea commit status, and — after a stable parallel phase — retiring the legacy service on vm2176.
+7 -1
View File
@@ -71,10 +71,16 @@ Added after the initial plan (ADR 0005):
Added after the 2026-08-10 overhead measurements on vm2176:
- [ ] `14-build-phase-timing-and-overhead.md` — per-build phase timing, overhead budget warning, ownership/metrics fixes
- [ ] `14-build-phase-timing-and-overhead.md` — per-build phase timing, overhead budget warning, ownership/metrics fixes — deferred until after step 15; revisit relevance on vm4006
Added for the vm2176 → vm4006 migration (2026-08-10):
- [ ] `15-runtime-bundle-distribution.md` — self-contained runtime bundle (jlink JRE + jar) for hosts without a Java runtime
- [ ] `16-git-in-docker-builds.md` — read-only git metadata inside Docker build containers, with `.git/gittally/` masked
Steps 0103 are independent of each other.
Steps 0406 depend on 0103.
Steps 0709 depend on 0406.
Steps 11 and 12 are optional/deferrable; 10 only needs 0406.
Step 13 depends on 07, 11, and 12.
Step 15 depends on 12 and 13 and revises the containerized-runtime sketch in `docs/bootstrapping.md` (ADR 0006 is written as part of the step; GraalVM native image was evaluated and rejected there).
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
# Launcher for the self-contained GitTally runtime bundle (jlink JRE + jar).
# Built by `./gradlew runtimeBundle`; see docs/deployment.md.
DIR=$(CDPATH='' cd -- "$(dirname -- "$(readlink -f -- "$0")")" && pwd)
# shellcheck disable=SC2086 # JAVA_OPTS is intentionally word-split
exec "$DIR/../jre/bin/java" $JAVA_OPTS -jar "$DIR/../lib/gittally.jar" "$@"