Skip to content
Draft
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
73 changes: 69 additions & 4 deletions .github/workflows/nightly-dry-run.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,91 @@
name: nightly-dry-run

# Manual preview of the nightly version-computation plan (cross-test gate +
# Manual preview of the release version-computation plan (cross-test gate +
# git-cliff oracle, capped at minor, with the not-major assertion). Calls the
# same release-gate-plan reusable workflow the real nightly uses, so it
# same release-gate-plan reusable workflow the real release uses, so it
# exercises identical logic. Has NO publish jobs — it structurally cannot
# publish/tag anything. Use this to verify pipeline changes before they ship.
#
# release-type:
# nightly — preview the gated nightly plan (cross-test must be green).
# dev — preview the GATELESS dev plan AND the per-SDK resolved dev
# versions (e.g. node-sdk 6.1.0-dev.<sha>). This is the dry-run for
# the dev path: it runs the exact oracle + resolve-sdk-version the
# real dev release uses, minus every publish/tag step.

on:
workflow_dispatch:
inputs:
ref:
description: "Ref to compute the nightly plan for"
description: "Ref to compute the release plan for"
required: false
type: string
default: main
release-type:
description: "Which plan to preview"
required: false
type: choice
options:
- nightly
- dev
default: nightly

jobs:
plan:
uses: ./.github/workflows/release-gate-plan.yml
with:
ref: ${{ inputs.ref || github.ref_name }}
# dev previews the gateless path (no cross-test gate), exactly like the
# real dev release. nightly keeps the gate.
gate: ${{ inputs.release-type != 'dev' }}
secrets: inherit

# Preview the per-SDK versions a dev release would publish, using the same
# `resolve-sdk-version --release-type dev` the real release jobs call. Prints
# only — no manifest writes, no npm publish, no tags.
preview-dev-versions:
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
needs: [plan]
if: inputs.release-type == 'dev' && needs.plan.outputs.nothing-pending != 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
# Check out the exact SHA the plan oracle resolved against (not the
# branch HEAD), so the previewed short-sha + manifest match what a
# real dev run on this commit would compute — no drift if commits
# land between the plan job and this one.
ref: ${{ needs.plan.outputs.sha }}
fetch-depth: 0
- uses: ./.github/actions/setup-release-tools
- name: Resolve dev versions per SDK
env:
PENDING_VERSION: ${{ needs.plan.outputs.version }}
PENDING_KIND: ${{ needs.plan.outputs.kind }}
run: |
set -euo pipefail
if [ -z "$PENDING_VERSION" ] || [ -z "$PENDING_KIND" ]; then
echo "::error::plan produced empty pending version/kind; cannot preview dev versions"
exit 1
fi
{
echo "## Dev dry-run — resolved versions"
echo ""
echo "Pending libxmtp bump: \`$PENDING_VERSION\` (kind: $PENDING_KIND)"
echo ""
echo "| SDK | dev version |"
echo "| --- | --- |"
} >> "$GITHUB_STEP_SUMMARY"
for SDK in node-sdk browser-sdk node-bindings wasm-bindings ios android libxmtp; do
V=$(xmtp-release resolve-sdk-version \
--sdk "$SDK" --release-type dev \
--pending-version "$PENDING_VERSION" \
--pending-kind "$PENDING_KIND")
echo "| $SDK | \`$V\` |" >> "$GITHUB_STEP_SUMMARY"
echo "::notice::dev $SDK -> $V"
done
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "No publishes/tags occur in a dry-run." >> "$GITHUB_STEP_SUMMARY"

summary:
needs: [plan]
if: always()
Expand All @@ -29,7 +94,7 @@ jobs:
- name: Print plan result
run: |
{
echo "## Nightly dry-run plan"
echo "## Release dry-run plan (${{ inputs.release-type || 'nightly' }})"
echo ""
echo "- gate-skip: \`${{ needs.plan.outputs.gate-skip }}\`"
echo "- nothing-pending: \`${{ needs.plan.outputs.nothing-pending }}\`"
Expand Down
18 changes: 13 additions & 5 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,21 @@ jobs:

- name: Determine NPM tag
id: npm-tag
env:
VERSION: ${{ inputs.version }}
run: |
if [[ "${{ inputs.version }}" == *"nightly"* ]]; then
echo "tag=nightly" >> $GITHUB_OUTPUT
elif [[ "${{ inputs.version }}" == *"dev"* || "${{ inputs.version }}" == *"rc"* ]]; then
echo "tag=prerelease" >> $GITHUB_OUTPUT
set -euo pipefail
# Each prerelease channel gets its own dist-tag so consumers can pin
# @nightly / @dev / @rc independently. Check nightly first (a nightly
# string contains neither "dev" nor "rc"). A plain release -> latest.
if [[ "$VERSION" == *"nightly"* ]]; then
echo "tag=nightly" >> "$GITHUB_OUTPUT"
elif [[ "$VERSION" == *"-dev"* ]]; then
echo "tag=dev" >> "$GITHUB_OUTPUT"
elif [[ "$VERSION" == *"-rc"* ]]; then
echo "tag=rc" >> "$GITHUB_OUTPUT"
else
echo "tag=latest" >> $GITHUB_OUTPUT
echo "tag=latest" >> "$GITHUB_OUTPUT"
fi

- name: Update npm to latest
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/release-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,14 @@ jobs:
if [ "${{ inputs.release-type }}" = "rc" ]; then
VERSION=$(xmtp-release compute-version --sdk android --release-type rc --rc-number ${{ inputs.rc-number }})
elif [ "${{ inputs.release-type }}" = "dev" ]; then
VERSION=$(xmtp-release compute-version --sdk android --release-type dev)
if [ -z "${{ inputs.pending-version }}" ] || [ -z "${{ inputs.pending-kind }}" ]; then
echo "::error::dev resolve called with empty pending version/kind"
exit 1
fi
VERSION=$(xmtp-release resolve-sdk-version \
--sdk android --release-type dev \
--pending-version "${{ inputs.pending-version }}" \
--pending-kind "${{ inputs.pending-kind }}")
elif [ "${{ inputs.release-type }}" = "nightly" ]; then
# Belt-and-suspenders: release.yml already skips this whole job when
# the gate isn't green or there are no conventional commits
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/release-browser-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@ jobs:
if [ "${{ inputs.release-type }}" = "rc" ]; then
VERSION=$(xmtp-release compute-version --sdk browser-sdk --release-type rc --rc-number ${{ inputs.rc-number }})
elif [ "${{ inputs.release-type }}" = "dev" ]; then
VERSION=$(xmtp-release compute-version --sdk browser-sdk --release-type dev)
if [ -z "${{ inputs.pending-version }}" ] || [ -z "${{ inputs.pending-kind }}" ]; then
echo "::error::dev resolve called with empty pending version/kind"
exit 1
fi
VERSION=$(xmtp-release resolve-sdk-version \
--sdk browser-sdk --release-type dev \
--pending-version "${{ inputs.pending-version }}" \
--pending-kind "${{ inputs.pending-kind }}")
elif [ "${{ inputs.release-type }}" = "nightly" ]; then
if [ -z "${{ inputs.pending-version }}" ] || [ -z "${{ inputs.pending-kind }}" ]; then
echo "::error::nightly resolve called with empty pending version/kind"
Expand Down
22 changes: 18 additions & 4 deletions .github/workflows/release-gate-plan.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
name: release-gate-plan

# Reusable: cross-test gate + version oracle (the nightly "plan"). Computes the
# Reusable: cross-test gate + version oracle (the release "plan"). Computes the
# pending libxmtp {version,kind}, caps at minor, asserts not-major, prints the
# plan. Does NO publishing. Called by release.yml (real nightly) and
# nightly-dry-run.yml (manual preview) so both run identical logic.
# plan. Does NO publishing. Used for BOTH nightly and dev: nightly runs the
# cross-test gate (gate: true, the default); dev runs it gateless (gate: false)
# since the cross-test gate is a nightly-only correctness gate. Called by
# release.yml (real nightly + dev) and nightly-dry-run.yml (manual preview) so
# all paths run identical version-computation logic.

on:
workflow_call:
inputs:
ref:
required: true
type: string
gate:
required: false
type: boolean
default: true
description: "Run the nightly cross-test gate. dev builds pass false to skip it."
outputs:
sha:
value: ${{ jobs.gate.outputs.sha }}
Expand Down Expand Up @@ -53,8 +61,14 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
SHA: ${{ steps.sha.outputs.sha }}
GATE_ENABLED: ${{ inputs.gate }}
run: |
set -euo pipefail
if [ "$GATE_ENABLED" != "true" ]; then
echo "gate-skip=false" >> "$GITHUB_OUTPUT"
echo "::notice::Cross-test gate disabled for this run (e.g. dev build); proceeding."
exit 0
fi
gh api "repos/${REPO}/actions/workflows/cross-test.yml/runs?head_sha=${SHA}&status=completed" > /tmp/cross-runs.json
RESULT=$(xmtp-release cross-test-gate --input=/tmp/cross-runs.json --sha "$SHA")
PASS=$(printf '%s' "$RESULT" | jq -r '.pass')
Expand Down Expand Up @@ -143,7 +157,7 @@ jobs:
echo "kind=$KIND" >> "$GITHUB_OUTPUT"
# Human-readable release plan.
{
echo "## Nightly release plan"
echo "## Release plan"
echo ""
echo "- libxmtp: \`$VERSION\` (bump: $KIND, baseline: $LAST)"
echo ""
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/release-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,14 @@ jobs:
if [ "${{ inputs.release-type }}" = "rc" ]; then
VERSION=$(xmtp-release compute-version --sdk ios --release-type rc --rc-number ${{ inputs.rc-number }})
elif [ "${{ inputs.release-type }}" = "dev" ]; then
VERSION=$(xmtp-release compute-version --sdk ios --release-type dev)
if [ -z "${{ inputs.pending-version }}" ] || [ -z "${{ inputs.pending-kind }}" ]; then
echo "::error::dev resolve called with empty pending version/kind"
exit 1
fi
VERSION=$(xmtp-release resolve-sdk-version \
--sdk ios --release-type dev \
--pending-version "${{ inputs.pending-version }}" \
--pending-kind "${{ inputs.pending-kind }}")
elif [ "${{ inputs.release-type }}" = "nightly" ]; then
# Belt-and-suspenders: release.yml already skips this whole job when
# the gate isn't green or there are no conventional commits
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/release-node-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@ jobs:
if [ "${{ inputs.release-type }}" = "rc" ]; then
VERSION=$(xmtp-release compute-version --sdk node-sdk --release-type rc --rc-number ${{ inputs.rc-number }})
elif [ "${{ inputs.release-type }}" = "dev" ]; then
VERSION=$(xmtp-release compute-version --sdk node-sdk --release-type dev)
if [ -z "${{ inputs.pending-version }}" ] || [ -z "${{ inputs.pending-kind }}" ]; then
echo "::error::dev resolve called with empty pending version/kind"
exit 1
fi
VERSION=$(xmtp-release resolve-sdk-version \
--sdk node-sdk --release-type dev \
--pending-version "${{ inputs.pending-version }}" \
--pending-kind "${{ inputs.pending-kind }}")
elif [ "${{ inputs.release-type }}" = "nightly" ]; then
if [ -z "${{ inputs.pending-version }}" ] || [ -z "${{ inputs.pending-kind }}" ]; then
echo "::error::nightly resolve called with empty pending version/kind"
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/release-node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,14 @@ jobs:
if [ "${{ inputs.release-type }}" = "rc" ]; then
VERSION=$(xmtp-release compute-version --sdk node-bindings --release-type rc --rc-number ${{ inputs.rc-number }})
elif [ "${{ inputs.release-type }}" = "dev" ]; then
VERSION=$(xmtp-release compute-version --sdk node-bindings --release-type dev)
if [ -z "${{ inputs.pending-version }}" ] || [ -z "${{ inputs.pending-kind }}" ]; then
echo "::error::dev resolve called with empty pending version/kind"
exit 1
fi
VERSION=$(xmtp-release resolve-sdk-version \
--sdk node-bindings --release-type dev \
--pending-version "${{ inputs.pending-version }}" \
--pending-kind "${{ inputs.pending-kind }}")
elif [ "${{ inputs.release-type }}" = "nightly" ]; then
# Belt-and-suspenders: release.yml already skips this whole job when
# the gate isn't green or there are no conventional commits
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/release-wasm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,14 @@ jobs:
if [ "${{ inputs.release-type }}" = "rc" ]; then
VERSION=$(xmtp-release compute-version --sdk wasm-bindings --release-type rc --rc-number ${{ inputs.rc-number }})
elif [ "${{ inputs.release-type }}" = "dev" ]; then
VERSION=$(xmtp-release compute-version --sdk wasm-bindings --release-type dev)
if [ -z "${{ inputs.pending-version }}" ] || [ -z "${{ inputs.pending-kind }}" ]; then
echo "::error::dev resolve called with empty pending version/kind"
exit 1
fi
VERSION=$(xmtp-release resolve-sdk-version \
--sdk wasm-bindings --release-type dev \
--pending-version "${{ inputs.pending-version }}" \
--pending-kind "${{ inputs.pending-kind }}")
elif [ "${{ inputs.release-type }}" = "nightly" ]; then
# Belt-and-suspenders: release.yml already skips this whole job when
# the gate isn't green or there are no conventional commits
Expand Down
15 changes: 10 additions & 5 deletions .github/workflows/release.yml
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,15 @@ jobs:
# nothing-pending.
plan:
needs: [validate]
if: needs.validate.outputs.release-type == 'nightly' && needs.validate.outputs.skip != 'true'
if: >-
(needs.validate.outputs.release-type == 'nightly' || needs.validate.outputs.release-type == 'dev')
&& needs.validate.outputs.skip != 'true'
uses: ./.github/workflows/release-gate-plan.yml
with:
ref: ${{ needs.validate.outputs.ref }}
# dev builds preview the next version off main but must NOT be gated on
# cross-test (that's a nightly correctness gate). nightly keeps the gate.
gate: ${{ needs.validate.outputs.release-type == 'nightly' }}
secrets: inherit
Comment thread
macroscopeapp[bot] marked this conversation as resolved.

release-ios:
Expand All @@ -167,7 +172,7 @@ jobs:
!cancelled() && !contains(needs.*.result, 'failure')
&& needs.validate.outputs.ios == 'true' && needs.validate.outputs.skip != 'true'
&& needs.validate.outputs.dry-run != 'true'
&& (needs.validate.outputs.release-type != 'nightly'
&& ((needs.validate.outputs.release-type != 'nightly' && needs.validate.outputs.release-type != 'dev')
|| (needs.plan.outputs.gate-skip != 'true' && needs.plan.outputs.nothing-pending != 'true'))
uses: ./.github/workflows/release-ios.yml
with:
Expand All @@ -184,7 +189,7 @@ jobs:
!cancelled() && !contains(needs.*.result, 'failure')
&& needs.validate.outputs.android == 'true' && needs.validate.outputs.skip != 'true'
&& needs.validate.outputs.dry-run != 'true'
&& (needs.validate.outputs.release-type != 'nightly'
&& ((needs.validate.outputs.release-type != 'nightly' && needs.validate.outputs.release-type != 'dev')
|| (needs.plan.outputs.gate-skip != 'true' && needs.plan.outputs.nothing-pending != 'true'))
uses: ./.github/workflows/release-android.yml
with:
Expand All @@ -200,7 +205,7 @@ jobs:
if: >-
!cancelled() && !contains(needs.*.result, 'failure')
&& needs.validate.outputs.node-sdk == 'true' && needs.validate.outputs.skip != 'true'
&& (needs.validate.outputs.release-type != 'nightly'
&& ((needs.validate.outputs.release-type != 'nightly' && needs.validate.outputs.release-type != 'dev')
|| (needs.plan.outputs.gate-skip != 'true' && needs.plan.outputs.nothing-pending != 'true'))
uses: ./.github/workflows/release-node-sdk.yml
with:
Expand All @@ -217,7 +222,7 @@ jobs:
if: >-
!cancelled() && !contains(needs.*.result, 'failure')
&& needs.validate.outputs.browser-sdk == 'true' && needs.validate.outputs.skip != 'true'
&& (needs.validate.outputs.release-type != 'nightly'
&& ((needs.validate.outputs.release-type != 'nightly' && needs.validate.outputs.release-type != 'dev')
|| (needs.plan.outputs.gate-skip != 'true' && needs.plan.outputs.nothing-pending != 'true'))
uses: ./.github/workflows/release-browser-sdk.yml
with:
Expand Down
Loading