Skip to content
Open
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
63 changes: 63 additions & 0 deletions .github/workflows/ts-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: TypeScript CI

on:
pull_request:
branches:
- develop
push:
branches:
- develop
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ts-ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
quality:
name: Quality / Node ${{ matrix.node }}
runs-on: ubuntu-24.04
timeout-minutes: 20

strategy:
fail-fast: false
matrix:
node:
- "22"
- "24"

defaults:
run:
working-directory: ts

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
cache: npm
cache-dependency-path: ts/package-lock.json

- name: Install locked dependencies
run: npm ci

- name: Check architecture boundaries
run: npm run depcruise

- name: Type-check
run: npm run typecheck

- name: Run tests
run: npm test

- name: Build npm bundle
run: npm run build

- name: Validate npm package contents
run: npm pack --dry-run
228 changes: 228 additions & 0 deletions .github/workflows/ts-standalone-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
name: TypeScript Standalone Artifacts

on:
push:
branches:
- develop
workflow_dispatch:

permissions:
contents: read

env:
NODE_VERSION: "20"

jobs:
quality:
name: TypeScript quality gates
runs-on: ubuntu-24.04
defaults:
run:
working-directory: ts

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
cache-dependency-path: ts/package-lock.json

- name: Install dependencies
run: npm ci

- name: Check architecture boundaries
run: npm run depcruise

- name: Type-check
run: npm run typecheck

- name: Test
run: npm test

- name: Build npm bundle
run: npm run build

binaries:
name: ${{ matrix.asset }}
needs: quality
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
target: bun-linux-x64-baseline
asset: linux-x64
executable: wallet-cli
archive: tar.gz
glibc_max: "2.35"
- os: ubuntu-22.04-arm
target: bun-linux-arm64
asset: linux-arm64
executable: wallet-cli
archive: tar.gz
glibc_max: "2.35"
- os: macos-15-intel
target: bun-darwin-x64
asset: macos-x64
executable: wallet-cli
archive: tar.gz
- os: macos-15
target: bun-darwin-arm64
asset: macos-arm64
executable: wallet-cli
archive: tar.gz
- os: windows-2022
target: bun-windows-x64-baseline
asset: windows-x64
executable: wallet-cli.exe
archive: zip
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: ts

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
cache-dependency-path: ts/package-lock.json

- name: Install dependencies
run: npm ci

- name: Build standalone executable
run: >-
npm run build:standalone --
--target ${{ matrix.target }}
--outfile standalone/${{ matrix.executable }}

- name: Verify executable and embedded Ledger addon
run: node scripts/verify-standalone.mjs standalone/${{ matrix.executable }}

- name: Verify Linux ABI baseline
if: runner.os == 'Linux'
shell: bash
env:
MAX_GLIBC: ${{ matrix.glibc_max }}
run: |
set -euo pipefail
executable="standalone/${{ matrix.executable }}"
native_addon="$(
./node_modules/.bin/bun -e \
'import { resolveNodeHidAddon } from "./scripts/standalone/resolve-node-hid-addon.ts"; console.log(resolveNodeHidAddon().nativeAddon)'
)"
host_glibc="$(getconf GNU_LIBC_VERSION | awk '{print $2}')"

if [[ "${host_glibc}" != "${MAX_GLIBC}" ]]; then
echo "::error::runner glibc ${host_glibc} does not match the supported baseline ${MAX_GLIBC}"
exit 1
fi

required_versions="$(
{
readelf --version-info --wide "${executable}"
readelf --version-info --wide "${native_addon}"
} | grep -oE 'GLIBC_[0-9]+(\.[0-9]+)*' | sed 's/^GLIBC_//' | sort -Vu
)"
highest_required="$(printf '%s\n' "${required_versions}" | tail -n 1)"
highest_version="$(printf '%s\n%s\n' "${MAX_GLIBC}" "${highest_required}" | sort -V | tail -n 1)"

if [[ "${highest_version}" != "${MAX_GLIBC}" ]]; then
echo "::error::Linux artifact requires GLIBC_${highest_required}; maximum allowed is GLIBC_${MAX_GLIBC}"
exit 1
fi

dynamic_dependencies="$(readelf --dynamic --wide "${native_addon}")"
if ! grep -Fq 'Shared library: [libudev.so.1]' <<< "${dynamic_dependencies}"; then
echo "::error::Ledger native addon no longer declares its documented libudev.so.1 dependency"
exit 1
fi

echo "Verified GLIBC_${highest_required} <= GLIBC_${MAX_GLIBC} and libudev.so.1 dependency"

- name: Verify macOS code signature
if: runner.os == 'macOS'
run: codesign --verify --verbose standalone/${{ matrix.executable }}

- name: Package Unix archive
if: matrix.archive == 'tar.gz'
shell: bash
run: |
set -euo pipefail
version="$(node -p "require('./package.json').version")"
package="wallet-cli-${version}-${{ matrix.asset }}"
mkdir -p "standalone-assets/${package}"
cp "standalone/${{ matrix.executable }}" "standalone-assets/${package}/wallet-cli"
cp LICENSE "standalone-assets/${package}/LICENSE"
tar -C standalone-assets -czf "standalone-assets/${package}.tar.gz" "${package}"
rm -r "standalone-assets/${package}"

- name: Package Windows archive
if: matrix.archive == 'zip'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$version = node -p "require('./package.json').version"
$package = "wallet-cli-$version-${{ matrix.asset }}"
New-Item -ItemType Directory -Force "standalone-assets/$package" | Out-Null
Copy-Item "standalone/${{ matrix.executable }}" "standalone-assets/$package/wallet-cli.exe"
Copy-Item "LICENSE" "standalone-assets/$package/LICENSE"
Compress-Archive -Path "standalone-assets/$package" -DestinationPath "standalone-assets/$package.zip"
Remove-Item -Recurse "standalone-assets/$package"

- name: Upload platform artifact
uses: actions/upload-artifact@v4
with:
name: wallet-cli-${{ matrix.asset }}
path: ts/standalone-assets/*
if-no-files-found: error
retention-days: 1

artifacts:
name: Publish Actions artifact
needs: binaries
runs-on: ubuntu-24.04
permissions:
artifact-metadata: write
attestations: write
contents: read
id-token: write

steps:
- name: Download binaries
uses: actions/download-artifact@v4
with:
pattern: wallet-cli-*
path: standalone-assets
merge-multiple: true

- name: Create checksums and metadata
shell: bash
run: |
set -euo pipefail
cd standalone-assets
printf 'commit=%s\nref=%s\nrun_id=%s\n' \
"${GITHUB_SHA}" "${GITHUB_REF}" "${GITHUB_RUN_ID}" > BUILD_METADATA.txt
sha256sum BUILD_METADATA.txt wallet-cli-* > SHA256SUMS.txt

- name: Attest standalone archives
uses: actions/attest@v4
with:
subject-checksums: standalone-assets/SHA256SUMS.txt

- name: Upload standalone bundle
uses: actions/upload-artifact@v4
with:
name: wallet-cli-standalone-${{ github.sha }}
path: standalone-assets/*
if-no-files-found: error
retention-days: 30
Loading