release: cut 1.0.0

Pin compose and local trial to ghcr.io/mixeme/selfpost:1.0.0, close the
CHANGELOG cut, and retire implementation-plan / v1.x-closure-plan.

Includes the post-cut startup fixes needed for a green release e2e gate:
root-owned TLS copies for postfix check, maillog_file_prefixes for /data,
hostname gate and traversable /data, panel /healthz before setup, and
setup-token / TempDir reclaim via docker exec.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
mixeme
2026-08-09 12:33:29 +03:00
parent 18880a8286
commit 178424eaf6
20 changed files with 284 additions and 440 deletions
+13 -1
View File
@@ -9,6 +9,7 @@ import (
"fmt"
"math/big"
"os"
"os/exec"
"path/filepath"
"time"
)
@@ -23,7 +24,7 @@ const selfpostHostname = "mail.e2e.test"
// CoreDNS is authoritative for, and the sink-MX's dump directory. Called once
// per run before `docker compose up`, so every run starts from a clean slate.
func prepareStage(s *stack) error {
if err := os.RemoveAll(s.stageDir); err != nil {
if err := removeAllBestEffort(s.stageDir); err != nil {
return fmt.Errorf("clean stage dir: %w", err)
}
dirs := []string{"data", "certs", "dns-stage", "mail-stage"}
@@ -48,6 +49,17 @@ func prepareStage(s *stack) error {
return writeZone(s.stageDir, nil)
}
// removeAllBestEffort deletes path; if a previous run left container-UID files
// on the bind mount, a root alpine one-shot removes them first.
func removeAllBestEffort(path string) error {
if err := os.RemoveAll(path); err == nil {
return nil
}
_ = exec.Command("docker", "run", "--rm", "-v", path+":/wipe", "alpine:3.20",
"sh", "-c", "rm -rf /wipe/..?* /wipe/.[!.]* /wipe/*").Run()
return os.RemoveAll(path)
}
// writeSelfSignedCert generates a throwaway RSA key + self-signed certificate
// for selfpostHostname, valid for a day — this stand never outlives that.
// Postfix's smtpd_tls_security_level is "may" (opportunistic), not enforced,