Skip to content

Report push hashes and harden the release path - #11

Merged
jserv merged 6 commits into
mainfrom
refine
Oct 4, 2026
Merged

jserv merged 6 commits into
mainfrom
refine

Conversation

@jserv

@jserv jserv commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

hackmd_push_note now returns body_hash and remote_timestamp when it writes or finds nothing to write, so an agent can confirm a sync without a follow-up hackmd_get_note. Its success paths end in one place, and a large body is hashed once instead of up to three times. The rest closes the findings of a project analysis of what changed since the last one: the stdio tests no longer read a developer's real .env, which made make check fail for anyone following the setup; CI actions are pinned by commit, the release binaries build with the same pinned Rust as lint, and the release job attests its assets so a download can be checked beyond SHA256SUMS; and the README install commands work when pasted into zsh, the macOS default, where a trailing comment left $asset empty and the checksum step could never pass.

Verified locally: make check (266 unit and 11 stdio tests, with a real .env in the package root), cargo clippy with and without --all-features under -D warnings, cargo fmt --check, actionlint, shellcheck, and tests/install.py. Each code commit builds and passes on its own. The README install block was run against stubbed downloads under sh, bash, and interactive zsh, with a matching and a mismatched checksum. Codex and Claude reviewed the changes independently, and both resolved every pinned action SHA to its named release.

The attestation step runs only on a push to main, so it is first exercised after merge. The tool surface was reviewed for tools to remove; both reviewers found none worth the compatibility break, so all 14 stay.


Summary by cubic

hackmd_push_note now reports body_hash and remote_timestamp on a successful push or a no-op, so a caller can confirm the sync without a follow-up hackmd_get_note. The success paths end in one place, and a large body is hashed once instead of up to three times; conflicts and remote_changed results still return only remote_body_hash.

Release and CI hardening

  • Pins every CI action to a commit and runs tests, lint, and the release binaries on the same pinned Rust compiler.
  • Attests release assets with build provenance, and the README shows how to verify it beyond SHA256SUMS.

Other fixes

  • Stdio tests run from a directory with no .env, so make check no longer fails for developers following the setup.
  • README install commands now work when pasted into zsh, the macOS default, and use sha256sum -c - so the checksum check actually passes on macOS.

Written for commit acab735. Summary will update on new commits.

Review in cubic

jserv added 5 commits October 4, 2026 23:18
Confirming that a push landed took a follow-up hackmd_get_note, one
more request just to read back a hash the push already knew. A push
that writes, or finds nothing to write, now returns body_hash, the
body the baseline and the remote share as of this push, and
remote_timestamp in the form hackmd_get_note's sync state uses. A
conflict or a remote-only change returns neither; remote_body_hash
stays the hash that matters there.

The converged, overwrite no-op, and pushed paths now end in one place:
the baseline advances to the body both sides hold, and the hash comes
from the state just persisted, so a large body is hashed once rather
than up to three times. Only a failure after a real write is reported
as a partial write. The remote and baseline bodies are dropped before
that step on every path, not only before a write.

The overwrite no-op branch had no test; one now checks that it sends
no PATCH, reports the hash and timestamp, and moves the baseline.
FolderError::Payload was never constructed: folder requests run no
note-payload validation, so its kind arm and the From conversion were
unreachable. The rate-limit reset fallback in retry_after repeated
parse_header line for line; it now calls it.
The binary loads .env from its working directory, and the tests that
named none ran it from the package root, where .env.example tells a
developer to put a real one. With a token there, the missing-token
tests found one and the self-check printed a permissions warning, so
make check failed for anyone who followed the setup; CI passed only
because its checkout has no .env.

Every test now builds its command through server(), which runs from
CARGO_TARGET_TMPDIR and clears the server's own variables. A test that
needs a .env still sets its own current_dir.
The jobs that build and publish the release ran third-party actions by
tag, which their owners can move, while the zig wheels beside them were
already pinned by hash for that reason. Every action is now pinned to a
commit, with its release in a comment so Dependabot keeps bumping it.

The shipped binaries were built by whatever stable was current that
day, unlike the lint job, so the compiler behind a release could change
with no commit. Both now build with PINNED_RUST.

SHA256SUMS lives in the same release as the binaries, so it catches a
corrupt download but not a replaced release. The release job now
attests every listed asset with a build provenance signed for this
workflow, and the README shows how to verify it. The command pins the
branch and the signer workflow, because --repo alone accepts an
attestation from any branch or workflow in the repository.
zsh, the macOS default shell, does not treat # as a comment when
commands are pasted interactively. The README's trailing "# from the
table above" made the asset assignment a prefix to a command named #,
so $asset stayed empty, and the make and --self-check examples passed
their comments on as arguments. Those notes are prose now, and no
pasted block carries a trailing comment.

The checksum step ran sha256sum -c on standard input, which the
sha256sum macOS ships refuses without -, so the check never passed
there. It now reads - and joins the extraction and install with &&,
so a mismatch stops before anything is unpacked. The block was run
under sh, bash, and interactive zsh with a matching and a bad sum.
cubic-dev-ai[bot]

This comment was marked as resolved.

The test job still ran on stable while lint and the release binaries
build with PINNED_RUST, so a new stable could fail main with no code
change, which the pin's own comment says cannot happen, and once stable
moved past the pin the suite no longer ran on the compiler that builds
a release. The test job now uses PINNED_RUST too; MSRV stays separate.
@jserv
jserv merged commit 8ff45ff into main Oct 4, 2026
11 checks passed
@jserv
jserv deleted the refine branch October 4, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant