Add install script, release Makefile and self-update

Releases are built locally (no runner) and uploaded to the Gitea generic
package registry. install.sh installs submarine plus a sub symlink;
`submarine update` replaces the binary after verifying its checksum.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 20:01:04 +02:00
co-authored by Claude Sonnet 5.5
parent 6e1236d495
commit 633de5aab1
10 changed files with 619 additions and 1 deletions
+10
View File
@@ -15,6 +15,16 @@ bare commit hash.
| [architecture.md](architecture.md) | Package layout, the provider boundary, async loading, testing |
| [decisions.md](decisions.md) | Decision log with rationale (ADR style) |
## Install
```sh
curl -fsSL https://git.simon-franken.de/simonfranken/submarine/raw/branch/main/install.sh | sh
```
Installs `submarine` and the alias `sub` (linux/macOS, amd64/arm64) to
`~/.local/bin`. Update with `submarine update`. Maintainers publish with
`GITEA_TOKEN=… make release` (see [architecture](architecture.md#distribution)).
## Status
**Prototype.** The UI is complete for browsing. It reads real repositories
+27
View File
@@ -21,6 +21,7 @@ internal/submodule/git/ provider using the git CLI (reads, fetch, watch)
watch.go Watch: polls git metadata for changes
gitdir.go resolves .git dirs and gitdir: files
internal/submodule/mock/ fixture provider with artificial latency
internal/update/ self-update from the package registry; NO UI imports
internal/tui/ Bubble Tea application
app.go root model: layout, message routing, loading
keys.go key bindings (also feed the help view)
@@ -167,6 +168,32 @@ Fetch (network) ──writes refs──▶ .git/… ◀──writes refs──
`ansi.Truncate`, which keeps escape sequences and bubblezone markers intact.
- `zone.Scan` runs once, on the root view.
## Distribution
No CI runner: releases are built and published from a developer machine.
- `make dist` cross-compiles `CGO_ENABLED=0` binaries for linux/darwin ×
amd64/arm64 into `dist/` plus `checksums.txt`; the version comes from
`git describe` and is baked in with `-X main.version`.
- `make release` with `GITEA_TOKEN` set, e.g. in `.env` (copy `.env.template`; token needs `write:package`; HEAD must be a
clean `vX.Y.Z` tag) runs tests, builds and uploads to the Gitea generic
registry `https://git.simon-franken.de/api/packages/simonfranken/generic`:
- `submarine/<version>/submarine_<os>_<arch>` and `checksums.txt`
- `submarine-latest/latest/VERSION`, replaced **last**, so a failed upload
never advertises a broken version.
- `install.sh` (`curl -fsSL …/raw/branch/main/install.sh | sh`) detects
OS/arch, verifies the sha256, installs `submarine` and a `sub` symlink to
`~/.local/bin` (`SUBMARINE_INSTALL_DIR`, `SUBMARINE_VERSION`,
`SUBMARINE_BASE_URL` override).
- `submarine update [--check] [--force]` (package `internal/update`) reads
`VERSION`, downloads the asset for the running platform, verifies the
checksum and atomically replaces the real executable (symlinks resolved,
so `sub` works too). `SUBMARINE_UPDATE_URL` overrides the registry.
- After the TUI exits, a one-line notice is printed if a newer release
exists. The check runs in the background at startup, at most once a day
(cached in the user cache dir) and is off for dev builds, `--no-fetch`,
`--mock` and `SUBMARINE_NO_UPDATE_CHECK=1`.
## Testing
```sh
+15
View File
@@ -183,3 +183,18 @@ once per submodule at a time and coalesce bursts.
**Consequences.** One mechanism to test and trust. After our fetch the
data updates up to one poll interval later; the header shows "fetched just
now" right away.
## D21 — Distribution: local build, Gitea generic registry, curl | sh
**Decision.** Releases are built locally with `make` and uploaded with an
access token to the Gitea generic package registry. Installation is a
POSIX `install.sh`; `sub` is a symlink to `submarine`. Updates are
`submarine update`, driven by a `latest/VERSION` pointer file.
**Rationale.** The Gitea instance has no working runner. The generic
registry needs no extra hosting and is publicly readable. A symlink keeps
one binary to update. The pointer is uploaded last and lives in its own
package so versions stay clean.
**Consequences.** Integrity is a sha256 from the same host: it catches
corrupt or partial downloads, not a compromised registry (no signatures).
The update check is a deliberate, outside-the-provider network use: only
on `update` and a once-a-day background check that can be disabled.