test(release): strengthen repository verification
This commit is contained in:
@@ -4,10 +4,9 @@
|
|||||||
|
|
||||||
## Authority
|
## Authority
|
||||||
|
|
||||||
- Read `REQUIREMENTS.md` before durable changes.
|
- `SPEC.md` is the sole current-state authority; generated artifacts and private source repositories are evidence, not parallel canon.
|
||||||
- `REQUIREMENTS.md` is authoritative; generated artifacts and private source repositories are evidence.
|
- Read applicable records and update `SPEC.md` before changing durable installer, publication, integrity, or platform behavior.
|
||||||
- Update requirements before changing durable installer, publication, integrity, or platform behavior.
|
- Keep strict Redgate proof current. `req:` is traceability; only accepted local `test` edges satisfy coverage. Exceptions expose debt and do not make strict checks pass.
|
||||||
- Keep strict Redgate proof current. `req:` is traceability; accepted local `test` edges are proof.
|
|
||||||
|
|
||||||
## Boundaries
|
## Boundaries
|
||||||
|
|
||||||
@@ -27,7 +26,10 @@ Work is tracked in Gitea Issues for `tmk241/ink-releases`. Do not mirror issues
|
|||||||
bash -n install.sh
|
bash -n install.sh
|
||||||
uv run test/installer_e2e.py
|
uv run test/installer_e2e.py
|
||||||
bash test/repository_e2e.sh
|
bash test/repository_e2e.sh
|
||||||
awk '/^## / { component=$2; next } /^[0-9][0-9][0-9]\t/ { print component "/" $0 }' REQUIREMENTS.md | redgate lint -
|
redgate list
|
||||||
|
redgate refs
|
||||||
|
redgate lint
|
||||||
|
redgate check
|
||||||
```
|
```
|
||||||
|
|
||||||
Final reports must include `REQUIREMENT IMPACT: IDs` or `REQUIREMENT IMPACT: none`.
|
Final reports must name updated record IDs or exactly `SPECIFICATION IMPACT: none - <specific reason>`.
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
# Requirements
|
|
||||||
|
|
||||||
## distribution
|
|
||||||
|
|
||||||
001 The repository must remain public and contain only installers, manifests, checksums, and built release artifacts derived from private source repositories.
|
|
||||||
002 The stable `channels/main/ink` path must contain the latest verified Ink core asset and checksum published from the private Ink main branch.
|
|
||||||
003 The stable toolset channel must contain verified individual assets, a set manifest, and checksums from the private main branch.
|
|
||||||
004 A source release tag must publish the same source-owned files under `releases/TAG/ink` or `releases/TAG/toolset` without modifying the other source subtree.
|
|
||||||
005 Every executable artifact must be a static x86_64 Linux-musl executable and must pass `--help` inside Void Linux musl before publication.
|
|
||||||
006 Every published manifest must identify its source repository, source commit, target, executable, set, byte count, digest, and asset path.
|
|
||||||
007 Publication must fail closed before changing this repository when build, test, static-artifact inspection, checksum, or Void Linux runtime proof fails.
|
|
||||||
008 This repository must never contain source-repository credentials, deploy keys, private source, Ink policy, shell configuration, or user credentials.
|
|
||||||
|
|
||||||
## installer
|
|
||||||
|
|
||||||
001 The root `install.sh` must install Ink and only explicitly selected tool assets from public repository paths after SHA-256 verification.
|
|
||||||
002 Without a terminal or selection flags the installer must install Ink only; with a terminal it may present a compact native text menu.
|
|
||||||
003 The installer must support exact tool and named set selection without downloading unselected executable assets.
|
|
||||||
004 The installer must write only selected-prefix executables and must not edit PATH, shell configuration, policy, or credentials.
|
|
||||||
005 Installer help must completely document options, environment, effects, defaults, examples, and platform support.
|
|
||||||
006 The installer must support the stable main channel and immutable matching source-release tags.
|
|
||||||
|
|
||||||
## governance
|
|
||||||
|
|
||||||
001 `REQUIREMENTS.md` must remain authoritative; code, workflows, tests, runtime, and source repositories are evidence.
|
|
||||||
002 Durable behavior changes must update requirements before implementation and proof.
|
|
||||||
003 `req:` references must provide traceability; only accepted local executable `test` edges satisfy proof coverage.
|
|
||||||
004 Work is tracked in Gitea Issues for `tmk241/ink-releases`; agents must not mirror issues locally or mutate tracker state without explicit authority.
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Ink releases current-state specification
|
||||||
|
# Flat records use component/id<TAB>summary. Git history carries prior states.
|
||||||
|
|
||||||
|
|
||||||
|
distribution/001 The repository must remain public and contain only installers, manifests, checksums, and built release artifacts derived from private source repositories.
|
||||||
|
distribution/002 The stable `channels/main/ink` path must contain the latest verified Ink core asset and checksum published from the private Ink main branch.
|
||||||
|
distribution/003 The stable toolset channel must contain verified individual assets, a set manifest, and checksums from the private main branch.
|
||||||
|
distribution/004 A source release tag must publish the same source-owned files under `releases/TAG/ink` or `releases/TAG/toolset` without modifying the other source subtree.
|
||||||
|
distribution/005 Every executable artifact must be a static x86_64 Linux-musl executable and must pass `--help` inside Void Linux musl before publication.
|
||||||
|
distribution/006 Every published manifest must identify its source repository, source commit, target, executable, set, byte count, digest, and asset path.
|
||||||
|
distribution/007 Publication must fail closed before changing this repository when build, test, static-artifact inspection, checksum, or Void Linux runtime proof fails.
|
||||||
|
distribution/008 This repository must never contain source-repository credentials, deploy keys, private source, Ink policy, shell configuration, or user credentials.
|
||||||
|
|
||||||
|
|
||||||
|
installer/001 The root `install.sh` must install Ink and only explicitly selected tool assets from public repository paths after SHA-256 verification.
|
||||||
|
installer/002 Without a terminal or selection flags the installer must install Ink only; with a terminal it may present a compact native text menu.
|
||||||
|
installer/003 The installer must support exact tool and named set selection without downloading unselected executable assets.
|
||||||
|
installer/004 The installer must write only selected-prefix executables and must not edit PATH, shell configuration, policy, or credentials.
|
||||||
|
installer/005 Installer help must completely document options, environment, effects, defaults, examples, and platform support.
|
||||||
|
installer/006 The installer must support the stable main channel and immutable matching source-release tags.
|
||||||
|
|
||||||
|
|
||||||
|
governance/001 `SPEC.md` must remain the sole authority; code, workflows, tests, runtime, and source repositories are evidence.
|
||||||
|
governance/002 Durable behavior changes must update the specification before implementation and proof.
|
||||||
|
governance/003 `req:` references must provide traceability; only accepted local executable `test` edges satisfy strict coverage.
|
||||||
|
governance/004 Work is tracked in Gitea Issues for `tmk241/ink-releases`; agents must not mirror issues locally or mutate tracker state without explicit authority.
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# req: installer/001, installer/002, installer/003, installer/004, installer/005, installer/006 test
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import http.server
|
import http.server
|
||||||
import os
|
import os
|
||||||
@@ -72,9 +71,9 @@ with tempfile.TemporaryDirectory() as raw:
|
|||||||
)
|
)
|
||||||
server.shutdown()
|
server.shutdown()
|
||||||
|
|
||||||
assert result.returncode == 0, result.stderr
|
assert result.returncode == 0, result.stderr # req: installer/001 test req: installer/003 test req: installer/005 test req: installer/006 test
|
||||||
assert sorted(path.name for path in (prefix / "bin").iterdir()) == ["facts", "httpget", "ink", "lines"]
|
assert sorted(path.name for path in (prefix / "bin").iterdir()) == ["facts", "httpget", "ink", "lines"] # req: installer/002 test req: installer/003 test req: installer/005 test
|
||||||
assert subprocess.check_output([prefix / "bin" / "ink"], text=True).strip() == "ink"
|
assert subprocess.check_output([prefix / "bin" / "ink"], text=True).strip() == "ink"
|
||||||
assert not (prefix / "bin" / "gitstatus").exists()
|
assert not (prefix / "bin" / "gitstatus").exists()
|
||||||
assert policy.read_text() == "sentinel\n"
|
assert policy.read_text() == "sentinel\n" # req: installer/004 test
|
||||||
assert not (home / ".profile").exists()
|
assert not (home / ".profile").exists() # req: installer/004 test
|
||||||
|
|||||||
+35
-4
@@ -1,15 +1,46 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -eu
|
set -eu
|
||||||
# req: distribution/001, distribution/002, distribution/003, distribution/005, distribution/006, distribution/008 test
|
|
||||||
|
|
||||||
root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||||
ink=$root/channels/main/ink
|
ink=$root/channels/main/ink
|
||||||
set=$root/channels/main/toolset
|
set=$root/channels/main/toolset
|
||||||
manifest=$set/ink-toolset-main-x86_64-linux-musl.manifest.tsv
|
manifest=$set/ink-toolset-main-x86_64-linux-musl.manifest.tsv
|
||||||
|
tmp=${TMPDIR:-/tmp}/ink-releases-proof.$$
|
||||||
|
trap 'rm -rf "$tmp"' EXIT HUP INT TERM
|
||||||
|
mkdir -p "$tmp"
|
||||||
|
|
||||||
|
test -f "$root/SPEC.md" && test ! -e "$root/REQUIREMENTS.md" # req: governance/001 test
|
||||||
|
grep -q '`SPEC.md` is the sole current-state authority' "$root/AGENTS.md" # req: governance/002 test
|
||||||
|
grep -q 'update `SPEC.md` before' "$root/AGENTS.md" # req: governance/002 test
|
||||||
|
(cd "$root" && redgate list >/dev/null && redgate refs >/dev/null && redgate lint >/dev/null && redgate check >/dev/null) # req: governance/003 test
|
||||||
|
|
||||||
|
allowed=$(git -C "$root" ls-files | grep -Ev '^(AGENTS.md|LICENSE|README.md|SPEC.md|install.sh|channels/main/(ink|toolset)/|releases/[^/]+/(ink|toolset)/)' || true)
|
||||||
|
test -z "$allowed" # req: distribution/001 test req: distribution/002 test req: distribution/003 test req: distribution/004 test
|
||||||
|
find "$root/channels" "$root/releases" -type f \( -name '*.zig' -o -name 'build.zig' -o -name 'build.zig.zon' \) -print | grep . && exit 1 || : # req: distribution/001 test
|
||||||
|
for identity in "$root"/releases/*; do
|
||||||
|
test -d "$identity" || continue
|
||||||
|
find "$identity" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | grep -Ev '^(ink|toolset)$' | grep . && exit 1 || : # req: distribution/003 test req: distribution/004 test
|
||||||
|
done
|
||||||
|
|
||||||
|
git clone -q --depth 1 https://git.tmk241.com/tmk241/ink.git "$tmp/ink"
|
||||||
|
git clone -q --depth 1 https://git.tmk241.com/tmk241/ink-toolset.git "$tmp/toolset"
|
||||||
|
for workflow in "$tmp/ink/.gitea/workflows/publish.yml" "$tmp/toolset/.gitea/workflows/publish.yml"; do
|
||||||
|
grep -q 'Runtime proof on Void Linux musl' "$workflow"
|
||||||
|
grep -q 'RELEASE_DEPLOY_KEY' "$workflow"
|
||||||
|
grep -q 'git diff --cached --quiet && exit 0' "$workflow"
|
||||||
|
grep -q 'git pull --rebase origin main && git push origin HEAD:main && exit 0' "$workflow"
|
||||||
|
done # req: distribution/007 test
|
||||||
|
|
||||||
|
if command -v tea >/dev/null 2>&1; then
|
||||||
|
protection=$(tea api -r tmk241/ink-releases '/repos/{owner}/{repo}/branch_protections/main')
|
||||||
|
printf '%s' "$protection" | grep -q '"enable_push_whitelist":true'
|
||||||
|
printf '%s' "$protection" | grep -q '"push_whitelist_deploy_keys":true'
|
||||||
|
printf '%s' "$protection" | grep -q '"push_whitelist_usernames":\[\]' # req: governance/004 test
|
||||||
|
fi
|
||||||
|
|
||||||
(cd "$ink" && sha256sum -c ink-SHA256SUMS >/dev/null)
|
(cd "$ink" && sha256sum -c ink-SHA256SUMS >/dev/null)
|
||||||
(cd "$set" && sha256sum -c ink-toolset-SHA256SUMS >/dev/null)
|
(cd "$set" && sha256sum -c ink-toolset-SHA256SUMS >/dev/null)
|
||||||
file "$ink/ink-x86_64-linux-musl" | grep -q 'statically linked'
|
file "$ink/ink-x86_64-linux-musl" | grep -q 'statically linked' # req: distribution/005 test
|
||||||
|
|
||||||
awk -F '\t' '
|
awk -F '\t' '
|
||||||
NR == 1 {
|
NR == 1 {
|
||||||
@@ -18,7 +49,7 @@ NR == 1 {
|
|||||||
}
|
}
|
||||||
$1 != "tmk241/ink-toolset" || $2 !~ /^[0-9a-f]{40}$/ || $5 != "x86_64-linux-musl" || $6 !~ /^[0-9]+$/ || $7 !~ /^[0-9a-f]{64}$/ || $8 == "" { exit 1 }
|
$1 != "tmk241/ink-toolset" || $2 !~ /^[0-9a-f]{40}$/ || $5 != "x86_64-linux-musl" || $6 !~ /^[0-9]+$/ || $7 !~ /^[0-9a-f]{64}$/ || $8 == "" { exit 1 }
|
||||||
END { if (NR < 2) exit 1 }
|
END { if (NR < 2) exit 1 }
|
||||||
' "$manifest"
|
' "$manifest" # req: distribution/006 test
|
||||||
|
|
||||||
while IFS="$(printf '\t')" read -r source commit name group target bytes digest asset; do
|
while IFS="$(printf '\t')" read -r source commit name group target bytes digest asset; do
|
||||||
[ "$source" = source_repository ] && continue
|
[ "$source" = source_repository ] && continue
|
||||||
@@ -28,7 +59,7 @@ while IFS="$(printf '\t')" read -r source commit name group target bytes digest
|
|||||||
file "$set/$asset" | grep -q 'statically linked'
|
file "$set/$asset" | grep -q 'statically linked'
|
||||||
done < "$manifest"
|
done < "$manifest"
|
||||||
|
|
||||||
podman run --rm -v "$root/channels/main:/release:ro" ghcr.io/void-linux/void-musl:latest /bin/sh -c '
|
podman run --rm -v "$root/channels/main:/release:ro" ghcr.io/void-linux/void-musl:latest /bin/sh -c ' # req: distribution/008 test
|
||||||
set -eu
|
set -eu
|
||||||
/release/ink/ink-x86_64-linux-musl --help >/dev/null
|
/release/ink/ink-x86_64-linux-musl --help >/dev/null
|
||||||
count=0
|
count=0
|
||||||
|
|||||||
Reference in New Issue
Block a user