diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index dfdb8b771..000000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.sh text eol=lf diff --git a/.github/workflows/formula.yml b/.github/workflows/formula.yml index f78c6e7cd..dc89f00f6 100644 --- a/.github/workflows/formula.yml +++ b/.github/workflows/formula.yml @@ -3,10 +3,14 @@ name: Homebrew Formula on: pull_request: paths: + - .github/actions/setup-nix/** - .github/workflows/formula.yml - .github/workflows/release.yml - Formula/oxide-code.rb - - scripts/update-homebrew-formula.sh + - flake.lock + - flake.nix + - scripts/oxide-code-formula.rb + - scripts/update-homebrew-formula.nu workflow_call: inputs: tag: @@ -42,6 +46,11 @@ jobs: - name: Checkout uses: actions/checkout@v7 + - name: Set up Nix + uses: ./.github/actions/setup-nix + with: + cachix_auth_token: ${{ secrets.CACHIX_AUTH_TOKEN }} + - name: Render formula env: GH_TOKEN: ${{ github.token }} @@ -54,7 +63,7 @@ jobs: fi mkdir sidecars gh release download "$tag" --pattern '*.sha256' --dir sidecars - ./scripts/update-homebrew-formula.sh "$tag" sidecars + nix develop .#lint -c nu scripts/update-homebrew-formula.nu "$tag" sidecars - name: Put Homebrew on PATH if: runner.os == 'Linux' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3c407f7f9..4bd8e69b5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,14 +5,11 @@ on: tags: ['v[0-9]+.*'] pull_request: paths: - - .gitattributes - .github/workflows/ci.yml - .github/workflows/release.yml - - about.hbs - about.toml - LICENSE - licenses/** - - scripts/generate-notices.sh - Cargo.lock - Cargo.toml - crates/*/Cargo.toml @@ -119,7 +116,12 @@ jobs: - name: Generate third-party notices shell: bash run: | - ./scripts/generate-notices.sh "${{ matrix.target }}" THIRD-PARTY-NOTICES + cargo about generate \ + --locked \ + --manifest-path crates/ox/Cargo.toml \ + --target "${{ matrix.target }}" \ + --name about licenses \ + -o THIRD-PARTY-NOTICES - name: Build archive id: binary @@ -148,9 +150,6 @@ jobs: tar -xzf "$ARCHIVE" -C smoke fi - cmp LICENSE smoke/LICENSE - cmp THIRD-PARTY-NOTICES smoke/THIRD-PARTY-NOTICES - if [[ "$GITHUB_REF_TYPE" == 'tag' ]]; then expected=${GITHUB_REF_NAME#v} else diff --git a/.prettierignore b/.prettierignore index 75b156282..618d4984f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,2 @@ -about.hbs +licenses/*.hbs pnpm-lock.yaml diff --git a/AGENTS.md b/AGENTS.md index c3fa76b05..c7b07d9aa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,16 +12,15 @@ The tree lists only the top-level entries whose names leave their purpose unclea . ├── .agents/ # Agent skills for the release procedure ├── .claude/ # Link to the agent skills for Claude Code -├── about.hbs # Plain-text third-party notice template ├── about.toml # Release dependency license selection ├── crates/ox/ # The `ox` binary crate with the CLI and the stdio frontends, mapped in docs/design/architecture.md ├── crates/ox-core/ # The `ox-core` library crate with the agent core, mapped in docs/design/architecture.md ├── crates/ox-tui/ # The `ox-tui` library crate with the terminal UI, mapped in docs/design/architecture.md ├── docs/ # User guides, conventions, design specs, and research notes ├── Formula/ # Homebrew formula, served from this repo as a tap -├── licenses/ # Licenses for bundled syntax definitions and themes +├── licenses/ # Third-party notice templates and bundled asset licenses ├── nix/ # Nix package and Home Manager module with its tests -└── scripts/ # Distribution notices, Homebrew formula updater, credential leak check, and comment width check +└── scripts/ # Homebrew formula renderer and Ruby template ``` ## Read Before You Edit diff --git a/Formula/oxide-code.rb b/Formula/oxide-code.rb index 4335e2eb2..189c25b13 100644 --- a/Formula/oxide-code.rb +++ b/Formula/oxide-code.rb @@ -1,5 +1,4 @@ -# Regenerated by scripts/update-homebrew-formula.sh for each release the formula follows. -# Edit that script, not this file, when the formula's structure needs to change. +# Generated by scripts/update-homebrew-formula.nu from scripts/oxide-code-formula.rb. class OxideCode < Formula desc "Terminal-based AI coding assistant written in Rust" diff --git a/RELEASING.md b/RELEASING.md index fb0b4efbf..e94a4ed6f 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -164,5 +164,5 @@ The `v0.1.0` release PR also switches the install snippets to `releases/latest/d The build matrix in `release.yml` lists the targets. Each release attaches one archive per target with a `.sha256` sidecar and a build provenance attestation, and release asset names belong to the [stability surface](#stability-surface). -- The platform floors in `docs/guide/install.md` bind the build. The Linux job fails when the binary needs a newer glibc than the guide promises, reading the floor from the phrase `Linux with glibc or later`, so keep that wording when editing it. If a runner image update trips the check, either pin the Linux build to the previous image or raise the floor in the guide. The macOS job fails when the binary's minimum macOS version differs from the guide's `**macOS**: or later` floor, so keep that wording too. Raising that floor also means changing `MACOSX_DEPLOYMENT_TARGET` in `release.yml` and the formula's `depends_on macos:` in `scripts/update-homebrew-formula.sh`. +- The platform floors in `docs/guide/install.md` bind the build. The Linux job fails when the binary needs a newer glibc than the guide promises, reading the floor from the phrase `Linux with glibc or later`, so keep that wording when editing it. If a runner image update trips the check, either pin the Linux build to the previous image or raise the floor in the guide. The macOS job fails when the binary's minimum macOS version differs from the guide's `**macOS**: or later` floor, so keep that wording too. Raising that floor also means changing `MACOSX_DEPLOYMENT_TARGET` in `release.yml` and the formula's `depends_on macos:` in `scripts/oxide-code-formula.rb`. - Add a target by extending the matrix. Each target needs a runner that executes its binary, since every build job checks its archive there. diff --git a/docs/conventions/development.md b/docs/conventions/development.md index 9c3204ed4..9ef6e53a6 100644 --- a/docs/conventions/development.md +++ b/docs/conventions/development.md @@ -18,7 +18,7 @@ If you use [direnv](https://direnv.net), run `direnv allow` once. After that, `. Entering the dev shell in this repository installs a [pre-commit](https://pre-commit.com) hook generated by [`git-hooks.nix`](https://github.com/cachix/git-hooks.nix). The shell generates its config, `.pre-commit-config.yaml`, as a gitignored symlink in each checkout, which is why [`AGENTS.md`](../../AGENTS.md#rules-for-every-change) asks for `nix develop -c true` in every new worktree. -The hook runs the compile-free subset of the [verification](../../AGENTS.md#verification) checks on staged files: file hygiene, Nix formatting and lints, ShellCheck, `rustfmt`, Prettier and Taplo formatting, `markdownlint`, and `cspell`, plus a `comment-width` check. Hooks that fix files in place fail the commit if they make changes, so stage the fixes and commit again. Checks that compile the workspace are excluded because their build cost would gate every commit. +The hook runs the compile-free subset of the [verification](../../AGENTS.md#verification) checks on staged files: file hygiene, Nix formatting and lints, ShellCheck, `rustfmt`, Prettier and Taplo formatting, `markdownlint`, and `cspell`. Hooks that fix files in place fail the commit if they make changes, so stage the fixes and commit again. Checks that compile the workspace are excluded because their build cost would gate every commit. The Node-based hooks run tools pinned in `node_modules`, which pnpm installs before running them whenever it is missing or out of date with the lockfile. `nix flake check` leaves them out because its sandbox cannot install `node_modules`, and it leaves out `rustfmt` so that it never fetches the pinned toolchain. CI's Lint job runs the Node tools, and its Rust (Linux) job runs `cargo fmt --check`. Run `pnpm format`, `pnpm lint`, and `pnpm spellcheck` directly when changing documentation, comments, or config files. @@ -63,7 +63,7 @@ The Node-based hooks run tools pinned in `node_modules`, which pnpm installs bef Prettier and Taplo leave long strings alone, and no formatter covers shell code, so wrap YAML, TOML, and shell files by hand: -- Wrap comments as [Writing](writing.md#comments) describes, and keep other lines within about 100 columns, counting the indent. Leave a line long when it holds an unbreakable token, such as a URL or path, or sits in a heredoc body or literal block whose line breaks are content, unless it starts with `#`, which the `comment-width` hook checks as a comment. +- Wrap comments as [Writing](writing.md#comments) describes, and keep other lines within about 100 columns, counting the indent. Leave a line long when it holds an unbreakable token, such as a URL or path, or sits in a heredoc body or literal block whose line breaks are content. - Wrap a long string only where its value stays the same, and break it where [Rust](rust.md#strings) breaks a continued literal. - Split a shell command that runs past the limit at its pipelines and `&&` or `||` lists, and split one in a Markdown code block the same way where that helps readability. If a part is still too long, keep at most what names the operation on its first line, such as the command, its subcommands, and a leading script, pattern, or message, and put each further flag with its value, or each group of related arguments, on a line of its own. Indent continuation lines one level past the first line, and end each line but the last with `\`, unless it ends with `|`, `&&`, or `||`, which already continue the command. In a workflow, write the command in a `run: |` block. - For a multiline command substitution, put the command on the next line and the closing `)` on its own line, aligned with the assignment. @@ -81,7 +81,7 @@ CI runs on pushes to `main`, pull requests, and manual dispatch. The release wor | Lint | `pnpm format`, `pnpm lint`, `pnpm spellcheck`, and `cargo deny check` in the flake's `lint` shell, which leaves out the pinned toolchain | | Rust (Linux, macOS, Windows) | `cargo fmt --check` on Linux, then `cargo clippy --all-targets`, `cargo clippy`, `cargo doc --no-deps`, and the test suite under `cargo llvm-cov`, uploaded to Codecov, then on Linux a check that every snapshot baseline has a test | | MSRV | `cargo hack check --rust-version` against `rust-version` | -| Nix (Linux, macOS) | `nix flake check`, which also runs the test suite in the build sandbox, then the package build with its `ox --version` and installed license notice checks | +| Nix (Linux, macOS) | `nix flake check`, which also runs the test suite in the build sandbox, then the package build with its `ox --version` check | Branch protection requires only the CI Result job and Codecov's `codecov/patch` status. CI Result reads every job of the current run attempt from the Actions API and fails when any job of the CI workflow, including each matrix leg, concludes anything other than success or skipped. That catches a leg cancelled before a runner picks it up, which the `needs` results of a matrix job omit. CI Result runs once the jobs in its `needs` finish and counts any unfinished job as a failure, so list each new job there. diff --git a/docs/conventions/testing.md b/docs/conventions/testing.md index 2c3e194c7..d957506f6 100644 --- a/docs/conventions/testing.md +++ b/docs/conventions/testing.md @@ -103,4 +103,3 @@ The tests drive headless and REPL runs. Check the TUI-only paths by hand in a sc 2. **`/model` swap**: Send a prompt on Opus, switch to Haiku with `/model`, and send another. Then switch back to Opus and send a third. Every reply succeeds and remembers the earlier exchanges. 3. **Cancellation**: Press Esc while a long reply streams, and check that the stream stops within a second. Then send a new prompt and verify that the reply answers only that prompt. 4. **Scrolling and the mouse**: In a bare terminal, inside tmux with `mouse on`, and over SSH, wheel-scroll the chat and check that tmux stays out of copy mode. Press PageUp until the jump-to-bottom pill appears, then press Ctrl+End and check that the chat follows the tail again. Drag-select a region and paste it elsewhere, double-click a word, triple-click a row, and hold a drag past the top edge until the chat scrolls. Press Ctrl+C on a selection and check that it copies without interrupting, then hold the bypass modifier and check that native selection still works. Click a `... +N lines` footer and a `Thought` header, and check that only that block expands. Click each pull request segment, then Ctrl+click it (Cmd+click in iTerm2 and Terminal.app), and check that each opens its page once. Quit, and check that the terminal screen is restored. -5. **Leak check**: Run `scripts/check-leaks.sh` in the environment the sessions used. It searches the logs, a SQL dump of the session store, and the saved tool output for each credential the live tests check and prints only the names of the variables whose values it finds. diff --git a/docs/conventions/writing.md b/docs/conventions/writing.md index 224614bbc..ba87af814 100644 --- a/docs/conventions/writing.md +++ b/docs/conventions/writing.md @@ -10,7 +10,7 @@ Conventions for comments, docstrings, documentation, commit and PR text, and str - When editing, delete comments that repeat the code and correct any that have become misleading. Before deleting a comment that records a constraint, check whether the constraint still applies. - Delete commented-out code because version control preserves it. - Write `//` comments as prose. A docstring may use a Markdown list for parallel options or a sequence. -- Wrap comment and docstring prose at 100 columns, including indentation and comment markers. Fill each paragraph naturally, allowing sentences to share a line, and separate paragraphs with a blank comment line. Break early when it improves readability, such as to keep a code span or link together. Reword a paragraph whose last line would contain one short word. Preserve intentional layout in code examples, lists, tables, and diagrams. Apply the same rule to shell, Nix, YAML, and TOML comments, and to prose literals continued by hand. The `comment-width` pre-commit hook checks this limit on every line that starts with `//` in Rust or `#` elsewhere, which includes string, heredoc, and literal-block lines that start that way, and exempts a comment holding only a URL, an ``, or a link reference definition. It skips trailing comments after code. +- Wrap comment and docstring prose at 100 columns, including indentation and comment markers. Fill each paragraph naturally, allowing sentences to share a line, and separate paragraphs with a blank comment line. Break early when it improves readability, such as to keep a code span or link together. Reword a paragraph whose last line would contain one short word. Preserve intentional layout in code examples, lists, tables, and diagrams. Apply the same rule to shell, Nix, YAML, and TOML comments, and to prose literals continued by hand. ## Docstrings diff --git a/flake.nix b/flake.nix index bea815054..45e9cdba0 100644 --- a/flake.nix +++ b/flake.nix @@ -118,24 +118,6 @@ shellcheck.enable = true; - comment-width = { - enable = true; - entry = pkgs.lib.getExe ( - pkgs.writeShellApplication { - name = "check-comment-width"; - runtimeInputs = [ pkgs.gawk ]; - text = builtins.readFile ./scripts/check-comment-width.sh; - } - ); - types_or = [ - "nix" - "rust" - "shell" - "toml" - "yaml" - ]; - }; - rustfmt = { enable = true; packageOverrides = { @@ -228,11 +210,8 @@ sqlite ]); - # git-hooks.nix sets a hooks path relative to the main checkout, which linked worktrees - # cannot resolve (cachix/git-hooks.nix#688). Unset it to use the shared hooks directory. - # The lock prevents concurrent loads from setting the path during `pre-commit install`, - # which rejects it. Install only when the current repository is this project, since - # `nix develop` can borrow this toolchain from a sibling repository. + # Use shared hooks to avoid the relative path bug in linked worktrees (git-hooks.nix#688). + # Serialize installation, and leave sibling repositories that borrow this shell untouched. shellHook = '' if common=$(${git} rev-parse --path-format=absolute --git-common-dir 2>/dev/null) && top=$(${git} rev-parse --show-toplevel 2>/dev/null) && @@ -256,6 +235,7 @@ cargo cargo-deny nodejs + nushell pnpm ]; }; diff --git a/about.hbs b/licenses/about.hbs similarity index 93% rename from about.hbs rename to licenses/about.hbs index 8ed201b39..ad522a1a4 100644 --- a/about.hbs +++ b/licenses/about.hbs @@ -12,3 +12,4 @@ Used by: {{{text}}} {{/each}} +{{> syntect-assets}} diff --git a/licenses/syntect-assets.txt b/licenses/syntect-assets.hbs similarity index 100% rename from licenses/syntect-assets.txt rename to licenses/syntect-assets.hbs diff --git a/nix/package.nix b/nix/package.nix index d6ec09a8a..b66f5d630 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -17,13 +17,11 @@ rustPlatform.buildRustPackage { ../Cargo.toml ../Cargo.lock ../LICENSE - ../about.hbs ../about.toml ../crates # The config tests check that the guide's reference lists every key and variable. ../docs/guide/configuration.md ../licenses - ../scripts/generate-notices.sh ]; }; @@ -32,8 +30,13 @@ rustPlatform.buildRustPackage { nativeBuildInputs = [ cargo-about ]; preBuild = '' - bash scripts/generate-notices.sh \ - ${stdenv.hostPlatform.rust.rustcTarget} THIRD-PARTY-NOTICES --offline + cargo about generate \ + --locked \ + --manifest-path crates/ox/Cargo.toml \ + --target ${stdenv.hostPlatform.rust.rustcTarget} \ + --name about licenses \ + -o THIRD-PARTY-NOTICES \ + --offline ''; # The flake's `tests` check runs the suite, so builds of the package skip it. @@ -54,19 +57,6 @@ rustPlatform.buildRustPackage { doInstallCheck = true; nativeInstallCheckInputs = [ versionCheckHook ]; - postInstallCheck = '' - doc="$out/share/doc/oxide-code" - cmp LICENSE "$doc/LICENSE" - cmp THIRD-PARTY-NOTICES "$doc/THIRD-PARTY-NOTICES" - grep -q '^- syntect ' "$doc/THIRD-PARTY-NOTICES" - tail -c "$(wc -c < licenses/syntect-assets.txt)" "$doc/THIRD-PARTY-NOTICES" | - cmp licenses/syntect-assets.txt - - if grep -Eq '^- (insta|temp-env|wiremock) ' "$doc/THIRD-PARTY-NOTICES"; then - echo "Third-party notices include test dependencies" >&2 - exit 1 - fi - ''; - meta = { description = "Terminal-based AI coding assistant written in Rust"; homepage = "https://github.com/hakula139/oxide-code"; diff --git a/scripts/check-comment-width.sh b/scripts/check-comment-width.sh deleted file mode 100755 index ee1fc1545..000000000 --- a/scripts/check-comment-width.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash -# Check that comment lines in the given files fit in 100 columns, as docs/conventions/writing.md -# asks. A line is a comment when it starts with `//` in Rust or `#` elsewhere, after indentation. -# -# Columns count characters, so a multibyte character such as an arrow counts once. A comment that -# holds only a URL, an ``, or a Markdown link reference definition may run long, since it -# cannot break. Exits 1 when a line is too long and 2 on a usage error. - -set -euo pipefail - -if [[ "$#" -eq 0 ]]; then - echo "Usage: $0 ..." >&2 - exit 2 -fi - -LC_ALL=C awk ' - FNR == 1 { marker = FILENAME ~ /\.rs$/ ? "^[ \t]*//[/!]?" : "^[ \t]*#" } - $0 ~ marker { - text = $0 - sub(marker, "", text) - # A lone URL, optionally in angle brackets or after a link reference label. - if (text ~ /^[ \t]*(\[[^\]]*\]:[ \t]*)??$/) next - text = $0 - # UTF-8 continuation bytes, which belong to the character before them. - gsub(/[\200-\277]/, "", text) - if (length(text) > 100) { - printf "%s:%d: comment is %d columns, over the limit of 100\n", FILENAME, FNR, length(text) - long = 1 - } - } - END { exit long } -' "$@" diff --git a/scripts/check-leaks.sh b/scripts/check-leaks.sh deleted file mode 100755 index 39531d5fb..000000000 --- a/scripts/check-leaks.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash -# Check that no environment credential appears in ox's logs, session store, or saved tool output. -# -# Run after the TUI checklist in docs/conventions/testing.md, in the same environment. Like the live -# tests, it checks ANTHROPIC_AUTH_TOKEN, ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, and each -# ANTHROPIC_CUSTOM_HEADERS value of 8 or more characters. It prints only variable names and exits 1 -# on a leak and 2 on an error. It needs `sqlite3`, which the dev shell provides. - -set -euo pipefail - -state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/ox" -data_dir="${XDG_DATA_HOME:-$HOME/.local/share}/ox" -db="$data_dir/sessions.db" -for path in "$state_dir" "$db"; do - if [[ ! -e "$path" ]]; then - echo "$path is missing. Rerun with the XDG directories the sessions used." >&2 - exit 2 - fi -done - -# The database splits long values across pages, so search its SQL dump, which also folds in the -# write-ahead log. -dump="$(mktemp)" -trap 'rm -f "$dump"' EXIT -sqlite3 -readonly "$db" .dump >"$dump" -sources=("$state_dir" "$dump") -if [[ -d "$data_dir/artifacts" ]]; then - sources+=("$data_dir/artifacts") -fi - -secrets=() -names=() -for name in ANTHROPIC_AUTH_TOKEN ANTHROPIC_API_KEY CLAUDE_CODE_OAUTH_TOKEN; do - if [[ -n "${!name:-}" ]]; then - secrets+=("${!name}") - names+=("$name") - fi -done -while IFS= read -r line; do - value="${line#*:}" - value="${value#"${value%%[![:space:]]*}"}" - value="${value%"${value##*[![:space:]]}"}" - if [[ "$line" == *:* && ${#value} -ge 8 ]]; then - secrets+=("$value") - names+=("ANTHROPIC_CUSTOM_HEADERS (${line%%:*})") - fi -done <<<"${ANTHROPIC_CUSTOM_HEADERS:-}" - -if [[ "${#secrets[@]}" -eq 0 ]]; then - echo "No credential variable is set. Export the one the sessions used, then rerun." >&2 - exit 2 -fi - -leaked=0 -for i in "${!secrets[@]}"; do - status=0 - grep -rqF -f <(printf '%s\n' "${secrets[$i]}") -- "${sources[@]}" || status=$? - case "$status" in - 0) - echo "Found the value of ${names[$i]} in $state_dir or $data_dir" >&2 - leaked=1 - ;; - 1) ;; - *) exit 2 ;; - esac -done - -if [[ "$leaked" -eq 0 ]]; then - echo "No credential value found in $state_dir or $data_dir" -fi -exit "$leaked" diff --git a/scripts/generate-notices.sh b/scripts/generate-notices.sh deleted file mode 100755 index ba75d61ac..000000000 --- a/scripts/generate-notices.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -if [[ "$#" -lt 2 || "$#" -gt 3 || ("$#" -eq 3 && "$3" != --offline) ]]; then - echo "Usage: $0 [--offline]" >&2 - exit 2 -fi - -target="$1" -output="$2" -shift 2 - -# A workspace scan would also count ox-core's test-only `testing` dependencies. -cargo about generate \ - --locked \ - --manifest-path crates/ox/Cargo.toml \ - --target "$target" \ - --config about.toml \ - about.hbs \ - -o "$output" \ - "$@" -cat licenses/syntect-assets.txt >>"$output" diff --git a/scripts/oxide-code-formula.rb b/scripts/oxide-code-formula.rb new file mode 100644 index 000000000..cf91a44bc --- /dev/null +++ b/scripts/oxide-code-formula.rb @@ -0,0 +1,34 @@ +# Generated by scripts/update-homebrew-formula.nu from scripts/oxide-code-formula.rb. + +class OxideCode < Formula + desc "Terminal-based AI coding assistant written in Rust" + homepage "https://github.com/hakula139/oxide-code" + url "{{base}}/oxide-code-x86_64-unknown-linux-gnu.tar.gz" + version "{{version}}" + sha256 "{{linux_x86_sha}}" + license "MIT" + + # Every platform needs a URL for the arch requirements below to load and report. + on_macos do + depends_on arch: :arm64 + depends_on macos: :monterey + + on_arm do + url "{{base}}/oxide-code-aarch64-apple-darwin.tar.gz" + sha256 "{{mac_arm_sha}}" + end + end + + on_linux do + depends_on arch: :x86_64 + end + + def install + bin.install "ox" + doc.install "LICENSE", "THIRD-PARTY-NOTICES" + end + + test do + assert_match version.to_s, shell_output("#{bin}/ox --version") + end +end diff --git a/scripts/update-homebrew-formula.nu b/scripts/update-homebrew-formula.nu new file mode 100755 index 000000000..9e0063a67 --- /dev/null +++ b/scripts/update-homebrew-formula.nu @@ -0,0 +1,27 @@ +#!/usr/bin/env nu + +const SCRIPT_DIR = path self . + +# Regenerate the Homebrew formula from a release's checksum sidecars. +def main [ + tag: string # Release tag, including the v prefix + sidecars: string # Directory containing the release's .sha256 files +] { + let version = $tag | str replace --regex '^v' '' + let base = $"https://github.com/hakula139/oxide-code/releases/download/($tag)" + let mac_arm_sha = open --raw ($sidecars | path join 'oxide-code-aarch64-apple-darwin.sha256') + | split words + | first + let linux_x86_sha = open --raw ($sidecars | path join 'oxide-code-x86_64-unknown-linux-gnu.sha256') + | split words + | first + + open --raw ($SCRIPT_DIR | path join 'oxide-code-formula.rb') + | str replace --all '{{base}}' $base + | str replace --all '{{version}}' $version + | str replace --all '{{linux_x86_sha}}' $linux_x86_sha + | str replace --all '{{mac_arm_sha}}' $mac_arm_sha + | save --force Formula/oxide-code.rb + + print $"Updated Formula/oxide-code.rb to ($tag)" +} diff --git a/scripts/update-homebrew-formula.sh b/scripts/update-homebrew-formula.sh deleted file mode 100755 index ba500ee85..000000000 --- a/scripts/update-homebrew-formula.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash -# Regenerate Formula/oxide-code.rb for a release tag from its `.sha256` sidecars. -# -# The release workflow runs this after publishing, with the sidecars its build jobs produced. -# To run it by hand, download the sidecars from the published release first: -# -# gh release download vX.Y.Z --pattern '*.sha256' --dir /tmp/oxide-code-vX.Y.Z -# ./scripts/update-homebrew-formula.sh vX.Y.Z /tmp/oxide-code-vX.Y.Z - -set -euo pipefail - -if [[ "$#" -ne 2 ]]; then - echo "Usage: $0 " >&2 - exit 2 -fi - -tag="$1" -sidecars="$2" -if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$ ]]; then - echo "Error: expected a tag such as v1.2.3 or v1.2.3-rc.1 (got: $tag)" >&2 - exit 1 -fi - -version="${tag#v}" -formula="Formula/oxide-code.rb" -base="https://github.com/hakula139/oxide-code/releases/download/$tag" - -# Each `oxide-code-.sha256` sidecar holds ` `. -sha() { - awk '{print $1}' "$sidecars/oxide-code-$1.sha256" -} - -mac_arm_sha=$(sha aarch64-apple-darwin) -linux_x86_sha=$(sha x86_64-unknown-linux-gnu) - -cat >"$formula" <