10 Commits
Author SHA1 Message Date
jcoffey-dev 42900ae1d5 Merge pull request 'Point links at git.coffeylabs.org after the move from GitHub' (#5) from fix/links-after-move into main
ci / test (push) Successful in 2m34s
ci / release (push) Skipped
2026-09-22 16:11:59 +00:00
jcoffey-dev f1aff4ba07 Point links at git.coffeylabs.org after the move from GitHub
ci / test (pull_request) Successful in 2m4s
ci / release (pull_request) Skipped
GitHub took the organization's repos and GHCR offline on 2026-09-20. Repo,
release, raw-file and clone links now go to Gitea at git.coffeylabs.org,
container images to registry.coffeylabs.org, and GitLab-style /-/blob paths
to Gitea's /src/branch form. Go module paths are identifiers and stay as
they are; links to GitHub issues and pull requests are left as history.
2026-09-22 09:08:15 -07:00
jcoffey-dev f5478cb50e Merge pull request 'ci: run socket-free jobs on the light label' (#4) from ci/light-runner into main
ci / test (push) Successful in 1m0s
ci / release (push) Skipped
2026-09-22 13:52:18 +00:00
jcoffey-dev f45ef16d98 ci: run socket-free jobs on the light label
ci / test (pull_request) Successful in 1m8s
ci / release (pull_request) Skipped
Both runners carry `light` (host1, and host2 over the wg-hosts link), so
these jobs run on whichever host is free. Jobs that mount the docker socket
keep `runs-on: docker`, which only host1 has.
2026-09-22 06:45:29 -07:00
jcoffey-dev 139f7e73da Merge branch 'ci/gitea-actions' into 'main'
ci / test (push) Successful in 4m6s
ci / release (push) Skipped
ci: add Gitea Actions workflow

See merge request coffey-labs/stalwart-migrator!3
2026-09-21 23:22:30 -07:00
jcoffey-dev 8ba5d58f76 ci: add Gitea Actions workflow ported from .gitlab-ci.yml
ci / test (pull_request) Successful in 2m48s
ci / release (pull_request) Skipped
ci / test (push) Successful in 3m4s
ci / release (push) Failing after 1m59s
2026-09-21 22:44:22 -07:00
jcoffey-dev 5760eed824 Merge branch 'ci/release-permalink' into 'main'
Give releases a stable latest-download URL

See merge request coffey-labs/stalwart-migrator!2
2026-09-20 21:15:20 -07:00
jcoffey-dev c4e496d3e1 Give releases a stable latest-download URL
The install guide tells people to curl
  .../releases/latest/download/<file>
which is a GitHub URL shape. GitLab's equivalent is
  /-/releases/permalink/latest/downloads/<path>
but it only resolves for assets that declare direct_asset_path, and the
release job was creating plain links to the package registry. Those carry
the tag in the URL, so they can never be a "latest" link.

Each asset now also declares /binaries/<file>, which is what the docs will
point at. The path is load-bearing: changing it breaks a documented install
command.
2026-09-20 20:58:31 -07:00
jcoffey-dev a83dc51158 Merge branch 'ci/gitlab-pipeline' into 'main'
Run CI on the self-hosted GitLab

See merge request coffey-labs/stalwart-migrator!1
2026-09-20 20:36:05 -07:00
jcoffey-dev 7b07bd6bbb Run releases on the self-hosted GitLab
Ports .github/workflows/release.yml after the GitHub account was suspended.
Same shape: tag-driven, amd64 and arm64, SOURCE_DATE_EPOCH for reproducible
tarballs, and the same refusal to release a tag that is not an ancestor of
the default branch.

The publishing half had to change. There is no `gh release`, so the
tarballs go to the project's generic package registry and the Release is
created with release-cli pointing at them. The install guide sends people
straight at release asset URLs, so uploading before creating the Release is
deliberate: a Release whose assets 404 is worse than no Release.

The Actions workflow stays in the tree as the reference.
2026-09-20 20:12:41 -07:00
4 changed files with 209 additions and 5 deletions
+96
View File
@@ -0,0 +1,96 @@
# CI on the self-hosted Gitea, ported from .gitlab-ci.yml during the move off
# GitLab (2026-09-22). Gitea reads .gitea/workflows and ignores .github/ once
# this directory exists; .github/workflows stays as it was for GitHub.
#
# Every job runs in an image pinned by digest (tag in the trailing comment),
# and the only action used is coffey-labs/actions/checkout pinned by SHA. The
# instance resolves short `uses:` against itself, never GitHub, so nothing
# unreviewed can be pulled in.
#
# The shape is the same as before -- tag-driven, amd64 and arm64,
# reproducible. GitLab needed a generic package registry plus release-cli
# links; Gitea attaches the tarballs to the Release itself, as GitHub did, so
# the build and the release are one job and nothing is handed between jobs.
name: ci
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: light
container:
image: golang:1.26-bookworm@sha256:a688600ca24f8a4d3ca77f95b0dd40704a9fc787c826660eb7ba0b641b8b175d # 1.26-bookworm
steps:
- uses: coffey-labs/actions/checkout@fab0c4d45e0162963965f1555df27b7bed5e20ec
- run: go vet ./...
- run: go test ./...
# Kept as `go run ...@latest` exactly as the workflow had it: the point
# of a vulnerability check is to use today's database, not a pinned copy
# of last month's.
- run: go run golang.org/x/vuln/cmd/govulncheck@latest ./...
release:
if: startsWith(github.ref, 'refs/tags/')
needs: [test]
runs-on: light
container:
image: golang:1.26-bookworm@sha256:a688600ca24f8a4d3ca77f95b0dd40704a9fc787c826660eb7ba0b641b8b175d # 1.26-bookworm
steps:
# Full history: the ancestry check below cannot be answered from a
# shallow clone. The checkout also fetches every branch as origin/*.
- uses: coffey-labs/actions/checkout@fab0c4d45e0162963965f1555df27b7bed5e20ec
with:
fetch-depth: 0
# The workflow refused to release a tag that is not an ancestor of main,
# so that a release can never describe code that was never reviewed onto
# the default branch.
- shell: bash
env:
TAG: ${{ github.ref_name }}
run: |
git merge-base --is-ancestor "$(git rev-parse "${TAG}^{commit}")" origin/main \
|| { echo "!! $TAG is not on main"; exit 1; }
# SOURCE_DATE_EPOCH is what makes the tarballs reproducible: without it
# every build stamps a new mtime and two builds of one tag differ.
- shell: bash
env:
TAG: ${{ github.ref_name }}
run: |
SOURCE_DATE_EPOCH="$(git log -1 --format=%ct "$TAG")" scripts/build-release.sh "$TAG" dist
sha256sum dist/*.tar.gz
# Create the Release, then attach every file. Archive names carry no
# version, so /releases/latest/download/<name> always means the newest.
# The API is reached on the internal address so the uploads never cross
# Cloudflare. If an upload fails the half-made Release is deleted: a
# Release whose assets 404 is worse than no Release, since the install
# guide sends people straight at these URLs.
- shell: bash
env:
TAG: ${{ github.ref_name }}
TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
# CI_SERVER_INTERNAL is set on every job container by the runner.
API="$CI_SERVER_INTERNAL/api/v1/repos/$REPO"
auth=(--header "Authorization: token $TOKEN")
id=$(curl --fail --silent --show-error "${auth[@]}" \
--header "Content-Type: application/json" \
--data "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"body\":\"Binaries for linux/amd64 and linux/arm64. Verify with SHA256SUMS.\"}" \
"$API/releases" | grep -o '^{"id":[0-9]*' | cut -d: -f2)
[ -n "$id" ] || { echo "!! could not create the release"; exit 1; }
for f in dist/*; do
n=$(basename "$f")
echo "uploading $n"
curl --fail --silent --show-error --output /dev/null "${auth[@]}" \
--form "attachment=@$f" "$API/releases/$id/assets?name=$n" \
|| { curl --silent "${auth[@]}" -X DELETE "$API/releases/$id"; exit 1; }
done
+108
View File
@@ -0,0 +1,108 @@
# CI on the self-hosted GitLab, ported from .github/workflows/release.yml when
# the GitHub account was suspended on 2026-09-20. The Actions file stays in the
# tree: it is the reference this was written from and works unchanged if the
# appeal succeeds.
#
# The shape is the same -- tag-driven, amd64 and arm64, reproducible -- but the
# publishing half is necessarily different. There is no `gh release`, so the
# tarballs go to this project's generic package registry and the Release is
# created with release-cli, linking to them. The docs guide installs from
# release assets, so those links are the part that has to keep working.
#
# Images are pinned by digest, with the tag in the trailing comment: the
# replacement for the workflow's SHA-pinned actions, since GitLab has no
# action allowlist.
stages: [test, build, release]
variables:
PKG: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/stalwart-migrate"
default:
interruptible: true
.go: &go
image: golang:1.26-bookworm@sha256:a688600ca24f8a4d3ca77f95b0dd40704a9fc787c826660eb7ba0b641b8b175d # 1.26-bookworm
cache:
key: go-mod
paths: [.gocache/]
variables:
GOPATH: "$CI_PROJECT_DIR/.gocache"
test:
<<: *go
stage: test
script:
- go vet ./...
- go test ./...
# Kept as `go run ...@latest` exactly as the workflow had it: the point of
# a vulnerability check is to use today's database, not a pinned copy of
# last month's.
- go run golang.org/x/vuln/cmd/govulncheck@latest ./...
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
build:
<<: *go
stage: build
needs: [test]
script:
# The workflow refused to release a tag that is not an ancestor of main,
# so that a release can never describe code that was never reviewed onto
# the default branch. GIT_DEPTH is unset below to make the ancestry
# available -- a shallow clone cannot answer this.
- git fetch --quiet origin "$CI_DEFAULT_BRANCH"
- |
git merge-base --is-ancestor "$(git rev-parse "${CI_COMMIT_TAG}^{commit}")" "origin/$CI_DEFAULT_BRANCH" \
|| { echo "!! $CI_COMMIT_TAG is not on $CI_DEFAULT_BRANCH"; exit 1; }
# SOURCE_DATE_EPOCH is what makes the tarballs reproducible: without it
# every build stamps a new mtime and two builds of one tag differ.
- SOURCE_DATE_EPOCH="$(git log -1 --format=%ct "$CI_COMMIT_TAG")" scripts/build-release.sh "$CI_COMMIT_TAG" dist
- sha256sum dist/*.tar.gz
variables:
GIT_DEPTH: "0"
artifacts:
paths: [dist/]
expire_in: 1 week
rules:
- if: $CI_COMMIT_TAG
release:
stage: release
image: registry.gitlab.com/gitlab-org/cli:latest@sha256:3f0a591b3b96c39ac8e28480ee99bb93201b7bcea1fbca7ed50c034098111db2 # latest
needs: [build]
script:
# Upload first, then create the Release pointing at what was uploaded. A
# Release whose assets 404 is worse than no Release: the install guide
# sends people straight at these URLs.
- |
set -eu
for f in dist/*; do
n=$(basename "$f")
echo "uploading $n"
curl --fail --silent --show-error \
--header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
--upload-file "$f" \
"${PKG}/${CI_COMMIT_TAG}/${n}"
done
- |
set -eu
args=""
for f in dist/*; do
n=$(basename "$f")
# direct_asset_path is what makes the permalink work. Without it the
# only stable URL is the package registry one, which carries the tag
# and so cannot be a "latest" link. With it, every release exposes
# /-/releases/permalink/latest/downloads/binaries/<file>
# which is the GitLab equivalent of the GitHub
# /releases/latest/download/<file> URL the install guide has always
# used. Changing this path breaks documented install commands.
args="$args --assets-link {\"name\":\"${n}\",\"url\":\"${PKG}/${CI_COMMIT_TAG}/${n}\",\"direct_asset_path\":\"/binaries/${n}\"}"
done
# shellcheck disable=SC2086
release-cli create --name "$CI_COMMIT_TAG" --tag-name "$CI_COMMIT_TAG" \
--description "Binaries for linux/amd64 and linux/arm64. Verify with SHA256SUMS." $args
rules:
- if: $CI_COMMIT_TAG
+1 -1
View File
@@ -27,7 +27,7 @@ write access to the checkpoint directory.
Releases are tagged by date, like ihasmail's: `v2026.9.15`, with `.1`, `.2` Releases are tagged by date, like ihasmail's: `v2026.9.15`, with `.1`, `.2`
added for another release the same day. Binaries for `linux/amd64` and added for another release the same day. Binaries for `linux/amd64` and
`linux/arm64` and a `SHA256SUMS` file are attached to every `linux/arm64` and a `SHA256SUMS` file are attached to every
[release](https://github.com/Coffey-Labs/stalwart-migrator/releases). [release](https://git.coffeylabs.org/coffey-labs/stalwart-migrator/releases).
Every release is built by the [release workflow](.github/workflows/release.yml) Every release is built by the [release workflow](.github/workflows/release.yml)
from a tagged commit on `main`, after the tests and a known-vulnerabilities from a tagged commit on `main`, after the tests and a known-vulnerabilities
+4 -4
View File
@@ -1,6 +1,6 @@
# stalwart-migrator # stalwart-migrator
[![Latest release](https://img.shields.io/github/v/release/Coffey-Labs/stalwart-migrator?sort=date)](https://github.com/Coffey-Labs/stalwart-migrator/releases/latest) [![Latest release](https://img.shields.io/gitea/v/release/coffey-labs/stalwart-migrator?gitea_url=https%3A%2F%2Fgit.coffeylabs.org)](https://git.coffeylabs.org/coffey-labs/stalwart-migrator/releases/latest)
[![License: GPL-3.0-or-later](https://img.shields.io/badge/license-GPL--3.0--or--later-blue)](LICENSE) [![License: GPL-3.0-or-later](https://img.shields.io/badge/license-GPL--3.0--or--later-blue)](LICENSE)
[![Docs: docs.ihasmail.org](https://img.shields.io/badge/docs-docs.ihasmail.org-0ea5e9)](https://docs.ihasmail.org/install/stalwart-migrator/) [![Docs: docs.ihasmail.org](https://img.shields.io/badge/docs-docs.ihasmail.org-0ea5e9)](https://docs.ihasmail.org/install/stalwart-migrator/)
@@ -9,7 +9,7 @@ checkpoint at every step so an interrupted run resumes instead of restarting,
and automated validation that the server still works afterwards. Go, standard and automated validation that the server still works afterwards. Go, standard
library only. library only.
A companion to [**ihasmail**](https://github.com/Coffey-Labs/ihasmail), a A companion to [**ihasmail**](https://git.coffeylabs.org/coffey-labs/ihasmail), a
JMAP-first webmail client for Stalwart. That one is what you read your mail in; JMAP-first webmail client for Stalwart. That one is what you read your mail in;
this one gets the server underneath it onto a version that speaks the protocol this one gets the server underneath it onto a version that speaks the protocol
it needs. it needs.
@@ -60,8 +60,8 @@ Details: [Known Stalwart problems](docs/known-stalwart-problems.md).
```sh ```sh
ARCH=amd64 # or arm64 ARCH=amd64 # or arm64
curl -fsSLO https://github.com/Coffey-Labs/stalwart-migrator/releases/latest/download/stalwart-migrate-linux-$ARCH.tar.gz curl -fsSLO https://git.coffeylabs.org/coffey-labs/stalwart-migrator/releases/download/latest/stalwart-migrate-linux-$ARCH.tar.gz
curl -fsSLO https://github.com/Coffey-Labs/stalwart-migrator/releases/latest/download/SHA256SUMS curl -fsSLO https://git.coffeylabs.org/coffey-labs/stalwart-migrator/releases/download/latest/SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMS sha256sum --ignore-missing -c SHA256SUMS
tar -xzf stalwart-migrate-linux-$ARCH.tar.gz tar -xzf stalwart-migrate-linux-$ARCH.tar.gz
sudo install -m 0755 stalwart-migrate /usr/local/bin/ sudo install -m 0755 stalwart-migrate /usr/local/bin/