Skip to content

libsinclair: extract the terminal as an embeddable library #59

libsinclair: extract the terminal as an embeddable library

libsinclair: extract the terminal as an embeddable library #59

Workflow file for this run

name: Release
on:
push:
branches: [main]
paths:
- "Cargo.toml"
workflow_dispatch: {}
permissions:
contents: write
jobs:
check-version:
runs-on: ubuntu-latest
outputs:
changed: ${{ steps.diff.outputs.changed }}
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v5
# Release when the workspace version has no matching tag yet. Idempotent
# for both push and manual dispatch (a HEAD~1 diff would miss dispatches
# and re-released versions).
- name: Read workspace version
id: version
run: |
VERSION=$(sed -n 's/^version = "\([0-9][^"]*\)".*/\1/p' Cargo.toml | head -1)
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Decide whether to release
id: diff
run: |
NEW="${{ steps.version.outputs.version }}"
if git ls-remote --tags origin "refs/tags/v${NEW}" | grep -q .; then
echo "v${NEW} already released — skipping."
echo "changed=false" >> "$GITHUB_OUTPUT"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
create-release:
runs-on: ubuntu-latest
needs: check-version
if: needs.check-version.outputs.changed == 'true'
steps:
- uses: actions/checkout@v5
- name: Create git tag
run: |
git tag "v${{ needs.check-version.outputs.version }}"
git push origin "v${{ needs.check-version.outputs.version }}"
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: "v${{ needs.check-version.outputs.version }}"
generate_release_notes: true
draft: false
build:
needs: [check-version, create-release]
if: needs.check-version.outputs.changed == 'true'
runs-on: macos-latest
# Signing creds are optional: with a Developer ID the app is signed with a
# hardened runtime and notarized; without them the build falls back to an
# ad-hoc signature (runs locally, but unsigned for distribution).
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
steps:
- uses: actions/checkout@v5
with:
ref: "v${{ needs.check-version.outputs.version }}"
submodules: recursive
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: Swatinem/rust-cache@v2
- name: Import signing certificate
if: env.APPLE_SIGNING_IDENTITY != ''
env:
CERT_P12: ${{ secrets.APPLE_CERT_P12 }}
CERT_PASSWORD: ${{ secrets.APPLE_CERT_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# Import the Developer ID Application cert into a temporary keychain so
# codesign can find it. The keychain is wiped when the job ends.
KEYCHAIN="$RUNNER_TEMP/build.keychain"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
security set-keychain-settings -lut 21600 "$KEYCHAIN"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
echo "$CERT_P12" | base64 --decode > "$RUNNER_TEMP/cert.p12"
security import "$RUNNER_TEMP/cert.p12" -k "$KEYCHAIN" -P "$CERT_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
security list-keychains -d user -s "$KEYCHAIN" $(security list-keychains -d user | sed s/\"//g)
rm -f "$RUNNER_TEMP/cert.p12"
- name: Build + bundle (.app)
run: |
export CODESIGN_IDENTITY="${APPLE_SIGNING_IDENTITY:--}"
scripts/bundle.sh
- name: Notarize app
if: env.APPLE_SIGNING_IDENTITY != ''
run: |
set -euo pipefail
ditto -c -k --keepParent dist/Sinclair.app "$RUNNER_TEMP/Sinclair.zip"
xcrun notarytool submit "$RUNNER_TEMP/Sinclair.zip" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_PASSWORD" \
--wait
xcrun stapler staple dist/Sinclair.app
- name: Package (.dmg)
run: |
export CODESIGN_IDENTITY="${APPLE_SIGNING_IDENTITY:--}"
scripts/dmg.sh
- name: Notarize dmg
if: env.APPLE_SIGNING_IDENTITY != ''
run: |
set -euo pipefail
xcrun notarytool submit dist/Sinclair.dmg \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_PASSWORD" \
--wait
xcrun stapler staple dist/Sinclair.dmg
- name: Verify bundle
run: |
codesign --verify --strict --verbose=2 dist/Sinclair.app
test -x dist/Sinclair.app/Contents/MacOS/sinclair
test -f dist/Sinclair.app/Contents/Resources/icon.icns
if [ -z "${APPLE_SIGNING_IDENTITY}" ]; then
echo "::warning::Ad-hoc build — unsigned and not notarized; Gatekeeper will warn on install."
fi
- name: Upload DMG
run: gh release upload "v${{ needs.check-version.outputs.version }}" dist/Sinclair.dmg --clobber
env:
GH_TOKEN: ${{ github.token }}
build-linux:
needs: [check-version, create-release]
if: needs.check-version.outputs.changed == 'true'
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
runner: ubuntu-22.04
- arch: aarch64
runner: ubuntu-22.04-arm
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v5
with:
ref: "v${{ needs.check-version.outputs.version }}"
submodules: recursive
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
clang \
curl \
file \
desktop-file-utils \
libasound2-dev \
libfontconfig-dev \
libssl-dev \
libvulkan1 \
libwayland-dev \
libx11-xcb-dev \
libxkbcommon-x11-dev \
libwebkit2gtk-4.1-dev
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: Swatinem/rust-cache@v2
- name: Install cargo-deb
run: cargo install cargo-deb --locked
- name: Build + package
run: scripts/linux.sh ${{ matrix.arch }}
- name: Upload to release
run: gh release upload "v${{ needs.check-version.outputs.version }}" dist/linux/*.tar.gz dist/linux/*.deb dist/linux/*.AppImage --clobber
env:
GH_TOKEN: ${{ github.token }}
update-homebrew:
runs-on: ubuntu-latest
needs: [check-version, build]
if: needs.check-version.outputs.changed == 'true'
steps:
- name: Download DMG and compute SHA
id: shas
run: |
VERSION="${{ needs.check-version.outputs.version }}"
URL="https://github.com/wess/sinclair/releases/download/v${VERSION}/Sinclair.dmg"
for i in $(seq 1 30); do
if curl -fsSL -o /tmp/Sinclair.dmg "${URL}" 2>/dev/null; then
break
fi
echo "Waiting for Sinclair.dmg... ($i/30)"
sleep 10
done
SHA=$(sha256sum /tmp/Sinclair.dmg | cut -d' ' -f1)
echo "sha_darwin_arm64=${SHA}" >> "$GITHUB_OUTPUT"
- name: Checkout homebrew tap
uses: actions/checkout@v5
with:
repository: wess/homebrew-packages
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
path: tap
- name: Update cask
run: |
VERSION="${{ needs.check-version.outputs.version }}"
mkdir -p tap/Casks
cat > tap/Casks/sinclair.rb << 'RUBY'
cask "sinclair" do
version "VERSION_PLACEHOLDER"
sha256 "SHA_DARWIN_ARM64_PLACEHOLDER"
url "https://github.com/wess/sinclair/releases/download/v#{version}/Sinclair.dmg"
name "Sinclair"
desc "Fast, GPU-accelerated terminal with tabs, splits, themes, and live-reload config"
homepage "https://github.com/wess/sinclair"
depends_on arch: :arm64
depends_on macos: :big_sur
app "Sinclair.app"
zap trash: [
"~/Library/Application Support/Sinclair",
"~/Library/Preferences/io.wess.sinclair.plist",
"~/.config/sinclair",
]
end
RUBY
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" tap/Casks/sinclair.rb
sed -i "s/SHA_DARWIN_ARM64_PLACEHOLDER/${{ steps.shas.outputs.sha_darwin_arm64 }}/g" tap/Casks/sinclair.rb
- name: Push updated cask
run: |
cd tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Casks/sinclair.rb
if git diff --cached --quiet; then
echo "Cask unchanged — skipping push."
else
git commit -m "Update sinclair to v${{ needs.check-version.outputs.version }}"
git push
fi
# Windows is beta: artifacts are compile/link-verified in CI but not yet
# runtime-tested, and the MSI is unsigned. See packaging/README.md.
build-windows:
needs: [check-version, create-release]
if: needs.check-version.outputs.changed == 'true'
runs-on: windows-latest
steps:
- uses: actions/checkout@v5
with:
ref: "v${{ needs.check-version.outputs.version }}"
submodules: recursive
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: Swatinem/rust-cache@v2
- name: Build + package (.zip + .msi)
shell: pwsh
run: scripts/windows.ps1 -Arch x86_64
- name: Pack Chocolatey (.nupkg)
shell: pwsh
run: |
$version = "${{ needs.check-version.outputs.version }}"
$zip = "dist/windows/sinclair-$version-windows-x86_64.zip"
$sha = (Get-FileHash $zip -Algorithm SHA256).Hash.ToLower()
(Get-Content packaging/chocolatey/sinclair.nuspec) -replace '0\.0\.0', $version |
Set-Content packaging/chocolatey/sinclair.nuspec
(Get-Content packaging/chocolatey/tools/chocolateyinstall.ps1) -replace '0\.0\.0', $version -replace '0{64}', $sha |
Set-Content packaging/chocolatey/tools/chocolateyinstall.ps1
choco pack packaging/chocolatey/sinclair.nuspec --outputdirectory dist/windows
- name: Upload to release
shell: pwsh
run: |
$v = "${{ needs.check-version.outputs.version }}"
$assets = Get-ChildItem -Path dist/windows/* -Include *.zip, *.msi, *.nupkg
gh release upload "v$v" $assets.FullName --clobber
env:
GH_TOKEN: ${{ github.token }}
- name: Mark Windows artifacts beta in release notes (best-effort)
shell: pwsh
continue-on-error: true
run: |
$v = "${{ needs.check-version.outputs.version }}"
$body = gh release view "v$v" --json body -q .body
if ($body -notmatch 'Windows builds are beta') {
$note = "$body`n`n---`n**Windows builds are beta** and currently unsigned (SmartScreen may warn about an unknown publisher)."
gh release edit "v$v" --notes $note
}
env:
GH_TOKEN: ${{ github.token }}
# Refresh the in-repo Scoop manifest with the released zip's URL + SHA-256.
# Non-fatal: if pushing to a protected main is blocked, the release still
# succeeds and the manifest can be updated by hand (see packaging/README.md).
update-scoop:
runs-on: ubuntu-latest
needs: [check-version, build-windows]
if: needs.check-version.outputs.changed == 'true'
continue-on-error: true
steps:
- uses: actions/checkout@v5
- name: Update Scoop manifest
run: |
VERSION="${{ needs.check-version.outputs.version }}"
URL="https://github.com/wess/sinclair/releases/download/v${VERSION}/sinclair-${VERSION}-windows-x86_64.zip"
for i in $(seq 1 30); do
if curl -fsSL -o /tmp/p.zip "${URL}" 2>/dev/null; then break; fi
echo "Waiting for windows zip... ($i/30)"; sleep 10
done
SHA=$(sha256sum /tmp/p.zip | cut -d' ' -f1)
python3 - "$VERSION" "$SHA" << 'PY'
import json, sys
version, sha = sys.argv[1], sys.argv[2]
path = "packaging/scoop/sinclair.json"
with open(path) as f:
m = json.load(f)
m["version"] = version
a = m["architecture"]["64bit"]
a["url"] = f"https://github.com/wess/sinclair/releases/download/v{version}/sinclair-{version}-windows-x86_64.zip"
a["hash"] = sha
a["extract_dir"] = f"sinclair-{version}-windows-x86_64"
with open(path, "w") as f:
json.dump(m, f, indent=4)
f.write("\n")
PY
- name: Commit manifest
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add packaging/scoop/sinclair.json
if git diff --cached --quiet; then
echo "Scoop manifest unchanged."
else
git commit -m "Update Scoop manifest to v${{ needs.check-version.outputs.version }}"
git push
fi