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
20 changes: 20 additions & 0 deletions .github/installer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"schemaVersion": 1,
"repo": "pablozaiden/devbox",
"installDir": "$HOME/.local/bin",
"binaries": [
{
"name": "devbox",
"assetPrefix": "devbox",
"postInstallMessage": "Run 'devbox --help' to see available commands."
}
],
"checksums": {
"required": true,
"extension": ".sha256"
},
"platforms": {
"linux": ["x64", "arm64"],
"darwin": ["x64", "arm64"]
}
}
24 changes: 24 additions & 0 deletions .github/workflows/binary-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Binary Release

on:
release:
types: [published]

jobs:
binaries:
uses: pablozaiden/installer/.github/workflows/reusable-binary-release.yml@2e28bc9fbf69385ac5172e526a1268f72db02363
permissions:
contents: write
with:
install_command: bun install --frozen-lockfile
update_package_version: false
prebuild_command: bun run validate:release-version && bun run test:fast
binaries: |
[
{
"name": "devbox",
"asset_prefix": "devbox",
"build_command": "bun build --compile --target=$BUN_TARGET --outfile dist/devbox-$RELEASE_TARGET src/cli.ts",
"output_path": "dist/devbox-$RELEASE_TARGET"
}
]
5 changes: 4 additions & 1 deletion .github/workflows/pr-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,8 @@ jobs:
- name: Run tests
run: bun test

- name: Build package
- name: Build binary
run: bun run build

- name: Smoke test binary
run: ./dist/devbox --help
85 changes: 0 additions & 85 deletions .github/workflows/release-npm-package.yml

This file was deleted.

28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,27 @@ It does not modify the original `devcontainer.json`. Instead, it generates a der

## Installation

Install globally with Bun:
Install `devbox` with the generic `@pablozaiden/installer` script:

```bash
bun install -g @pablozaiden/devbox
curl -fsSL https://raw.githubusercontent.com/pablozaiden/installer/main/install.sh | sh -s -- pablozaiden/devbox
```

Or install globally with npm:
The installer downloads the Linux or macOS binary for your CPU architecture, verifies the published `.sha256` checksum, and installs it into `$HOME/.local/bin` by default. Add that directory to `PATH` if the installer reports it is not already available.

Run `devbox` with no arguments to see the CLI help.

To update an installed binary later:

```bash
npm install -g @pablozaiden/devbox
devbox update
```

After either install, `devbox` is available in any directory.

Run `devbox` with no arguments to see the CLI help.
Use `devbox update --check` to check for a newer release without installing it, or `devbox update --version <version>` to install a specific release.

## Requirements

- macOS or Linux
- [Node.js](https://nodejs.org/) or [Bun](https://bun.sh/) to run the installed CLI
- Docker
- Dev Container CLI available as `devcontainer` in whatever environment runs `devbox`
- For SSH agent sharing: either a valid host `SSH_AUTH_SOCK`, or Docker Desktop host services
Expand Down Expand Up @@ -78,6 +79,9 @@ devbox up <port> --devcontainer-subpath services/api
# Start from a specific built-in template instead of a repo devcontainer
devbox up --template python

# Check for and install newer devbox release binaries
devbox update

# List the built-in templates as JSON
devbox templates

Expand Down Expand Up @@ -181,25 +185,25 @@ Use `bun test tests/examples.live.test.ts` to run only the live example-devconta

Use `bun run test:fast` if you want the non-live suite only, or run `DEVBOX_SKIP_LIVE_EXAMPLE_TESTS=1 bun test` directly.

The build step emits a bundled executable JS entrypoint at `dist/devbox.js`.
The build step emits a standalone Bun-compiled binary at `dist/devbox`.

For local development from this repository:

- use `bun run src/cli.ts` while iterating on source changes
- use `./dist/devbox.js` after `bun run build` to exercise the packaged artifact
- use `./dist/devbox` after `bun run build` to exercise the packaged artifact

For a quick smoke test, this repository includes `examples/smoke-workspace/.devcontainer/devcontainer.json`, which also exercises the Docker-in-Docker path:

```bash
cd examples/smoke-workspace
../../dist/devbox.js up --allow-missing-ssh
../../dist/devbox up --allow-missing-ssh
```

For a more realistic feature-heavy example, this repository also includes `examples/complex-workspace/.devcontainer/devcontainer.json`:

```bash
cd examples/complex-workspace
../../dist/devbox.js up
../../dist/devbox up
```

The complex example uses several devcontainer features, so the first `up` or `rebuild` can take a while. `devbox` prints periodic elapsed-time progress lines while the devcontainer image/features are still being prepared and while the SSH runner is being installed.
Expand Down
7 changes: 6 additions & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 5 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "@pablozaiden/devbox",
"version": "0.1.0",
"private": true,
"type": "module",
"description": "CLI to run and expose a devcontainer with SSH agent sharing and a bundled SSH server.",
"repository": {
Expand All @@ -17,31 +18,16 @@
"ssh",
"cli"
],
"publishConfig": {
"access": "public"
},
"os": [
"darwin",
"linux"
],
"engines": {
"node": ">=18"
},
"files": [
"dist/",
"README.md"
],
"bin": {
"devbox": "./dist/devbox.js"
},
"scripts": {
"build": "rm -rf dist && mkdir -p dist && bun build --target=node --packages=bundle --outfile dist/devbox.js src/cli.ts && chmod +x dist/devbox.js",
"build": "rm -rf dist && mkdir -p dist && bun build --compile --outfile dist/devbox src/cli.ts && chmod +x dist/devbox",
"build:release": "rm -rf dist && mkdir -p dist && bun build --compile --target=$BUN_TARGET --outfile dist/devbox-$RELEASE_TARGET src/cli.ts && chmod +x dist/devbox-$RELEASE_TARGET",
"dev": "bun run src/cli.ts",
"test": "bun test",
"test:fast": "DEVBOX_SKIP_LIVE_EXAMPLE_TESTS=1 bun test",
"pack:dry-run": "npm pack --dry-run"
"validate:release-version": "bun run scripts/validate-release-version.ts"
},
"dependencies": {
"@pablozaiden/installer": "0.0.6",
"jsonc-parser": "3.3.1"
},
"devDependencies": {
Expand Down
17 changes: 17 additions & 0 deletions scripts/validate-release-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import packageJson from "../package.json";

const releaseTag = process.env.TAG ?? process.env.GITHUB_REF_NAME;

if (releaseTag === undefined || releaseTag.trim() === "") {
throw new Error("Expected TAG or GITHUB_REF_NAME to contain the release tag.");
}

const normalizedTagVersion = releaseTag.trim().replace(/^v/, "");

if (normalizedTagVersion !== packageJson.version) {
throw new Error(
`Release tag version ${releaseTag} does not match package.json version ${packageJson.version}.`,
);
}

console.log(`Release tag ${releaseTag} matches package.json version ${packageJson.version}.`);
12 changes: 12 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { createRunnerMetadata, serializeRunnerMetadata } from "./runnerState";
import { getDevboxStatus } from "./status";
import { ariseManagedWorkspaces } from "./arise";
import { listTemplateSummaries } from "./templates";
import { runUpdateCommand } from "./update";

async function main(): Promise<void> {
const parsed = parseArgs(process.argv.slice(2));
Expand All @@ -86,6 +87,17 @@ async function main(): Promise<void> {
return;
}

if (parsed.command === "update") {
const exitCode = await runUpdateCommand({
checkOnly: parsed.checkOnly ?? false,
version: parsed.version,
});
if (exitCode !== 0) {
process.exitCode = exitCode;
}
return;
}

const workspacePath = await realpath(process.cwd());
const state = await loadWorkspaceState(workspacePath);

Expand Down
Loading
Loading