Skip to content

Release artifact pipeline, SDK sync dispatch, and docs regeneration - #100

Open
adnanhq wants to merge 3 commits into
mainfrom
feat/release-artifacts-and-docs-sync
Open

Release artifact pipeline, SDK sync dispatch, and docs regeneration#100
adnanhq wants to merge 3 commits into
mainfrom
feat/release-artifacts-and-docs-sync

Conversation

@adnanhq

@adnanhq adnanhq commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Adds the release side of the SDK documentation sync pipeline, plus automated docs regeneration.

Release artifact pipeline (release.yml)

On every published GitHub Release: checks out the tag, builds with a pinned Foundry version (forge build --ast), and attaches a deterministic ABI bundle (abis-<tag>.tar.gz + SHA256SUMS) as release assets. Stable (non-prerelease) releases then send a contracts-release-published repository_dispatch to oak-network/sdk, whose Contracts Sync workflow opens an ABI update PR there. Any failure opens a deduplicated release-pipeline issue here.

The bundle is reproducible (sorted entries, fixed mtime) and contains trimmed per-contract ABI JSON for every src/ contract, DataRegistryKeys.sol, and metadata (tag, commit, solc and forge versions, contract inventory). contractKind from the AST separates deployable contracts from libraries and interfaces so the SDK can detect genuinely new contracts.

Docs regeneration (docs.yml)

Pushes to main that touch src/** regenerate the forge-doc markdown into a temp dir, sanitize any machine-specific absolute link paths (sanitize-docs.py, with a hard-failing guard), and open an auto PR. The customized book.toml, book.css and solidity.min.js are never touched.

This branch also includes a one-time regeneration of docs/src: the committed output was stale (documented the removed TestUSD contract, still listed Ownable members that were removed, and contained hardcoded absolute paths from the original author's machine).

Setup required

  • Secret SDK_SYNC_TOKEN: fine-grained PAT scoped to oak-network/sdk only, Contents read/write (required by the dispatch API). A dead token fails the dispatch job loudly.
  • Label release-pipeline for the failure issues.
  • Releases must be published GitHub Releases, not bare tags; the trigger is release: published.
  • Note: the existing v1.0.0 tag cannot be rebuilt from a fresh clone (its tree is missing the openzeppelin-contracts submodule gitlink), so manual dispatch against it will fail at checkout. The next release cut from current main is fine.

Testing

  • build-abi-bundle.sh ran end-to-end against the real artifacts tree (37 contracts, 9 deployable, byte-reproducible tarball across runs).
  • sanitize-docs.py was validated against the previously committed stale docs (28 absolute links rewritten, none broken, idempotent second run) and the regenerated docs are verified free of machine paths.
  • Both workflows pass actionlint; action pins were verified against upstream tags.

Companion PRs: oak-network/sdk (sync workflows and tooling) and oak-network/crowdsplit (dispatch job).

adnanhq added 2 commits July 17, 2026 15:12
- release.yml: on every published release, build the contracts at the
  tag and attach a deterministic ABI bundle (abis-<tag>.tar.gz plus
  SHA256SUMS) as release assets. Stable releases also dispatch a
  contracts-release-published event to oak-network/sdk (SDK_SYNC_TOKEN
  secret) so its Contracts Sync workflow opens an ABI update PR.
  Failures open a deduplicated release-pipeline issue.
- docs.yml: regenerate forge-doc markdown on pushes to main that touch
  src/, sanitize machine-specific link paths, and open an auto PR.
- build-abi-bundle.sh: reproducible tarball (sorted entries, fixed
  mtime) of all src/ ABIs plus DataRegistryKeys.sol and metadata.
  contractKind from the AST separates deployable contracts from
  libraries and interfaces. Errors on two sources sharing a contract
  name.
- sanitize-docs.py: rewrites absolute machine paths in generated doc
  links to correct relative links, and fails on broken targets.
Replaces stale committed output: removes the deleted TestUSD contract
doc, drops removed Ownable members and eliminates hardcoded absolute
paths from the original author's machine. Generated with forge 1.7.1
plus .github/scripts/sanitize-docs.py, the same path docs.yml runs in
CI.
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@Suvadra-Barua

Suvadra-Barua commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Code review

Found 3 issues:

  1. Compiled Python bytecode file committed — .github/scripts/__pycache__/sanitize-docs.cpython-314.pyc is a machine-generated binary cache artifact that should never be tracked in git. The .gitignore does not exclude __pycache__/ or *.pyc, so this was accidentally staged. It encodes the developer's local CPython 3.14 version and will create spurious diffs for anyone running the script on a different Python version.

https://github.com/oak-network/contracts/blob/4d1e4164ee073c0e104f8abcd59a7fa41ddd54ff/.github/scripts/__pycache__/sanitize-docs.cpython-314.pyc

Fix: delete the file, and add __pycache__/ and *.pyc to .gitignore.

  1. build-abi-bundle.sh overwrites the $FOUNDRY_VERSION env var with the raw forge --version runtime output, ignoring the clean pinned value set by the workflow (FOUNDRY_VERSION: v1.7.1 with comment "explicit pin to keep bundle builds reproducible"). The foundryVersion field in metadata.json will embed the full version string (e.g. forge 1.7.1-stable (abc1234...)) rather than the pinned tag, which may break consumers that expect the exact pin value and defeats the stated reproducibility goal.

GIT_SHA="$(git -C "$REPO_ROOT" rev-parse HEAD)"
FOUNDRY_VERSION="$(forge --version | head -n1)"

Fix: replace line 89 with FOUNDRY_VERSION="${FOUNDRY_VERSION:-$(forge --version | head -n1)}" so the env var from the workflow takes precedence.

  1. sanitize-docs.py docstring claims "Idempotent" but is not fully idempotent. When a rewritten link target does not exist on disk, the script still writes the broken relative path to the file (line 42 always returns the rewritten path), then exits non-zero. On a second run the regex (which only matches absolute paths starting with /) no longer matches the now-relative link, so the broken-link error is silently swallowed and the script exits 0 — the opposite of idempotent behavior.

This script rewrites any link target containing `/docs/src/` to the correct
path relative to the file containing the link. Idempotent; stdlib only.

Fix: either don't write the file when broken links are detected (collect all errors before writing), or correct the docstring to clarify the caveat.

- Drop committed __pycache__ bytecode and ignore Python cache artifacts
- Honor the workflow-pinned FOUNDRY_VERSION in metadata.json, falling
  back to the runtime forge version for local runs
- Make sanitize-docs.py truly idempotent: compute all rewrites in
  memory and write nothing when a rewritten target is missing, so a
  failed run leaves the tree untouched and re-runs fail identically
@Suvadra-Barua

Copy link
Copy Markdown
Collaborator

All three issues from the earlier review are addressed in commit 4256469:

  1. .pyc removed — file deleted, __pycache__/ and *.pyc added to .gitignore.

  2. FOUNDRY_VERSION — changed to ${FOUNDRY_VERSION:-$(forge --version | head -n1)}, so the workflow-pinned value takes precedence and local runs fall back to the runtime version.

  3. sanitize-docs.py idempotency — rewrites are now computed in memory first; files are written only after confirming no broken links. A failed run leaves the tree untouched, making re-runs fail identically.

LGTM.

🤖 Generated with Claude Code

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.

2 participants