oracle: an independent library reads every Office package back #93
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| pull_request: | |
| workflow_dispatch: | |
| schedule: | |
| # Weekly, for the fuzzing job below only. Every other job here runs on | |
| # push, and fuzzing deliberately does not - see that job for why. | |
| - cron: "17 4 * * 1" | |
| permissions: | |
| contents: read | |
| env: | |
| # The exact toolchain used for tests and releases. go.mod declares a | |
| # minimum - this is the pin. Raising it can change generated bytes, so the | |
| # byte stability guard has to be green before it moves. | |
| GO_VERSION: "1.26.6" | |
| jobs: | |
| test: | |
| name: test on ${{ matrix.os }} | |
| # A hung job otherwise holds a runner until the GitHub default of six | |
| # hours. Every number here is well above what the job takes today: the | |
| # matrix runs in about a minute, the race detector took 148 s when it was | |
| # measured, and fuzzing is given 5 minutes a target by its own loop. | |
| timeout-minutes: 20 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| # The engine, the command line and the tests build without CGO on every | |
| # system. Only the desktop window needs a C compiler, and it is built | |
| # separately. | |
| CGO_ENABLED: "0" | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| check-latest: false | |
| # Keyed on go.sum. The one dependency is the YAML parser behind the | |
| # recipe - see docs/STACK.md. | |
| cache: true | |
| - name: formatting | |
| run: | | |
| test -z "$(gofmt -l .)" || { echo "gofmt found unformatted files:"; gofmt -l .; exit 1; } | |
| shell: bash | |
| - name: vet | |
| run: go vet ./... | |
| - name: the dependency list has not grown by accident | |
| # Every dependency is a licence question and a byte stability | |
| # question. A new one arriving as somebody's transitive import has | |
| # to be visible rather than discovered later. | |
| # | |
| # Two questions rather than one, since the graphics toolkit arrived | |
| # on 2026-08-05. The graph below is 68 modules and most of them are | |
| # Fyne's. Their licences were measured before it was added and every | |
| # one in the linked set is permissive and one way compatible with | |
| # GPL-3.0: 13 BSD-3, 10 MIT, 1 Apache-2.0, 1 BSD-2, 1 ISC. | |
| # | |
| # Changed once since, on 2026-08-05, when the window gained a | |
| # folder picker. github.com/FyshOS/fancyfs was already named here | |
| # because the toolkit requires it - importing the dialog package | |
| # moved it from named to downloaded, checksummed and compiled in. | |
| # Checked before it was accepted: BSD-3, 129 lines, written by the | |
| # toolkit's own author, and reached from one line of the dialog | |
| # package where it decorates folder icons. Tidying after it pruned | |
| # github.com/kr/text from go.mod, which took github.com/creack/pty | |
| # out of the graph, taking the count from 69 to 68. | |
| # | |
| # The second question protects what ships to most people. The | |
| # command line binary links exactly two external modules and the | |
| # toolkit is not among them, so a build for a server carries no | |
| # window, no OpenGL and - see internal/guard - no socket. | |
| run: | | |
| set -euo pipefail | |
| # Built with printf rather than written across several lines. A | |
| # continuation starting in column one reads as the end of the jobs | |
| # block to anything scanning this file by indentation, and the | |
| # guard on job timeouts does exactly that - it counted one job | |
| # instead of seven and went green whatever it was given. | |
| expected=$(printf '%s\n' \ | |
| fyne.io/fyne/v2 \ | |
| fyne.io/systray \ | |
| github.com/BurntSushi/toml \ | |
| github.com/FyshOS/fancyfs \ | |
| github.com/akavel/rsrc \ | |
| github.com/anthonynsimon/bild \ | |
| github.com/clipperhouse/uax29/v2 \ | |
| github.com/cpuguy83/go-md2man/v2 \ | |
| github.com/davecgh/go-spew \ | |
| github.com/felixge/fgprof \ | |
| github.com/fogleman/gg \ | |
| github.com/fredbi/uri \ | |
| github.com/fsnotify/fsnotify \ | |
| github.com/fyne-io/gl-js \ | |
| github.com/fyne-io/glfw-js \ | |
| github.com/fyne-io/image \ | |
| github.com/fyne-io/oksvg \ | |
| github.com/go-gl/gl \ | |
| github.com/go-gl/glfw/v3.4/glfw \ | |
| github.com/go-ole/go-ole \ | |
| github.com/go-text/render \ | |
| github.com/go-text/typesetting \ | |
| github.com/go-text/typesetting-utils \ | |
| github.com/goccy/go-yaml \ | |
| github.com/godbus/dbus/v5 \ | |
| github.com/golang/freetype \ | |
| github.com/google/pprof \ | |
| github.com/hack-pad/go-indexeddb \ | |
| github.com/hack-pad/safejs \ | |
| github.com/inconshreveable/mousetrap \ | |
| github.com/jackmordaunt/icns/v2 \ | |
| github.com/jeandeaual/go-locale \ | |
| github.com/josephspurrier/goversioninfo \ | |
| github.com/jsummers/gobmp \ | |
| github.com/kr/text \ | |
| github.com/lucor/goinfo \ | |
| github.com/mattn/go-runewidth \ | |
| github.com/mcuadros/go-version \ | |
| github.com/natefinch/atomic \ | |
| github.com/nfnt/resize \ | |
| github.com/nicksnyder/go-i18n/v2 \ | |
| github.com/niemeyer/pretty \ | |
| github.com/pkg/profile \ | |
| github.com/pmezard/go-difflib \ | |
| github.com/russross/blackfriday/v2 \ | |
| github.com/rymdport/portal \ | |
| github.com/spf13/cobra \ | |
| github.com/spf13/pflag \ | |
| github.com/srwiley/oksvg \ | |
| github.com/srwiley/rasterx \ | |
| github.com/stretchr/objx \ | |
| github.com/stretchr/testify \ | |
| github.com/urfave/cli/v2 \ | |
| github.com/xrash/smetrics \ | |
| github.com/yuin/goldmark \ | |
| golang.org/x/crypto \ | |
| golang.org/x/image \ | |
| golang.org/x/mobile \ | |
| golang.org/x/mod \ | |
| golang.org/x/net \ | |
| golang.org/x/sync \ | |
| golang.org/x/sys \ | |
| golang.org/x/term \ | |
| golang.org/x/text \ | |
| golang.org/x/tools \ | |
| golang.org/x/tools/go/vcs \ | |
| gopkg.in/check.v1 \ | |
| gopkg.in/yaml.v3) | |
| actual=$(go list -m -f '{{.Path}}' all | grep -v '^github.com/donislawdev/TestingFilesGenerator$' | sort) | |
| if [ "$actual" != "$expected" ]; then | |
| echo "the module list changed." | |
| echo "expected: $expected" | |
| echo "actual : $actual" | |
| exit 1 | |
| fi | |
| echo "dependencies unchanged, $(echo "$expected" | wc -l) modules" | |
| linked=$(go list -deps -f '{{if .Module}}{{.Module.Path}}{{end}}' ./cmd/tfg | sort -u | grep -v '^github.com/donislawdev/TestingFilesGenerator$' | grep .) | |
| wanted=$(printf '%s\n' github.com/goccy/go-yaml golang.org/x/text) | |
| if [ "$linked" != "$wanted" ]; then | |
| echo "the command line binary links a different set of modules." | |
| echo "expected: $wanted" | |
| echo "actual : $linked" | |
| exit 1 | |
| fi | |
| echo "the command line binary links only: $linked" | |
| shell: bash | |
| - name: test | |
| run: go test ./... -count=1 | |
| - name: build the command line binary | |
| run: go build ./cmd/tfg | |
| - name: build the window binary without a C toolchain | |
| # The toolkit arrived on 2026-08-05 and this step still passes, which | |
| # is the point of how it was wired in. Reaching the toolkit's app | |
| # package needs CGO and, on Linux, X11 development headers no runner | |
| # carries by default - so that one file sits behind a cgo build tag and | |
| # everything building a widget tree sits outside it. | |
| # | |
| # CGO_ENABLED is 0 for this job, so this proves the tree still compiles | |
| # where there is no compiler and no graphics. The tests go further: | |
| # they render a screen to an image and read it, also with CGO off. | |
| # | |
| # What it does NOT prove is that the windowed binary links, because | |
| # that build needs a C compiler per platform and is done natively - | |
| # docs/STACK.md section 4.4. Naming the gap beats implying it is | |
| # covered. | |
| run: go build ./cmd/tfg-gui | |
| govulncheck: | |
| name: known vulnerabilities | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| check-latest: false | |
| cache: true | |
| - name: govulncheck | |
| # The official Go vulnerability scanner. BSD-3-Clause, read from the | |
| # LICENSE file of the pinned version rather than recalled. Run rather | |
| # than imported, so it never enters go.mod. | |
| # | |
| # It reports only what is actually reachable from our code, which is | |
| # what makes it worth having: a scanner that lists every advisory | |
| # touching the module graph produces noise, and noise gets switched off. | |
| # Measured before switching it on, 2026-08-02: no vulnerabilities found. | |
| run: go run golang.org/x/vuln/cmd/govulncheck@v1.1.4 ./... | |
| staticcheck: | |
| name: staticcheck | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| check-latest: false | |
| cache: true | |
| - name: staticcheck | |
| # MIT, pinned. Run rather than imported, so it never enters go.mod and | |
| # the dependency gate above does not see it - measured twice on | |
| # 2026-08-02, go.mod and go.sum both untouched afterwards. | |
| # | |
| # The version is pinned because an unpinned analyser turns somebody | |
| # else's release into a red build on a commit that changed nothing. | |
| # | |
| # Which checks run, and why ST1005 does not, is in staticcheck.conf. | |
| # Measured before switching this on: two findings in the whole tree, | |
| # both of them the word "Pillow" at the start of an error string, which | |
| # is the name of the library that refused the image rather than a | |
| # sentence. Zero findings with the config in place. | |
| run: go run honnef.co/go/tools/cmd/staticcheck@v0.7.0 ./... | |
| race: | |
| name: race detector | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| env: | |
| # The one thing in this project that needs a C toolchain. Linux runners | |
| # ship one, so this job carries the cost and the matrix above stays on | |
| # CGO_ENABLED=0 and stays fast. | |
| CGO_ENABLED: "1" | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| check-latest: false | |
| cache: true | |
| - name: test under the race detector | |
| # A data race is the one defect class here that nothing else notices. It | |
| # does not change a size, and on the run that happens to interleave the | |
| # safe way it does not change a byte either - so determinism and the | |
| # pinned values both stay green while the file is wrong once a month on | |
| # somebody else's machine. | |
| # | |
| # Measured on 2026-08-02: 31 s without, 148 s with, and zero races found | |
| # in the tree as it stands. The guard that keeps concurrency confined to | |
| # two files lives in internal/guard, so this and that one answer | |
| # different halves of the same worry. | |
| run: go test ./... -count=1 -race | |
| coverage: | |
| name: coverage gate | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| env: | |
| CGO_ENABLED: "0" | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| check-latest: false | |
| # Keyed on go.sum. The one dependency is the YAML parser behind the | |
| # recipe - see docs/STACK.md. | |
| cache: true | |
| - name: measure | |
| # -coverpkg is not optional here. The guard tests live in their own | |
| # package, and by default Go credits coverage only to the package | |
| # under test - which reports 0.0% and makes the gate meaningless. | |
| # Measured, not assumed. | |
| run: > | |
| go test ./... -count=1 -covermode=atomic | |
| -coverpkg=./internal/...,./cmd/... | |
| -coverprofile=coverage.out | |
| - name: gate | |
| # The threshold lives in exactly one place, .github/coverage-threshold. | |
| # It rises with coverage and is never lowered to turn a red run green. | |
| # Lowering it is a decision for the owner, not a way to get unblocked. | |
| run: | | |
| set -euo pipefail | |
| threshold=$(tr -d '[:space:]' < .github/coverage-threshold) | |
| actual=$(go tool cover -func=coverage.out | awk '/^total:/ {gsub("%","",$3); print $3}') | |
| echo "coverage ${actual}% - threshold ${threshold}%" | |
| awk -v a="$actual" -v t="$threshold" 'BEGIN { exit (a+0 >= t+0) ? 0 : 1 }' \ | |
| || { echo "coverage ${actual}% is below the threshold ${threshold}%"; exit 1; } | |
| shell: bash | |
| fidelity: | |
| name: reference tools actually installed | |
| # The oracle guards skip when the tool they need is missing, loudly, and a | |
| # skip is not a check. The matrix runners have python and node and nothing | |
| # else, so on an ordinary push most of those guards report a skip and the | |
| # run is green having verified almost nothing about the files themselves. | |
| # | |
| # This job installs Inkscape, 7z, ffmpeg and poppler so the same guards run | |
| # for real. It is separate from the matrix because installing a graphics | |
| # stack takes minutes and a push should not wait for it. | |
| # | |
| # What it does NOT do, said plainly: it does not walk many sizes. The | |
| # guards it runs check the realistic size and the smallest ones, which is | |
| # where the one defect this ever found was hiding - an SVG that rendered to | |
| # a blank canvas at exactly its minimum, 2026-08-03. The wider sweep across | |
| # sizes, seeds and label settings lives in tools/probes/fidelity-sweep.py | |
| # and is run by hand, because tools/ is outside the repository. See O51. | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| env: | |
| CGO_ENABLED: "0" | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| check-latest: false | |
| cache: true | |
| - name: the reference tools | |
| run: | | |
| set -euo pipefail | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| p7zip-full ffmpeg poppler-utils inkscape python3-pil | |
| shell: bash | |
| - name: every format past its reference tool | |
| # -v so that a skip is visible in the log. A tool that failed to install | |
| # would otherwise turn this job back into the green nothing it exists | |
| # to replace. | |
| run: go test ./internal/guard/ -count=1 -run 'ReferenceTool' -v | |
| shell: bash | |
| fuzz: | |
| name: fuzzing with a time budget | |
| # Weekly and by hand, never on a push. Fuzzing searches without end, so a | |
| # budget picked to fit a commit gate would be a gate that says "fuzzing | |
| # passed" after twenty seconds of looking - which is worth less than not | |
| # claiming it. See docs/OBSERVATIONS.md, O34. | |
| # | |
| # What the push jobs do run is the seed corpus in testdata/fuzz, because | |
| # go test executes fuzz targets as ordinary tests. That is regression | |
| # cover for what has already been found, and it is not the same thing as | |
| # searching. | |
| if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| # Four targets at five minutes each, plus the build and the baseline pass. | |
| timeout-minutes: 45 | |
| env: | |
| CGO_ENABLED: "0" | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| check-latest: false | |
| cache: true | |
| - name: search | |
| # One target at a time, because -fuzz takes exactly one. Five minutes | |
| # each - long enough to be a search rather than a gesture, short enough | |
| # that a weekly run stays cheap. | |
| # | |
| # A finding here is not fixed by this job. It has to be pulled into | |
| # testdata/fuzz and committed, or it disappears with the runner and the | |
| # next search starts from nothing. | |
| run: | | |
| set -euo pipefail | |
| for target in FuzzParseSize FuzzParseRecipe FuzzNameTemplate FuzzCanonicalRecipe; do | |
| echo "=== $target ===" | |
| go test ./internal/guard/ -run "^$" -fuzz "^${target}$" -fuzztime 5m | |
| done | |
| shell: bash | |
| - name: keep what was found | |
| # The corpus a failing run leaves behind is the whole value of the run, | |
| # and it lives in the runner's cache directory rather than the tree. | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: fuzz-findings | |
| path: | | |
| internal/guard/testdata/fuzz/** | |
| ~/.cache/go-build/fuzz/** | |
| if-no-files-found: warn |