Compare commits
3 Commits
5601f73622
..
v1.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f888dc7be | |||
| 5598b62e4e | |||
| 32f5085c48 |
@@ -180,19 +180,25 @@ jobs:
|
|||||||
- name: Remove per-arch tags from GHCR
|
- name: Remove per-arch tags from GHCR
|
||||||
# Side-effect tags for imagetools assembly only — not part of the public
|
# Side-effect tags for imagetools assembly only — not part of the public
|
||||||
# version surface (deploy/docker-compose.yml pins X.Y.Z, not X.Y.Z-amd64).
|
# version surface (deploy/docker-compose.yml pins X.Y.Z, not X.Y.Z-amd64).
|
||||||
|
# imagetools has no "rm" subcommand; delete via the GitHub Packages API.
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
repo="ghcr.io/${{ github.repository }}"
|
|
||||||
version="${{ needs.prepare.outputs.version }}"
|
version="${{ needs.prepare.outputs.version }}"
|
||||||
|
owner="${{ github.repository_owner }}"
|
||||||
|
pkg="${{ github.event.repository.name }}"
|
||||||
|
api="/users/${owner}/packages/container/${pkg}/versions"
|
||||||
for suffix in amd64 arm64; do
|
for suffix in amd64 arm64; do
|
||||||
tag="${repo}:${version}-${suffix}"
|
tag="${version}-${suffix}"
|
||||||
set +e
|
mapfile -t ids < <(gh api "$api" --paginate \
|
||||||
out=$(docker buildx imagetools rm "$tag" 2>&1)
|
--jq ".[] | select([.metadata.container.tags[]] | index(\"${tag}\")) | .id")
|
||||||
rc=$?
|
if [ "${#ids[@]}" -eq 0 ]; then
|
||||||
set -e
|
echo "no GHCR package version for tag ${tag}"
|
||||||
if [ "$rc" -eq 0 ]; then
|
continue
|
||||||
printf '%s\n' "$out"
|
|
||||||
else
|
|
||||||
echo "::warning::could not remove ${tag}: ${out}" >&2
|
|
||||||
fi
|
fi
|
||||||
|
for id in "${ids[@]}"; do
|
||||||
|
echo "deleting GHCR package version ${id} (tag ${tag})"
|
||||||
|
gh api -X DELETE "${api}/${id}"
|
||||||
|
done
|
||||||
done
|
done
|
||||||
|
|||||||
+29
-5
@@ -5,6 +5,13 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.3.0] - 2026-08-14
|
||||||
|
|
||||||
|
Security and quality after 1.2.5: domain-admin send-log authorization,
|
||||||
|
fail-closed sign-in and application delete, level-2 rate-limit race fix,
|
||||||
|
restore Resync, expanded tests, operator docs, release CI, and OFL for IBM
|
||||||
|
Plex. Upgrading from 1.2.x is a tag bump; no migration.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- licence: the SIL Open Font License 1.1 text now travels with the IBM Plex
|
- licence: the SIL Open Font License 1.1 text now travels with the IBM Plex
|
||||||
@@ -82,6 +89,11 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
|||||||
the same Resync on demand. The `internal/backup` package comment now matches
|
the same Resync on demand. The `internal/backup` package comment now matches
|
||||||
this behaviour.
|
this behaviour.
|
||||||
|
|
||||||
|
- ci (GHCR): per-arch package tags (`X.Y.Z-amd64`, `X.Y.Z-arm64`) are dropped
|
||||||
|
after the manifest merge via the GitHub Packages API. The merge job had called
|
||||||
|
`docker buildx imagetools rm`, which is not a valid subcommand — cleanup failed
|
||||||
|
with a warning and the side-effect tags stayed in the registry.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- docs: operator and as-built docs aligned with the code after a full
|
- docs: operator and as-built docs aligned with the code after a full
|
||||||
@@ -121,14 +133,26 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
|||||||
|
|
||||||
- licence: [NOTICE](NOTICE) tells modifiers to update `SourceURL` in
|
- licence: [NOTICE](NOTICE) tells modifiers to update `SourceURL` in
|
||||||
`internal/legal/legal.go` (the value the panel footer actually injects), not
|
`internal/legal/legal.go` (the value the panel footer actually injects), not
|
||||||
`layout.html`. `release.yml` refuses a `workflow_dispatch` whose version is
|
`layout.html`. Per-file `SPDX-License-Identifier` headers on the two command
|
||||||
not SemVer `X.Y.Z`, so a run from `main` cannot publish `ghcr.io/...:main`.
|
packages were dropped so the tree is consistent; AGPL-3.0 does not require
|
||||||
Per-file `SPDX-License-Identifier` headers on the two command packages were
|
them ([development.md](docs/development.md) § External libraries). Deleted the
|
||||||
dropped so the tree is consistent; AGPL-3.0 does not require them
|
|
||||||
([development.md](docs/development.md) § External libraries). Deleted the
|
|
||||||
completed `docs/plans/logrotate-mode.md` (history in git and
|
completed `docs/plans/logrotate-mode.md` (history in git and
|
||||||
[1.2.3](#123---2026-08-12)).
|
[1.2.3](#123---2026-08-12)).
|
||||||
|
|
||||||
|
- ci: the release image is published only for a **published** GitHub Release
|
||||||
|
(`vX.Y.Z`) or a manual `workflow_dispatch` with an explicit SemVer version — a
|
||||||
|
bare git tag push no longer starts the build. `release.yml` listens for
|
||||||
|
`release: published`, checks out that tag (not `main` HEAD), e2e-gates each
|
||||||
|
native arch build, merges `X.Y.Z-amd64` and `X.Y.Z-arm64` into one manifest,
|
||||||
|
then removes the per-arch tags from GHCR via the GitHub Packages API so
|
||||||
|
operators see only `ghcr.io/mixeme/selfpost:X.Y.Z` (what
|
||||||
|
`deploy/docker-compose.yml` pins). A dispatch whose version input is missing
|
||||||
|
or not `X.Y.Z` fails in `prepare`. [development.md](docs/development.md)
|
||||||
|
documents draft vs published releases, why deleting a release tag converts
|
||||||
|
it back to draft, and Gitea → GitHub tag-mirror pitfalls (do not prune release
|
||||||
|
tags on GitHub; a mirrored `v1.0.0` still runs that tag's `on: push: tags`
|
||||||
|
workflow).
|
||||||
|
|
||||||
- test: the authorization and sign-in surfaces that had no tests now have them.
|
- test: the authorization and sign-in surfaces that had no tests now have them.
|
||||||
The login limiter is covered for its ceiling, its per-address scope, the reset
|
The login limiter is covered for its ceiling, its per-address scope, the reset
|
||||||
at the end of a window and the sweep that keeps finished buckets out of
|
at the end of a window and the sweep that keeps finished buckets out of
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ docker run --rm -d --name selfpost-try \
|
|||||||
-e SELFPOST_HOSTNAME=mail.local.test \
|
-e SELFPOST_HOSTNAME=mail.local.test \
|
||||||
-e PANEL_COOKIE_SECURE=false \
|
-e PANEL_COOKIE_SECURE=false \
|
||||||
-v selfpost-try-data:/data \
|
-v selfpost-try-data:/data \
|
||||||
ghcr.io/mixeme/selfpost:1.2.5
|
ghcr.io/mixeme/selfpost:1.3.0
|
||||||
```
|
```
|
||||||
|
|
||||||
**Get the setup URL** (pick one):
|
**Get the setup URL** (pick one):
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
selfpost:
|
selfpost:
|
||||||
image: ghcr.io/mixeme/selfpost:1.2.5
|
image: ghcr.io/mixeme/selfpost:1.3.0
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
SELFPOST_HOSTNAME: "${SELFPOST_HOSTNAME:?set the mail/panel hostname, e.g. mail.example.com}"
|
SELFPOST_HOSTNAME: "${SELFPOST_HOSTNAME:?set the mail/panel hostname, e.g. mail.example.com}"
|
||||||
|
|||||||
+22
-1
@@ -220,6 +220,27 @@ GHCR package versions, not the git tag.
|
|||||||
Push workflow and source changes to **github.com/mixeme/selfpost** before
|
Push workflow and source changes to **github.com/mixeme/selfpost** before
|
||||||
publishing — Actions reads that repo, not Gitea.
|
publishing — Actions reads that repo, not Gitea.
|
||||||
|
|
||||||
|
**Gitea → GitHub tag mirror.** If every tag push from Gitea is mirrored to
|
||||||
|
GitHub, two things follow:
|
||||||
|
|
||||||
|
1. **GitHub Release tags must not be deleted on GitHub.** Many mirror setups
|
||||||
|
prune remote tags that are absent on Gitea (or re-push with `--force` /
|
||||||
|
`--prune`). Deleting `v1.0.0` / `v1.3.0` on GitHub converts a published
|
||||||
|
Release back to draft. Mirror **branches and new tags forward**; do not
|
||||||
|
delete release tags on the GitHub side. GHCR cleanup is package versions in
|
||||||
|
the UI — not `git push github --delete` and not tag prune on the mirror.
|
||||||
|
|
||||||
|
2. **Tag push runs the workflow file at that tag's commit**, not `main`. `v1.0.0`
|
||||||
|
still points at a commit whose `release.yml` has `on: push: tags` and no
|
||||||
|
per-arch GHCR cleanup — every mirror (re)push of that tag can republish
|
||||||
|
`1.0.0-amd64` / `1.0.0-arm64`. Tags from `v1.3.0` onward only run
|
||||||
|
`release.yml` on **Publish release** (`release: published`), so mirroring
|
||||||
|
those tags alone does not start the image build.
|
||||||
|
|
||||||
|
Safe mirror: push tags to GitHub without deleting existing ones; keep release
|
||||||
|
tags on Gitea; publish the GitHub Release on github.com after the mirror has
|
||||||
|
the tag.
|
||||||
|
|
||||||
Ordinary commits **do not** publish an image. The compose pin and the git tag
|
Ordinary commits **do not** publish an image. The compose pin and the git tag
|
||||||
must match (`1.0.0` / `v1.0.0` for the first published release). Intermediate
|
must match (`1.0.0` / `v1.0.0` for the first published release). Intermediate
|
||||||
CHANGELOG sections (`0.2.0`…`0.6.0`) record development history before that cut.
|
CHANGELOG sections (`0.2.0`…`0.6.0`) record development history before that cut.
|
||||||
@@ -324,7 +345,7 @@ prepare (version from release tag or workflow_dispatch input; checkout vX.Y.Z)
|
|||||||
→ push ghcr.io/...:X.Y.Z-amd64 | X.Y.Z-arm64
|
→ push ghcr.io/...:X.Y.Z-amd64 | X.Y.Z-arm64
|
||||||
→ merge
|
→ merge
|
||||||
→ docker buildx imagetools create → unified manifest X.Y.Z
|
→ docker buildx imagetools create → unified manifest X.Y.Z
|
||||||
→ imagetools rm → drop X.Y.Z-amd64 and X.Y.Z-arm64 from GHCR
|
→ GitHub Packages API → drop X.Y.Z-amd64 and X.Y.Z-arm64 from GHCR
|
||||||
```
|
```
|
||||||
|
|
||||||
Native per-arch matrix (no QEMU): running the full Postfix/OpenDKIM stack under
|
Native per-arch matrix (no QEMU): running the full Postfix/OpenDKIM stack under
|
||||||
|
|||||||
+2
-2
@@ -165,7 +165,7 @@ cat ./data/setup-token
|
|||||||
#### Fixed image tag
|
#### Fixed image tag
|
||||||
|
|
||||||
`deploy/docker-compose.yml` pins an explicit version (`ghcr.io/mixeme/selfpost:X.Y.Z`),
|
`deploy/docker-compose.yml` pins an explicit version (`ghcr.io/mixeme/selfpost:X.Y.Z`),
|
||||||
deliberately never `:latest`. The current pin is `1.2.5`. Intermediate
|
deliberately never `:latest`. The current pin is `1.3.0`. Intermediate
|
||||||
CHANGELOG sections (`0.2.0`…`0.6.0`) record development cuts from before that
|
CHANGELOG sections (`0.2.0`…`0.6.0`) record development cuts from before that
|
||||||
image was published. Pinning matters because of the backup version check (see
|
image was published. Pinning matters because of the backup version check (see
|
||||||
[Full backup and restore](#full-backup-and-restore)): the panel binary's
|
[Full backup and restore](#full-backup-and-restore)): the panel binary's
|
||||||
@@ -466,7 +466,7 @@ A version mismatch at step 5 refuses to start and leaves `/data` untouched —
|
|||||||
the panel exits with a message naming the tag to use, e.g.:
|
the panel exits with a message naming the tag to use, e.g.:
|
||||||
|
|
||||||
```
|
```
|
||||||
backup: this backup was created by SelfPost 1.2.3 but this image is 1.2.5 — restore into the matching image (selfpost:1.2.3)
|
backup: this backup was created by SelfPost 1.2.3 but this image is 1.3.0 — restore into the matching image (selfpost:1.2.3)
|
||||||
```
|
```
|
||||||
|
|
||||||
Fix the tag in `docker-compose.yml`, `docker compose pull && docker compose up
|
Fix the tag in `docker-compose.yml`, `docker compose pull && docker compose up
|
||||||
|
|||||||
Reference in New Issue
Block a user