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
2 changes: 1 addition & 1 deletion .githooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Committed Git hooks that run the fleet's gate locally, before a commit lands,
so a failure surfaces here rather than after a push.

Fleet-managed by [conf](https://github.com/PowderworksCode/conf); edit them
Distributed by [Ordnung](https://ordnung.dev)'s `recommended` tier; edit them
there, not here — a local change is drift the next sync reports.

## Activate
Expand Down
2 changes: 1 addition & 1 deletion .githooks/commit-msg
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# applies the same rule to the commit subject, so a bad message fails here
# rather than after a push. Bypass with `git commit --no-verify`.
#
# Fleet-managed by conf (.ordnung/managed/githooks/commit-msg): edit it there.
# Distributed by Ordnung's `recommended` tier: edit it there, not here.
msg_file="$1"

# First non-blank, non-comment line is the subject.
Expand Down
7 changes: 5 additions & 2 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
# The local gate: what CI would say about this tree, said before the commit
# lands. Bypass with `git commit --no-verify`.
#
# Fleet-managed by conf (.ordnung/managed/githooks/pre-commit).
# Fleet-managed by conf (.ordnung/managed/githooks/pre-commit): it replaces the
# `ordnung check` hook Ordnung's `recommended` tier ships.
set -eu

# No path argument: a positional overrides the `paths` a repository set in its
# own straitjacket.toml, and a hook has no business overruling that.
hooks_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)
exec "$hooks_dir/run-straitjacket" .
exec "$hooks_dir/run-straitjacket"
8 changes: 4 additions & 4 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
version: 2

# Fleet-owned. Edit this in conf, not in the member repository: a local change is
# drift, and the next sync overwrites it.
# Fleet-owned. Edit this in the policy tier, not in the member repository: a
# local change is drift, and the next sync overwrites it.
#
# One file for every member. The directories inside it are globs rather than a
# list of each repository's actual layout, so this file states fleet POLICY —
# weekly, minor and patch grouped into one pull request, five open at most — and
# lets the globs discover the topology. A glob matching nothing is not an error,
# so the same bytes are correct in a Cargo workspace, a Bun site, and conf,
# which has neither.
# so the same bytes are correct in a Cargo workspace, a Bun site, and a fleet
# definition, which has neither.
#
# The alternative was a file per repository shape. That put facts about each
# repository's layout into the fleet, where they would go stale silently the
Expand Down
11 changes: 7 additions & 4 deletions .github/workflows/fleet-lint.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
name: fleet-lint

# Fleet-managed by conf (.ordnung/managed/github/lint.yml): the static-analysis
# floor every member runs — prose, workflow, shell, and public-API hygiene.
# Edit it in conf and let ordnung distribute it; a local edit here is drift the
# next fleet sync will report.
# Distributed by Ordnung's `paranoid` tier: the static-analysis floor every
# member runs — prose, workflow, shell, and public-API hygiene. Edit it in the
# tier and let ordnung distribute it; a local edit here is drift the next fleet
# sync will report.
#
# It replaces, by entry name, the codespell-only workflow the `recommended` tier
# ships: the extra jobs are the checks this tier raises.
on:
push:
branches: [main]
Expand Down
225 changes: 225 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
name: release (rust)

# A tag is the whole trigger. Everything the installer reads — the archives, the
# checksums, and the "latest" pointer — is produced here, so a release is never
# assembled by hand.
on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
tag:
description: Existing tag to build a release for
required: true

permissions:
contents: read

# Never run two releases at once, and never cancel one in flight: a cancelled
# `cargo publish` can leave a version uploaded that can never be reused.
concurrency:
group: release-${{ inputs.tag || github.ref_name }}
cancel-in-progress: false

env:
TAG: ${{ inputs.tag || github.ref_name }}

jobs:
# The release is created as a draft and published only after every archive and
# the checksum file are in place. A draft is not returned by the "latest"
# endpoint, so `install.sh` can never see a half-uploaded release.
draft:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
outputs:
# What this repository publishes, answered where the checkout is. Neither
# can be a job-level `if: hashFiles(...)`: that reads a workspace which
# does not exist before checkout, and a workflow whose expression cannot
# be evaluated does not start at all — no jobs, no release, on a tag push
# nobody is watching.
crate: ${{ steps.publishes.outputs.crate }}
binary: ${{ steps.publishes.outputs.binary }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ env.TAG }}
- id: publishes
run: |
if [ -f scripts/publish.sh ]; then
echo "crate=true" >> "$GITHUB_OUTPUT"
else
echo "crate=false" >> "$GITHUB_OUTPUT"
echo "no scripts/publish.sh; this release ends at the smoke test"
fi
# A library crate has nothing to put in an archive, nothing to
# install, and nothing to smoke-test. Cargo is asked rather than the
# file tree, because a binary target can be declared several ways.
if cargo metadata --no-deps --format-version 1 |
jq -e '[.packages[].targets[] | select(any(.kind[]; . == "bin"))] | length > 0' >/dev/null; then
echo "binary=true" >> "$GITHUB_OUTPUT"
else
echo "binary=false" >> "$GITHUB_OUTPUT"
echo "no binary targets; this release is a crate publish only"
fi
- name: Check the tag against Cargo.toml
run: |
crate=$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -n 1)
if [ "$TAG" != "v${crate}" ]; then
echo "tag ${TAG} does not match Cargo.toml version ${crate}" >&2
exit 1
fi
- name: Create the draft release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
if ! gh release view "$TAG" >/dev/null 2>&1; then
gh release create "$TAG" --draft --title "$TAG" --generate-notes
fi

build:
needs: draft
if: needs.draft.outputs.binary == 'true'
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
- target: aarch64-unknown-linux-musl
os: ubuntu-latest
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-apple-darwin
os: macos-latest
runs-on: ${{ matrix.os }}
timeout-minutes: 45
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ env.TAG }}
# Both Linux targets cross-compile from an ordinary x86_64 host, because
# .cargo/config.toml links them with rust-lld against a self-contained
# musl. No cross toolchain, container, or apt package is involved.
- run: rustup target add ${{ matrix.target }}
# No build cache here, deliberately: a cache is writable from other
# workflows, and these binaries are the release. Cold builds are the
# price of artifacts nothing else could have written to (zizmor's
# cache-poisoning audit).
- run: cargo build --release --locked --target ${{ matrix.target }}
- name: Package
run: |
stage=$(mktemp -d)
cp "target/${{ matrix.target }}/release/jawohl" "$stage/"
cp README.md LICENSE "$stage/"
tar -czf "jawohl-${TAG}-${{ matrix.target }}.tar.gz" -C "$stage" \
jawohl README.md LICENSE
- name: Upload
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: gh release upload "$TAG" "jawohl-${TAG}-${{ matrix.target }}.tar.gz" --clobber

# Runs for every release, because un-drafting is not a binary concern: a
# library's release would otherwise stay a draft forever. Only the archives
# are conditional. `always()` is what lets a skipped `build` through; without
# it a skipped dependency skips this job too.
publish:
needs: [draft, build]
if: >-
always() && needs.draft.result == 'success'
&& (needs.build.result == 'success' || needs.build.result == 'skipped')
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
steps:
# The checksums are computed from what the release actually holds rather
# than from what each build job believes it uploaded.
- name: Checksum every archive
if: needs.draft.outputs.binary == 'true'
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
gh release download "$TAG" --pattern '*.tar.gz' --dir assets
cd assets && sha256sum ./*.tar.gz | sed 's|\./||' > SHA256SUMS
cat SHA256SUMS
gh release upload "$TAG" SHA256SUMS --clobber
- name: Publish
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: gh release edit "$TAG" --draft=false --latest

# Install the release that was just published, on every supported platform,
# through both the script and the action. Called rather than triggered: a
# release published by GITHUB_TOKEN raises no event that starts a workflow.
smoke:
needs: [draft, publish]
if: >-
always() && needs.publish.result == 'success'
&& needs.draft.outputs.binary == 'true'
permissions:
contents: read
uses: ./.github/workflows/install-smoke.yml
with:
version: ${{ inputs.tag || github.ref_name }}

# Publish the same commit to crates.io, after the binaries have been proven
# to install and run. crates.io is the one place a version can never be
# replaced, so it goes last and everything checkable is checked first.
#
# The logic lives in scripts/publish.sh so the same path can be run by hand,
# and so this job is a caller rather than a second definition of it.
crate:
needs: [draft, publish, smoke]
# Only repositories that publish a crate carry the script; everyone else
# ends at the smoke test. The answer comes from the draft job, which has
# the checkout — see the note on its outputs.
#
# A skipped smoke job means either "no binary in this repository" or "the
# binary never got that far", and those must not read alike. `publish`
# tells them apart: it succeeds when the archives are up or when there were
# none to build, and is skipped when the build failed.
if: >-
always() && needs.draft.outputs.crate == 'true'
&& needs.publish.result == 'success'
&& (needs.smoke.result == 'success' || needs.smoke.result == 'skipped')
runs-on: ubuntu-latest
timeout-minutes: 20
# One place to require a human approval before an irreversible publish:
# Settings > Environments > crates-io > Required reviewers.
environment: crates-io
permissions:
id-token: write
env:
# `secrets` cannot be read from an `if`, so the question of whether one
# exists is answered here, where it can.
HAS_STORED_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN != '' }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag || github.ref_name }}
# Trusted publishing is the normal path and needs no secret. The stored
# token exists only to bootstrap: a crate's first version cannot be
# published this way, because crates.io will not attach a trusted
# publisher to a crate that does not exist yet. Delete the secret once
# the trusted publisher is configured and this step takes over again.
- uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5
id: auth
if: env.HAS_STORED_TOKEN == 'false'
- name: Publish
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token || secrets.CARGO_REGISTRY_TOKEN }}
run: |
if [ "${HAS_STORED_TOKEN}" = "true" ]; then
echo "publishing with the stored bootstrap token; delete it once trusted publishing is configured"
else
echo "publishing with a short-lived trusted-publishing token"
fi
scripts/publish.sh --execute
21 changes: 20 additions & 1 deletion .vale.ini
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Fleet-managed by conf (.ordnung/managed/vale/vale.ini).
# Distributed by Ordnung's `paranoid` tier: edit it there, not here.
#
# proselint and write-good rather than a house style guide: they catch prose
# that is weak on anyone's terms — weasel words, passive constructions,
Expand All @@ -12,5 +12,24 @@ Packages = proselint, write-good
[*.md]
BasedOnStyles = proselint, write-good

# write-good ships these two at error level and the rest of its style advice at
# warning, which is its packaging rather than a considered position: opening a
# sentence with "There is" is a preference exactly like the passive voice it
# warns about. Levelled down so that prose style informs and does not gate;
# what still fails a build is a real defect, such as a TODO left in a document.
write-good.ThereIs = warning
write-good.So = warning

# Prose lives in sentences; a fenced block is code someone will run.
BlockIgnores = (?s) *(```.*?```)

# `vale sync` writes the style packages here, README files and all, and Vale
# then grades the prose of the linters it just downloaded.
[.vale/styles/**]
BasedOnStyles =

# Verbatim text from other projects, reproduced to satisfy their licences.
# proselint asks for `©` where a licence header writes `(c)`, and taking that
# advice would edit somebody else's licence.
[THIRD-PARTY-NOTICES.md]
BasedOnStyles =
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ documents through `complete_json`, checking whether the result parses:
| `{"que` | `{"que"}` ✗ | `{}` |

A closer-counter has no model of where it is in the grammar, so it cannot know
that `tru` needs an `e` or that a trailing `\` escapes the very quote it just
that `tru` needs an `e` or that a trailing `\` escapes the quote it just
appended. That is structural, not a bug list.

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ See `examples/sse.rs`.
## Upgrading from 0.1

`complete_json` and `get_closing_string_for_partial_json` keep their signatures.
The behaviour is stricter and more correct:
The behaviour is stricter and better defined:

- **Malformed input now returns `Err`.** 0.1 returned `Ok` with output that did
not parse — for `{"a": tru`, `{"a": "x\`, `{"a":`, `{"a":1,`, `{"que`, and a
Expand Down
4 changes: 2 additions & 2 deletions notes/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ and `jawohl.stream(MyZodSchema)` are host-side sugar over it.
the same handle; `push`/`snapshot`/`status` are synchronous **methods** on it;
`changes()` is a **stream op**; a native validator is a **value-returning
callback param**. jawohl exercises all three of
jedem's hard three — which is what makes it a good acid test and also what
jedem's hard three — which is what makes it a good proving ground and also what
makes §8 the risk.

---
Expand Down Expand Up @@ -420,7 +420,7 @@ bet:
1. **The core track absorbs the wait.** C1–C4 is the majority of the engineering
and none of it is blocked. By the time jedem reaches step 3, jawohl should
have a complete, tested Rust core waiting for a surface.
2. **jawohl is the acid test that pulls jedem forward.** `findings.md`'s
2. **jawohl is the proving ground that pulls jedem forward.** `findings.md`'s
method — author the complete real surface before freezing the IR — is what
caught every gap in fluessig. jawohl's surface is small, precise, and hits all
three hard cases, which makes it a far better forcing function than a demo
Expand Down
Loading