Archived
Compare commits
2
Commits
c9932932bb
...
v0.13.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8133b4884 | ||
|
|
1ee394cfdf |
@@ -1,23 +1,23 @@
|
||||
---
|
||||
name: release
|
||||
description: Prepare and publish a submarine release. Finds the latest release, picks the next semver from the changes since, runs a release check, creates and pushes the git tag, hands over to the developer for `make release`, then creates the Gitea release with MCP. Use only when the user asks to release.
|
||||
description: Prepare and publish a submarine release. Finds the latest release, picks the next semver from the changes since, runs a release check, creates and pushes the git tag, writes the release notes, hands over to the developer for `make release`, then verifies the GitHub release. Use only when the user asks to release.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Release submarine
|
||||
|
||||
A release is built on the developer's machine (`make release`, see
|
||||
`docs/architecture.md#distribution`). `make release` publishes the binaries
|
||||
of the **clean `vX.Y.Z` tag at HEAD** to the Gitea package registry. This
|
||||
skill does everything around it. The developer runs `make release`
|
||||
themselves, in the middle: you never run it and never read or print `.env`
|
||||
or `GITEA_TOKEN`.
|
||||
`docs/architecture.md#distribution`). `make release` creates the GitHub
|
||||
release of the **clean `vX.Y.Z` tag at HEAD**, with the binaries as assets.
|
||||
This skill does everything around it. The developer runs `make release`
|
||||
themselves, in the middle: you never run it and never read or print `.env`,
|
||||
`GH_TOKEN` or `GITEA_TOKEN`.
|
||||
|
||||
Pushing a tag and creating a release are outward-facing and hard to undo.
|
||||
Do them only after the confirmation in step 5, and never force-push, move or
|
||||
delete a tag without asking.
|
||||
|
||||
Gitea: owner `simonfranken`, repo `submarine`, `https://git.simon-franken.de`.
|
||||
GitHub: owner `simonfranken`, repo `submarine`, remote `origin`.
|
||||
|
||||
## 1. Preconditions
|
||||
|
||||
@@ -29,7 +29,7 @@ git branch --show-current # main
|
||||
git status --porcelain # empty: make release refuses a dirty tree
|
||||
git rev-parse HEAD origin/main # equal: HEAD is pushed, nothing to release from a local-only commit
|
||||
git tag --points-at HEAD # empty: HEAD is not released yet
|
||||
grep -q '^GITEA_TOKEN=.\+' .env # exists: only test it, never print it
|
||||
gh auth status >/dev/null 2>&1 || grep -q '^GH_TOKEN=.\+' .env # one holds: only test, never print
|
||||
```
|
||||
|
||||
## 2. Latest release
|
||||
@@ -38,11 +38,12 @@ grep -q '^GITEA_TOKEN=.\+' .env # exists: only test it, never print i
|
||||
git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -1
|
||||
```
|
||||
|
||||
Cross-check with the `mcp__gitea__get_latest_release` MCP tool (load it with
|
||||
ToolSearch first). If the tag and the Gitea release differ (a tag without a
|
||||
Cross-check with the `mcp__github__get_latest_release` MCP tool (load it with
|
||||
ToolSearch first). If the tag and the GitHub release differ (a tag without a
|
||||
release is normal for the one in progress, a release without a tag is not),
|
||||
tell the user and ask before going on. With no tag at all, the first release
|
||||
is `v0.1.0`.
|
||||
is `v0.1.0`. While the Makefile has `release-gitea` (step 10), GitHub has
|
||||
no releases yet: cross-check with `mcp__gitea__get_latest_release` instead.
|
||||
|
||||
## 3. Changes since
|
||||
|
||||
@@ -55,7 +56,7 @@ No commits since the last tag: stop, there is nothing to release. Read the
|
||||
commits, and where a subject is not clear, the diff. Sort each change as:
|
||||
|
||||
- **breaking**: removes or changes a key, a command or flag, the on-disk or
|
||||
registry contract (`install.sh`, asset names, `submarine-latest/latest/VERSION`,
|
||||
release contract (`install.sh`, asset names, `latest/download/VERSION`,
|
||||
`submarine update`), or behavior users rely on;
|
||||
- **feature**: a new key, action, view or option, or a visible behavior;
|
||||
- **fix / other**: bug fixes, docs, refactoring, tests, performance.
|
||||
@@ -108,51 +109,64 @@ git ls-remote --tags origin vX.Y.Z # the tag is on the remote
|
||||
If the push fails, report git's message. Do not retry with force. If the tag
|
||||
is wrong after it was pushed, ask before deleting it.
|
||||
|
||||
## 7. Hand over: the developer runs `make release`
|
||||
## 7. Write the release notes
|
||||
|
||||
Write them to `/tmp/submarine-vX.Y.Z-notes.md` (outside the repository:
|
||||
`make release` refuses a dirty tree). They are for users of the tool, not a
|
||||
commit dump: short, grouped under **Breaking changes** (only if any),
|
||||
**New**, **Fixes** (leave out empty groups and pure refactoring/tests), each
|
||||
item one line naming the key or behavior (`b` switches the branch of a
|
||||
repository...). End with:
|
||||
|
||||
```
|
||||
Install or update: `curl -fsSL https://raw.githubusercontent.com/simonfranken/submarine/main/install.sh | sh`, or `submarine update`.
|
||||
|
||||
Full changelog: https://github.com/simonfranken/submarine/compare/<last>...vX.Y.Z
|
||||
```
|
||||
|
||||
Show the notes to the user; they can change them before the next step.
|
||||
|
||||
## 8. Hand over: the developer runs `make release`
|
||||
|
||||
HEAD is now the clean tag, which is what `make release` needs. Tell the
|
||||
developer to run, in the repository:
|
||||
|
||||
```sh
|
||||
make release
|
||||
make release NOTES=/tmp/submarine-vX.Y.Z-notes.md
|
||||
```
|
||||
|
||||
(it reads `GITEA_TOKEN` from `.env`, tests, builds, uploads, and points
|
||||
`submarine-latest` at the version last). **Stop here and wait** until they
|
||||
say it finished. If it failed, help with the output; do not run it yourself.
|
||||
(it uses `gh`, tests, builds, creates the release as a draft with all
|
||||
assets and publishes it last). **Stop here and wait** until they say it
|
||||
finished. If it failed, help with the output; do not run it yourself. A
|
||||
leftover draft from a failed run must be deleted (ask first) before a retry.
|
||||
|
||||
## 8. Verify the upload, create the Gitea release
|
||||
## 9. Verify the release
|
||||
|
||||
Once the developer reports success:
|
||||
|
||||
```sh
|
||||
curl -fsSL https://github.com/simonfranken/submarine/releases/latest/download/VERSION
|
||||
```
|
||||
|
||||
must print `vX.Y.Z` (this is what `install.sh` and `submarine update`
|
||||
read). Check with `mcp__github__get_release_by_tag` that it is not a draft
|
||||
and has the assets `submarine_{linux,darwin}_{amd64,arm64}`,
|
||||
`checksums.txt` and `VERSION`. If anything is missing, report it. Finish
|
||||
with the link to the release and the version.
|
||||
|
||||
## 10. Bridge release (only while the Makefile has `release-gitea`)
|
||||
|
||||
The first GitHub release is also published to the old Gitea registry, so
|
||||
binaries that still update from Gitea pick it up (D33). After step 9, and
|
||||
after asking the user, push the tag to the `gitea` remote and hand over
|
||||
again:
|
||||
|
||||
```sh
|
||||
git push gitea main vX.Y.Z
|
||||
make release-gitea # run by the developer, needs GITEA_TOKEN
|
||||
curl -fsS https://git.simon-franken.de/api/packages/simonfranken/generic/submarine-latest/latest/VERSION
|
||||
```
|
||||
|
||||
must print `vX.Y.Z` (the registry is public; `install.sh` reads it the same
|
||||
way). If it doesn't, the upload did not complete: do not create the release,
|
||||
report it.
|
||||
|
||||
Then create the release with `mcp__gitea__create_release` (load it with
|
||||
ToolSearch first):
|
||||
|
||||
- `owner`: `simonfranken`, `repo`: `submarine`
|
||||
- `tag_name`: `vX.Y.Z`
|
||||
- `target`: the tag's commit, `git rev-parse vX.Y.Z^{commit}`
|
||||
- `title`: `vX.Y.Z`
|
||||
- `is_draft`: false, `is_pre_release`: false
|
||||
- `body`: release notes, see below
|
||||
|
||||
Release notes are for users of the tool, not a commit dump: short, grouped
|
||||
under **Breaking changes** (only if any), **New**, **Fixes** (leave out empty
|
||||
groups and pure refactoring/tests), each item one line naming the key or
|
||||
behavior (`b` switches the branch of a repository...). End with:
|
||||
|
||||
```
|
||||
Install or update: `curl -fsSL https://git.simon-franken.de/simonfranken/submarine/raw/branch/main/install.sh | sh`, or `submarine update`.
|
||||
|
||||
Full changelog: https://git.simon-franken.de/simonfranken/submarine/compare/<last>...vX.Y.Z
|
||||
```
|
||||
|
||||
The binaries live in the package registry, not on the release: attach
|
||||
nothing. Finish with the link to the release and the version.
|
||||
The last command must print `vX.Y.Z`. Then create a Gitea release for the
|
||||
tag with `mcp__gitea__create_release`, its body saying that submarine moved
|
||||
to https://github.com/simonfranken/submarine.
|
||||
|
||||
+9
-3
@@ -1,8 +1,14 @@
|
||||
# Copy to .env (gitignored); the Makefile loads it automatically.
|
||||
|
||||
# Access token with write:package scope. Required for `make release`.
|
||||
GITEA_TOKEN=
|
||||
# GitHub token for `make release` (gh): classic with the `repo` scope, or
|
||||
# fine-grained with Contents read/write on simonfranken/submarine.
|
||||
GH_TOKEN=
|
||||
|
||||
# Optional overrides (defaults shown).
|
||||
# Optional override (default shown).
|
||||
# GITHUB_REPO=simonfranken/submarine
|
||||
|
||||
# Bridge release only (`make release-gitea`, see docs/decisions.md D33):
|
||||
# Gitea token with the write:package scope. Remove once the bridge is out.
|
||||
GITEA_TOKEN=
|
||||
# GITEA_URL=https://git.simon-franken.de
|
||||
# GITEA_OWNER=simonfranken
|
||||
|
||||
@@ -22,7 +22,7 @@ go run ./cmd/submarine [dir] # run on a repository (default: cwd)
|
||||
go run ./cmd/submarine --mock # run on mock data
|
||||
scripts/make-fixture.sh DIR # build a test superproject in DIR
|
||||
make dist # cross-compile into dist/
|
||||
GITEA_TOKEN=… make release # publish a tagged release (outward-facing; run by the developer)
|
||||
make release NOTES=FILE # publish a tagged release to GitHub (outward-facing; run by the developer)
|
||||
go vet ./...
|
||||
go test ./...
|
||||
gofmt -l . # must print nothing
|
||||
@@ -32,7 +32,7 @@ gofmt -l . # must print nothing
|
||||
|
||||
Use the `release` skill (`/release`, `.claude/skills/release/SKILL.md`): it
|
||||
picks the next semver, runs a check, tags and pushes, asks the developer to
|
||||
run `make release`, then creates the Gitea release. Never run `make release`
|
||||
run `make release`, then verifies the GitHub release. Never run `make release`
|
||||
or push tags outside of it.
|
||||
|
||||
## Rules
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
# Build and publish submarine. Releases are built locally and uploaded to the
|
||||
# Gitea generic package registry (see docs/architecture.md#distribution).
|
||||
# Build and publish submarine. Releases are built locally and uploaded as
|
||||
# GitHub release assets (see docs/architecture.md#distribution).
|
||||
#
|
||||
# make dist cross-compile into dist/
|
||||
# GITEA_TOKEN=... make release test, build and publish the tagged HEAD
|
||||
# make dist cross-compile into dist/
|
||||
# make release NOTES=FILE test, build and publish the tagged HEAD (gh)
|
||||
# GITEA_TOKEN=... make release-gitea bridge only: the same version to the old Gitea registry
|
||||
|
||||
# Settings from .env (see .env.template); `make VAR=...` on the command line wins.
|
||||
-include .env
|
||||
export GITEA_TOKEN
|
||||
export GITEA_TOKEN GH_TOKEN
|
||||
|
||||
GITHUB_REPO ?= simonfranken/submarine
|
||||
RELEASES := https://github.com/$(GITHUB_REPO)/releases
|
||||
|
||||
GITEA_URL ?= https://git.simon-franken.de
|
||||
GITEA_OWNER ?= simonfranken
|
||||
@@ -17,7 +21,7 @@ LDFLAGS := -s -w -X main.version=$(VERSION)
|
||||
PLATFORMS := linux_amd64 linux_arm64 darwin_amd64 darwin_arm64
|
||||
BINARIES := $(addprefix dist/submarine_,$(PLATFORMS))
|
||||
|
||||
.PHONY: build test vet dist release clean FORCE
|
||||
.PHONY: build test vet dist check-tag release release-gitea clean FORCE
|
||||
|
||||
build:
|
||||
go build -ldflags '$(LDFLAGS)' -o submarine ./cmd/submarine
|
||||
@@ -41,11 +45,31 @@ dist/submarine_%: FORCE
|
||||
dist/checksums.txt: $(BINARIES)
|
||||
cd dist && sha256sum $(notdir $(BINARIES)) > checksums.txt
|
||||
|
||||
release:
|
||||
@test -n "$$GITEA_TOKEN" || { echo "GITEA_TOKEN is not set (needs write:package)"; exit 1; }
|
||||
check-tag:
|
||||
@echo '$(VERSION)' | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$$' || \
|
||||
{ echo "VERSION '$(VERSION)' is not a clean tag: tag HEAD as vX.Y.Z with a clean tree"; exit 1; }
|
||||
@test -z "$$(git status --porcelain)" || { echo "working tree is not clean"; exit 1; }
|
||||
|
||||
# A draft gets all assets first and is published last, so releases/latest
|
||||
# never points at a release with missing files.
|
||||
release: check-tag
|
||||
@test -n "$(NOTES)" && test -f "$(NOTES)" || { echo "NOTES=FILE with the release notes is required"; exit 1; }
|
||||
@command -v gh >/dev/null || { echo "gh (GitHub CLI) is required"; exit 1; }
|
||||
@gh auth status >/dev/null 2>&1 || { echo "gh is not logged in: run gh auth login or set GH_TOKEN"; exit 1; }
|
||||
$(MAKE) clean test vet dist VERSION=$(VERSION)
|
||||
printf '%s\n' '$(VERSION)' > dist/VERSION
|
||||
gh release create '$(VERSION)' --repo '$(GITHUB_REPO)' --verify-tag --draft \
|
||||
--title '$(VERSION)' --notes-file '$(NOTES)' $(BINARIES) dist/checksums.txt dist/VERSION
|
||||
gh release edit '$(VERSION)' --repo '$(GITHUB_REPO)' --draft=false --latest
|
||||
@echo "released $(VERSION): $(RELEASES)/tag/$(VERSION)"
|
||||
|
||||
# Bridge release for binaries that still update from Gitea: same version,
|
||||
# old registry layout. Refuses until GitHub serves this version, so a binary
|
||||
# updated from Gitea finds its release on GitHub. Remove after the bridge.
|
||||
release-gitea: check-tag
|
||||
@test -n "$$GITEA_TOKEN" || { echo "GITEA_TOKEN is not set (needs write:package)"; exit 1; }
|
||||
@test "$$(curl -fsSL '$(RELEASES)/latest/download/VERSION' | tr -d '[:space:]')" = '$(VERSION)' || \
|
||||
{ echo "GitHub does not serve $(VERSION) as latest yet: run make release first"; exit 1; }
|
||||
$(MAKE) clean test vet dist VERSION=$(VERSION)
|
||||
@set -e; \
|
||||
for f in $(BINARIES) dist/checksums.txt; do \
|
||||
@@ -59,7 +83,7 @@ release:
|
||||
"$(GITEA_URL)/api/v1/packages/$(GITEA_OWNER)/generic/submarine-latest/latest" >/dev/null || true; \
|
||||
curl -fsS -X PUT -H "Authorization: token $$GITEA_TOKEN" \
|
||||
--upload-file dist/VERSION "$(REGISTRY)/submarine-latest/latest/VERSION" >/dev/null; \
|
||||
echo "released $(VERSION)"
|
||||
echo "released $(VERSION) to Gitea"
|
||||
|
||||
clean:
|
||||
rm -rf dist submarine
|
||||
|
||||
@@ -38,7 +38,7 @@ the questions you actually have:
|
||||
## Install
|
||||
|
||||
```sh
|
||||
curl -fsSL https://git.simon-franken.de/simonfranken/submarine/raw/branch/main/install.sh | sh
|
||||
curl -fsSL https://raw.githubusercontent.com/simonfranken/submarine/main/install.sh | sh
|
||||
```
|
||||
|
||||
Installs `sub` and `submarine` (linux/macOS, amd64/arm64) to `~/.local/bin`.
|
||||
|
||||
@@ -93,7 +93,7 @@ func main() {
|
||||
}
|
||||
|
||||
// latestRelease returns the version of a newer release, or "" if there is
|
||||
// none or the registry can't be reached within a few seconds.
|
||||
// none or GitHub can't be reached within a few seconds.
|
||||
func latestRelease(ctx context.Context) string {
|
||||
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
+2
-2
@@ -20,12 +20,12 @@ bare commit hash.
|
||||
## Install
|
||||
|
||||
```sh
|
||||
curl -fsSL https://git.simon-franken.de/simonfranken/submarine/raw/branch/main/install.sh | sh
|
||||
curl -fsSL https://raw.githubusercontent.com/simonfranken/submarine/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)).
|
||||
`make release NOTES=…` (see [architecture](architecture.md#distribution)).
|
||||
|
||||
## Status
|
||||
|
||||
|
||||
+17
-10
@@ -23,7 +23,7 @@ internal/submodule/git/ provider using the git CLI (reads, fetch, watch)
|
||||
switch.go Branches, SwitchLoss, Switch (stage the tip, restore, attach or fast-forward the branch)
|
||||
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/update/ self-update from GitHub releases; 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)
|
||||
@@ -203,17 +203,24 @@ 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.
|
||||
- `make release NOTES=FILE` (needs `gh` logged in, or `GH_TOKEN` in `.env`,
|
||||
see `.env.template`; HEAD must be a clean `vX.Y.Z` tag pushed to GitHub)
|
||||
runs tests, builds and creates the GitHub release `vX.Y.Z` with the notes
|
||||
and the assets `submarine_<os>_<arch>`, `checksums.txt` and `VERSION`. It
|
||||
is created as a **draft** and published last, so `releases/latest` never
|
||||
points at a release with missing assets. Clients read, without the API
|
||||
(plain redirects, no rate limit), below
|
||||
`https://github.com/simonfranken/submarine/releases`:
|
||||
- `latest/download/VERSION`
|
||||
- `download/<version>/submarine_<os>_<arch>` and `checksums.txt`
|
||||
- Before the move to GitHub, releases lived in the Gitea generic registry
|
||||
(`git.simon-franken.de/api/packages/simonfranken/generic`, see D33). It
|
||||
stays online for old binaries. `make release-gitea` publishes the bridge
|
||||
version there in the old layout and refuses until GitHub serves it.
|
||||
- The `release` skill (`.claude/skills/release/SKILL.md`) drives a release:
|
||||
latest tag → next semver from the commits since → check (`vet`, `test`,
|
||||
`gofmt`, `make dist`) → tag and push → the developer runs `make release` →
|
||||
the skill verifies the registry's `VERSION` and creates the Gitea release
|
||||
with MCP (notes only; the binaries stay in the registry).
|
||||
the skill verifies that `latest/download/VERSION` serves the new 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`,
|
||||
@@ -221,7 +228,7 @@ No CI runner: releases are built and published from a developer machine.
|
||||
- `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.
|
||||
so `sub` works too). `SUBMARINE_UPDATE_URL` overrides the releases root.
|
||||
- On every start the TUI looks for a newer release in the background (3 s
|
||||
timeout, no cache, no blocking). `main` passes `tui.WithUpdateCheck` and
|
||||
the TUI gets the result as a message; the footer then offers `q quit and
|
||||
|
||||
+29
-1
@@ -201,7 +201,8 @@ package so versions stay clean.
|
||||
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 background check at startup that can be disabled (it
|
||||
was once a day; see D27).
|
||||
was once a day; see D27). The registry was replaced by GitHub releases in
|
||||
D33; local builds, `install.sh` and the pointer file stay.
|
||||
|
||||
## Copy path with `c` via OSC 52
|
||||
|
||||
@@ -634,3 +635,30 @@ skipped. Contextual palettes that grey out impossible keys: larger, kept for
|
||||
later (item 10 of the usability plan).
|
||||
**Consequences.** `footer.Keys` drops `help.KeyMap` for `Tools`,
|
||||
`HelpGroups` and `LegendKeys`; the footer renders the full help itself.
|
||||
|
||||
## D33 — Releases move to GitHub, with one bridge release on Gitea
|
||||
|
||||
**Context.** The repository moves from `git.simon-franken.de` (Gitea) to
|
||||
`github.com/simonfranken/submarine`. Installed binaries only know the Gitea
|
||||
registry (D21), and check it on every start (D27).
|
||||
**Decision.**
|
||||
- Releases are GitHub releases with the binaries, `checksums.txt` and
|
||||
`VERSION` as assets. GitHub Packages has no generic registry. Clients read
|
||||
`releases/latest/download/VERSION` and `releases/download/<v>/<asset>`:
|
||||
plain redirects, not the API, so the check on every start is not rate
|
||||
limited and needs no token.
|
||||
- `make release` creates a draft, uploads all assets, then publishes: the
|
||||
draft takes the role of D21's "pointer last".
|
||||
- One bridge version, the first that reads from GitHub, is also published to
|
||||
Gitea in the old layout (`make release-gitea`). Old binaries update to it
|
||||
from Gitea, and from then on update from GitHub. `release-gitea` refuses
|
||||
until GitHub serves the version, so no bridge binary finds GitHub empty.
|
||||
- The Gitea registry stays online with the bridge as its latest version, and
|
||||
the Gitea repository is archived, not deleted: a binary started for the
|
||||
first time in months still finds its way over.
|
||||
**Alternatives.** Keep Gitea and mirror to GitHub: two places to publish
|
||||
to. Have the updater try both hosts: permanent code for a one-time move.
|
||||
**Consequences.** `SUBMARINE_UPDATE_URL` and `SUBMARINE_BASE_URL` now take
|
||||
a releases root, not a registry root. Versions before the bridge exist only
|
||||
on Gitea; `SUBMARINE_VERSION=<old>` with the new `install.sh` doesn't find
|
||||
them. `release-gitea` and the Gitea settings go once the bridge is out.
|
||||
|
||||
+6
-6
@@ -1,14 +1,14 @@
|
||||
#!/bin/sh
|
||||
# Install submarine (and the `sub` alias).
|
||||
# curl -fsSL https://git.simon-franken.de/simonfranken/submarine/raw/branch/main/install.sh | sh
|
||||
# curl -fsSL https://raw.githubusercontent.com/simonfranken/submarine/main/install.sh | sh
|
||||
#
|
||||
# Environment:
|
||||
# SUBMARINE_VERSION install this version (default: latest)
|
||||
# SUBMARINE_INSTALL_DIR target directory (default: ~/.local/bin)
|
||||
# SUBMARINE_BASE_URL registry root (default: the Gitea generic registry)
|
||||
# SUBMARINE_BASE_URL releases root (default: the GitHub releases)
|
||||
set -eu
|
||||
|
||||
base="${SUBMARINE_BASE_URL:-https://git.simon-franken.de/api/packages/simonfranken/generic}"
|
||||
base="${SUBMARINE_BASE_URL:-https://github.com/simonfranken/submarine/releases}"
|
||||
dir="${SUBMARINE_INSTALL_DIR:-$HOME/.local/bin}"
|
||||
|
||||
die() { echo "install: $*" >&2; exit 1; }
|
||||
@@ -35,7 +35,7 @@ fi
|
||||
|
||||
version="${SUBMARINE_VERSION:-}"
|
||||
if [ -z "$version" ]; then
|
||||
version="$(curl -fsSL "$base/submarine-latest/latest/VERSION")" || die "cannot determine the latest version"
|
||||
version="$(curl -fsSL "$base/latest/download/VERSION")" || die "cannot determine the latest version"
|
||||
version="$(printf '%s' "$version" | tr -d '[:space:]')"
|
||||
fi
|
||||
case "$version" in
|
||||
@@ -48,8 +48,8 @@ tmp="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp"' EXIT INT TERM
|
||||
|
||||
echo "Installing submarine $version ($os/$arch) to $dir"
|
||||
curl -fsSL -o "$tmp/$asset" "$base/submarine/$version/$asset" || die "download failed: $asset"
|
||||
curl -fsSL -o "$tmp/checksums.txt" "$base/submarine/$version/checksums.txt" || die "download failed: checksums.txt"
|
||||
curl -fsSL -o "$tmp/$asset" "$base/download/$version/$asset" || die "download failed: $asset"
|
||||
curl -fsSL -o "$tmp/checksums.txt" "$base/download/$version/checksums.txt" || die "download failed: checksums.txt"
|
||||
|
||||
want="$(awk -v a="$asset" '$2 == a || $2 == "*" a { print $1 }' "$tmp/checksums.txt")"
|
||||
[ -n "$want" ] || die "no checksum for $asset"
|
||||
|
||||
+12
-10
@@ -1,5 +1,5 @@
|
||||
// Package update implements self-update from the Gitea generic package
|
||||
// registry. It has no UI imports; the network is only used on explicit calls.
|
||||
// Package update implements self-update from GitHub release assets. It has no
|
||||
// UI imports; the network is only used on explicit calls.
|
||||
package update
|
||||
|
||||
import (
|
||||
@@ -19,16 +19,18 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultBaseURL is the registry root; see docs/architecture.md#distribution.
|
||||
const DefaultBaseURL = "https://git.simon-franken.de/api/packages/simonfranken/generic"
|
||||
// DefaultBaseURL is the releases root; see docs/architecture.md#distribution.
|
||||
// Both paths below it are plain redirects to the assets, not API calls, so
|
||||
// they are not rate limited.
|
||||
const DefaultBaseURL = "https://github.com/simonfranken/submarine/releases"
|
||||
|
||||
// EnvBaseURL overrides the registry root (tests, manual verification).
|
||||
// EnvBaseURL overrides the releases root (tests, manual verification).
|
||||
const EnvBaseURL = "SUBMARINE_UPDATE_URL"
|
||||
|
||||
// maxBinary bounds a download so a bad server cannot fill the disk.
|
||||
const maxBinary = 256 << 20
|
||||
|
||||
// Client talks to the registry.
|
||||
// Client talks to the releases.
|
||||
type Client struct {
|
||||
BaseURL string
|
||||
HTTP *http.Client
|
||||
@@ -36,7 +38,7 @@ type Client struct {
|
||||
GOARCH string
|
||||
}
|
||||
|
||||
// New returns a client for the default (or $SUBMARINE_UPDATE_URL) registry.
|
||||
// New returns a client for the default (or $SUBMARINE_UPDATE_URL) releases.
|
||||
func New() *Client {
|
||||
base := os.Getenv(EnvBaseURL)
|
||||
if base == "" {
|
||||
@@ -52,13 +54,13 @@ func New() *Client {
|
||||
|
||||
// Latest returns the newest released version, e.g. "v0.2.0".
|
||||
func (c *Client) Latest(ctx context.Context) (string, error) {
|
||||
b, err := c.get(ctx, c.BaseURL+"/submarine-latest/latest/VERSION", 1<<10)
|
||||
b, err := c.get(ctx, c.BaseURL+"/latest/download/VERSION", 1<<10)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
v := strings.TrimSpace(string(b))
|
||||
if _, ok := parse(v); !ok {
|
||||
return "", fmt.Errorf("registry returned an invalid version %q", v)
|
||||
return "", fmt.Errorf("releases returned an invalid version %q", v)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
@@ -120,7 +122,7 @@ func (c *Client) Install(ctx context.Context, version string) (string, error) {
|
||||
// InstallTo is Install with an explicit target path.
|
||||
func (c *Client) InstallTo(ctx context.Context, version, target string) error {
|
||||
asset := fmt.Sprintf("submarine_%s_%s", c.GOOS, c.GOARCH)
|
||||
base := c.BaseURL + "/submarine/" + version + "/"
|
||||
base := c.BaseURL + "/download/" + version + "/"
|
||||
|
||||
sums, err := c.get(ctx, base+"checksums.txt", 1<<20)
|
||||
if err != nil {
|
||||
|
||||
@@ -32,16 +32,25 @@ func TestNewer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// registry mimics GitHub releases: every asset URL redirects to storage.
|
||||
func registry(t *testing.T, bin, sum string) *Client {
|
||||
t.Helper()
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/submarine-latest/latest/VERSION", func(w http.ResponseWriter, _ *http.Request) {
|
||||
redirect := func(from, to string) {
|
||||
mux.HandleFunc(from, func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, to, http.StatusFound)
|
||||
})
|
||||
}
|
||||
redirect("/latest/download/VERSION", "/assets/VERSION")
|
||||
redirect("/download/v0.2.0/checksums.txt", "/assets/checksums.txt")
|
||||
redirect("/download/v0.2.0/submarine_linux_arm64", "/assets/submarine_linux_arm64")
|
||||
mux.HandleFunc("/assets/VERSION", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Write([]byte("v0.2.0\n"))
|
||||
})
|
||||
mux.HandleFunc("/submarine/v0.2.0/checksums.txt", func(w http.ResponseWriter, _ *http.Request) {
|
||||
mux.HandleFunc("/assets/checksums.txt", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Write([]byte(sum + " submarine_linux_arm64\n"))
|
||||
})
|
||||
mux.HandleFunc("/submarine/v0.2.0/submarine_linux_arm64", func(w http.ResponseWriter, _ *http.Request) {
|
||||
mux.HandleFunc("/assets/submarine_linux_arm64", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Write([]byte(bin))
|
||||
})
|
||||
srv := httptest.NewServer(mux)
|
||||
|
||||
Reference in New Issue
Block a user