Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
14ff420
perf(remote): reuse HTTP sessions and trace pushes
mayneyao Jul 29, 2026
bb4807d
bench(push): cover CLI and resident SDK paths
mayneyao Jul 29, 2026
a54d8a0
fix(remote): redact unexpected error details
mayneyao Jul 29, 2026
5b221cf
docs: record push performance investigation
mayneyao Jul 29, 2026
2d7c426
docs: record staging identity blocker
mayneyao Jul 29, 2026
2a1f363
docs: clarify staging rollback targets
mayneyao Jul 29, 2026
d07d6ba
Merge Graft push performance into staging
mayneyao Jul 29, 2026
67f5edf
fix(remote): serialize SQLite HTTP publication
mayneyao Jul 29, 2026
a1964b8
fix(remote): isolate HTTP uploads from reads
mayneyao Jul 29, 2026
9c02877
fix(remote): refresh HTTP pools between commands
mayneyao Jul 29, 2026
cea6ff4
docs(perf): record final staging benchmarks
mayneyao Jul 29, 2026
9ac9e23
feat(remote): add streaming receive-pack protocol
mayneyao Jul 29, 2026
a1abcd7
perf(push): publish object packs with receive-pack
mayneyao Jul 29, 2026
c11f8eb
docs(perf): record receive-pack staging benchmarks
mayneyao Jul 29, 2026
2a865b7
feat(remote): add streamed receive-bundle protocol
mayneyao Jul 29, 2026
c16b2f7
perf(push): bundle snapshot and external objects
mayneyao Jul 29, 2026
c56b9fa
perf(remote): reuse the ref connection for bundles
mayneyao Jul 29, 2026
c412163
docs(perf): record receive-bundle staging benchmarks
mayneyao Jul 29, 2026
d27bc30
feat(remote): add independent npm release train
mayneyao Jul 29, 2026
7b57c32
chore(release): prepare Graft 0.11.0 and SDK 0.2.0
mayneyao Jul 29, 2026
3362ab7
fix(remote): constrain release artifact cleanup
mayneyao Jul 29, 2026
7ac14d0
chore(rust): resolve push path clippy warnings
mayneyao Jul 29, 2026
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
253 changes: 253 additions & 0 deletions .github/workflows/remote-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
name: Release Graft Remote packages

on:
pull_request:
branches: ["main"]
paths:
- ".github/workflows/remote-release.yml"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- "scripts/remote-release.mjs"
- "scripts/remote-release.test.mjs"
- "packages/graft-remote/**"
- "packages/graft-remote-hono/**"
- "packages/graft-remote-cloudflare/**"
- "services/graft-remote-cloudflare/**"
push:
tags:
- "graft-remote-v*"
workflow_dispatch:

concurrency:
group: graft-remote-release-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: read

jobs:
metadata:
name: Resolve Remote release metadata
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.meta.outputs.version }}
prerelease: ${{ steps.meta.outputs.prerelease }}
is_release: ${{ steps.meta.outputs.is_release }}
steps:
- name: Checkout release commit
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
persist-credentials: false

- name: Validate tag, commit, and package versions
id: meta
shell: bash
run: |
set -euo pipefail

version="$(node -p "require('./packages/graft-remote/package.json').version")"
is_release="false"
if [[ "$GITHUB_REF_TYPE" == "tag" ]]; then
version="${GITHUB_REF_NAME#graft-remote-v}"
is_release="true"
fi
version_regex='^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'
if [[ ! "$version" =~ $version_regex ]]; then
echo "::error::Remote release tag must match graft-remote-vX.Y.Z or a SemVer prerelease"
exit 1
fi

if [[ "$is_release" == "true" ]]; then
git fetch --no-tags origin main:refs/remotes/origin/main
head_commit="$(git rev-parse HEAD)"
if ! git merge-base --is-ancestor "$head_commit" origin/main; then
echo "::error::release commit $head_commit is not reachable from origin/main"
exit 1
fi
if ! git ls-remote --exit-code origin "refs/tags/${GITHUB_REF_NAME}^{}" >/dev/null; then
echo "::error::release tag ${GITHUB_REF_NAME} must be annotated"
exit 1
fi
fi

node scripts/remote-release.mjs validate "$version"
prerelease="false"
[[ "$version" == *-* ]] && prerelease="true"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT"
echo "is_release=$is_release" >> "$GITHUB_OUTPUT"

package:
name: Test and package Remote libraries
needs: metadata
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
persist-credentials: false

- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
with:
version: 10.14.0

- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Typecheck and test Remote packages
run: |
pnpm check:remote
pnpm test:remote
node --test scripts/remote-release.test.mjs

- name: Build verified release archives
run: |
node scripts/remote-release.mjs prepare "${{ needs.metadata.outputs.version }}"
node scripts/remote-release.mjs verify "${{ needs.metadata.outputs.version }}"

- name: Validate the Cloudflare deployment bundle
run: pnpm --filter graft-remote-cloudflare-verification exec wrangler deploy --dry-run --outdir /tmp/graft-remote-worker

- name: Upload exact release archives
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: graft-remote-${{ needs.metadata.outputs.version }}
path: release-assets/graft-remote
if-no-files-found: error
retention-days: 7

publish:
name: Publish npm packages and GitHub release
needs: [metadata, package]
if: needs.metadata.outputs.is_release == 'true'
runs-on: ubuntu-24.04
environment: npm
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
persist-credentials: false

- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
with:
node-version: 24
registry-url: https://registry.npmjs.org
package-manager-cache: false

- name: Install npm with trusted-publishing support
run: npm install --global npm@11.16.0

- name: Download verified release archives
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
with:
name: graft-remote-${{ needs.metadata.outputs.version }}
path: release-assets/graft-remote

- name: Verify downloaded release archives
run: node scripts/remote-release.mjs verify "${{ needs.metadata.outputs.version }}"

- name: Publish dependency-safe package sequence
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: node scripts/remote-release.mjs publish "${{ needs.metadata.outputs.version }}"

- name: Create or update Remote GitHub release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
with:
name: Graft Remote v${{ needs.metadata.outputs.version }}
tag_name: ${{ github.ref_name }}
target_commitish: ${{ github.sha }}
prerelease: ${{ needs.metadata.outputs.prerelease }}
make_latest: false
fail_on_unmatched_files: true
generate_release_notes: true
files: release-assets/graft-remote/*

verify-published:
name: Verify npm install on Node.js ${{ matrix.node }}
needs: [metadata, publish]
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
node: [20, 24]
steps:
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
with:
node-version: ${{ matrix.node }}
registry-url: https://registry.npmjs.org
package-manager-cache: false

- name: Install and import public packages
env:
REMOTE_VERSION: ${{ needs.metadata.outputs.version }}
run: |
fixture="$(mktemp -d)"
cd "$fixture"
npm init -y >/dev/null
npm install --ignore-scripts \
"@eidos.space/graft-remote@${REMOTE_VERSION}" \
"@eidos.space/graft-remote-hono@${REMOTE_VERSION}" \
hono
node --input-type=module <<'NODE'
import { createGraftRemoteHandler } from '@eidos.space/graft-remote'
import { createGraftRemote } from '@eidos.space/graft-remote-hono'
if (typeof createGraftRemoteHandler !== 'function' || typeof createGraftRemote !== 'function') {
throw new Error('published Remote packages did not expose the expected API')
}
NODE

verify-cloudflare-published:
name: Verify public Cloudflare package bundle
needs: [metadata, publish]
runs-on: ubuntu-24.04
steps:
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
with:
node-version: 24
registry-url: https://registry.npmjs.org
package-manager-cache: false

- name: Install and bundle the public Cloudflare package
env:
REMOTE_VERSION: ${{ needs.metadata.outputs.version }}
run: |
fixture="$(mktemp -d)"
cd "$fixture"
npm init -y >/dev/null
npm install --ignore-scripts \
"@eidos.space/graft-remote@${REMOTE_VERSION}" \
"@eidos.space/graft-remote-cloudflare@${REMOTE_VERSION}" \
wrangler@4.113.0
cat > index.js <<'WORKER'
import { RepositoryDurableObject } from '@eidos.space/graft-remote-cloudflare'
export { RepositoryDurableObject }
export default { fetch() { return new Response('ok') } }
WORKER
cat > wrangler.jsonc <<'CONFIG'
{
"name": "graft-remote-public-package-smoke",
"main": "index.js",
"compatibility_date": "2026-07-29",
"compatibility_flags": ["nodejs_compat"],
"durable_objects": {
"bindings": [
{ "name": "REPOSITORIES", "class_name": "RepositoryDurableObject" }
]
},
"migrations": [
{ "tag": "v1", "new_sqlite_classes": ["RepositoryDurableObject"] }
]
}
CONFIG
npx wrangler deploy --dry-run --outdir dist
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/mutants.out*
/dist/
/distx/
/release-assets/
/node_modules/
packages/graft-sdk/native/*.node
packages/graft-sdk/*.node
Expand Down
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,44 @@
# Changelog

## Graft 0.11.0 — 2026-07-30

### Added

- HTTP Remotes can now negotiate `receive-pack` and `receive-bundle` so an
incremental push publishes immutable objects and the ref update in two
protocol requests instead of checking and uploading every object separately.
- `@eidos.space/graft-remote`, `@eidos.space/graft-remote-hono`, and
`@eidos.space/graft-remote-cloudflare` provide reusable protocol, routing,
and R2/Durable Object layers for compatible hosted Remotes.
- Push tracing reports safe phase timings, request counts, transferred bytes,
correlation IDs, and `Server-Timing` data without exposing credentials,
repository URLs, row contents, or local paths.

### Changed

- `@eidos.space/graft` 0.2.0 uses the bundled push protocols from resident
repository sessions while preserving the public session API and fallback
compatibility with existing HTTP Remotes.
- Snapshot and external-file objects are bundled deterministically, uploaded
with bounded streaming, and published through the existing compare-and-swap
ref boundary.

### Performance

- A one-row incremental SQLite push to the staging HTTP Remote now needs two
data-plane requests rather than six, with measured resident-session median
latency reduced from 5.23 seconds to 3.16 seconds.
- Reusing one HTTP connection for ref discovery and bundle publication removes
duplicate DNS, TCP, TLS, and authentication setup within a push.

### Compatibility

- Clients automatically fall back to the public object-by-object protocol when
a Remote does not advertise bundled receive capabilities.
- Non-fast-forward protection, force behavior, atomic ref publication, retry
after partial upload, clone/fetch/pull, and logical SQLite row diffs retain
their existing behavior.

## Graft 0.10.0 — 2026-07-29

### Added
Expand Down
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,16 @@ anything.

### On your machine

Install Graft v0.10.0:
Install Graft v0.11.0:

```sh
curl -fsSL https://raw.githubusercontent.com/eidos-space/graft/main/install.sh \
| GRAFT_VERSION=0.10.0 sh
| GRAFT_VERSION=0.11.0 sh

graft --version
```

Omit `GRAFT_VERSION=0.10.0` to install the latest published release. Prebuilt
Omit `GRAFT_VERSION=0.11.0` to install the latest published release. Prebuilt
archives are available on the
[releases page](https://github.com/eidos-space/graft/releases).

Expand Down
Loading
Loading