Skip to main content
Suchi uses Conventional Commits, Keep a Changelog, and SemVer. Pre-1.0 releases may break documented interfaces when that produces a cleaner product; the changelog must still explain the impact.

Commits

Use this shape:
Common types are feat, fix, perf, refactor, docs, test, build, and chore. Scopes name the affected area, such as mail, ui, or api. Use ! and a BREAKING CHANGE: footer when a commit breaks a documented interface. Keep subjects brief and bodies focused.

Changelog and versions

User-visible changes update the [Unreleased] section of CHANGELOG.md in the same pull request. Write for operators and call out required migration steps directly. A release normally adds at most one numbered database migration; never rewrite a published migration. For this taxonomy cutover, beta.2 at schema 0002 is the published upgrade baseline. Published 0001/0002 are unchanged; all unreleased mobile and taxonomy changes are consolidated in 0003_taxonomy_foundation.sql. Verify fresh initialization and populated 0002 → 0003 upgrades, restart idempotence, preserved data and failed-upgrade rollback. Intermediate development schemas are not supported upgrade inputs. The rebuild marker must remain the first line of 0003. See Architecture for the runner. Before v1.0, plan a fresh-schema baseline and a final beta upgrade bridge. The final beta must bring supported older archives to the baseline’s schema version. v1.0 can then create that schema directly for a new database, accept an archive already at that version, and direct older archives to the tagged final beta first. Keep schema versions increasing independently of release versions. Remove the old upgrade SQL from the active runner only after testing exact schema equivalence and the complete upgrade path. This transition requires migration-runner support; renaming branches or resetting schema version numbers cannot implement it. Suchi follows SemVer:
  • MAJOR: incompatible stable-interface changes after 1.0
  • MINOR: features and potentially breaking pre-1.0 changes
  • PATCH: fixes and internal changes
Only the current pre-1.0 release is supported.

Release checklist

  1. When the web app changed, rebuild its committed embedded copy and review the generated asset changes:
  2. Move the [Unreleased] entries under the new version and date. Update developmentVersion in distro/cmd/suchi/main.go to the current source version line with a -dev suffix.
  3. Confirm the shipped Microsoft OAuth registration still permits personal and work/school device-code sign-in.
  4. Run the local verification suite:
  5. Manually exercise behavior that depends on external services or accounts, including Microsoft sign-in, configured email providers, and hosted LLMs affected by the release.
  6. Recheck public size, memory, startup, network, and feature claims against the accepted benchmark and the final build.
  7. Commit the release metadata, then create and push a signed annotated tag:
  8. Dispatch the manual release workflow:
    Use PUBLISH=false for an artifacts-only run.
The workflow builds standard and full multi-architecture images, cross-platform binaries, checksums, SBOMs, signatures, and GitHub release notes derived from the changelog. Tag pushes alone do not publish anything. Release notes travel as a workflow artifact into the collector, rather than a multiline job output that GitHub’s secret detection may silently suppress. AnyDoc is compiled once per Linux architecture through the Dockerfile’s anydoc export target. Those binaries and licenses are packaged for standalone downloads. The image job downloads only that run’s AnyDoc artifacts, verifies their checksums, and supplies them as an anydoc named build context for both runtime variants. Docker selects the matching amd64 or arm64 directory; missing or damaged artifacts fail the build instead of triggering recompilation. Plain local Docker builds omit that context and still compile from pinned source. Release builds inject the version and source revision into the Go binary. Settings reads the identity from /api/whoami, showing only the version for tagged production releases. Unversioned builds use the source’s developmentVersion (currently v0.1.0-beta.2-dev) and append the short revision when available. suchi version likewise retains an injected release version and adds the revision for development builds. Docker has no .git directory, so manual builds must pass REVISION to retain source identification and VERSION for a tagged release. Local VCS builds without an injected version use the source version line, not Go’s tag-derived module pseudo-version. Missing development revisions are labeled unavailable, not guessed.

Publishing container images without Actions

GHCR images can be built and pushed directly when Actions is unavailable. Build from the release tag, never from a different branch with the release version injected: the image contents must agree with the Git tag. Set the release coordinates on every build machine:
Check out that exact tag in a detached worktree or fresh clone:
Authenticate with a classic GitHub personal access token carrying write:packages. The login username is the personal account that owns the token, which may differ from the organization in IMAGE:

Build on native architecture hosts

Rust compilation for anydoc is very slow through QEMU. A cancelled cargo build step is not cached because its Docker layer never completes. For a faster multi-architecture release, build AMD64 on an x86-64 Linux host and ARM64 on an Apple Silicon or Linux ARM64 host, push temporary architecture tags, and combine them into the public manifest. On the x86-64 Linux host:
On the native ARM64 host, after checking out the same tag and logging into GHCR:
After both builds are present in GHCR, create the versioned and moving beta manifests:
Confirm that both platform manifests are available:
The first successful native build seeds its architecture’s registry cache. Later local releases import those caches, while the release workflow imports both native caches for runtime layers and maintains its own GitHub Actions cache. Release images obtain AnyDoc from the standalone artifacts described above. For local source builds, the anydoc source commit and build instructions are pinned independently of the Suchi version, so the completed Rust layer is reused until that pin or recipe changes. Cargo registry and target cache mounts also retain incremental work in the local BuildKit builder when a compile is interrupted before its image layer can be exported. The full image follows the same process with --target full and temporary tags ending in -full-amd64 and -full-arm64; publish the final tags as $VERSION-full and beta-full. beta-standard is an explicit alias for the standard beta image and is useful where the selected image variant should be obvious from configuration alone. The first command-line push creates a private, unlinked GHCR package by default. In the package settings, make it public, connect it to the Suchi repository, and grant that repository Actions access so later workflow pushes can update it. Direct local publication does not reproduce the workflow’s keyless Cosign signatures or GitHub release artifacts; handle those separately before calling the release complete.

Automation

  • checks.yml is the single manual entry point for repository validation.
  • ci.yml runs formatting, tests, vet, lint, and UI checks when called by it.
  • smoke.yml tests the built images and ingest paths when called by it. Both variants are rebuilt with the exported AnyDoc context and an unusable Rust base to catch accidental recompilation; binary and license bytes must match the export before the usual ingestion checks run.
  • restore-drill.yml validates backup restoration when called by it.
  • release.yml publishes only when manually dispatched from an existing tag. Its preflight runs fresh Go/UI checks, embedded bundle parity, and dependency advisory scans. Images wait for both binary matrices before publication and reuse the AnyDoc matrix’s verified binaries rather than rebuilding them. Artifacts-only branch runs use snapshot-<commit> names and skip signing and publication; they still produce ordinary workflow artifacts and GHA caches.
Pushes, pull requests, and schedules do not start Actions. A maintainer chooses checks in the Actions tab and clicks Run workflow to run all validation jobs against the selected ref. Publishing remains a separate manual action. For a security hotfix, branch from the affected tag, land the fix and changelog entry, publish the patch release, then merge the fix back into main.