feat: publish the Sandwich Hime source preview
Signed-off-by: Cole Speelman <gamertan@noreply.localhost>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
# Repository verification tools
|
||||
|
||||
These scripts are intentionally understandable shell and PowerShell rather than a release framework with hidden defaults.
|
||||
|
||||
- `verify.sh` runs root and nested-module tests and vet, builds `himesan`, tests each example module, checks committed generated output, and proves two generation passes leave the same bytes and unchanged modification times. Set `HIMESAN_RACE=1` for race tests.
|
||||
- `verify.ps1` provides the equivalent native Windows lane; pass `-Race` to include the race detector.
|
||||
- `check-licenses.sh` enforces the AGPL compiler / Apache runtime / 0BSD example boundary and prevents generated application Go from inheriting an AGPL identifier.
|
||||
- `check-site.sh` verifies vanity metadata, no-script/local-asset policy, pre-release honesty, and baseline accessibility scaffolding for the static site.
|
||||
- `eql-integration.sh` is opt-in. Until a repository-owned `internal/integration/eql` test exists it exits successfully without inspecting EQL. Later it requires external `HIMESAN_EQL_ROOT` and `HIMESAN_EQL_DB` paths and passes a read-only-mode contract to that package. It never copies a database.
|
||||
- `release-check.sh --version vX.Y.Z` is a clean-checkout technical preflight. Add `--public` and point `HIMESAN_RELEASE_EVIDENCE_DIR` at a human-reviewed evidence bundle for the public-launch gate. It never tags, pushes, publishes, or deploys.
|
||||
- `verify-public-install.sh --version vX.Y.Z` is a post-tag/publication check. It verifies exact `go-get=1` package routes and runs the documented compiler install and runtime get from fresh direct-fetch and public-proxy caches without interactive Git credentials.
|
||||
|
||||
The canonical Linux CI and release preflight also run bounded fuzz sessions for the parser/context compiler and Go-aware delimiter scanner. Seed-corpus execution remains part of ordinary `go test`; the bounded sessions are extra evidence, not a substitute for longer scheduled fuzzing before v1.
|
||||
|
||||
The release preflight invokes `govulncheck` from the official Go vulnerability project at the exact module version `golang.org/x/vuln@v1.6.0`. Updating that pin requires reviewing the upstream tag and rerunning the supported Go lines.
|
||||
|
||||
## Preview automation status
|
||||
|
||||
Forge workflows are intentionally excluded from the sanitized pre-1.0 public snapshot until the project has confirmed its own Gitea runner availability and reviewed locally hosted or otherwise pinned dependencies. Local `verify.sh`, `verify.ps1`, license, and release-preflight results are the preview gates.
|
||||
|
||||
If Gitea automation is later added to the public repository, pin every external action to a reviewed immutable commit, document its provenance, grant minimum permissions, and keep a local verification path. No secondary forge mirror or hosted workflow is planned.
|
||||
Executable
+201
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
cd "$repo_root"
|
||||
|
||||
failures=0
|
||||
|
||||
fail() {
|
||||
printf 'license error: %s\n' "$*" >&2
|
||||
failures=$((failures + 1))
|
||||
}
|
||||
|
||||
expected_spdx() {
|
||||
case "$1" in
|
||||
examples/*)
|
||||
printf '0BSD\n'
|
||||
;;
|
||||
sando/*)
|
||||
printf 'Apache-2.0\n'
|
||||
;;
|
||||
*)
|
||||
printf 'AGPL-3.0-only\n'
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Read one complete SPDX expression from the first eight lines. Removing only
|
||||
# recognized comment closers makes expressions such as "AGPL-3.0-only OR MIT"
|
||||
# fail instead of passing a substring search.
|
||||
has_exact_spdx() {
|
||||
local path=$1
|
||||
local expected=$2
|
||||
local lines line value count
|
||||
|
||||
lines=$(head -n 8 -- "$path" | grep 'SPDX-License-Identifier:' || true)
|
||||
count=$(printf '%s\n' "$lines" | sed '/^$/d' | wc -l)
|
||||
count=${count//[[:space:]]/}
|
||||
[[ $count -eq 1 ]] || return 1
|
||||
line=$lines
|
||||
value=${line#*SPDX-License-Identifier:}
|
||||
value=${value%%-->*}
|
||||
value=${value%%\*/*}
|
||||
value=$(printf '%s' "$value" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
|
||||
[[ $value == "$expected" ]]
|
||||
}
|
||||
|
||||
check_sha256() {
|
||||
local path=$1
|
||||
local expected=$2
|
||||
local actual
|
||||
actual=$(sha256sum -- "$path" | awk '{print $1}')
|
||||
[[ $actual == "$expected" ]] || fail "$path does not match the reviewed legal text ($actual)"
|
||||
}
|
||||
|
||||
is_comment_capable_project_file() {
|
||||
case "$1" in
|
||||
COPYRIGHT | */COPYRIGHT | .editorconfig | .gitattributes | .gitignore | *.go | *.mod | *.md | *.sh | *.ps1 | *.yml | *.yaml | *.html | *.css | *.js | *.toml | *.allow)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
list_project_files() {
|
||||
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
git ls-files --cached --others --exclude-standard -z
|
||||
else
|
||||
find . -type d \( -name .git -o -name vendor -o -name bin -o -name dist -o -name coverage \) -prune -o \
|
||||
-type f -print0 | sed -z 's#^\./##'
|
||||
fi
|
||||
}
|
||||
|
||||
[[ -f LICENSE ]] || fail 'root LICENSE is missing'
|
||||
[[ -f sando/LICENSE ]] || fail 'sando/LICENSE is missing'
|
||||
[[ -f examples/eql-shaped/LICENSE ]] || fail 'example LICENSE is missing'
|
||||
[[ -f DCO.txt ]] || fail 'DCO.txt is missing'
|
||||
|
||||
if [[ -f LICENSE ]]; then
|
||||
check_sha256 LICENSE 0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0
|
||||
fi
|
||||
if [[ -f sando/LICENSE ]]; then
|
||||
check_sha256 sando/LICENSE c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4
|
||||
fi
|
||||
if [[ -f examples/eql-shaped/LICENSE ]]; then
|
||||
check_sha256 examples/eql-shaped/LICENSE 7e2406acc98391ec126b13d512c00b930bbc2c19f4d5b0fe52286ae41bfbc92d
|
||||
grep -Fq 'Copyright (c) 2025-2026 Cole Speelman' examples/eql-shaped/LICENSE || \
|
||||
fail 'example LICENSE must identify its original author'
|
||||
fi
|
||||
if [[ -f DCO.txt ]]; then
|
||||
check_sha256 DCO.txt f7ac75b443f4ca16b503241344b41aeff9503b0c30bedc2b119551d83cb0fa90
|
||||
fi
|
||||
|
||||
for required in COPYRIGHT OUTPUT_EXCEPTION.md sando/COPYRIGHT examples/eql-shaped/LICENSES.md; do
|
||||
[[ -f $required ]] || fail "$required is required for ownership/output licensing"
|
||||
done
|
||||
|
||||
if [[ -f COPYRIGHT ]]; then
|
||||
has_exact_spdx COPYRIGHT AGPL-3.0-only || fail 'COPYRIGHT must carry exactly AGPL-3.0-only'
|
||||
grep -Fq 'SPDX-FileCopyrightText: 2025-2026 Cole Speelman' COPYRIGHT || \
|
||||
fail 'COPYRIGHT must identify Cole Speelman original work'
|
||||
fi
|
||||
if [[ -f sando/COPYRIGHT ]]; then
|
||||
has_exact_spdx sando/COPYRIGHT Apache-2.0 || fail 'sando/COPYRIGHT must carry exactly Apache-2.0'
|
||||
grep -Fq 'SPDX-FileCopyrightText: 2025-2026 Cole Speelman' sando/COPYRIGHT || \
|
||||
fail 'sando/COPYRIGHT must identify Cole Speelman original runtime work'
|
||||
fi
|
||||
if [[ -f OUTPUT_EXCEPTION.md ]]; then
|
||||
grep -Fq 'additional permission under section 7' OUTPUT_EXCEPTION.md || \
|
||||
fail 'OUTPUT_EXCEPTION.md must contain the AGPL section 7 additional permission'
|
||||
grep -Fq 'Himesan-Output-Permission: v1.0' OUTPUT_EXCEPTION.md || \
|
||||
fail 'OUTPUT_EXCEPTION.md must define the contributor grant marker'
|
||||
fi
|
||||
if [[ -f CONTRIBUTING.md ]]; then
|
||||
grep -Fq 'Himesan-Output-Permission: v1.0' CONTRIBUTING.md || \
|
||||
fail 'CONTRIBUTING.md must require the emitted-scaffolding permission grant'
|
||||
grep -Fq 'DCO sign-off does not supply that separate grant' CONTRIBUTING.md || \
|
||||
fail 'CONTRIBUTING.md must distinguish DCO from the output permission'
|
||||
fi
|
||||
|
||||
while IFS= read -r -d '' path; do
|
||||
[[ -f $path ]] || continue
|
||||
|
||||
case "$path" in
|
||||
LICENSE | sando/LICENSE | examples/*/LICENSE | DCO.txt)
|
||||
# These are reviewed legal texts with their own notices.
|
||||
continue
|
||||
;;
|
||||
*.sum)
|
||||
# Cryptographic dependency records are externally covered data.
|
||||
continue
|
||||
;;
|
||||
PUBLIC-SNAPSHOT.json | PUBLIC-SNAPSHOT.sha256)
|
||||
# Generated factual provenance; covered by LICENSES.md.
|
||||
continue
|
||||
;;
|
||||
*.sando.go)
|
||||
if grep -Fq 'SPDX-License-Identifier:' "$path"; then
|
||||
fail "$path is compiler-managed output and must use its module-level license map"
|
||||
fi
|
||||
if grep -Fq 'Copyright (c) 2025-2026 Cole Speelman' "$path"; then
|
||||
fail "$path must not receive a compiler copyright claim"
|
||||
fi
|
||||
continue
|
||||
;;
|
||||
*.sando)
|
||||
expected=$(expected_spdx "$path")
|
||||
count=$(grep -Fc "SPDX-License-Identifier: $expected" "$path" || true)
|
||||
[[ $count -eq 1 ]] || fail "$path must carry one template comment for $expected"
|
||||
continue
|
||||
;;
|
||||
*.json)
|
||||
if [[ $path != examples/eql-shaped/himesan.json ]]; then
|
||||
fail "$path cannot carry a comment and needs an explicit license-map entry"
|
||||
fi
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! is_comment_capable_project_file "$path"; then
|
||||
fail "$path has no fail-closed license policy"
|
||||
continue
|
||||
fi
|
||||
|
||||
expected=$(expected_spdx "$path")
|
||||
has_exact_spdx "$path" "$expected" || \
|
||||
fail "$path must carry exactly one SPDX identifier: $expected"
|
||||
done < <(list_project_files)
|
||||
|
||||
if [[ -f examples/eql-shaped/LICENSES.md ]]; then
|
||||
grep -Fq '`himesan.json`' examples/eql-shaped/LICENSES.md || \
|
||||
fail 'example license map must cover himesan.json'
|
||||
grep -Fq '`.sando.go`' examples/eql-shaped/LICENSES.md || \
|
||||
fail 'example license map must cover generated output'
|
||||
fi
|
||||
|
||||
if [[ -d examples ]] && find examples -type f -name go.mod -print -quit | grep -q .; then
|
||||
while IFS= read -r -d '' module_file; do
|
||||
module_dir=$(dirname -- "$module_file")
|
||||
license_file=$module_dir/LICENSE
|
||||
map_file=$module_dir/LICENSES.md
|
||||
[[ -f $license_file ]] || fail "example module $module_dir needs a local 0BSD LICENSE"
|
||||
[[ -f $map_file ]] || fail "example module $module_dir needs a local license map"
|
||||
done < <(find examples -type d \( -name .git -o -name vendor \) -prune -o -type f -name go.mod -print0)
|
||||
fi
|
||||
|
||||
if find sando -type f -name '*.go' -exec grep -En \
|
||||
'"gamertan\.com/sandwich-hime/(cmd|internal)(/|"|$)' {} + | grep -q .; then
|
||||
fail 'the Apache runtime imports AGPL compiler or CLI code'
|
||||
fi
|
||||
|
||||
if (( failures > 0 )); then
|
||||
printf '\n%d license/SPDX policy violation(s) found.\n' "$failures" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'Reviewed license texts, ownership records, SPDX boundaries, generated-output permission, and runtime separation are consistent.\n'
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
cd "$repo_root"
|
||||
|
||||
public_version=''
|
||||
if (( $# > 0 )); then
|
||||
if [[ "$1" != --public || $# -ne 2 ]]; then
|
||||
printf 'usage: scripts/check-site.sh [--public vX.Y.Z]\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
public_version=$2
|
||||
fi
|
||||
|
||||
fail() {
|
||||
printf 'site error: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
for page in site/index.html site/sando/index.html; do
|
||||
[[ -f "$page" ]] || fail "missing $page"
|
||||
grep -Fq '<html lang="en">' "$page" || fail "$page needs a document language"
|
||||
grep -Fq '<meta name="viewport"' "$page" || fail "$page needs responsive viewport metadata"
|
||||
grep -Fq 'class="skip-link"' "$page" || fail "$page needs a keyboard skip link"
|
||||
grep -Fq '<main id="main">' "$page" || fail "$page needs the skip-link target"
|
||||
grep -Fq '<h1>' "$page" || grep -Fq '<h1 ' "$page" || fail "$page needs an h1"
|
||||
grep -Fq 'Content-Security-Policy' "$page" || fail "$page needs a preview CSP"
|
||||
grep -Fq '<meta name="himesan-release-status" content="' "$page" || \
|
||||
fail "$page needs machine-readable release status"
|
||||
done
|
||||
|
||||
grep -Fq '<meta name="go-import" content="gamertan.com/sandwich-hime git https://gitea.speelman.ca/gamertan/sandwich-hime.git">' \
|
||||
site/index.html || fail 'compiler vanity-import metadata is missing or changed'
|
||||
grep -Fq '<meta name="go-import" content="gamertan.com/sandwich-hime/sando git https://gitea.speelman.ca/gamertan/sandwich-hime.git sando">' \
|
||||
site/sando/index.html || fail 'nested runtime vanity-import metadata is missing or changed'
|
||||
|
||||
if find site -type f -name '*.html' -exec grep -Ein '<script([[:space:]>])' {} + | grep -q .; then
|
||||
fail 'the static project site must not contain JavaScript'
|
||||
fi
|
||||
if find site -type f -name '*.html' -exec grep -Ein \
|
||||
'(src|href)="https?://[^" ]+\.(js|css)([?"#])' {} + | grep -q .; then
|
||||
fail 'the static project site must not load remote JavaScript or CSS'
|
||||
fi
|
||||
if find site -type f -name '*.css' -exec grep -Ein \
|
||||
"(@import|url\\()[[:space:]\"']*https?://" {} + | grep -q .; then
|
||||
fail 'the static project site must not load remote CSS assets'
|
||||
fi
|
||||
|
||||
grep -Fq 'prefers-reduced-motion' site/assets/site.css || fail 'site CSS needs a reduced-motion preference'
|
||||
grep -Fq 'forced-colors' site/assets/site.css || fail 'site CSS needs a forced-colors fallback'
|
||||
grep -Fq 'class="wordmark" role="img"' site/index.html || \
|
||||
fail 'the ASCII wordmark needs an accessible semantic role'
|
||||
grep -Fq 'class="code" tabindex="0" role="region"' site/index.html || \
|
||||
fail 'the scrollable code example needs keyboard access and a region role'
|
||||
|
||||
if [[ -n "$public_version" ]]; then
|
||||
for page in site/index.html site/sando/index.html; do
|
||||
grep -Fq "<meta name=\"himesan-release-status\" content=\"$public_version\">" "$page" || \
|
||||
fail "$page release status does not match $public_version"
|
||||
done
|
||||
if grep -Eiq 'not a public release|not released yet|private pre-release|public pre-1\.0|unsupported pre-1\.0|no (supported )?public .*tag' \
|
||||
site/index.html site/sando/index.html; then
|
||||
fail 'public-release site still contains a pre-release warning'
|
||||
fi
|
||||
else
|
||||
if grep -Fq '<meta name="himesan-release-status" content="pre-release">' site/index.html; then
|
||||
grep -Eiq 'public pre-1\.0|unsupported pre-1\.0|not released yet' site/index.html || \
|
||||
fail 'the pre-release landing page must state its status in human-readable text'
|
||||
fi
|
||||
fi
|
||||
|
||||
printf 'Static site metadata, local-asset policy, and accessibility scaffolding are present.\n'
|
||||
Executable
+174
@@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
cd "$repo_root"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: scripts/release-check.sh --version vX.Y.Z [--public]
|
||||
|
||||
Runs a read-only release preflight. It never creates tags, commits, release
|
||||
artifacts in the repository, pushes, or deploys.
|
||||
|
||||
--version Candidate compiler version. The corresponding runtime tag is
|
||||
sando/vX.Y.Z.
|
||||
--public Additionally require the human-reviewed launch evidence bundle
|
||||
named by HIMESAN_RELEASE_EVIDENCE_DIR.
|
||||
EOF
|
||||
}
|
||||
|
||||
version=''
|
||||
public_release=0
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--version)
|
||||
[[ $# -ge 2 ]] || { usage >&2; exit 2; }
|
||||
version=$2
|
||||
shift 2
|
||||
;;
|
||||
--public)
|
||||
public_release=1
|
||||
shift
|
||||
;;
|
||||
-h | --help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
printf 'unknown argument: %s\n' "$1" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ! "$version" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?(\+[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$ ]]; then
|
||||
printf 'error: --version must be a semantic version beginning with v\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
runtime_tag="sando/$version"
|
||||
|
||||
if [[ -n "$(git status --porcelain=v1 --untracked-files=all)" ]]; then
|
||||
printf 'error: release preflight requires a clean canonical checkout\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
origin_url=$(git remote get-url origin)
|
||||
case "$origin_url" in
|
||||
ssh://git@gitea.speelman.ca:2222/gamertan/sandwich-hime.git | \
|
||||
git@gitea.speelman.ca:gamertan/sandwich-hime.git | \
|
||||
https://gitea.speelman.ca/gamertan/sandwich-hime.git)
|
||||
;;
|
||||
*)
|
||||
printf 'error: origin is not the canonical Gamertan Gitea repository: %s\n' "$origin_url" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
branch=$(git symbolic-ref --quiet --short HEAD || true)
|
||||
if [[ "$branch" != main ]]; then
|
||||
printf 'error: release preflight must run from canonical main, not %s\n' "${branch:-detached HEAD}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for tag in "$version" "$runtime_tag"; do
|
||||
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
|
||||
printf 'error: candidate tag already exists locally: %s\n' "$tag" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! remote_tags=$(git ls-remote --tags origin "refs/tags/$tag" "refs/tags/$tag^{}" 2>/dev/null); then
|
||||
printf 'error: could not verify candidate tag against canonical origin: %s\n' "$tag" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -n "$remote_tags" ]]; then
|
||||
printf 'error: candidate tag already exists on canonical origin: %s\n' "$tag" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
./scripts/check-licenses.sh
|
||||
HIMESAN_RACE=1 ./scripts/verify.sh
|
||||
|
||||
printf '\n==> bounded compiler fuzz gates\n'
|
||||
go test ./internal/compiler -run '^$' -fuzz '^FuzzCompileNeverPanics$' -fuzztime=20s
|
||||
go test ./internal/compiler -run '^$' -fuzz '^FuzzGoDelimiterNeverPanics$' -fuzztime=20s
|
||||
|
||||
printf '\n==> vulnerability scan (pinned golang.org/x/vuln v1.6.0)\n'
|
||||
go run golang.org/x/vuln/cmd/govulncheck@v1.6.0 ./...
|
||||
(
|
||||
cd sando
|
||||
go run golang.org/x/vuln/cmd/govulncheck@v1.6.0 ./...
|
||||
)
|
||||
|
||||
artifact_dir=$(mktemp -d "${TMPDIR:-/tmp}/himesan-release-check.XXXXXXXX")
|
||||
cleanup() {
|
||||
if [[ -n "${artifact_dir:-}" && -d "$artifact_dir" ]]; then
|
||||
rm -rf -- "$artifact_dir"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
printf '\n==> cross-compiling release binary smoke set\n'
|
||||
for target in \
|
||||
linux/amd64 \
|
||||
linux/arm64 \
|
||||
darwin/amd64 \
|
||||
darwin/arm64 \
|
||||
windows/amd64 \
|
||||
windows/arm64; do
|
||||
target_os=${target%/*}
|
||||
target_arch=${target#*/}
|
||||
extension=''
|
||||
if [[ "$target_os" == windows ]]; then
|
||||
extension='.exe'
|
||||
fi
|
||||
CGO_ENABLED=0 GOOS="$target_os" GOARCH="$target_arch" \
|
||||
go build -trimpath -o "$artifact_dir/himesan-$target_os-$target_arch$extension" ./cmd/himesan
|
||||
done
|
||||
|
||||
for required in \
|
||||
site/index.html \
|
||||
site/sando/index.html \
|
||||
site/README.md \
|
||||
scripts/verify-public-install.sh \
|
||||
RELEASE.md \
|
||||
SECURITY.md \
|
||||
TRADEMARKS.md \
|
||||
CLA.md; do
|
||||
[[ -f "$required" ]] || { printf 'error: required release file is missing: %s\n' "$required" >&2; exit 1; }
|
||||
done
|
||||
|
||||
if ! grep -Fq 'gamertan.com/sandwich-hime/sando git' site/sando/index.html; then
|
||||
printf 'error: nested runtime vanity-import metadata is missing\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( public_release == 1 )); then
|
||||
evidence_dir=${HIMESAN_RELEASE_EVIDENCE_DIR:-}
|
||||
if [[ -z "$evidence_dir" || ! -d "$evidence_dir" ]]; then
|
||||
printf 'error: --public requires HIMESAN_RELEASE_EVIDENCE_DIR\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
for evidence in \
|
||||
counsel-review.md \
|
||||
cross-platform.md \
|
||||
eql-production-soak.md \
|
||||
security-and-accessibility.md \
|
||||
benchmark-methodology.md \
|
||||
vanity-and-mirror.md; do
|
||||
if [[ ! -s "$evidence_dir/$evidence" ]]; then
|
||||
printf 'error: public release evidence is missing or empty: %s\n' "$evidence_dir/$evidence" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
./scripts/check-site.sh --public "$version"
|
||||
printf '\nHuman review is still required; evidence presence is not automatic approval.\n'
|
||||
else
|
||||
printf '\nTechnical preflight passed. Public launch remains blocked until --public evidence review passes.\n'
|
||||
fi
|
||||
|
||||
printf 'No tag, push, publication, or deployment was performed for %s / %s.\n' "$version" "$runtime_tag"
|
||||
Executable
+170
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: scripts/verify-public-install.sh --version vX.Y.Z
|
||||
|
||||
Post-publication verification for Gamertan vanity metadata and the exact
|
||||
documented install commands. It uses fresh temporary Go caches and never writes
|
||||
to the repository. Signed compiler and sando tags must already be public.
|
||||
EOF
|
||||
}
|
||||
|
||||
version=''
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--version)
|
||||
[[ $# -ge 2 ]] || { usage >&2; exit 2; }
|
||||
version=$2
|
||||
shift 2
|
||||
;;
|
||||
-h | --help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
printf 'unknown argument: %s\n' "$1" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ! "$version" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?(\+[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$ ]]; then
|
||||
printf 'error: --version must be a semantic version beginning with v\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
for command_name in curl go git false; do
|
||||
command -v "$command_name" >/dev/null 2>&1 || {
|
||||
printf 'error: required command is unavailable: %s\n' "$command_name" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
false_command=$(command -v false)
|
||||
|
||||
public_origin=${HIMESAN_PUBLIC_ORIGIN:-https://gamertan.com}
|
||||
public_origin=${public_origin%/}
|
||||
case "$public_origin" in
|
||||
https://*) ;;
|
||||
*)
|
||||
printf 'error: HIMESAN_PUBLIC_ORIGIN must use HTTPS\n' >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
compiler_meta='<meta name="go-import" content="gamertan.com/sandwich-hime git https://gitea.speelman.ca/gamertan/sandwich-hime.git">'
|
||||
runtime_meta='<meta name="go-import" content="gamertan.com/sandwich-hime/sando git https://gitea.speelman.ca/gamertan/sandwich-hime.git sando">'
|
||||
|
||||
check_metadata() {
|
||||
local path=$1
|
||||
local expected=$2
|
||||
local body
|
||||
body=$(curl --fail --silent --show-error --location \
|
||||
--proto '=https' --max-redirs 3 --connect-timeout 10 --max-time 30 \
|
||||
"$public_origin$path")
|
||||
if [[ "$body" != *"$expected"* ]]; then
|
||||
printf 'error: expected vanity metadata missing at %s%s\n' "$public_origin" "$path" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
printf '==> exact vanity-import discovery routes\n'
|
||||
check_metadata '/sandwich-hime?go-get=1' "$compiler_meta"
|
||||
check_metadata '/sandwich-hime/cmd/himesan?go-get=1' "$compiler_meta"
|
||||
check_metadata '/sandwich-hime/sando?go-get=1' "$runtime_meta"
|
||||
check_metadata '/sandwich-hime/sando/future-package?go-get=1' "$runtime_meta"
|
||||
|
||||
browser_status=$(curl --silent --show-error --output /dev/null --write-out '%{http_code}' \
|
||||
--proto '=https' --max-redirs 0 --connect-timeout 10 --max-time 30 \
|
||||
"$public_origin/sandwich-hime/not-a-browser-route")
|
||||
if [[ "$browser_status" != 404 ]]; then
|
||||
printf 'error: query-scoped metadata fallback leaked into ordinary browser routing (status %s)\n' "$browser_status" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
scratch_dir=$(mktemp -d "${TMPDIR:-/tmp}/himesan-public-install.XXXXXXXX")
|
||||
cleanup() {
|
||||
if [[ -n "${scratch_dir:-}" && -d "$scratch_dir" ]]; then
|
||||
rm -rf -- "$scratch_dir"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
run_install_pair() {
|
||||
local mode=$1
|
||||
local proxy=$2
|
||||
local no_sum_db=$3
|
||||
local mode_dir="$scratch_dir/$mode"
|
||||
local installed_binary installed_version go_executable_suffix
|
||||
mkdir -p "$mode_dir/gopath" "$mode_dir/modcache" "$mode_dir/buildcache" "$mode_dir/consumer"
|
||||
|
||||
printf '\n==> %s clean-cache install\n' "$mode"
|
||||
env \
|
||||
GIT_TERMINAL_PROMPT=0 \
|
||||
GIT_CONFIG_NOSYSTEM=1 \
|
||||
GIT_CONFIG_GLOBAL=/dev/null \
|
||||
GIT_ASKPASS="$false_command" \
|
||||
SSH_ASKPASS="$false_command" \
|
||||
GOPATH="$mode_dir/gopath" \
|
||||
GOMODCACHE="$mode_dir/modcache" \
|
||||
GOCACHE="$mode_dir/buildcache" \
|
||||
GOPROXY="$proxy" \
|
||||
GOPRIVATE= \
|
||||
GONOPROXY=none \
|
||||
GONOSUMDB="$no_sum_db" \
|
||||
GOSUMDB=sum.golang.org \
|
||||
GOINSECURE= \
|
||||
GOAUTH=off \
|
||||
go install "gamertan.com/sandwich-hime/cmd/himesan@$version"
|
||||
|
||||
go_executable_suffix=$(go env GOEXE)
|
||||
installed_binary="$mode_dir/gopath/bin/himesan$go_executable_suffix"
|
||||
installed_version=$("$installed_binary" version --json)
|
||||
if [[ "$installed_version" != *"\"compiler\":\"$version\""* ]]; then
|
||||
printf 'error: installed compiler did not report module version %s: %s\n' "$version" "$installed_version" >&2
|
||||
exit 1
|
||||
fi
|
||||
cat >"$mode_dir/version_probe.sando" <<'EOF'
|
||||
<?sando go
|
||||
package probe
|
||||
func VersionProbe()
|
||||
?>
|
||||
<p>version probe</p>
|
||||
EOF
|
||||
"$installed_binary" generate "$mode_dir/version_probe.sando" >/dev/null
|
||||
if ! grep -Fq "// himesan:compiler $version" "$mode_dir/version_probe.sando.go"; then
|
||||
printf 'error: generated provenance did not record installed compiler version %s\n' "$version" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$mode_dir/consumer"
|
||||
go mod init example.invalid/himesan-public-install >/dev/null
|
||||
env \
|
||||
GIT_TERMINAL_PROMPT=0 \
|
||||
GIT_CONFIG_NOSYSTEM=1 \
|
||||
GIT_CONFIG_GLOBAL=/dev/null \
|
||||
GIT_ASKPASS="$false_command" \
|
||||
SSH_ASKPASS="$false_command" \
|
||||
GOPATH="$mode_dir/gopath" \
|
||||
GOMODCACHE="$mode_dir/modcache" \
|
||||
GOCACHE="$mode_dir/buildcache" \
|
||||
GOPROXY="$proxy" \
|
||||
GOPRIVATE= \
|
||||
GONOPROXY=none \
|
||||
GONOSUMDB="$no_sum_db" \
|
||||
GOSUMDB=sum.golang.org \
|
||||
GOINSECURE= \
|
||||
GOAUTH=off \
|
||||
go get "gamertan.com/sandwich-hime/sando@$version"
|
||||
)
|
||||
}
|
||||
|
||||
run_install_pair direct direct gamertan.com/sandwich-hime
|
||||
run_install_pair public-proxy 'https://proxy.golang.org' ''
|
||||
|
||||
printf '\nPublic vanity metadata and exact install commands passed for %s.\n' "$version"
|
||||
@@ -0,0 +1,154 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$Race
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
||||
Set-Location $RepoRoot
|
||||
|
||||
function Invoke-Checked {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Label,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[scriptblock]$Command
|
||||
)
|
||||
|
||||
Write-Host "`n==> $Label"
|
||||
& $Command
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "$Label failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-ModuleChecks {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Directory,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Label
|
||||
)
|
||||
|
||||
Push-Location $Directory
|
||||
try {
|
||||
Invoke-Checked "$Label`: go test" { go test ./... }
|
||||
Invoke-Checked "$Label`: go vet" { go vet ./... }
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
}
|
||||
}
|
||||
|
||||
function Get-SandoSources {
|
||||
if (-not (Test-Path "examples" -PathType Container)) {
|
||||
return @()
|
||||
}
|
||||
|
||||
return @(Get-ChildItem "examples" -Recurse -File -Filter "*.sando" |
|
||||
Where-Object { $_.FullName -notmatch '[\\/](vendor|\.git)[\\/]' } |
|
||||
Sort-Object FullName)
|
||||
}
|
||||
|
||||
function Get-GeneratedManifest {
|
||||
$lines = foreach ($source in (Get-SandoSources)) {
|
||||
$output = "$($source.FullName).go"
|
||||
if (-not (Test-Path $output -PathType Leaf)) {
|
||||
"missing $output"
|
||||
continue
|
||||
}
|
||||
$hash = (Get-FileHash -Algorithm SHA256 $output).Hash.ToLowerInvariant()
|
||||
$modified = (Get-Item -LiteralPath $output).LastWriteTimeUtc.Ticks
|
||||
"$hash $modified $output"
|
||||
}
|
||||
return ($lines -join "`n")
|
||||
}
|
||||
|
||||
$TempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("himesan-verify-" + [guid]::NewGuid())
|
||||
New-Item -ItemType Directory -Path $TempRoot | Out-Null
|
||||
|
||||
try {
|
||||
Invoke-ModuleChecks "." "compiler module"
|
||||
Invoke-Checked "compiler module: go build" {
|
||||
go build -trimpath -o (Join-Path $TempRoot "himesan.exe") ./cmd/himesan
|
||||
}
|
||||
|
||||
if (-not (Test-Path "sando/go.mod" -PathType Leaf)) {
|
||||
throw "nested Apache runtime module sando/go.mod is missing"
|
||||
}
|
||||
Invoke-ModuleChecks "sando" "sando runtime module"
|
||||
|
||||
if (Test-Path "examples" -PathType Container) {
|
||||
$ExampleModules = @(Get-ChildItem "examples" -Recurse -File -Filter "go.mod" |
|
||||
Where-Object { $_.FullName -notmatch '[\\/](vendor|\.git)[\\/]' } |
|
||||
Sort-Object FullName)
|
||||
}
|
||||
else {
|
||||
$ExampleModules = @()
|
||||
}
|
||||
|
||||
$Sources = @(Get-SandoSources)
|
||||
if ($Sources.Count -eq 0) {
|
||||
Write-Host "`n==> generation: no .sando examples exist yet; skipping deterministic-generation check"
|
||||
}
|
||||
else {
|
||||
$SourcePaths = @($Sources | ForEach-Object { $_.FullName })
|
||||
$CheckArgs = @("run", "./cmd/himesan", "check") + $SourcePaths
|
||||
$GenerateArgs = @("run", "./cmd/himesan", "generate") + $SourcePaths
|
||||
Invoke-Checked "generation: read-only freshness check" {
|
||||
& go $CheckArgs
|
||||
}
|
||||
$Before = Get-GeneratedManifest
|
||||
|
||||
Invoke-Checked "generation: first deterministic pass" {
|
||||
& go $GenerateArgs
|
||||
}
|
||||
$First = Get-GeneratedManifest
|
||||
if ($Before -cne $First) {
|
||||
throw "generation changed committed output after check declared it fresh"
|
||||
}
|
||||
|
||||
Invoke-Checked "generation: second deterministic pass" {
|
||||
& go $GenerateArgs
|
||||
}
|
||||
$Second = Get-GeneratedManifest
|
||||
if ($First -cne $Second) {
|
||||
throw "repeated generation changed output bytes or an unchanged timestamp"
|
||||
}
|
||||
|
||||
Invoke-Checked "generation: final freshness check" {
|
||||
& go $CheckArgs
|
||||
}
|
||||
}
|
||||
|
||||
# Do not execute generated application code until check and both generation
|
||||
# passes prove that it is compiler-owned, current, and deterministic.
|
||||
if ($ExampleModules.Count -eq 0) {
|
||||
Write-Host "`n==> examples: no example module exists yet; skipping module tests"
|
||||
}
|
||||
else {
|
||||
foreach ($module in $ExampleModules) {
|
||||
Invoke-ModuleChecks $module.Directory.FullName "example module $($module.Directory.FullName)"
|
||||
}
|
||||
}
|
||||
|
||||
if ($Race) {
|
||||
Invoke-Checked "compiler module: race tests" { go test -race ./... }
|
||||
Push-Location "sando"
|
||||
try {
|
||||
Invoke-Checked "sando runtime module: race tests" { go test -race ./... }
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "`n==> verification complete"
|
||||
}
|
||||
finally {
|
||||
if (Test-Path $TempRoot -PathType Container) {
|
||||
Remove-Item -LiteralPath $TempRoot -Recurse -Force
|
||||
}
|
||||
}
|
||||
Executable
+158
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
cd "$repo_root"
|
||||
|
||||
log() {
|
||||
printf '\n==> %s\n' "$*"
|
||||
}
|
||||
|
||||
run_module_checks() {
|
||||
local module_dir=$1
|
||||
local label=$2
|
||||
|
||||
log "$label: go test"
|
||||
(
|
||||
cd "$module_dir"
|
||||
go test ./...
|
||||
)
|
||||
|
||||
log "$label: go vet"
|
||||
(
|
||||
cd "$module_dir"
|
||||
go vet ./...
|
||||
)
|
||||
}
|
||||
|
||||
example_sources() {
|
||||
if [[ ! -d examples ]]; then
|
||||
return 0
|
||||
fi
|
||||
find examples \
|
||||
-type d \( -name .git -o -name vendor \) -prune -o \
|
||||
-type f -name '*.sando' -print | LC_ALL=C sort
|
||||
}
|
||||
|
||||
example_modules() {
|
||||
if [[ ! -d examples ]]; then
|
||||
return 0
|
||||
fi
|
||||
find examples \
|
||||
-type d \( -name .git -o -name vendor \) -prune -o \
|
||||
-type f -name go.mod -print | LC_ALL=C sort
|
||||
}
|
||||
|
||||
generated_manifest() {
|
||||
local source output digest modified
|
||||
while IFS= read -r source; do
|
||||
[[ -n "$source" ]] || continue
|
||||
output="${source}.go"
|
||||
if [[ ! -f "$output" ]]; then
|
||||
printf 'missing %s\n' "$output"
|
||||
continue
|
||||
fi
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
digest=$(sha256sum -- "$output" | awk '{print $1}')
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
digest=$(shasum -a 256 -- "$output" | awk '{print $1}')
|
||||
else
|
||||
printf 'error: sha256sum or shasum is required for generation verification\n' >&2
|
||||
return 1
|
||||
fi
|
||||
if stat -c '%Y' -- "$output" >/dev/null 2>&1; then
|
||||
modified=$(stat -c '%Y' -- "$output")
|
||||
else
|
||||
modified=$(stat -f '%m' -- "$output")
|
||||
fi
|
||||
printf '%s %s %s\n' "$digest" "$modified" "$output"
|
||||
done < <(example_sources)
|
||||
}
|
||||
|
||||
log "repository scripts: shell syntax"
|
||||
bash -n scripts/*.sh
|
||||
|
||||
run_module_checks . "compiler module"
|
||||
|
||||
log "compiler module: go build"
|
||||
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/himesan-verify.XXXXXXXX")
|
||||
cleanup() {
|
||||
if [[ -n "${build_dir:-}" && -d "$build_dir" ]]; then
|
||||
rm -rf -- "$build_dir"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
go build -trimpath -o "$build_dir/himesan" ./cmd/himesan
|
||||
|
||||
if [[ ! -f sando/go.mod ]]; then
|
||||
printf 'error: nested Apache runtime module sando/go.mod is missing\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
run_module_checks sando "sando runtime module"
|
||||
|
||||
log "static project site"
|
||||
./scripts/check-site.sh
|
||||
|
||||
sources=()
|
||||
while IFS= read -r source; do
|
||||
[[ -n "$source" ]] || continue
|
||||
sources[${#sources[@]}]=$source
|
||||
done < <(example_sources)
|
||||
if (( ${#sources[@]} == 0 )); then
|
||||
log "generation: no .sando examples exist yet; skipping deterministic-generation check"
|
||||
else
|
||||
log "generation: read-only freshness check"
|
||||
go run ./cmd/himesan check "${sources[@]}"
|
||||
|
||||
manifest_before="$build_dir/generated-before.txt"
|
||||
manifest_first="$build_dir/generated-first.txt"
|
||||
manifest_second="$build_dir/generated-second.txt"
|
||||
generated_manifest >"$manifest_before"
|
||||
|
||||
log "generation: first deterministic pass"
|
||||
go run ./cmd/himesan generate "${sources[@]}"
|
||||
generated_manifest >"$manifest_first"
|
||||
if ! cmp -s "$manifest_before" "$manifest_first"; then
|
||||
printf 'error: generation changed committed output after check declared it fresh\n' >&2
|
||||
diff -u "$manifest_before" "$manifest_first" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "generation: second deterministic pass"
|
||||
go run ./cmd/himesan generate "${sources[@]}"
|
||||
generated_manifest >"$manifest_second"
|
||||
if ! cmp -s "$manifest_first" "$manifest_second"; then
|
||||
printf 'error: repeated generation changed output bytes or an unchanged timestamp\n' >&2
|
||||
diff -u "$manifest_first" "$manifest_second" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
go run ./cmd/himesan check "${sources[@]}"
|
||||
fi
|
||||
|
||||
# Generated application code is not executed until the read-only freshness
|
||||
# check and both deterministic passes prove it is compiler-owned and current.
|
||||
module_count=0
|
||||
while IFS= read -r module_file; do
|
||||
[[ -n "$module_file" ]] || continue
|
||||
module_count=$((module_count + 1))
|
||||
module_dir=$(dirname -- "$module_file")
|
||||
run_module_checks "$module_dir" "example module $module_dir"
|
||||
done < <(example_modules)
|
||||
if (( module_count == 0 )); then
|
||||
log "examples: no example module exists yet; skipping module tests"
|
||||
fi
|
||||
|
||||
if [[ "${HIMESAN_RACE:-0}" == 1 ]]; then
|
||||
log "compiler module: race tests"
|
||||
go test -race ./...
|
||||
log "sando runtime module: race tests"
|
||||
(
|
||||
cd sando
|
||||
go test -race ./...
|
||||
)
|
||||
fi
|
||||
|
||||
log "verification complete"
|
||||
Reference in New Issue
Block a user