Skip to content

Print elapsed-time status while transcribe engine runs #279

Print elapsed-time status while transcribe engine runs

Print elapsed-time status while transcribe engine runs #279

Workflow file for this run

name: ci
# Lightweight checks on every push and pull request: format, build, vet, and
# test on Linux, a cross-compile check for the other release platforms, a
# version-stamp ldflags check, a smoke test of the merge → report pipeline on
# the bundled sample, installer syntax and flag-handling checks, full-history
# secret scanning, and a workflow audit. Also runs on `merge_group` so the
# same jobs gate each entry the merge queue builds — the queue tests every PR
# against the exact main it will land on, and its required checks are these
# same contexts, so they MUST trigger here or the queue hangs.
on:
push:
branches: ['**']
pull_request:
merge_group:
# Cancel superseded runs of the same ref (PRs and non-default branch pushes).
# Never cancel a merge-queue run (each queue entry must complete to merge) or a
# default-branch push; each already has its own ref, so this only affects
# rapidly-superseded PR/branch pushes.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name != 'merge_group' && github.ref != format('refs/heads/{0}', github.event.repository.default_branch) }}
permissions:
contents: read
jobs:
# Build + vet + test + pipeline smoke, race-enabled, plus a compile-only
# cross-check for the other release targets. Ubuntu-only for the test run: a
# macos-latest leg aborts the demo/record race binaries with a runner-specific
# dyld "missing LC_UUID" trap (a GitHub image + race-detector issue, not our
# code — the suite is green on a real Mac). Actually running the suite on
# darwin is covered by no gate here; only the binary compiling is (the
# Cross-compile check step below builds ./cmd/testimony, not _test.go
# files). A single job named
# "check" is also the context branch protection requires; a matrix would
# rename it and never report that context.
check:
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version-file: go.mod
# cache: false — the repo has zero dependencies and no go.sum, so
# there is nothing for actions/setup-go's module cache to hold; the
# default (true) restores nothing every run and prints a standing
# "cache folder ... doesn't exist on disk" warning. release.yml
# already sets this for its own reason (cache-poisoning surface on
# a tag-triggered workflow); here it is pure removed overhead.
cache: false
# Any file gofmt lists needs `gofmt -w` and fails the job.
- name: Format (gofmt)
run: |
set -euo pipefail
unformatted="$(gofmt -l .)"
if [ -n "$unformatted" ]; then
echo "gofmt: these files are not formatted (run gofmt -w .):" >&2
echo "$unformatted" >&2
exit 1
fi
- name: Build
run: go build -o testimony ./cmd/testimony
- name: Vet
run: go vet ./...
- name: Test
run: go test -race ./...
# A GOOS-conditional compile break — GOOS-sensitive syscall usage (e.g.
# internal/record's Setpgid or internal/transcribe's Umask) built for a
# platform other than the one it was written and tested against — would
# pass every gate above and only surface in release.yml's
# cross-compile — after a tag is already pushed. Compile-only (this job
# runs no test binaries but its own), CGO_ENABLED=0 to match release.yml's
# build flags; linux/amd64 (the host) is already covered by Build above.
- name: Cross-compile check (darwin, linux/arm64)
run: |
set -euo pipefail
for target in darwin/arm64 darwin/amd64 linux/arm64; do
goos="${target%/*}"
goarch="${target#*/}"
echo "building for $goos/$goarch"
GOOS="$goos" GOARCH="$goarch" CGO_ENABLED=0 go build -o /dev/null ./cmd/testimony
done
# The Go linker silently ignores -X for a symbol that does not exist —
# rename or move internal/cli.Version, or the module path, and every
# gate above stays green while release.yml's -ldflags stamp silently
# stops taking effect, surfacing only after a tag is already pushed (the
# same "surfaces only after the tag is public" class the cross-compile
# check above exists to close, applied to the version stamp instead of
# a GOOS-conditional break). Built and run on the host (linux/amd64) with
# the identical -X path release.yml uses, so a broken symbol path fails
# here first.
- name: Version-stamp ldflags check
run: |
set -euo pipefail
go build -o /tmp/testimony-stamp-check \
-ldflags "-X github.com/REPPL/Testimony/internal/cli.Version=vTEST" \
./cmd/testimony
got="$(/tmp/testimony-stamp-check version)"
if [ "$got" != "testimony vTEST" ]; then
echo "version-stamp ldflags had no effect: got \"$got\", want \"testimony vTEST\" — internal/cli.Version or the module path may have moved" >&2
exit 1
fi
# The full pipeline against the bundled sample session: merge must produce
# a timeline, report must render it, and both outputs must be non-empty.
# Ubuntu-only, like the rest of this job — it also proves the freshly built
# binary actually runs the pipeline, not merely that it compiles.
- name: Pipeline smoke test
run: |
set -euo pipefail
./testimony merge -session examples/sample-session
./testimony report -session examples/sample-session
test -s examples/sample-session/timeline.jsonl
test -s examples/sample-session/report.md
grep -q "## Timeline" examples/sample-session/report.md
grep -q "save button" examples/sample-session/report.md
# The bundled findings.jsonl renders the Findings section with the
# confirmed save-feedback bug (F-001).
grep -q "## Findings" examples/sample-session/report.md
grep -q "### Confirmed (1)" examples/sample-session/report.md
grep -q "\*\*F-001\*\* bug" examples/sample-session/report.md
# The event half of the pipeline: every assertion above is satisfiable
# from the transcript and findings alone (verified: deleting
# interactions.jsonl left them all green) — the header counts assertion
# below is what catches that, since the save-btn selector also renders
# via findings.jsonl's Findings section regardless of events. The exact
# counts pin the bundled fixture; extending the sample session means
# updating them here and in release.yml.
grep -q "\*\*Utterances:\*\* 10 · \*\*Events:\*\* 10" examples/sample-session/report.md
grep -q "data-testid=save-btn" examples/sample-session/report.md
# The counts line above catches events going missing from the merge,
# but not a windowing/attachment regression: report's header counts are
# raw entry counts, computed before the join, so they stay "10 · 10"
# even when every event is detached from the speech it accompanies
# (verified: -window set absurdly negative still passes every assertion
# above). An indented bullet is report's join marker (an unattached
# event renders as a standalone "- ", flush with the left margin), so
# assert one specifically: the first Save click, joined under its
# utterance.
grep -q '^ - \[00:19\] click `\[data-testid=save-btn\]`' examples/sample-session/report.md
# install.sh is served live from main as the documented install path, so
# a merged syntax error breaks `curl | sh` for every new user with no
# gate having run. Parse it with both shells an operator realistically
# pipes it into.
- name: Installer syntax
run: |
set -euo pipefail
sh -n install.sh
bash -n install.sh
# A syntax check alone never runs a single line of install.sh, so a
# behavioural regression in --help or the flag-error paths (CHANGELOG's
# "Installer" entries) would ship to `curl | sh` users with no gate having
# exercised it. These six paths all return before any network access or
# write, so they run safely offline, unlike a full install. Each failing
# case is wrapped in `{ ...; || true; }` so its deliberate non-zero exit
# does not trip pipefail before grep gets to check the message — a plain
# `(cmd; true)` subshell does not do this under `set -e`: -e is inherited
# into the subshell, so `cmd` failing aborts it right there and `true`
# never runs, leaving the subshell's own exit status non-zero after all.
- name: Installer flag handling
run: |
set -euo pipefail
sh install.sh --help | grep -q "Flags:"
{ sh install.sh --dir || true; } 2>&1 | grep -q "needs a value"
{ sh install.sh --version || true; } 2>&1 | grep -q "needs a value"
{ sh install.sh --dir "" || true; } 2>&1 | grep -q "needs a value"
{ sh install.sh --version "" || true; } 2>&1 | grep -q "needs a value"
{ sh install.sh --bogus || true; } 2>&1 | grep -q "unknown flag"
# Secrets never land in history: full-history scan on every push/PR. Run as a
# pinned, checksum-verified CLI (no PR-comment / SARIF GitHub-API integration
# and no marketplace-action licence caveat), so it is self-contained. A
# non-zero exit means gitleaks found a secret.
gitleaks:
timeout-minutes: 10
runs-on: ubuntu-latest
env:
GITLEAKS_VERSION: 8.24.3
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Install gitleaks (pinned, checksum-verified)
run: |
set -euo pipefail
cd "$RUNNER_TEMP"
base="gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
url="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}"
curl -sSLf -o "$base" "$url/$base"
curl -sSLf -o checksums.txt "$url/gitleaks_${GITLEAKS_VERSION}_checksums.txt"
grep " $base\$" checksums.txt | sha256sum -c -
tar -xzf "$base" gitleaks
sudo install gitleaks /usr/local/bin/gitleaks
gitleaks version
- name: Scan full history
run: gitleaks git --redact --no-banner .
# Audit the workflows themselves (pwn requests, template injection, cache
# poisoning, unpinned uses). The action runs zizmor and gates on its findings
# (a non-zero exit means a finding); SARIF upload to Code Scanning is off — see
# the advanced-security note below.
zizmor:
timeout-minutes: 10
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7
with:
# advanced-security: false — Code Scanning is not enabled on this repo,
# so a SARIF upload would fail the job even with zero findings. The
# action still runs zizmor and fails on any finding above its threshold;
# findings surface in the job log. Flip to true (and add
# security-events: write) if Code Scanning default setup is enabled later.
advanced-security: false