Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
82 changes: 79 additions & 3 deletions .github/workflows/build-binary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,39 @@ name: Build Binaries
# writes a wrong page hash, which macOS 27 rejects), so the macOS smoke jobs
# re-sign the binary with `codesign` and verify the signature before running it.
#
# On a published release the same build and smoke jobs run against the release
# tag, and a final `release` job uploads the six smoke-tested binaries (the
# darwin ones re-signed by the macOS smoke jobs) as they are, plus a
# `SHA256SUMS` file, as release assets. The files keep their build names, so
# `releases/latest/download/open-connector-<target>` always resolves and a
# `curl | chmod +x` install needs no archive tool. The upload only runs after
# every smoke job passed, so a release never ships a binary that failed on its
# own platform. To backfill an existing release, dispatch the workflow on its
# tag with `release_tag` set.
#
# A `runs-on` label that no runner serves queues forever instead of failing, so
# `workflow_dispatch` is here to verify the labels before relying on them.
on:
push:
branches: [main]
pull_request:
release:
types: [published]
workflow_dispatch:
inputs:
release_tag:
description: "Upload the binaries to this existing release (leave empty to only build and smoke)"
required: false
type: string

# Cancel superseded runs for the same ref (e.g. new push to an open PR).
# Release runs use their own per-tag group and are never cancelled.
concurrency:
group: build-binary-${{ github.ref }}
cancel-in-progress: true
cancel-in-progress: ${{ github.event_name != 'release' }}

# Least privilege: these jobs only read the repository; the binaries are
# workflow artifacts, nothing is published.
# Least privilege: the build and smoke jobs only read the repository; only the
# `release` job gets `contents: write`, to upload the release assets.
permissions:
contents: read

Expand All @@ -59,6 +77,10 @@ env:
# on by default only on Node >= 22.18 / >= 23.6, so pin an LTS that supports it.
# Do NOT downgrade below 22.18 — `npm ci` (postinstall codegen) and typecheck fail.
NODE_VERSION: "24"
# The release the binaries are uploaded to: the published release, or the
# `release_tag` input of a manual dispatch. Empty on push and pull_request
# runs, which build and smoke-test only.
RELEASE_TAG: ${{ github.event.release.tag_name || inputs.release_tag }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

jobs:
build:
Expand Down Expand Up @@ -255,3 +277,57 @@ jobs:

- name: Smoke test ${{ matrix.target }}
run: node scripts/smoke-binary.ts dist/${{ matrix.file }}

# The release must ship the re-signed darwin binary, not the Linux-built
# one with the invalid signature, so replace the build job's artifact with
# the file that just passed codesign --verify and the smoke test.
- name: Replace ${{ matrix.target }} artifact with the signed binary
if: runner.os == 'macOS' && env.RELEASE_TAG != ''
uses: actions/upload-artifact@v7
with:
name: binary-${{ matrix.target }}
path: dist/${{ matrix.file }}
if-no-files-found: error
retention-days: 1
overwrite: true

# Attach the smoke-tested binaries to the release. Needs every smoke job, so
# one failing platform blocks the whole upload rather than publishing a
# partial set.
release:
name: Upload release assets
needs: [build, smoke]
if: github.event_name == 'release' || inputs.release_tag != ''
runs-on: blacksmith-2vcpu-ubuntu-2404
timeout-minutes: 15
permissions:
contents: write
steps:
- name: Download all binaries
uses: actions/download-artifact@v8
with:
path: dist
pattern: binary-*
merge-multiple: true

# The binaries are uploaded uncompressed, exactly as built. Fail early if
# any of the six is missing rather than publishing a partial set.
- name: Write checksums
working-directory: dist
run: |
set -euo pipefail
for file in open-connector-linux-x64 open-connector-linux-arm64 open-connector-darwin-x64 \
open-connector-darwin-arm64 open-connector-windows-x64.exe open-connector-windows-arm64.exe; do
test -f "${file}" || { echo "::error::missing ${file}"; exit 1; }
done
sha256sum open-connector-* > SHA256SUMS
ls -l
cat SHA256SUMS

# --clobber lets a re-run (or a manual backfill) replace assets that are
# already attached instead of failing on the name conflict.
- name: Upload to release ${{ env.RELEASE_TAG }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: gh release upload "$RELEASE_TAG" dist/* --clobber
34 changes: 31 additions & 3 deletions docs/single-binary.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,34 @@ first time that provider is used, so the process only parses the server itself a
what keeps the binary's resident memory well under what a single bundle needs, and it is invisible
to clients: the same routes, responses, and ETags.

## Download

Every [release](https://github.com/oomol-lab/open-connector/releases) attaches the six executables
uncompressed, built and smoke-tested on each platform by CI, plus a `SHA256SUMS` file:

| Asset | Platform |
| ---------------------------------- | ------------------- |
| `open-connector-linux-x64` | Linux x86_64 |
| `open-connector-linux-arm64` | Linux ARM64 |
| `open-connector-darwin-arm64` | macOS Apple Silicon |
| `open-connector-darwin-x64` | macOS Intel |
| `open-connector-windows-x64.exe` | Windows x86_64 |
| `open-connector-windows-arm64.exe` | Windows ARM64 |

The names are stable across releases, so `releases/latest/download/` always resolves to the newest
release. A plain download does not keep the executable bit, so `chmod +x` after it:

```bash
curl -fsSLO https://github.com/oomol-lab/open-connector/releases/latest/download/open-connector-linux-x64
curl -fsSLO https://github.com/oomol-lab/open-connector/releases/latest/download/SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMS
chmod +x open-connector-linux-x64
./open-connector-linux-x64
```

Replace `latest` with a tag such as `v1.4.1` to pin a release. The macOS binaries were re-signed on
a macOS runner, so they run without the `codesign` step described below.

## Build

Building requires Node.js for the npm scripts and the Bun version pinned in `.bun-version`; the
Expand Down Expand Up @@ -94,9 +122,9 @@ prints a notice that SQLite migrations are applied automatically and exits.
- `NODE_ENV` is fixed to `production` inside the binary, so logs are always JSON (no pretty
printing). `OOMOL_CONNECT_LOG_LEVEL` and every other environment variable are read at runtime as
usual.
- macOS: binaries built on macOS are ad-hoc signed by the build script. Binaries built on another
operating system carry an invalid ad-hoc signature, and macOS 27 and newer refuses to run them
until you re-sign them:
- macOS: binaries built on macOS are ad-hoc signed by the build script, and the release binaries
are re-signed on a macOS runner. Binaries built on another operating system carry an invalid
ad-hoc signature, and macOS 27 and newer refuses to run them until you re-sign them:
Comment thread
coderabbitai[bot] marked this conversation as resolved.

```bash
codesign --force --sign - dist/open-connector-darwin-arm64
Expand Down
Loading