Reconcile no-Actions local release workflow with accepted Hop implementation
Hop-State: A_06FN5GEZTNDD5V7A7PXHBR0 Hop-Proposal: R_06FN5GDDA8WVH1V6JW0FBY0 Hop-Task: T_06FN3MBF98GWD4NA5PA1RWG Hop-Attempt: AT_06FN5FPYNDP5C49S0G52NQG
This commit is contained in:
@@ -1,67 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
code: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out repository
|
|
||||||
uses: https://gitea.com/actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
||||||
- name: Set up Go
|
|
||||||
uses: https://gitea.com/actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
cache: true
|
|
||||||
- name: Test with race detection
|
|
||||||
run: go test -race ./...
|
|
||||||
- name: Vet
|
|
||||||
run: go vet ./...
|
|
||||||
- name: Cross-compile supported targets
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
for target in darwin/amd64 darwin/arm64 linux/amd64 linux/arm64 windows/amd64 windows/arm64; do
|
|
||||||
os=${target%/*}
|
|
||||||
arch=${target#*/}
|
|
||||||
output="dist-smoke/hop_${os}_${arch}"
|
|
||||||
if [[ "$os" == windows ]]; then output="${output}.exe"; fi
|
|
||||||
GOOS="$os" GOARCH="$arch" CGO_ENABLED=0 go build -trimpath -o "$output" ./cmd/hop
|
|
||||||
done
|
|
||||||
- name: Validate POSIX installer syntax
|
|
||||||
run: sh -n scripts/install.sh
|
|
||||||
- name: Smoke-test POSIX installer
|
|
||||||
run: sh scripts/test-install.sh
|
|
||||||
|
|
||||||
powershell-installer:
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out repository
|
|
||||||
uses: https://gitea.com/actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
||||||
- name: Set up Go
|
|
||||||
uses: https://gitea.com/actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
cache: true
|
|
||||||
- name: Validate PowerShell installer syntax
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
$tokens = $null
|
|
||||||
$errors = $null
|
|
||||||
[void][System.Management.Automation.Language.Parser]::ParseFile(
|
|
||||||
(Resolve-Path scripts/install.ps1), [ref]$tokens, [ref]$errors
|
|
||||||
)
|
|
||||||
if ($errors.Count -gt 0) {
|
|
||||||
$errors | Format-List | Out-String | Write-Error
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
- name: Smoke-test PowerShell installer
|
|
||||||
shell: pwsh
|
|
||||||
run: ./scripts/test-install.ps1
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
code: read
|
|
||||||
releases: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out full history
|
|
||||||
uses: https://gitea.com/actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Set up Go
|
|
||||||
uses: https://gitea.com/actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
cache: true
|
|
||||||
- name: Require a public-release license
|
|
||||||
run: test -f LICENSE
|
|
||||||
- name: Test with race detection
|
|
||||||
run: go test -race ./...
|
|
||||||
- name: Vet
|
|
||||||
run: go vet ./...
|
|
||||||
- name: Build and upload a draft Gitea Release
|
|
||||||
env:
|
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
version=2.17.0
|
|
||||||
case "$(uname -m)" in
|
|
||||||
x86_64|amd64)
|
|
||||||
arch=x86_64
|
|
||||||
expected=dde10e2d5a13cef969c0eec00c74f359c0ac306d702b1bd291ad9337b4e54c1d
|
|
||||||
;;
|
|
||||||
aarch64|arm64)
|
|
||||||
arch=arm64
|
|
||||||
expected=75f93fc0e25d10d8535ffd0e4abcf39d6784a2467ba453d479ae513729a9ebbf
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unsupported release runner architecture: $(uname -m)" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
archive="goreleaser_Linux_${arch}.tar.gz"
|
|
||||||
tmp_dir=$(mktemp -d)
|
|
||||||
trap 'rm -rf "$tmp_dir"' EXIT
|
|
||||||
curl -fsSL --proto '=https' --tlsv1.2 \
|
|
||||||
-o "$tmp_dir/$archive" \
|
|
||||||
"https://github.com/goreleaser/goreleaser/releases/download/v${version}/${archive}"
|
|
||||||
printf '%s %s\n' "$expected" "$tmp_dir/$archive" | sha256sum -c -
|
|
||||||
tar -xzf "$tmp_dir/$archive" -C "$tmp_dir" goreleaser
|
|
||||||
"$tmp_dir/goreleaser" release --clean
|
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
# Local build and verification artifacts.
|
# Local build and verification artifacts.
|
||||||
/hop
|
/hop
|
||||||
|
/dist/
|
||||||
/SHA256SUMS
|
/SHA256SUMS
|
||||||
/coverage.out
|
/coverage.out
|
||||||
*.test
|
*.test
|
||||||
|
|||||||
@@ -148,6 +148,8 @@ No project is modified during installation. A project receives its local
|
|||||||
|
|
||||||
Release installer URLs become available after the corresponding Gitea Release
|
Release installer URLs become available after the corresponding Gitea Release
|
||||||
is published. Until the first public release exists, use the source build.
|
is published. Until the first public release exists, use the source build.
|
||||||
|
Hop does not require Gitea Actions: maintainers build and upload draft releases
|
||||||
|
from a trusted local machine with `scripts/release-local.sh`.
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
|
|||||||
@@ -65,3 +65,21 @@ func TestProductDocumentationUsesCanonicalGiteaHost(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDistributionDoesNotRequireGiteaActions(t *testing.T) {
|
||||||
|
root := filepath.Clean(filepath.Join("..", ".."))
|
||||||
|
workflows, err := filepath.Glob(filepath.Join(root, ".gitea", "workflows", "*"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(workflows) != 0 {
|
||||||
|
t.Fatalf("Gitea Actions workflows remain enabled in source: %v", workflows)
|
||||||
|
}
|
||||||
|
contents, err := os.ReadFile(filepath.Join(root, "wiki", "Release-Checklist.md"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if strings.Contains(string(contents), ".gitea/workflows/") {
|
||||||
|
t.Fatal("release checklist still depends on a Gitea Actions workflow")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Executable
+105
@@ -0,0 +1,105 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
printf 'hop release: %s\n' "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: scripts/release-local.sh --snapshot
|
||||||
|
scripts/release-local.sh --publish
|
||||||
|
|
||||||
|
--snapshot Test and build all release archives locally without uploading.
|
||||||
|
--publish Test, build, and upload a draft release to githop.xyz.
|
||||||
|
Requires a clean signed tag, LICENSE, and GITEA_TOKEN.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$#" -eq 1 ] || { usage >&2; exit 2; }
|
||||||
|
mode=$1
|
||||||
|
case "$mode" in
|
||||||
|
--snapshot | --publish) ;;
|
||||||
|
*) usage >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||||
|
cd "$root"
|
||||||
|
|
||||||
|
for command in curl git go tar; do
|
||||||
|
command -v "$command" >/dev/null 2>&1 || fail "$command is required"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$mode" = --publish ]; then
|
||||||
|
[ -f LICENSE ] || fail "LICENSE is required before publishing"
|
||||||
|
[ -n "${GITEA_TOKEN:-}" ] || fail "set a scoped GITEA_TOKEN in the local environment"
|
||||||
|
[ -z "$(git status --porcelain)" ] || fail "the Git worktree must be clean"
|
||||||
|
tag=$(git describe --tags --exact-match HEAD 2>/dev/null) ||
|
||||||
|
fail "HEAD must have an exact release tag"
|
||||||
|
case "$tag" in
|
||||||
|
v[0-9]*) ;;
|
||||||
|
*) fail "release tag must start with v followed by a number" ;;
|
||||||
|
esac
|
||||||
|
[ "$(git cat-file -t "$tag")" = tag ] || fail "$tag must be an annotated tag"
|
||||||
|
git verify-tag "$tag" >/dev/null 2>&1 || fail "$tag must have a valid signature"
|
||||||
|
git ls-remote --exit-code origin "refs/tags/$tag" >/dev/null 2>&1 ||
|
||||||
|
fail "$tag must be pushed to origin before publishing"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'Running local release validation...\n'
|
||||||
|
go test -race ./...
|
||||||
|
go vet ./...
|
||||||
|
sh -n scripts/install.sh
|
||||||
|
sh scripts/test-install.sh
|
||||||
|
git diff --check
|
||||||
|
|
||||||
|
goreleaser_version=2.17.0
|
||||||
|
case "$(uname -s)/$(uname -m)" in
|
||||||
|
Darwin/arm64)
|
||||||
|
archive=goreleaser_Darwin_arm64.tar.gz
|
||||||
|
expected=58912a80159199c0fd5c8484e4c868bf87414129655d6d87cd1cd84ee645736c
|
||||||
|
;;
|
||||||
|
Darwin/x86_64)
|
||||||
|
archive=goreleaser_Darwin_x86_64.tar.gz
|
||||||
|
expected=f37e89fb844ddfd23cffb97e30d91f972c42da68232a676bfba2beacea300543
|
||||||
|
;;
|
||||||
|
Linux/arm64 | Linux/aarch64)
|
||||||
|
archive=goreleaser_Linux_arm64.tar.gz
|
||||||
|
expected=75f93fc0e25d10d8535ffd0e4abcf39d6784a2467ba453d479ae513729a9ebbf
|
||||||
|
;;
|
||||||
|
Linux/x86_64 | Linux/amd64)
|
||||||
|
archive=goreleaser_Linux_x86_64.tar.gz
|
||||||
|
expected=dde10e2d5a13cef969c0eec00c74f359c0ac306d702b1bd291ad9337b4e54c1d
|
||||||
|
;;
|
||||||
|
*) fail "unsupported release host: $(uname -s)/$(uname -m)" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
tmp_dir=$(mktemp -d 2>/dev/null || mktemp -d -t hop-release)
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "$tmp_dir"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
|
||||||
|
printf 'Downloading verified GoReleaser %s...\n' "$goreleaser_version"
|
||||||
|
curl -fsSL --retry 3 --proto '=https' --tlsv1.2 \
|
||||||
|
-o "$tmp_dir/$archive" \
|
||||||
|
"https://github.com/goreleaser/goreleaser/releases/download/v${goreleaser_version}/${archive}"
|
||||||
|
if command -v sha256sum >/dev/null 2>&1; then
|
||||||
|
actual=$(sha256sum "$tmp_dir/$archive" | awk '{print $1}')
|
||||||
|
elif command -v shasum >/dev/null 2>&1; then
|
||||||
|
actual=$(shasum -a 256 "$tmp_dir/$archive" | awk '{print $1}')
|
||||||
|
else
|
||||||
|
fail "sha256sum or shasum is required"
|
||||||
|
fi
|
||||||
|
[ "$actual" = "$expected" ] || fail "GoReleaser checksum verification failed"
|
||||||
|
tar -xzf "$tmp_dir/$archive" -C "$tmp_dir" goreleaser
|
||||||
|
|
||||||
|
"$tmp_dir/goreleaser" check
|
||||||
|
if [ "$mode" = --snapshot ]; then
|
||||||
|
"$tmp_dir/goreleaser" release --snapshot --clean
|
||||||
|
printf 'Local snapshot complete: %s/dist\n' "$root"
|
||||||
|
else
|
||||||
|
"$tmp_dir/goreleaser" release --clean
|
||||||
|
printf 'Draft release uploaded to https://githop.xyz/GnosysLabs/Hop/releases\n'
|
||||||
|
fi
|
||||||
+25
-28
@@ -1,7 +1,8 @@
|
|||||||
# Release checklist
|
# Release checklist
|
||||||
|
|
||||||
Hop releases are built on `githop.xyz` by Gitea Actions and uploaded by
|
Hop releases are built on a trusted maintainer machine and uploaded by
|
||||||
GoReleaser as draft Gitea Releases.
|
GoReleaser as draft Gitea Releases. Gitea Actions and act runners are not
|
||||||
|
required; the server only stores source, tags, release metadata, and assets.
|
||||||
|
|
||||||
The canonical repository is `githop.xyz/GnosysLabs/Hop`.
|
The canonical repository is `githop.xyz/GnosysLabs/Hop`.
|
||||||
|
|
||||||
@@ -9,15 +10,12 @@ The canonical repository is `githop.xyz/GnosysLabs/Hop`.
|
|||||||
|
|
||||||
- Create the `GnosysLabs/Hop` repository and set its default branch to `main`.
|
- Create the `GnosysLabs/Hop` repository and set its default branch to `main`.
|
||||||
- Configure `origin` as `https://githop.xyz/GnosysLabs/Hop.git`.
|
- Configure `origin` as `https://githop.xyz/GnosysLabs/Hop.git`.
|
||||||
- Run a currently patched Gitea 1.26.x or newer, enable Actions, and enable
|
- Keep Gitea Actions disabled when the instance does not have dedicated runner
|
||||||
Actions for the repository. Hop's least-privilege workflow uses the 1.26 job
|
capacity; Hop's release process does not depend on it.
|
||||||
token permission model.
|
- Create a narrowly scoped maintainer access token that can write releases.
|
||||||
- Register trusted, isolated `ubuntu-latest` and `windows-latest` act runners.
|
Export it as `GITEA_TOKEN` only for the local publish command, then unset it.
|
||||||
- Allow the repository job token `code: read` and `releases: write`.
|
- When upgrading GoReleaser, update its pinned version and four archive
|
||||||
- Ensure `${{ secrets.GITEA_TOKEN }}` can create releases in this repository.
|
checksums in `scripts/release-local.sh` from the official checksum file.
|
||||||
- Keep third-party Actions pinned to reviewed commit SHAs. When upgrading
|
|
||||||
GoReleaser, update its version and both hard-coded Linux archive checksums in
|
|
||||||
`.gitea/workflows/release.yml` from the official release checksum file.
|
|
||||||
- Permit release attachment MIME types for `.tar.gz`, `.zip`, and `.txt` in
|
- Permit release attachment MIME types for `.tar.gz`, `.zip`, and `.txt` in
|
||||||
Gitea's `[attachment] ALLOWED_TYPES` configuration.
|
Gitea's `[attachment] ALLOWED_TYPES` configuration.
|
||||||
- Enable the repository wiki, then push the files in `wiki/` to its wiki Git
|
- Enable the repository wiki, then push the files in `wiki/` to its wiki Git
|
||||||
@@ -25,24 +23,19 @@ The canonical repository is `githop.xyz/GnosysLabs/Hop`.
|
|||||||
|
|
||||||
## Public-launch gates
|
## Public-launch gates
|
||||||
|
|
||||||
- Choose and add a `LICENSE`. The release workflow intentionally fails without
|
- Choose and add a `LICENSE`. The local publishing script intentionally fails
|
||||||
one; this is a product/legal decision, not a build default.
|
without one; this is a product/legal decision, not a build default.
|
||||||
- Add `SECURITY.md` with a monitored private disclosure address.
|
- Add `SECURITY.md` with a monitored private disclosure address.
|
||||||
- Create an offline-controlled release-signing key, publish its public key, and
|
- Create an offline-controlled release-signing key, publish its public key, and
|
||||||
add detached signing for `checksums.txt` before general availability.
|
add detached signing for `checksums.txt` before general availability.
|
||||||
- Confirm the `githop.xyz/GnosysLabs/Hop` Go import path serves valid `go-import`
|
- Confirm the `githop.xyz/GnosysLabs/Hop` Go import path serves valid `go-import`
|
||||||
metadata.
|
metadata.
|
||||||
- Back up the Gitea database, repositories, release attachments, and Actions
|
- Back up the Gitea database, repositories, and release attachments.
|
||||||
secrets.
|
|
||||||
|
|
||||||
## Validate before tagging
|
## Validate before tagging
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go test -race ./...
|
scripts/release-local.sh --snapshot
|
||||||
go vet ./...
|
|
||||||
sh -n scripts/install.sh
|
|
||||||
goreleaser check
|
|
||||||
goreleaser release --snapshot --clean
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Inspect `dist/` and test at least one archive on each operating system family.
|
Inspect `dist/` and test at least one archive on each operating system family.
|
||||||
@@ -51,15 +44,19 @@ Confirm `hop version` reports the snapshot/tag-injected version and
|
|||||||
|
|
||||||
## Create a release
|
## Create a release
|
||||||
|
|
||||||
1. Update release notes and the expected version.
|
1. Update release notes. The signed Git tag is the version source and is
|
||||||
2. Create a signed semantic-version tag such as `v0.1.0-alpha.1`.
|
injected into the binaries automatically.
|
||||||
3. Push the tag to `githop.xyz`.
|
2. Run `scripts/release-local.sh --snapshot` and inspect the artifacts.
|
||||||
4. The `.gitea/workflows/release.yml` workflow runs race tests and vet, builds
|
3. Create a signed semantic-version tag such as `v0.1.0-alpha.1` and push it.
|
||||||
six platform archives, generates `checksums.txt`, and uploads a draft.
|
4. Export a locally stored, scoped token: `export GITEA_TOKEN=...`.
|
||||||
5. Download the draft assets and independently verify checksums, version output,
|
5. Run `scripts/release-local.sh --publish`. It reruns race tests, vet, installer
|
||||||
|
checks, builds six platform archives, generates `checksums.txt`, and uploads
|
||||||
|
a draft without executing build work on the Gitea server.
|
||||||
|
6. Immediately run `unset GITEA_TOKEN`.
|
||||||
|
7. Download the draft assets and independently verify checksums, version output,
|
||||||
skill installation, and a disposable Hop project.
|
skill installation, and a disposable Hop project.
|
||||||
6. Publish the Gitea draft only after those checks pass.
|
8. Publish the Gitea draft only after those checks pass.
|
||||||
7. Test both one-command installers against the now-published release.
|
9. Test both one-command installers against the now-published release.
|
||||||
|
|
||||||
## Expected assets
|
## Expected assets
|
||||||
|
|
||||||
|
|||||||
@@ -33,11 +33,12 @@ sign `checksums.txt` with an offline-controlled release key and publish the
|
|||||||
public key independently. Checksum signing is listed as a launch gate in the
|
public key independently. Checksum signing is listed as a launch gate in the
|
||||||
[release checklist](Release-Checklist).
|
[release checklist](Release-Checklist).
|
||||||
|
|
||||||
## Runner trust
|
## Release-machine trust
|
||||||
|
|
||||||
Release jobs execute on Gitea act runners. Only trusted, isolated runners should
|
Release builds execute on a maintainer machine, not on the Gitea server. Use a
|
||||||
receive release tags or release tokens. Do not run secret-bearing release jobs
|
trusted, patched machine; keep the release token out of shell history and source
|
||||||
from unreviewed fork pull requests.
|
files; scope it to the Hop repository; export it only for the publish command;
|
||||||
|
and unset it immediately afterward. Releases upload as drafts for review.
|
||||||
|
|
||||||
## Filesystem safety
|
## Filesystem safety
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user