Skip to content
Merged
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
18 changes: 6 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ jobs:
#
# Add a second job ONLY for work the Nix sandbox cannot do: network access,
# a real PTY/TTY, or artifact upload. "A different runner OS" is no longer
# one of them: the flake's aarch64-darwin target is for local development,
# while the Linux target is the hosted release and integration gate. The two
# extra jobs below still qualify; see the comment on each.
# one of them: the Linux target is the hosted release and integration gate.
# The two extra jobs below still qualify; see the comment on each.
check:
name: nix flake check
runs-on: ubuntu-latest
Expand All @@ -47,9 +46,7 @@ jobs:
# suite (checks.weave), the treefmt formatting gate (checks.formatting),
# the mkdocs --strict build (checks.docs), and the binary smoke test.
#
# No `--all-systems`: this hosted job is the Linux gate. The Darwin target
# is intentionally evaluated on its native development platform rather
# than realised as a foreign derivation on this runner.
# No `--all-systems`: this hosted job is the sole declared target.
- name: Check flake
run: nix flake check --print-build-logs

Expand All @@ -64,10 +61,7 @@ jobs:
# processes — get genuine coverage on a runner where those facilities
# exist. The sandbox has no controlling terminal, so there is nothing for
# those tests to attach to.
# One runner, no matrix: aarch64-darwin (macos-14) used to carry this leg
# too, but its runs were unreliable enough under CI load to be worse than
# no coverage. x86_64-linux is what CI gates; aarch64-darwin remains the
# local development target (see flake.nix), just without a CI leg here.
# One runner, no matrix: x86_64-linux is the sole declared and gated target.
name: integration tests (x86_64-linux)
runs-on: ubuntu-latest
timeout-minutes: 45
Expand All @@ -88,6 +82,7 @@ jobs:
XDG_CONFIG_HOME: ${{ runner.temp }}/nshell-config
XDG_STATE_HOME: ${{ runner.temp }}/nshell-state
NSHELL_AI_COMMAND: /nonexistent
NSHELL_TEST_PTY: "1"
run: nix develop -c sbcl --script run-tests.lisp

coverage:
Expand Down Expand Up @@ -143,8 +138,7 @@ jobs:
# derivation that builds the image proves it links; only running it proves
# `save-lisp-and-die`'s toplevel and saved runtime options survived.
#
# One Linux runner, no matrix. The aarch64-darwin target is for local
# development; release artifacts are intentionally built on Linux.
# One Linux runner, no matrix. Release artifacts are built on Linux.
name: build release binary
runs-on: ubuntu-latest
timeout-minutes: 60
Expand Down
71 changes: 31 additions & 40 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ concurrency:
cancel-in-progress: false

jobs:
# Gate the release once, before any binary is built. Splitting this out of
# the matrix means the org invariants are checked a single time rather than
# once per target, and a mismatch fails before spending two native builds on
# a release that must not be published.
verify:
name: Verify the tagged tree
runs-on: ubuntu-latest
Expand Down Expand Up @@ -57,10 +53,6 @@ jobs:
fi
echo "commit-sha=$commit_sha" >> "$GITHUB_OUTPUT"

# Enforces the org invariant "git tag == .asd :version" at the only point
# where it can still be corrected cheaply. Without this gate the two drift
# silently, which is how cl-boundary-kit reached :version 1.0.0 while its
# newest tag was still v0.6.0.
- name: Verify tag matches .asd version
env:
PACKAGE: nshell
Expand All @@ -84,19 +76,38 @@ jobs:
cachix-cache: ${{ vars.CACHIX_CACHE || 'takeokunn-nshell' }}
cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }}

# Never publish a release that does not pass its own test suite.
- name: Check flake
run: nix flake check --print-build-logs

integration:
name: Test the tagged tree with real PTYs
needs: verify
runs-on: ubuntu-latest
timeout-minutes: 45
env:
HOME: ${{ runner.temp }}/nshell-home
XDG_CACHE_HOME: ${{ runner.temp }}/nshell-cache
XDG_CONFIG_HOME: ${{ runner.temp }}/nshell-config
XDG_STATE_HOME: ${{ runner.temp }}/nshell-state
NSHELL_AI_COMMAND: /nonexistent
NSHELL_TEST_PTY: "1"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.verify.outputs.commit-sha }}
persist-credentials: false

- uses: ./.github/actions/nix-setup
with:
cachix-cache: ${{ vars.CACHIX_CACHE || 'takeokunn-nshell' }}
cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }}

- name: Run full test suite
run: nix develop -c sbcl --script run-tests.lisp

release:
# One tarball, for the one platform `systems` declares. The matrix that used
# to sit here carried a macos-14 (aarch64-darwin) leg; the 2026-08-01
# revision reduced `systems` to x86_64-linux alone, so `nix build` on macOS
# would now fail on a missing attribute. A one-element matrix is deleted
# rather than left in place — it signals an intent to add a platform, and
# there is none. See PACKAGE_STANDARD.md "CI の粒度".
name: build and publish x86_64-linux
needs: verify
needs: [verify, integration]
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
Expand All @@ -120,6 +131,9 @@ jobs:
- name: Build release bundle
run: nix build .#releaseBundle --print-build-logs

- name: Verify release bundle
run: perl scripts/verify-release-bundle.pl result

- name: Package tarball + checksum
run: |
set -euo pipefail
Expand All @@ -141,30 +155,7 @@ jobs:
rm "dist/${name}.repro.tar.gz"
( cd dist && shasum -a 256 "${name}.tar.gz" > "${name}.tar.gz.sha256" )

# Creates the release as an empty draft, with the tarball and checksum
# attached. This workflow does not produce a release body at all.
#
# As of the 2026-08-01 revision the GitHub Release description is the only
# canonical changelog in this org; there is no CHANGELOG.md to read from.
# See RELEASE_STANDARD.md. After this job goes green the maintainer fills
# the body in and publishes:
#
# gh release edit "$RELEASE_REF" --notes-file <file> --draft=false
#
# `draft: true` is load-bearing. Publishing straight away would make "the
# maintainer forgot the notes" a user-visible state. A draft appears
# neither under "Latest release" nor in the default output of
# `gh release list`, so an unfinished release never reaches downstream.
#
# `generate_release_notes` is deliberately left off. Generated notes are a
# list of commit titles and PR numbers, and RELEASE_STANDARD.md requires
# the notes to be selected by "does a user of this package have to change
# their own code" — a judgement no generator can make.
#
# Every `uses:` in this org references a 40-character SHA, never a mutable
# tag. Re-resolve before bumping, and do not copy a SHA you have not
# resolved yourself:
# gh api repos/softprops/action-gh-release/git/ref/tags/v2 --jq .object.sha
# Publish only after the maintainer supplies user-facing release notes.
- name: Create draft release
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
with:
Expand Down
66 changes: 28 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,37 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Documentation](https://img.shields.io/badge/docs-MkDocs%20Material-0a7a5a)](https://nerima-lisp.github.io/nshell/)

nshell is a modern, fish-inspired interactive shell written in Common Lisp for
SBCL. It puts the *interactive* experience first — real-time syntax
highlighting, history-aware autosuggestions, fish-style abbreviations, and a
context-aware completion engine driven by a logic knowledge base — on top of a
domain-driven core whose line editor is a pure reducer over an immutable input
state, and a reproducible Nix build that packages a dumped SBCL image with its
process-launch helper.

> **Status: development preview (0.5.x).** The interactive editor and core
> pipeline execution are solid and heavily tested. The shell *language* is a
> growing subset of POSIX/fish semantics. nshell is usable as a daily
> interactive shell for common workflows; it is not a script-compatible
> `/bin/sh` replacement.

Full documentation is published at <https://nerima-lisp.github.io/nshell/>.
The source for that site lives in [docs/src/](docs/src/).
nshell is a fish-inspired interactive shell written in Common Lisp for SBCL.
It provides syntax highlighting, history-aware autosuggestions, abbreviations,
and context-aware completion.

> **Status: development preview (0.6.x).** CI tests the interactive editor and
> pipeline execution on `x86_64-linux`. The shell language implements a subset
> of POSIX/fish semantics; it is not a script-compatible `/bin/sh` replacement.

Documentation source lives in [docs/src/](docs/src/).

## Quick Start

The release and Nix flake support `x86_64-linux` only. With
[Nix](https://nixos.org/download) and flakes enabled:

```sh
nix run github:nerima-lisp/nshell/v0.5.0
nix run github:nerima-lisp/nshell/v0.6.1
```

Then type as you would in any shell. Commands and paths colorize live, and a
dimmed completion of the most recent matching history entry trails the cursor —
dimmed completion of the most recent matching history entry trails the cursor;
press `→` or `Ctrl-F` to accept it:

```
~/src/nshell main ❯ git com # "mit -m " suggested from history
~/src/nshell main ❯ string upper hello
HELLO
```

After running that command, type `string up` to see `per hello` suggested
from history.

Colors come from a theme; run `theme list` to see the built-in presets and
`theme use dracula` (or any other name from that list) to switch, live, with
no restart needed.
Expand All @@ -50,26 +48,15 @@ the edited line to nshell.
## Install

```sh
nix profile install github:nerima-lisp/nshell/v0.5.0
nix profile install github:nerima-lisp/nshell/v0.6.1
```

```nix
# flake.nix
inputs.nshell = {
url = "github:nerima-lisp/nshell/v0.5.0";
inputs.nixpkgs.follows = "nixpkgs";
};
```

Pin a release tag rather than following the default branch. The v0.5.0 release
workflow publishes an `x86_64-linux` tarball only. `aarch64-darwin` remains a
local development target; other systems are outside the tested support
boundary.
Pin a release tag rather than following the default branch.

The `x86_64-linux` release bundle removes Nix store references, carries its
ELF runtime library closure, and is checked for required files and dependency
metadata. CI also runs `--help`, `--version`, and an `echo` smoke test on the
bundle; use the pinned Nix commands above on other platforms. See [Getting
bundle. See [Getting
started](https://nerima-lisp.github.io/nshell/getting-started/) for the bundle
verification and installation procedure.

Expand All @@ -83,7 +70,7 @@ verification and installation procedure.
## Development

```sh
nix develop # SBCL with CL_SOURCE_REGISTRY already set
nix develop # SBCL with CL_SOURCE_REGISTRY already set (x86_64-linux)
perl -e '$SIG{ALRM}=sub { exit 124 }; alarm 300; exec @ARGV' nix build .#checks.$(nix eval --raw --impure --expr 'builtins.currentSystem').default --no-link # run the test suite
perl -e '$SIG{ALRM}=sub { exit 124 }; alarm 300; exec @ARGV' nix flake check # full hermetic gate on x86_64-linux CI
nix fmt # format Nix sources (treefmt)
Expand All @@ -92,6 +79,9 @@ nix build .#releaseBundle
perl scripts/verify-release-bundle.pl result
```

The Perl wrappers limit each local check to five minutes and return exit code
124 if that limit expires.

To measure executable-source coverage, keep the report outside the checkout
and run the same hermetic test loader used by CI:

Expand All @@ -101,10 +91,10 @@ NSHELL_COVERAGE_DIR="$(mktemp -d)" \
```

The command writes `coverage-summary.json` and `coverage-files.json` to the
selected directory. Declarative data and package-definition forms are kept
out of the executable expression denominator; the report still lists every
source file so uncovered behavior is visible. The configured minimum is a
gate, while the target remains 100%.
selected directory. These reports cover executable source under `src/`,
excluding declarative data and package-definition files. The default minimum
is 85% (`NSHELL_COVERAGE_MIN`); the target is 100%
(`NSHELL_COVERAGE_TARGET`).

Tests live in `t/` and run under
[cl-weave](https://github.com/nerima-lisp/cl-weave), the org's test framework.
Expand Down
Loading
Loading