Skip to content

publish

publish #86

Workflow file for this run

name: publish
on:
workflow_dispatch:
inputs:
tag:
description: Git tag to release, e.g. v0.5.0-rc.0
required: true
type: string
push:
tags:
- 'v*'
workflow_run:
workflows: ['ci']
types: [completed]
branches: [main]
permissions:
contents: read
id-token: write
jobs:
publish-stable:
name: publish stable packages
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
steps:
- name: Resolve tag
id: tag
env:
INPUT_TAG: ${{ inputs.tag }}
REF_NAME: ${{ github.ref_name }}
run: |
set -euo pipefail
TAG="${INPUT_TAG:-$REF_NAME}"
if [[ -z "$TAG" || ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo "Expected a vX.Y.Z or vX.Y.Z-prerelease tag, got '$TAG'" >&2
exit 1
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
VERSION="${TAG#v}"
if [[ "$VERSION" == *-* ]]; then
NPM_TAG="${VERSION#*-}"
NPM_TAG="${NPM_TAG%%.*}"
else
NPM_TAG="latest"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "npm_tag=$NPM_TAG" >> "$GITHUB_OUTPUT"
- name: Checkout tag
uses: actions/checkout@v4
with:
ref: ${{ steps.tag.outputs.tag }}
fetch-depth: 0
submodules: recursive
- name: Validate tag and source versions
env:
TAG: ${{ steps.tag.outputs.tag }}
VERSION: ${{ steps.tag.outputs.version }}
run: |
set -euo pipefail
git fetch origin main --tags
if ! git merge-base --is-ancestor "$TAG" origin/main; then
echo "Tag $TAG is not an ancestor of origin/main" >&2
exit 1
fi
node <<'NODE'
const fs = require('fs');
const version = process.env.VERSION;
const root = JSON.parse(fs.readFileSync('package.json', 'utf8')).version;
const demo = JSON.parse(fs.readFileSync('demo/package.json', 'utf8')).version;
const flake = fs.readFileSync('flake.nix', 'utf8').match(/version = "([^"]+)";/)?.[1];
for (const [name, value] of Object.entries({ root, demo, flake })) {
if (value !== version) throw new Error(`${name} version ${value} does not match tag version ${version}`);
}
console.log(`Validated source versions for ${version}`);
NODE
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Setup Zig
uses: ./.github/actions/setup-zig
with:
version: 0.15.2
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Check formatting
run: bun run fmt
- name: Run linter
run: bun run lint
- name: Check types
run: bun run typecheck
- name: Run tests
run: bun test
- name: Build library
run: bun run build
- name: Check WASM size
run: |
SIZE=$(stat -c%s ghostty-vt.wasm)
echo "WASM size: $SIZE bytes"
if [ "$SIZE" -gt 524288 ]; then
echo "❌ Error: WASM exceeds 512 KiB limit"
exit 1
fi
- name: Setup Node.js for npm
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Ensure current npm for trusted publishing
run: npm install -g npm@latest
- name: Publish ghostty-web
env:
VERSION: ${{ steps.tag.outputs.version }}
NPM_TAG: ${{ steps.tag.outputs.npm_tag }}
run: |
set -euo pipefail
PACKAGE_NAME=$(node -p "require('./package.json').name")
if npm view "${PACKAGE_NAME}@${VERSION}" version >/dev/null 2>&1; then
echo "${PACKAGE_NAME}@${VERSION} already exists; skipping publish"
npm dist-tag add "${PACKAGE_NAME}@${VERSION}" "${NPM_TAG}" || echo "Unable to repair ${NPM_TAG} dist-tag with current npm credentials; package already exists."
else
npm publish --tag "${NPM_TAG}" --provenance --access public
fi
- name: Pin and validate demo package
env:
VERSION: ${{ steps.tag.outputs.version }}
run: |
set -euo pipefail
node <<'NODE'
const fs = require('fs');
const version = process.env.VERSION;
const path = 'demo/package.json';
const pkg = JSON.parse(fs.readFileSync(path, 'utf8'));
pkg.version = version;
pkg.dependencies['ghostty-web'] = version;
fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n');
console.log(`Pinned @ghostty-web/demo to ghostty-web ${version}`);
NODE
- name: Publish @ghostty-web/demo
working-directory: demo
env:
VERSION: ${{ steps.tag.outputs.version }}
NPM_TAG: ${{ steps.tag.outputs.npm_tag }}
run: |
set -euo pipefail
PACKAGE_NAME=$(node -p "require('./package.json').name")
if npm view "${PACKAGE_NAME}@${VERSION}" version >/dev/null 2>&1; then
echo "${PACKAGE_NAME}@${VERSION} already exists; skipping publish"
npm dist-tag add "${PACKAGE_NAME}@${VERSION}" "${NPM_TAG}" || echo "Unable to repair ${NPM_TAG} dist-tag with current npm credentials; package already exists."
else
npm publish --tag "${NPM_TAG}" --provenance --access public
fi
publish-next:
name: publish next packages
runs-on: ubuntu-latest
if: github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success'
steps:
- name: Checkout CI head
uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_sha }}
fetch-depth: 0
submodules: recursive
- name: Skip release commits
id: release-commit
env:
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
set -euo pipefail
SUBJECT=$(git log -1 --format=%s "$HEAD_SHA")
echo "subject=$SUBJECT"
if [[ "$SUBJECT" =~ ^chore\(release\):\ ]]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "Skipping next publish for release commit"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Setup Bun
if: steps.release-commit.outputs.skip != 'true'
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Setup Zig
if: steps.release-commit.outputs.skip != 'true'
uses: ./.github/actions/setup-zig
with:
version: 0.15.2
- name: Install dependencies
if: steps.release-commit.outputs.skip != 'true'
run: bun install --frozen-lockfile
- name: Build library
if: steps.release-commit.outputs.skip != 'true'
run: bun run build
- name: Setup Node.js for npm
if: steps.release-commit.outputs.skip != 'true'
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Ensure current npm for trusted publishing
if: steps.release-commit.outputs.skip != 'true'
run: npm install -g npm@latest
- name: Compute next version
if: steps.release-commit.outputs.skip != 'true'
id: version
run: |
set -euo pipefail
LATEST_TAG=$(git describe --tags --abbrev=0)
BASE_VERSION="${LATEST_TAG#v}"
SHORT_SHA=$(git rev-parse --short HEAD)
COMMITS_SINCE_TAG=$(git rev-list --count HEAD ^"$LATEST_TAG")
NEXT_VERSION="${BASE_VERSION}-next.${COMMITS_SINCE_TAG}.g${SHORT_SHA}"
echo "version=$NEXT_VERSION" >> "$GITHUB_OUTPUT"
echo "Publishing next version $NEXT_VERSION"
- name: Prepare root package
if: steps.release-commit.outputs.skip != 'true'
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
node <<'NODE'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
pkg.version = process.env.VERSION;
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
NODE
- name: Publish ghostty-web next
if: steps.release-commit.outputs.skip != 'true'
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
PACKAGE_NAME=$(node -p "require('./package.json').name")
if npm view "${PACKAGE_NAME}@${VERSION}" version >/dev/null 2>&1; then
echo "${PACKAGE_NAME}@${VERSION} already exists; skipping"
else
npm publish --tag next --provenance --access public
fi
- name: Prepare demo package
if: steps.release-commit.outputs.skip != 'true'
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
node <<'NODE'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('demo/package.json', 'utf8'));
pkg.version = process.env.VERSION;
pkg.dependencies['ghostty-web'] = process.env.VERSION;
fs.writeFileSync('demo/package.json', JSON.stringify(pkg, null, 2) + '\n');
NODE
- name: Publish @ghostty-web/demo next
if: steps.release-commit.outputs.skip != 'true'
working-directory: demo
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
PACKAGE_NAME=$(node -p "require('./package.json').name")
if npm view "${PACKAGE_NAME}@${VERSION}" version >/dev/null 2>&1; then
echo "${PACKAGE_NAME}@${VERSION} already exists; skipping"
else
npm publish --tag next --provenance --access public
fi