Release binaries, security policy, and a full README
scripts/build-release.sh builds reproducible linux/amd64 and linux/arm64 archives with SHA256SUMS; the release workflow runs it on a v* tag after vet, tests and govulncheck, and publishes the release. The README now covers what the tool is, what it does step by step, why each decision is made, how to deploy and run a mail host, the security model, and troubleshooting. -h exits 0.
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
# Funding platforms shown behind the repository's Sponsor button.
|
||||||
|
# https://docs.github.com/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository
|
||||||
|
|
||||||
|
github: jcoffey-dev
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# Publish a release when a version tag is pushed: test, check for known
|
||||||
|
# vulnerabilities, build the Linux binaries, attach them with their checksums.
|
||||||
|
#
|
||||||
|
# Tags are the date of the commit, as ihasmail's are: v2026.9.13, and
|
||||||
|
# v2026.9.13.1 for a second release on the same day.
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: ["v*"]
|
||||||
|
# For a tag whose run never started. GitHub has queued and then orphaned
|
||||||
|
# runs before, and a pushed tag has no other way to trigger this again.
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: "Existing tag to release, e.g. v2026.9.13"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: release-${{ github.event.inputs.tag || github.ref_name }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TAG: ${{ github.event.inputs.tag || github.ref_name }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.inputs.tag || github.ref }}
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-go@v7
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
- name: Check the tag names a commit on main
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
git fetch --quiet origin main
|
||||||
|
git merge-base --is-ancestor "$(git rev-parse "$TAG^{commit}")" origin/main \
|
||||||
|
|| { echo "::error::$TAG is not on main"; exit 1; }
|
||||||
|
|
||||||
|
- name: Vet and test
|
||||||
|
run: |
|
||||||
|
go vet ./...
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
- name: Known vulnerabilities
|
||||||
|
run: go run golang.org/x/vuln/cmd/govulncheck@latest ./...
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
SOURCE_DATE_EPOCH="$(git log -1 --format=%ct "$TAG")" scripts/build-release.sh "$TAG" dist
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if gh release view "$TAG" >/dev/null 2>&1; then
|
||||||
|
gh release upload "$TAG" dist/* --clobber
|
||||||
|
else
|
||||||
|
gh release create "$TAG" dist/* --verify-tag --title "$TAG" --generate-notes
|
||||||
|
fi
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Security fixes go into the latest release. Older releases do not receive
|
||||||
|
backported fixes; a fixed release is a download away, and the tool keeps no
|
||||||
|
state of its own between runs.
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| --- | --- |
|
||||||
|
| Latest release | :white_check_mark: |
|
||||||
|
| Older releases | :x: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
**Please do not open a public GitHub issue for security vulnerabilities.**
|
||||||
|
|
||||||
|
Report them privately by emailing **johnellisATlinuxDOTcom**, with:
|
||||||
|
|
||||||
|
- A description of the issue and its impact
|
||||||
|
- Steps to reproduce
|
||||||
|
- The ihasmail-oneshot version (`ihasmail-oneshot version`)
|
||||||
|
- Whether it is in the tool itself or in the deployment it writes
|
||||||
|
|
||||||
|
You should hear back within a few days. Once a fix is released, disclosure
|
||||||
|
timing and credit are coordinated with you.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
In scope:
|
||||||
|
|
||||||
|
- Secrets the tool generates or writes: file modes, where they end up, what
|
||||||
|
outlives the setup
|
||||||
|
- The deployment it writes: what it publishes, and what it tells Stalwart to
|
||||||
|
trust (forwarded client addresses, addresses exempt from bans)
|
||||||
|
- The Caddyfile and compose.yaml it renders
|
||||||
|
|
||||||
|
Out of scope, and best reported upstream:
|
||||||
|
|
||||||
|
- Vulnerabilities in Stalwart itself
|
||||||
|
- Vulnerabilities in ihasmail itself — see
|
||||||
|
[its security policy](https://github.com/Coffey-Labs/ihasmail/security/policy)
|
||||||
|
- Vulnerabilities in Caddy or Docker
|
||||||
@@ -60,10 +60,11 @@ func main() {
|
|||||||
fmt.Fprintf(os.Stderr, "unknown command %q\n\n%s", os.Args[1], usageText)
|
fmt.Fprintf(os.Stderr, "unknown command %q\n\n%s", os.Args[1], usageText)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if errors.Is(err, flag.ErrHelp) {
|
||||||
if !errors.Is(err, flag.ErrHelp) {
|
return // -h printed its usage, which is what was asked for
|
||||||
fmt.Fprintf(os.Stderr, "!! %v\n", err)
|
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "!! %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+40
@@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SPDX-FileCopyrightText: 2026 Coffey Labs
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
#
|
||||||
|
# Build the release archives: one per architecture, plus SHA256SUMS.
|
||||||
|
#
|
||||||
|
# Usage: scripts/build-release.sh VERSION [OUTDIR]
|
||||||
|
# scripts/build-release.sh v2026.9.13 dist
|
||||||
|
#
|
||||||
|
# The release workflow runs exactly this, so a release can be reproduced -- or
|
||||||
|
# checked before tagging -- on any machine with Go. Archive names carry no
|
||||||
|
# version, so .../releases/latest/download/<name> always means the newest.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
VERSION="${1:?usage: $0 VERSION [OUTDIR]}"
|
||||||
|
OUT="${2:-dist}"
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
|
||||||
|
rm -rf "$OUT" && mkdir -p "$OUT"
|
||||||
|
OUT="$(cd "$OUT" && pwd)"
|
||||||
|
STAGE="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$STAGE"' EXIT
|
||||||
|
|
||||||
|
# Linux only: the tool drives Docker on the host it runs on, and deploys a mail
|
||||||
|
# server that publishes ports there.
|
||||||
|
for arch in amd64 arm64; do
|
||||||
|
name="ihasmail-oneshot-linux-$arch"
|
||||||
|
mkdir -p "$STAGE/$name"
|
||||||
|
echo "==> building $name ($VERSION)"
|
||||||
|
(cd "$ROOT" && CGO_ENABLED=0 GOOS=linux GOARCH="$arch" go build -trimpath \
|
||||||
|
-ldflags "-s -w -X main.version=$VERSION" -o "$STAGE/$name/ihasmail-oneshot" ./cmd/ihasmail-oneshot)
|
||||||
|
cp "$ROOT/LICENSE" "$ROOT/README.md" "$STAGE/$name/"
|
||||||
|
# Fixed owner and time, so the same commit gives the same archive.
|
||||||
|
tar --sort=name --owner=0 --group=0 --numeric-owner --mtime="@${SOURCE_DATE_EPOCH:-0}" \
|
||||||
|
-C "$STAGE/$name" -czf "$OUT/$name.tar.gz" ihasmail-oneshot LICENSE README.md
|
||||||
|
done
|
||||||
|
|
||||||
|
(cd "$OUT" && sha256sum ./*.tar.gz | sed 's| \./| |' > SHA256SUMS)
|
||||||
|
echo "==> $OUT:"
|
||||||
|
(cd "$OUT" && cat SHA256SUMS)
|
||||||
Reference in New Issue
Block a user