This commit is contained in:
can1357
2026-04-24 00:03:59 +02:00
7 changed files with 234 additions and 36 deletions
+79 -4
View File
@@ -173,9 +173,81 @@ jobs:
- name: Install method smoke tests
run: bun run ci:test:install-methods
release:
release_binary:
if: startsWith(github.ref, 'refs/tags/v')
needs: [check, native, test, install_methods]
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
target_id: linux-x64
native_artifact: pi-natives-linux-x64
binary_path: packages/coding-agent/binaries/omp-linux-x64
- os: ubuntu-24.04-arm
target_id: linux-arm64
native_artifact: pi-natives-linux-arm64
binary_path: packages/coding-agent/binaries/omp-linux-arm64
- os: macos-15-intel
target_id: darwin-x64
native_artifact: pi-natives-darwin-x64
binary_path: packages/coding-agent/binaries/omp-darwin-x64
- os: macos-14
target_id: darwin-arm64
native_artifact: pi-natives-darwin-arm64
binary_path: packages/coding-agent/binaries/omp-darwin-arm64
- os: windows-latest
target_id: win32-x64
native_artifact: pi-natives-win32-x64
binary_path: packages/coding-agent/binaries/omp-windows-x64.exe
runs-on: ${{ matrix.os }}
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:
lfs: true
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3"
- name: Cache bun dependencies
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('**/bun.lock') }}
- run: bun install --frozen-lockfile
- name: Download native addon(s)
uses: actions/download-artifact@v4
with:
name: ${{ matrix.native_artifact }}
path: packages/natives/native
- name: Build release binary
env:
RELEASE_TARGETS: ${{ matrix.target_id }}
run: bun run ci:release:build-binaries
- name: Smoke release binary
if: runner.os != 'Windows'
run: |
runtime_dir="$(mktemp -d)"
HOME="$runtime_dir/home" XDG_DATA_HOME="$runtime_dir/xdg" "${{ matrix.binary_path }}" --version
- name: Smoke release binary (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$runtimeDir = Join-Path $env:TEMP ("omp-runtime-" + [System.Guid]::NewGuid().ToString("N"))
New-Item -ItemType Directory -Force -Path $runtimeDir | Out-Null
$env:HOME = Join-Path $runtimeDir "home"
$env:XDG_DATA_HOME = Join-Path $runtimeDir "xdg"
& "${{ matrix.binary_path }}" --version
- name: Upload release binary artifact
uses: actions/upload-artifact@v4
with:
name: omp-binary-${{ matrix.target_id }}
path: ${{ matrix.binary_path }}
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: [release_binary]
runs-on: ubuntu-22.04
permissions:
contents: write
@@ -197,6 +269,12 @@ jobs:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('**/bun.lock') }}
- run: bun install --frozen-lockfile
- name: Download release binaries
uses: actions/download-artifact@v4
with:
pattern: omp-binary-*
path: packages/coding-agent/binaries
merge-multiple: true
- name: Download native addons
uses: actions/download-artifact@v4
with:
@@ -205,9 +283,6 @@ jobs:
merge-multiple: true
- name: Verify native addons
run: bun run ci:release:verify-natives
- name: Build binaries
run: bun run ci:release:build-binaries
- name: Stage native addons for release
run: cp packages/natives/native/*.node packages/coding-agent/binaries/
- name: Create GitHub Release
+2 -1
View File
@@ -10,6 +10,7 @@
- Fixed edit tool diff and replace operations to report missing-file failures as `File not found: <path>` errors instead of raw filesystem ENOENT errors
- Fixed `local://` URL path leak on Linux where `//` collapsing to `/` produced `local:/path` forms that bypassed the internal protocol handler and leaked as filesystem paths, breaking plan mode file resolution
- Fixed Darwin compiled binaries failing to start under Bun 1.3.12 by ad-hoc signing local and release binary builds after applying Bun's no-codesign workaround ([#754](https://github.com/can1357/oh-my-pi/issues/754))
- Fixed Tavily web search silently returning off-topic news articles when `--recency` was set. The provider was unconditionally coupling `topic: "news"` to recency, which scoped Tavily's index to news publications and excluded documentation, release notes, GitHub, and all non-news technical content. Technical queries with `--recency` now return the correct corpus.
- Fixed status-line sanitization to strip OSC, DCS, PM, APC, and 8-bit CSI escape sequences instead of leaving payload fragments in the UI
- Fixed inline read tool previews to avoid rendering duplicate summary rows above the same code cell
@@ -7115,4 +7116,4 @@ Initial public release.
- Git branch display in footer
- Message queueing during streaming responses
- OAuth integration for Gmail and Google Calendar access
- HTML export with syntax highlighting and collapsible sections
- HTML export with syntax highlighting and collapsible sections
+1 -1
View File
@@ -31,7 +31,7 @@
"omp": "src/cli.ts"
},
"scripts": {
"build": "bun --cwd=../stats scripts/generate-client-bundle.ts --generate && bun --cwd=../natives run embed:native && bun build --compile --define PI_COMPILED=true --external mupdf --root ../.. ./src/cli.ts --outfile dist/omp && bun --cwd=../natives run embed:native --reset && bun --cwd=../stats scripts/generate-client-bundle.ts --reset",
"build": "bun scripts/build-binary.ts",
"check": "biome check . && bun run check:types",
"check:types": "tsgo -p tsconfig.json --noEmit",
"lint": "biome lint .",
@@ -0,0 +1,61 @@
#!/usr/bin/env bun
import * as path from "node:path";
const packageDir = path.join(import.meta.dir, "..");
const outputPath = path.join(packageDir, "dist", "omp");
function shouldAdhocSignDarwinBinary(): boolean {
return process.platform === "darwin";
}
async function runCommand(command: string[], env: NodeJS.ProcessEnv = Bun.env): Promise<void> {
const proc = Bun.spawn(command, {
cwd: packageDir,
env,
stdout: "inherit",
stderr: "inherit",
});
const exitCode = await proc.exited;
if (exitCode !== 0) {
throw new Error(`Command failed with exit code ${exitCode}: ${command.join(" ")}`);
}
}
async function main(): Promise<void> {
await runCommand(["bun", "--cwd=../stats", "scripts/generate-client-bundle.ts", "--generate"]);
try {
await runCommand(["bun", "--cwd=../natives", "run", "embed:native"]);
try {
const buildEnv = shouldAdhocSignDarwinBinary() ? { ...Bun.env, BUN_NO_CODESIGN_MACHO_BINARY: "1" } : Bun.env;
await runCommand(
[
"bun",
"build",
"--compile",
"--define",
"PI_COMPILED=true",
"--external",
"mupdf",
"--root",
"../..",
"./src/cli.ts",
"--outfile",
"dist/omp",
],
buildEnv,
);
// Bun 1.3.12 emits a truncated Mach-O signature on darwin builds.
if (shouldAdhocSignDarwinBinary()) {
await runCommand(["codesign", "--force", "--sign", "-", outputPath]);
}
} finally {
await runCommand(["bun", "--cwd=../natives", "run", "embed:native", "--reset"]);
}
} finally {
await runCommand(["bun", "--cwd=../stats", "scripts/generate-client-bundle.ts", "--reset"]);
}
}
await main();
+80 -13
View File
@@ -2,9 +2,9 @@
import * as fs from "node:fs/promises";
import * as path from "node:path";
import { $ } from "bun";
interface BinaryTarget {
id: string;
platform: string;
arch: string;
target: string;
@@ -17,30 +17,35 @@ const entrypoint = "./packages/coding-agent/src/cli.ts";
const isDryRun = process.argv.includes("--dry-run");
const targets: BinaryTarget[] = [
{
id: "darwin-arm64",
platform: "darwin",
arch: "arm64",
target: "bun-darwin-arm64",
outfile: "packages/coding-agent/binaries/omp-darwin-arm64",
},
{
id: "darwin-x64",
platform: "darwin",
arch: "x64",
target: "bun-darwin-x64",
outfile: "packages/coding-agent/binaries/omp-darwin-x64",
},
{
id: "linux-x64",
platform: "linux",
arch: "x64",
target: "bun-linux-x64-modern",
outfile: "packages/coding-agent/binaries/omp-linux-x64",
},
{
id: "linux-arm64",
platform: "linux",
arch: "arm64",
target: "bun-linux-arm64",
outfile: "packages/coding-agent/binaries/omp-linux-arm64",
},
{
id: "win32-x64",
platform: "win32",
arch: "x64",
target: "bun-windows-x64-modern",
@@ -48,19 +53,53 @@ const targets: BinaryTarget[] = [
},
];
function parseRequestedTargets(): Set<string> | null {
const flagIndex = process.argv.findIndex(arg => arg === "--targets");
const flagValue =
flagIndex >= 0
? process.argv[flagIndex + 1]
: process.argv.find(arg => arg.startsWith("--targets="))?.split("=", 2)[1] ?? Bun.env.RELEASE_TARGETS;
if (!flagValue) {
return null;
}
return new Set(
flagValue
.split(",")
.map(value => value.trim())
.filter(Boolean),
);
}
function shouldAdhocSignDarwinBinary(target: BinaryTarget): boolean {
return target.platform === "darwin" && process.platform === "darwin";
}
async function runCommand(command: string[], cwd: string, env: NodeJS.ProcessEnv = Bun.env): Promise<void> {
const proc = Bun.spawn(command, {
cwd,
env,
stdout: "inherit",
stderr: "inherit",
});
const exitCode = await proc.exited;
if (exitCode !== 0) {
throw new Error(`Command failed with exit code ${exitCode}: ${command.join(" ")}`);
}
}
async function embedNative(target: BinaryTarget): Promise<void> {
if (isDryRun) {
console.log(`DRY RUN bun --cwd=packages/natives run embed:native [${target.platform}/${target.arch}]`);
return;
}
await $`bun --cwd=packages/natives run embed:native`
.cwd(repoRoot)
.env({
...Bun.env,
TARGET_PLATFORM: target.platform,
TARGET_ARCH: target.arch,
});
await runCommand(["bun", "--cwd=packages/natives", "run", "embed:native"], repoRoot, {
...Bun.env,
TARGET_PLATFORM: target.platform,
TARGET_ARCH: target.arch,
});
}
async function buildBinary(target: BinaryTarget): Promise<void> {
@@ -71,9 +110,19 @@ async function buildBinary(target: BinaryTarget): Promise<void> {
return;
}
await $`bun build --compile --define PI_COMPILED=true --root . --external mupdf --target=${target.target} ${entrypoint} --outfile ${target.outfile}`.cwd(
const buildEnv = shouldAdhocSignDarwinBinary(target)
? { ...Bun.env, BUN_NO_CODESIGN_MACHO_BINARY: "1" }
: Bun.env;
await runCommand(
["bun", "build", "--compile", "--define", "PI_COMPILED=true", "--root", ".", "--external", "mupdf", "--target", target.target, entrypoint, "--outfile", target.outfile],
repoRoot,
buildEnv,
);
// Bun 1.3.12 emits a truncated Mach-O signature on darwin builds.
if (shouldAdhocSignDarwinBinary(target)) {
await runCommand(["codesign", "--force", "--sign", "-", path.join(repoRoot, target.outfile)], repoRoot);
}
}
async function generateBundle(): Promise<void> {
@@ -81,7 +130,7 @@ async function generateBundle(): Promise<void> {
console.log("DRY RUN bun --cwd=packages/stats scripts/generate-client-bundle.ts --generate");
return;
}
await $`bun --cwd=packages/stats scripts/generate-client-bundle.ts --generate`.cwd(repoRoot);
await runCommand(["bun", "--cwd=packages/stats", "scripts/generate-client-bundle.ts", "--generate"], repoRoot);
}
async function resetArtifacts(): Promise<void> {
@@ -90,15 +139,33 @@ async function resetArtifacts(): Promise<void> {
console.log("DRY RUN bun --cwd=packages/stats scripts/generate-client-bundle.ts --reset");
return;
}
await $`bun --cwd=packages/natives run embed:native --reset`.cwd(repoRoot);
await $`bun --cwd=packages/stats scripts/generate-client-bundle.ts --reset`.cwd(repoRoot);
await runCommand(["bun", "--cwd=packages/natives", "run", "embed:native", "--reset"], repoRoot);
await runCommand(["bun", "--cwd=packages/stats", "scripts/generate-client-bundle.ts", "--reset"], repoRoot);
}
async function main(): Promise<void> {
const requestedTargets = parseRequestedTargets();
const selectedTargets = requestedTargets
? targets.filter(target => requestedTargets.has(target.id))
: targets;
if (requestedTargets) {
const unknownTargets = [...requestedTargets].filter(
requestedTarget => !targets.some(target => target.id === requestedTarget),
);
if (unknownTargets.length > 0) {
throw new Error(`Unknown release target(s): ${unknownTargets.join(", ")}`);
}
}
if (selectedTargets.length === 0) {
throw new Error("No release targets selected.");
}
await fs.mkdir(binariesDir, { recursive: true });
await generateBundle();
try {
for (const target of targets) {
for (const target of selectedTargets) {
await buildBinary(target);
}
} finally {
+3 -5
View File
@@ -20,12 +20,10 @@ RUN bun install --frozen-lockfile
RUN bun --cwd=packages/natives run build
RUN cd packages/coding-agent && bun run build
# Install binary and native addon to PATH
# Install binary to PATH
RUN mkdir -p /root/.local/bin && \
cp packages/coding-agent/dist/omp /root/.local/bin/ && \
cp packages/natives/native/pi_natives.linux-x64-modern.node /root/.local/bin/ && \
cp packages/natives/native/pi_natives.linux-x64-baseline.node /root/.local/bin/
cp packages/coding-agent/dist/omp /root/.local/bin/
ENV PATH="/root/.local/bin:$PATH"
# Verify
RUN omp --version
RUN HOME=/tmp/omp-home XDG_DATA_HOME=/tmp/omp-xdg omp --version
+8 -12
View File
@@ -4,6 +4,9 @@ set -euo pipefail
cd "$(dirname "$0")/../.."
ROOT_DIR="$(pwd)"
WORK_DIR="$(mktemp -d)"
TMP_WORK_DIR="$WORK_DIR/tmp"
mkdir -p "$TMP_WORK_DIR"
export TMPDIR="$TMP_WORK_DIR"
trap 'rm -rf "$WORK_DIR"' EXIT
section() {
@@ -13,9 +16,11 @@ section() {
smoke_cli() {
local omp_bin="$1"
"$omp_bin" --version
"$omp_bin" --help >/dev/null
"$omp_bin" stats --summary >/dev/null
local runtime_dir
runtime_dir="$(mktemp -d "$WORK_DIR/compiled-runtime.XXXXXX")"
XDG_DATA_HOME="$runtime_dir/xdg" HOME="$runtime_dir/home" "$omp_bin" --version
XDG_DATA_HOME="$runtime_dir/xdg" HOME="$runtime_dir/home" "$omp_bin" --help >/dev/null
XDG_DATA_HOME="$runtime_dir/xdg" HOME="$runtime_dir/home" "$omp_bin" stats --summary >/dev/null
}
find_tarball() {
@@ -40,15 +45,6 @@ bun --cwd=packages/coding-agent run build
BINARY_DIR="$WORK_DIR/binary-bin"
mkdir -p "$BINARY_DIR"
cp packages/coding-agent/dist/omp "$BINARY_DIR/omp"
shopt -s nullglob
native_addons=(packages/natives/native/pi_natives.*.node)
shopt -u nullglob
if [ "${#native_addons[@]}" -eq 0 ]; then
echo "No native addon files found in packages/natives/native"
exit 1
fi
cp "${native_addons[@]}" "$BINARY_DIR/"
smoke_cli "$BINARY_DIR/omp"
section "Source install smoke"