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
1 change: 1 addition & 0 deletions .github/workflows/actions.lock
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ workflows:
- 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
'.github/workflows/deed-conformance.yml':
- 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
- 'oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6'
'.github/workflows/dependabot-lock-regen.yml':
- 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
- 'actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1'
Expand Down
77 changes: 68 additions & 9 deletions .github/workflows/deed-conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,21 @@
# SPDX-License-Identifier: MPL-2.0
name: deed-conformance

# Conformance lane for the DEED grammar (standards#837). Three gates:
# validator self-test β†’ fixture corpus β†’ every committed *.deed file.
# Conformance lane for the DEED grammar (standards#837).
#
# In this repo (push / pull_request) three gates run: validator self-test,
# fixture corpus, every committed *.deed file.
#
# As a reusable (workflow_call) it lints the CALLER's committed *.deed files
# with the linter at THIS workflow's own commit (job.workflow_sha), so a
# caller pinned to a standards SHA gets exactly that linter.
#
# Caller:
# jobs:
# deed:
# uses: hyperpolymath/standards/.github/workflows/deed-conformance.yml@<sha>
# with:
# require-files: true # fail when the caller has no .deed at all

on:
push:
Expand All @@ -16,27 +29,73 @@ on:
- '**.deed'
- '1-formats/deed/**'
- '.github/workflows/deed-conformance.yml'
workflow_call:
inputs:
require-files:
description: "Fail when the caller has no committed .deed file (a zero-file run is otherwise a pass)."
type: boolean
required: false
default: false

permissions: {}

jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
env:
# In this repo the PR's own linter is in the first checkout; a caller
# gets the linter from the canon checkout at job.workflow_sha.
T: ${{ github.repository == 'hyperpolymath/standards' && '1-formats/deed/tools' || '.deed-canon/1-formats/deed/tools' }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: checkout the deed linter at this workflow's commit
if: github.repository != 'hyperpolymath/standards'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: hyperpolymath/standards
ref: ${{ job.workflow_sha }}
path: .deed-canon
persist-credentials: false
sparse-checkout: "1-formats/deed/tools\n"
sparse-checkout-cone-mode: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
- name: validator self-test
run: python3 1-formats/deed/tools/deed_lint.py --self-test
if: github.repository == 'hyperpolymath/standards'
run: bun "$T/deed_lint.js" --self-test
- name: validator unit tests
if: github.repository == 'hyperpolymath/standards'
run: bun test "$T/"
- name: fixture corpus (valid must pass, invalid must fail)
run: python3 1-formats/deed/tools/deed_lint.py --fixtures 1-formats/deed/tools/fixtures
if: github.repository == 'hyperpolymath/standards'
run: bun "$T/deed_lint.js" --fixtures "$T/fixtures"
- name: python/bun parity (byte-identical output over every committed deed)
if: github.repository == 'hyperpolymath/standards'
run: |
set -euo pipefail
find . -name '*.deed' -not -path './.deed-canon/*' -print | sort > "$RUNNER_TEMP/all"
test -s "$RUNNER_TEMP/all"
xargs python3 "$T/deed_lint.py" < "$RUNNER_TEMP/all" > "$RUNNER_TEMP/py" || true
xargs bun "$T/deed_lint.js" < "$RUNNER_TEMP/all" > "$RUNNER_TEMP/js" || true
wc -l "$RUNNER_TEMP/py" "$RUNNER_TEMP/js"
diff -u "$RUNNER_TEMP/py" "$RUNNER_TEMP/js"
- name: lint every committed deed
env:
REQUIRE_FILES: ${{ inputs.require-files || false }}
run: |
set -e
FILES=$(find . -name '*.deed' -not -path './1-formats/deed/tools/fixtures/invalid/*' -print)
if [ -z "$FILES" ]; then
echo "no committed .deed files yet β€” fixtures carry the gate"
set -euo pipefail
find . -name '*.deed' -not -path './.deed-canon/*' -not -path './1-formats/deed/tools/fixtures/invalid/*' -print | sort > "$RUNNER_TEMP/deeds"
if [ ! -s "$RUNNER_TEMP/deeds" ]; then
if [ "$REQUIRE_FILES" = "true" ]; then
echo "::error::require-files is set and no committed .deed file was found"
exit 1
fi
echo "no committed .deed files β€” nothing to lint"
exit 0
fi
echo "$FILES" | xargs python3 1-formats/deed/tools/deed_lint.py
echo "linting $(wc -l < "$RUNNER_TEMP/deeds") file(s)"
xargs bun "$T/deed_lint.js" < "$RUNNER_TEMP/deeds"
24 changes: 18 additions & 6 deletions 1-formats/deed/tools/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,21 @@ Campaign: standards#837 Β· Grammar: link:../spec/abnf/deed.abnf[spec/abnf/deed.a
*Nothing invalid ever lands.* Every emitted deed passes the validator before
it may be written; every `.deed` committed in this repo is linted in CI.

== `deed_lint.py` β€” the validator
== `deed_lint.js` / `deed_lint.py` β€” the validator

Stdlib-only implementation of the normative grammar. Enforces:
Two implementations of the normative grammar, held byte-identical:

* `deed_lint.js` β€” bun, zero dependencies. **This is the CI gate**
(`.github/workflows/deed-conformance.yml`, callable from other repos via
`workflow_call`). Unit tests: `bun test 1-formats/deed/tools/`.
* `deed_lint.py` β€” stdlib-only. Kept because `a2ml_to_deed.py` imports it
in-process to lint what it emits. Retire it with the translator.

The workflow's parity step runs both over every committed `.deed`
(invalid fixtures included) and fails on any byte of difference in their
output, so the two cannot drift while both exist.

Enforced rules:

* SPDX header required; one form; full input consumed
* doc-head whitelist (`estate-deed / repo-deed / estate-atlas-deed / praxis-deed`)
Expand All @@ -25,11 +37,11 @@ Stdlib-only implementation of the normative grammar. Enforces:
(K9-consistent); bare CR rejected (CRLF or LF line-ends only)
* quoted values restricted to symbols and lists

Usage:
Usage (`deed_lint.py` takes the same arguments):

python3 deed_lint.py FILE.deed...
python3 deed_lint.py --self-test # 14-case embedded corpus
python3 deed_lint.py --fixtures fixtures/ # valid/ must pass, invalid/ must fail
bun deed_lint.js FILE.deed...
bun deed_lint.js --self-test # 14-case embedded corpus
bun deed_lint.js --fixtures fixtures/ # valid/ must pass, invalid/ must fail

== `a2ml_to_deed.py` β€” the translator

Expand Down
Loading
Loading