Skip to content

Commit c426044

Browse files
author
9qeklajc
committed
add build scripts
1 parent 5e28e8f commit c426044

4 files changed

Lines changed: 219 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
concurrency:
10+
group: ci-${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
build:
15+
name: Build, lint, format, typecheck
16+
runs-on: ubuntu-latest
17+
timeout-minutes: 10
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
22+
- name: Setup Bun
23+
uses: oven-sh/setup-bun@v2
24+
with:
25+
bun-version: latest
26+
27+
- name: Install dependencies
28+
run: bun install --frozen-lockfile
29+
30+
- name: Format check
31+
run: bun run format:check
32+
33+
- name: Lint
34+
run: bun run lint
35+
36+
- name: Typecheck
37+
run: bun run typecheck
38+
39+
- name: Build
40+
run: bun run build
41+
42+
- name: Verify build artifact
43+
run: test -f dist/index.js
44+
45+
- name: Upload build artifact
46+
uses: actions/upload-artifact@v4
47+
with:
48+
name: routstr-cli-dist
49+
path: dist/
50+
retention-days: 7
51+
if-no-files-found: error

.github/workflows/integration.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Integration
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
schedule:
9+
# Daily smoke at 06:00 UTC to catch upstream package drift.
10+
- cron: "0 6 * * *"
11+
workflow_dispatch:
12+
13+
concurrency:
14+
group: integration-${{ github.workflow }}-${{ github.ref }}
15+
cancel-in-progress: true
16+
17+
jobs:
18+
install-and-smoke:
19+
name: Install via ${{ matrix.runtime }} on ${{ matrix.os }}
20+
runs-on: ${{ matrix.os }}
21+
timeout-minutes: 15
22+
strategy:
23+
fail-fast: false
24+
matrix:
25+
os: [ubuntu-latest, macos-latest]
26+
runtime: [bun, npm]
27+
steps:
28+
- name: Checkout
29+
uses: actions/checkout@v4
30+
31+
- name: Setup Bun
32+
uses: oven-sh/setup-bun@v2
33+
with:
34+
bun-version: latest
35+
36+
- name: Setup Node (npm path only)
37+
if: matrix.runtime == 'npm'
38+
uses: actions/setup-node@v4
39+
with:
40+
node-version: "20"
41+
42+
- name: Install dependencies
43+
run: bun install --frozen-lockfile
44+
45+
- name: Build
46+
run: bun run build
47+
48+
- name: Run integration smoke tests
49+
env:
50+
INTEGRATION_RUNTIME: ${{ matrix.runtime }}
51+
run: bash scripts/integration-test.sh

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,15 @@
1515
"format": "biome format --write src",
1616
"format:check": "biome format src",
1717
"check": "biome check src",
18-
"check:fix": "biome check --write src"
18+
"check:fix": "biome check --write src",
19+
"test": "bun run typecheck && bun run lint && bun run format:check && bun run build",
20+
"test:integration": "bun run build && bash scripts/integration-test.sh"
1921
},
22+
"files": [
23+
"dist",
24+
"README.md",
25+
"LICENSE"
26+
],
2027
"dependencies": {
2128
"commander": "^12.1.0"
2229
},

scripts/integration-test.sh

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/env bash
2+
# Integration smoke test: pack the CLI, install it globally, and exercise
3+
# offline commands to confirm a fresh install actually works end-to-end.
4+
#
5+
# Required tools: bun (always); node + npm (only when INTEGRATION_RUNTIME=npm).
6+
# Configure via env:
7+
# INTEGRATION_RUNTIME bun | npm (default: bun)
8+
9+
set -euo pipefail
10+
11+
RUNTIME="${INTEGRATION_RUNTIME:-bun}"
12+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
13+
WORK_DIR="$(mktemp -d 2>/dev/null || mktemp -d -t routstr-it)"
14+
INSTALL_PREFIX="${WORK_DIR}/prefix"
15+
PACK_DIR="${WORK_DIR}/pack"
16+
mkdir -p "${INSTALL_PREFIX}" "${PACK_DIR}"
17+
18+
cleanup() {
19+
rm -rf "${WORK_DIR}" || true
20+
}
21+
trap cleanup EXIT
22+
23+
log() { printf '\n\033[1;36m==>\033[0m %s\n' "$*"; }
24+
fail() { printf '\n\033[1;31mFAIL:\033[0m %s\n' "$*" >&2; exit 1; }
25+
26+
cd "${REPO_ROOT}"
27+
28+
[ -f dist/index.js ] || fail "dist/index.js missing — run 'bun run build' first."
29+
30+
log "Packing CLI tarball"
31+
# `npm pack` is available on both runtimes via the bun-shipped node, but we
32+
# prefer the runtime under test so the tarball mirrors what users would publish.
33+
case "${RUNTIME}" in
34+
bun)
35+
if ! command -v bun >/dev/null 2>&1; then fail "bun not on PATH"; fi
36+
;;
37+
npm)
38+
if ! command -v npm >/dev/null 2>&1; then fail "npm not on PATH"; fi
39+
;;
40+
*)
41+
fail "Unknown INTEGRATION_RUNTIME='${RUNTIME}' (use bun or npm)"
42+
;;
43+
esac
44+
45+
# npm pack is the lingua franca for tarball creation; both runtimes consume it.
46+
TARBALL=$(cd "${PACK_DIR}" && npm pack "${REPO_ROOT}" --silent | tail -n 1)
47+
TARBALL_PATH="${PACK_DIR}/${TARBALL}"
48+
[ -f "${TARBALL_PATH}" ] || fail "Tarball not produced at ${TARBALL_PATH}"
49+
log "Created tarball: ${TARBALL_PATH}"
50+
51+
log "Installing globally via ${RUNTIME}"
52+
case "${RUNTIME}" in
53+
bun)
54+
# bun add -g installs into ~/.bun/install/global; override with BUN_INSTALL.
55+
export BUN_INSTALL="${INSTALL_PREFIX}"
56+
export PATH="${INSTALL_PREFIX}/bin:${PATH}"
57+
bun add -g "${TARBALL_PATH}"
58+
;;
59+
npm)
60+
# npm honors --prefix for non-root global installs.
61+
export PATH="${INSTALL_PREFIX}/bin:${PATH}"
62+
npm install -g --prefix "${INSTALL_PREFIX}" "${TARBALL_PATH}"
63+
;;
64+
esac
65+
66+
command -v routstr >/dev/null 2>&1 || fail "'routstr' binary not on PATH after install"
67+
log "Installed at: $(command -v routstr)"
68+
69+
run_check() {
70+
local name="$1"; shift
71+
log "Smoke: ${name}"
72+
if ! "$@" >/tmp/routstr-it.out 2>&1; then
73+
cat /tmp/routstr-it.out
74+
fail "command failed: ${name}"
75+
fi
76+
}
77+
78+
assert_contains() {
79+
local needle="$1" file="$2"
80+
grep -q -- "${needle}" "${file}" || {
81+
cat "${file}"
82+
fail "expected output to contain: ${needle}"
83+
}
84+
}
85+
86+
# --- Offline commands: do not require a running Routstr node. ---
87+
88+
run_check "routstr --version" routstr --version
89+
assert_contains "0." /tmp/routstr-it.out
90+
91+
run_check "routstr --help" routstr --help
92+
assert_contains "routstr" /tmp/routstr-it.out
93+
assert_contains "instruct" /tmp/routstr-it.out
94+
95+
run_check "routstr schema" routstr schema
96+
assert_contains "\"name\"" /tmp/routstr-it.out
97+
98+
run_check "routstr instruct" routstr instruct
99+
assert_contains "routstr" /tmp/routstr-it.out
100+
101+
run_check "routstr instruct --format json" routstr instruct --format json
102+
# JSON output must parse.
103+
node -e "JSON.parse(require('fs').readFileSync('/tmp/routstr-it.out','utf8'))" \
104+
|| fail "instruct --format json produced invalid JSON"
105+
106+
# init --show is safe even when no config exists.
107+
run_check "routstr init --show" routstr init --show
108+
109+
log "All integration smoke checks passed (${RUNTIME})"

0 commit comments

Comments
 (0)