Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Anvil ships Linux binaries and Linux-targeted deploy assets, and its CI runs
# on ubuntu-latest. Line endings are therefore LF everywhere, enforced here
# rather than left to each contributor's core.autocrlf setting.
#
# Without this, a Windows checkout gets CRLF working-tree files, `gofmt -l`
# reports every Go file as unformatted locally while CI (which sees LF) passes,
# and local verification stops being trustworthy. Every packet in
# plan/IMPLEMENTATION-PLAN.md owes build/test evidence, so local verification
# has to mean something.

* text=auto eol=lf

*.go text eol=lf diff=golang
*.sql text eol=lf
*.py text eol=lf
*.sh text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.json text eol=lf
*.md text eol=lf
*.toml text eol=lf
go.mod text eol=lf
go.sum text eol=lf
Makefile text eol=lf
Dockerfile text eol=lf

# systemd units are parsed on Linux only and must never carry CR.
*.service text eol=lf
*.timer text eol=lf
*.slice text eol=lf

# Model weights, archives, and fixture corpora.
*.gguf binary
*.safetensors binary
*.onnx binary
*.db binary
*.sqlite binary
*.tar binary
*.gz binary
*.zip binary
*.png binary
180 changes: 180 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
name: CI

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
go:
name: Go build, vet, test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
# Single source of truth: the toolchain line in go.mod.
go-version-file: go.mod
cache: true

- name: go mod tidy is a no-op
run: |
cp go.mod go.mod.bak
cp go.sum go.sum.bak
go mod tidy
diff -u go.mod.bak go.mod
diff -u go.sum.bak go.sum
rm go.mod.bak go.sum.bak

- name: gofmt
run: |
unformatted=$(gofmt -l .)
if [ -n "$unformatted" ]; then
echo "::error::gofmt needed on:"
echo "$unformatted"
gofmt -d .
exit 1
fi

- name: go vet
run: go vet ./...

- name: go build
run: go build ./...

- name: go test
run: go test -race -count=1 ./...

# plan/00-SPINE.md S9-AMENDED splits Anvil into two distribution artifacts:
# anvil (core, no network-probing capability compiled in) and anvil-dast.
# plan/IMPLEMENTATION-PLAN.md 2.2 rules that a config flag inside a single
# binary does not address the concern the split exists for. This job is what
# makes the split real rather than conventional.
artifact-split:
name: Two-artifact split (S9-AMENDED)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Core binary has no DAST capability in its import graph
run: go test ./cmd/anvil -run TestSplit -v

- name: Negative control -- the guard must be able to fail
run: |
set -e
mkdir -p internal/dast/ciprobe
cat > internal/dast/ciprobe/probe.go <<'EOF'
package ciprobe

const Sentinel = "ci-negative-control"
EOF
cat > cmd/anvil/ci_negctl.go <<'EOF'
package main

import _ "github.com/Susquehanna-Syntax/Anvil/internal/dast/ciprobe"
EOF
if go test ./cmd/anvil -run TestSplit; then
echo "::error::TestSplit PASSED with an internal/dast import present. The guard is a no-op."
exit 1
fi
echo "guard correctly failed on the injected violation"
rm -f cmd/anvil/ci_negctl.go
rm -rf internal/dast/ciprobe
go test ./cmd/anvil -run TestSplit

- name: Static, cgo-free builds for both artifacts
env:
CGO_ENABLED: "0"
run: |
set -e
for goarch in amd64 arm64; do
for target in anvil anvil-dast; do
GOOS=linux GOARCH=$goarch go build -o "/tmp/${target}-linux-${goarch}" "./cmd/${target}"
file "/tmp/${target}-linux-${goarch}"
done
done
# S12 mandates modernc.org/sqlite precisely so no cgo/C toolchain is
# needed. mattn/go-sqlite3 would reintroduce both.
if go list -deps ./... | grep -q 'github.com/mattn/go-sqlite3'; then
echo "::error::mattn/go-sqlite3 is in the dependency graph. S12 mandates modernc.org/sqlite."
exit 1
fi

plan:
name: Plan cross-reference invariants
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"

# plan/IMPLEMENTATION-PLAN.md 2.6: "Re-run it after any edit to an area
# file." Making that a CI job is the only way it actually happens.
- name: tools/plan_xref.py
run: python tools/plan_xref.py

- name: tools/plan_fields.py parses
run: python tools/plan_fields.py > /dev/null

licence:
name: Licence hygiene
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# plan/00-SPINE.md S8: read LICENSE file bodies, never API metadata.
# Seven audited artifacts return NOASSERTION over a real licence and one
# hides a restrictive licence behind a permissive tag.
- name: LICENSE is canonical Apache-2.0
run: |
expected=cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30
actual=$(sha256sum LICENSE | cut -d' ' -f1)
if [ "$actual" != "$expected" ]; then
echo "::error::LICENSE sha256 is $actual, expected the canonical Apache-2.0 $expected"
exit 1
fi

- name: Required compliance files exist
run: |
set -e
for f in LICENSE NOTICE THIRD-PARTY-LICENSES.md; do
test -s "$f" || { echo "::error::$f missing or empty (plan step C.1)"; exit 1; }
done

# plan/00-SPINE.md S5 is enforced, not advisory. This is a coarse
# placeholder for cmd/license-gate (plan steps C.9/C.10), which reads
# licence bodies properly; it exists so the exclusion list is not purely
# documentary between now and Phase 6.
- name: Hard exclusions (S5) are absent from the dependency graph
run: |
set -e
fail=0
# Deliberately narrow: module paths only, so prose in plan/ and
# NOTICE -- which must be free to *name* these to explain the
# exclusions -- does not trip the gate.
for banned in \
"github.com/github/codeql" \
"github.com/returntocorp/semgrep" \
"github.com/opengrep/opengrep-rules" \
"github.com/AFLplusplus/AFLplusplus" \
"github.com/mattn/go-sqlite3" ; do
if grep -rn --include=go.mod --include=go.sum "$banned" . ; then
echo "::error::$banned is on the S5 hard exclusion list"
fail=1
fi
done
exit $fail
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module github.com/Susquehanna-Syntax/Anvil

go 1.26.5

require modernc.org/sqlite v1.56.0

require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
Expand All @@ -12,5 +14,4 @@ require (
modernc.org/libc v1.74.4 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
modernc.org/sqlite v1.56.0 // indirect
)
30 changes: 30 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 h1:LMLX+LgTNWpfvCBdFebv6EsYotImrt/Ppc5cXIriCSo=
github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
modernc.org/cc/v4 v4.29.1 h1:MKgdCV3WykTSPqpVrnxdEDS0HEd2FHpKZDzxzU5LyeI=
modernc.org/cc/v4 v4.29.1/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU=
modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk=
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI=
modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.74.4 h1:fX1Omw4o2/1C2iRkkIsrQTasJQldLhRmuPreXLoWs9k=
modernc.org/libc v1.74.4/go.mod h1:eeQAS9W3sZeKYMFubydxJpII9ybHWshk+7or7bLG9co=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.56.0 h1:/D8e2RfFqoy/Zc6PuC76U28zFwmI/sYx1Kjm4yEn9e0=
modernc.org/sqlite v1.56.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
24 changes: 24 additions & 0 deletions internal/buildpin/pin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Package buildpin holds Phase 0 dependency pins alive in the module graph
// until the packages that actually use them exist.
//
// plan/00-SPINE.md S12 mandates modernc.org/sqlite, and
// plan/spine-c-language.md C5 pins it at v1.56.0. plan/IMPLEMENTATION-PLAN.md
// Phase 0 requires that dependency be added at bootstrap. But nothing imports
// it yet -- the store is plan step R.4 and its FTS5 startup guard is R.5 --
// and `go mod tidy` removes a module that no package in the module imports.
// Without this file, CI's tidy check would silently delete the pin the
// bootstrap was supposed to establish.
//
// DELETE THIS PACKAGE when internal/store lands (R.4). It has no runtime
// purpose and must never be imported by anything other than itself.
package buildpin

import (
// modernc.org/sqlite: the cgo-free SQLite driver S12 mandates. Do NOT
// substitute mattn/go-sqlite3 -- it needs cgo and a C toolchain, which
// breaks the single static binary and the cross-compilation matrix.
// Verified at bootstrap against this exact version: SQLite 3.53.3, FTS5
// virtual tables and MATCH queries both work. Licence read as a file
// body per S8: BSD-3-Clause.
_ "modernc.org/sqlite"
)
Loading