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
71 changes: 0 additions & 71 deletions .github/PUBLISHING.md

This file was deleted.

8 changes: 8 additions & 0 deletions .github/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Config for draft-release.yml — see specs/release-process-spec.md §3.
name-template: "v$RESOLVED_VERSION"
tag-template: "v$RESOLVED_VERSION"
change-template: "- $TITLE (#$NUMBER) @$AUTHOR"
template: |
## What's changed

$CHANGES
21 changes: 21 additions & 0 deletions .github/workflows/draft-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Draft release

# Keeps a rolling draft release: every merge to main updates its changelog
# with the PRs merged since the last published release. Publishing the draft
# creates the tag that starts the release. See specs/release-process-spec.md.

on:
push:
branches: [main]

permissions:
contents: write
pull-requests: read

jobs:
draft:
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@4d75298e00d9e34c483e5ff8c68d0ea1c1940c1e # v7.5.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
14 changes: 5 additions & 9 deletions .github/workflows/publish-one.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
name: Publish one package

# Reusable: build one @executablemd workspace member with dnt and publish it to
# npm (OIDC) and JSR (best-effort). publish-packages.yml calls this once per
# package and orders the calls with `needs:`.
# Reusable: build one @executablemd workspace member with dnt and publish it
# to npm (OIDC) and JSR (best-effort). See specs/release-process-spec.md.

on:
workflow_call:
Expand All @@ -23,9 +22,8 @@ permissions:
jobs:
publish:
runs-on: ubuntu-latest
# Environment protection rules (required reviewers, v* tag-only deployment
# rule) gate every publish; the npm trusted publisher is also bound to this
# environment name, so npm rejects OIDC tokens minted outside it.
# Gates every publish behind environment protection rules; the npm trusted
# publisher is bound to this name (spec §4).
environment: npm-publish
env:
# @effectionx/* peer-depend on effection `^3 || ^4`, which npm won't match
Expand Down Expand Up @@ -60,9 +58,7 @@ jobs:
echo "build failed after 4 attempts" >&2
exit 1

# Skip an already-published version so re-running a tag's workflow (or
# tagging a version that was hand-bootstrapped) is idempotent instead of
# failing on npm's no-republish rule.
# Idempotent: skip a version npm already has, so tag re-runs succeed.
- name: Publish to npm
working-directory: ${{ inputs.package }}/npm
run: |
Expand Down
65 changes: 42 additions & 23 deletions .github/workflows/publish-packages.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,16 @@
name: Publish packages

# GENERATED by scripts/gen-publish-workflow.ts — do not edit by hand.
# Run `deno task gen:publish-workflow` after adding/removing a workspace
# package or changing its @executablemd dependencies, then commit the result.
#
# One tag ships everything: release.yml builds the binary; this workflow fans
# out one publish-one.yml run per @executablemd package, ordered with `needs:`
# so dependencies publish before dependents (leaves run in parallel).
#
# Prerequisites (one-time) — see .github/PUBLISHING.md:
# - npm: an OIDC trusted publisher per @executablemd package. The workflow
# filename npm validates is the CALLER (publish-packages.yml), not the
# reusable publish-one.yml. No npm token is used.
# - JSR: create/link the @executablemd packages to this repo (best-effort).
#
# Note for consumers: core/runtime/cli depend on effection's 4.x prerelease,
# which npm's peer resolver rejects against @effectionx/* (`^3 || ^4`). Until
# effection 4 is stable, installing those packages needs `--legacy-peer-deps`.
# GENERATED by scripts/gen-publish-workflow.md — do not edit by hand.
# Run `deno task gen:publish-workflow` and commit the result.
# See specs/release-process-spec.md.

on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
tag:
description: "Tag to publish (e.g. v0.2.0)"
required: true

permissions:
contents: read
actions: read # the version job polls the release.yml run
id-token: write # granted to the reusable publish-one.yml jobs for OIDC

jobs:
Expand All @@ -37,11 +19,48 @@ jobs:
outputs:
value: ${{ steps.resolve.outputs.value }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6

- id: resolve
run: |
TAG="${{ github.event.inputs.tag || github.ref_name }}"
TAG="${{ github.ref_name }}"
echo "value=${TAG#v}" >> "$GITHUB_OUTPUT"

# Refuse a tag the manifests do not declare (spec §2).
- name: Validate the manifests declare this version
run: |
VERSION="${{ steps.resolve.outputs.value }}"
for f in durable-streams/deno.json runtime/deno.json core/deno.json cli/deno.json packages/code-review-agent/deno.json; do
declared="$(jq -r .version "$f")"
if [ "$declared" != "$VERSION" ]; then
echo "::error::$f declares $declared, not $VERSION — the tag does not match the manifests"
exit 1
fi
done

# Packages publish only after the binaries exist (spec §1): poll the
# release.yml run for this commit and fail if it fails.
- name: Wait for the binary release to succeed
env:
GH_TOKEN: ${{ github.token }}
run: |
for i in $(seq 1 60); do
RUNS="repos/${{ github.repository }}/actions/workflows/release.yml/runs?head_sha=${{ github.sha }}&per_page=1"
STATUS="$(gh api "$RUNS" --jq '.workflow_runs[0].status // "queued"')"
CONCLUSION="$(gh api "$RUNS" --jq '.workflow_runs[0].conclusion // "none"')"
if [ "$STATUS" = "completed" ]; then
if [ "$CONCLUSION" = "success" ]; then
echo "release.yml succeeded — binaries are published"
exit 0
fi
echo "::error::release.yml concluded '$CONCLUSION' — refusing to publish packages without binaries"
exit 1
fi
sleep 30
done
echo "::error::timed out waiting for release.yml"
exit 1

durable-streams:
needs: [version]
uses: ./.github/workflows/publish-one.yml
Expand Down
23 changes: 12 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ name: Release
on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
tag:
description: "Tag to build and release (e.g. v0.1.0)"
required: true

permissions:
contents: write
Expand All @@ -31,12 +26,18 @@ jobs:
- target: x86_64-pc-windows-msvc
artifact: xmd-x86_64-pc-windows-msvc.exe
steps:
# For a tag push, github.ref is the tag. For workflow_dispatch, check out
# the requested tag explicitly so the built revision matches the release
# name (checkout fails if the tag does not exist — fail closed).
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
ref: ${{ github.event.inputs.tag || github.ref }}

# Refuse a tag the manifests do not declare (spec §2) — the binary's
# --version comes from cli/deno.json.
- name: Tag matches the manifest version
run: |
TAG="${{ github.ref_name }}"
declared="$(jq -r .version cli/deno.json)"
if [ "v$declared" != "$TAG" ]; then
echo "::error::cli/deno.json declares $declared but the tag is $TAG — bump the manifests first"
exit 1
fi

- uses: denoland/setup-deno@e95548e56dfa95d4e1a28d6f422fafe75c4c26fb # v2.0.3
with:
Expand Down Expand Up @@ -78,7 +79,7 @@ jobs:
- name: Publish to GitHub Release
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
with:
tag_name: ${{ github.event.inputs.tag || github.ref_name }}
tag_name: ${{ github.ref_name }}
draft: false
fail_on_unmatched_files: true
files: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/repo-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
# analysis behavior; bump the pin as part of each release.
- name: Install xmd release binary
run: |
curl -fsSL https://executable.md/install.sh | XMD_VERSION=v0.2.0 sh
curl -fsSL https://executable.md/install.sh | XMD_VERSION=v0.3.0 sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"

- name: Run repo analysis
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
# PR-review behavior; bump the pin as part of each release.
- name: Install xmd release binary
run: |
curl -fsSL https://executable.md/install.sh | XMD_VERSION=v0.2.0 sh
curl -fsSL https://executable.md/install.sh | XMD_VERSION=v0.3.0 sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"

- name: Run review
Expand Down
5 changes: 5 additions & 0 deletions .reviews/ReviewPR.local.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ const pr = parseDiff(rawDiff, rawFiles, {
body: PR_BODY,
number: PR_NUMBER,
});

// TODO: we need an easier way to work with diffs here.
const changedFilePaths = pr.files.map((file) => file.path);
```

```bash silent exec
Expand Down Expand Up @@ -130,6 +133,8 @@ import { parseDiagnostics } from "@executablemd/code-review-agent";
const diagnostics = parseDiagnostics(rawDiagnostics, pr, doctor);
```

<ReleaseSpecWarning files={changedFilePaths} />

<ThinkFilter>
<OllamaProvider model="qwen3:30b-a3b">
<Instruction system="You are a precise TypeScript code review assistant. Be concise. Report only findings, not praise.">
Expand Down
4 changes: 4 additions & 0 deletions .reviews/ReviewPR.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const pr = parseDiff(rawDiff, rawFiles, {
body: prBody.trim(),
number: PR_NUMBER,
});

// TODO: we need an easier way to work with diffs here.
const changedFilePaths = pr.files.map((file) => file.path);
```

```bash silent exec
Expand Down Expand Up @@ -138,6 +141,7 @@ const diagnostics = parseDiagnostics(rawDiagnostics, pr, doctor);
<DeepInfraProvider model="Qwen/Qwen3-30B-A3B">
<Instruction system="You are a precise TypeScript code review assistant for the executable.md monorepo. Be concise. Report only findings, not praise.">
<GitHubComment>
<ReleaseSpecWarning files={changedFilePaths} />
<Format>
<PrPolicyReport pr={pr} diagnostics={diagnostics} doctor={doctor} />
</Format>
Expand Down
45 changes: 45 additions & 0 deletions .reviews/components/ReleaseSpecWarning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
inputs:
files:
type: array
required: true
---

# Release Config Files

The following is the list of files used in the release process. Update this
list when a file is added or removed.

<Capture as="releaseConfigFiles">

- .github/workflows/draft-release.yml
- .github/workflows/release.yml
- .github/workflows/publish-one.yml
- .github/workflows/publish-packages.yml
- .github/release-drafter.yml
- scripts/gen-publish-workflow.md
- scripts/build-npm.ts

</Capture>

```ts eval
const releaseChanged = files.filter((path) => releaseConfigFiles.includes(`- ${path}`));
const changedList = releaseChanged.join(", ");

// TODO: evaluate whether the diff's changes are actually reflected in the
// spec document, not just whether the spec file was touched.
```

<Output>

<Show when={releaseChanged.length > 0 && !files.includes("specs/release-process-spec.md")}>

> [!WARNING]
> This PR changes release configuration ({changedList}) without touching
> `specs/release-process-spec.md`. Review the spec and update it to match, or
> state `spec-reviewed: no changes needed` in the PR description (AGENTS.md
> rule 8).

</Show>

</Output>
2 changes: 1 addition & 1 deletion cli/deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@executablemd/cli",
"version": "0.2.0",
"version": "0.3.0",
"exports": "./src/cli.ts",
"imports": {
"@executablemd/core": "../core/mod.ts",
Expand Down
2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@executablemd/cli",
"version": "0.2.0",
"version": "0.3.0",
"description": "The xmd command-line interface for executable.md.",
"bin": {
"xmd": "./src/cli.ts"
Expand Down
Loading
Loading