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
3 changes: 2 additions & 1 deletion .agents/skills/release-devbox/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ result and the current `flake.nix` version in one shot.
| `cli-release` never started | Tag push didn't land | Re-run the same command |
| `cli-release` failed in `tests` | Red `main` (see below) | Fix the test, re-run |
| Draft has 0 assets | The upload step didn't run or failed | Check `cli-release`'s "Attach artifacts" step, re-run |
| Published but installer serves the old version | `cli-post-release` failed or is still running | `gh run list --workflow=cli-post-release.yml` |
| Published but installer serves the old version | Release not marked latest on GitHub (installer reads `releases/latest/download/checksums.txt`) | `gh release list --limit 3`; fix with `gh release edit <v> --latest` |
| devbox 0.20.0 or older doesn't see the release | Legacy `cli-post-release` (S3 `stable/version`) failed | `gh run list --workflow=cli-post-release.yml` |
| Docker build failed | Published before assets uploaded | Re-run `docker-image-release` via `workflow_dispatch` with the tag |

## Known breakage
Expand Down
4 changes: 2 additions & 2 deletions .agents/skills/use-devbox/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ command -v devbox
If that prints nothing, install it (non-interactive):

```bash
curl -fsSL https://get.jetify.com/devbox | bash
curl -fsSL https://raw.githubusercontent.com/jetify-com/devbox/main/install.sh | bash
```

devbox installs to `~/.local/bin` (or `/usr/local/bin`). If `devbox` still isn't found after installing, ensure that directory is on `PATH`. devbox itself requires the Nix package manager; the install script sets it up if missing.
Expand Down Expand Up @@ -110,7 +110,7 @@ devbox services stop # stop them

## Key facts

- devbox is not always installed — check with `command -v devbox` and install via `curl -fsSL https://get.jetify.com/devbox | bash` if missing.
- devbox is not always installed — check with `command -v devbox` and install via `curl -fsSL https://raw.githubusercontent.com/jetify-com/devbox/main/install.sh | bash` if missing.
- Only directories with a `devbox.json` use it.
- `devbox.json` may live in the repo root **or** a subdir root — check both.
- Packages/scripts defined in `devbox.json` are only on PATH inside `devbox run`/`devbox shell`, not the plain shell.
Expand Down
12 changes: 11 additions & 1 deletion .github/workflows/cli-post-release.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
name: cli-post-release
# Finalize and announce the release once its been published on Github.
# Legacy: promote the release on releases.jetify.com once it's published.
#
# The installer (install.sh) and the launcher in devbox releases after 0.20.0 find
# the latest stable version from GitHub itself
# (releases/latest/download/checksums.txt), so publishing a release as
# "latest" is all a release needs. devbox 0.20.0 and older, and the old bash
# launcher, still read s3://releases.jetpack.io/devbox/stable/version, so this
# keeps it in sync for them. Failures don't affect new installs. Delete this
# workflow once those versions are gone.

on:
release:
Expand Down Expand Up @@ -38,11 +46,13 @@ jobs:
needs: check-release
steps:
- name: Configure AWS Credentials
continue-on-error: true
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ secrets.AWS_ROLE }}
aws-region: us-west-2
- name: Update latest version in s3
continue-on-error: true
run: |
tmp_file=$(mktemp)
echo "${{ github.ref_name }}" > $tmp_file
Expand Down
17 changes: 14 additions & 3 deletions .github/workflows/cli-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,18 @@ jobs:
files: |
dist/checksums.txt
dist/*.tar.gz
- name: Configure AWS Credentials
# Legacy: devbox 0.20.0 and older (and the old bash launcher) read the
# edge channel from releases.jetify.com, which is served from this S3
# bucket. Newer versions don't need it, so failures here don't fail the
# release. Remove these steps once those versions are gone.
- name: Configure AWS Credentials (legacy)
continue-on-error: true
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ secrets.AWS_ROLE }}
aws-region: us-west-2
- name: Update edge version in s3
- name: Update edge version in s3 (legacy)
continue-on-error: true
run: |
tmp_file=$(mktemp)
echo "${{ env.EDGE_TAG }}" > $tmp_file
Expand Down Expand Up @@ -181,7 +187,12 @@ jobs:
fi
# --clobber so a re-run of this workflow replaces the assets instead of
# failing on the ones already uploaded.
gh release upload "$TAG" dist/*.tar.gz dist/checksums.txt \
gh release upload "$TAG" dist/*.tar.gz \
--repo "$GITHUB_REPOSITORY" --clobber
# checksums.txt goes last: the installer and launcher find a release's
# version and archives through it, and release.ts won't publish a
# release without it.
gh release upload "$TAG" dist/checksums.txt \
--repo "$GITHUB_REPOSITORY" --clobber
- name: Notify jetpack.io slack of release status
id: slack
Expand Down
2 changes: 1 addition & 1 deletion .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ announce:

Updating:
If you installed devbox via our recommended installer
(`curl -fsSL https://get.jetpack.io/devbox | bash`) you will get the new version
(`curl -fsSL https://raw.githubusercontent.com/jetify-com/devbox/main/install.sh | bash`) you will get the new version
_automatically_, the next time you run `devbox`

Thanks,
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The easiest way to develop Devbox is with Devbox!

1. Install Devbox:

curl -fsSL https://get.jetify.com/devbox | bash
curl -fsSL https://raw.githubusercontent.com/jetify-com/devbox/main/install.sh | bash

2. Clone this repository:

Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,15 @@ interactive shell in it with `devbox shell`:
Use the following install script to get the latest version of Devbox:

```sh
curl -fsSL https://get.jetify.com/devbox | bash
curl -fsSL https://raw.githubusercontent.com/jetify-com/devbox/main/install.sh | bash
```

The script ([install.sh](install.sh)) downloads the latest stable
[GitHub release](https://github.com/jetify-com/devbox/releases/latest), checks
its SHA-256 checksum, and installs it to `/usr/local/bin/devbox`. Pass `-f` to
skip the confirmation prompt. After that, devbox keeps itself up to date from
GitHub releases (`devbox version update`).

Read more on the
[Devbox docs](https://www.jetify.com/docs/devbox/installing-devbox/).

Expand Down
2 changes: 1 addition & 1 deletion devbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ development environment.
Install devbox:

```sh
curl -fsSL https://get.jetify.com/devbox | bash
curl -fsSL https://raw.githubusercontent.com/jetify-com/devbox/main/install.sh | bash
```

Start the devbox shell:
Expand Down
2 changes: 1 addition & 1 deletion examples/cloud_development/maelstrom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ You should also check out the [Fly.io Distributed Systems Challenge](https://fly
If you don't already have [Devbox](https://www.jetify.com/docs/devbox/installing-devbox/index), you can install it by running the following command:

```bash
curl -s https://get.jetify.com/install.sh | bash
curl -s https://raw.githubusercontent.com/jetify-com/devbox/main/install.sh | bash
```

You can skip this step if you're running on devbox.sh
Expand Down
33 changes: 20 additions & 13 deletions scripts/install.sh → install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ set -euo pipefail
# Customize install script
# ========================

# This script is published at get.jetify.com/devbox so users can install via:
# curl -fsSL https://get.jetify.com/devbox | bash
# Users install devbox with:
# curl -fsSL https://raw.githubusercontent.com/jetify-com/devbox/main/install.sh | bash
#
# Everything is downloaded from GitHub releases. The latest stable version is
# whichever release GitHub marks as "latest" (never a draft or prerelease).

readonly INSTALL_DIR="/usr/local/bin"
readonly BIN="devbox"
readonly RELEASES_URL="https://releases.jetify.com/devbox"
readonly DOWNLOAD_URL="https://github.com/jetify-com/devbox/releases/download"
# Must match releasesURL in internal/launcher/install.go.
readonly RELEASES_URL="https://github.com/jetify-com/devbox/releases"
# Must match the version files that devbox reads (internal/launcher/files.go).
readonly CACHE_DIR="${XDG_CACHE_HOME:-${HOME}/.cache}/${BIN}"

Expand Down Expand Up @@ -201,7 +204,7 @@ intro_msg() {
printf "\n"
header "Confirm Installation Details"
plain " Location: ${GREEN}${INSTALL_DIR}/${BIN}${NO_COLOR}"
plain " Download URL: ${UNDERLINE}${BLUE}${DOWNLOAD_URL}${NO_COLOR}"
plain " Download URL: ${UNDERLINE}${BLUE}${RELEASES_URL}${NO_COLOR}"
printf "\n"
}

Expand All @@ -214,22 +217,26 @@ install_flow() {
trap 'rm -rf "${TMP_DIR}"' EXIT
local -r tmp_dir="${TMP_DIR}"

# The latest release's checksums.txt names the latest version (in its
# archive names) and the checksum to verify, in one request.
start_task "Finding the latest version"
download "${RELEASES_URL}/stable/version" "${tmp_dir}/version"
download "${RELEASES_URL}/latest/download/checksums.txt" "${tmp_dir}/checksums.txt"
local -r platform="$(platform)"
local version
version="$(tr -d '[:space:]' <"${tmp_dir}/version")"
if [[ ! ${version} =~ ^v?[0-9]+\.[0-9]+\.[0-9]+ ]]; then
version="$(awk -v suffix="_${platform}.tar.gz" '
index($2, "devbox_") == 1 && substr($2, length($2) - length(suffix) + 1) == suffix {
print substr($2, 8, length($2) - 7 - length(suffix)); exit
}' "${tmp_dir}/checksums.txt")"
if [[ ! ${version} =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
fail_task "Finding the latest version"
error "Unexpected version: ${version}"
error "The latest release has no ${BIN} for ${platform}"
exit 1
fi
version="${version#v}"
end_task "Finding the latest version (${version})"

local -r archive="${BIN}_${version}_$(platform).tar.gz"
local -r archive="${BIN}_${version}_${platform}.tar.gz"
start_task "Downloading ${BIN} ${version}"
download "${DOWNLOAD_URL}/${version}/checksums.txt" "${tmp_dir}/checksums.txt"
download "${DOWNLOAD_URL}/${version}/${archive}" "${tmp_dir}/${archive}"
download "${RELEASES_URL}/download/${version}/${archive}" "${tmp_dir}/${archive}"
end_task "Downloading ${BIN} ${version}"

start_task "Verifying checksum"
Expand Down
2 changes: 1 addition & 1 deletion internal/devbox/docgen/readme.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This project uses [devbox](https://github.com/jetify-com/devbox) to manage its d

Install devbox:
```sh
curl -fsSL https://get.jetpack.io/devbox | bash
curl -fsSL https://raw.githubusercontent.com/jetify-com/devbox/main/install.sh | bash
```

Start the devbox shell:
Expand Down
2 changes: 1 addition & 1 deletion internal/devbox/generate/tmpl/DevboxImageDockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ ENV PATH="/home/${DEVBOX_USER}/.nix-profile/bin:$PATH"

# Step 5: Installing devbox
ENV DEVBOX_USE_VERSION=$DEVBOX_USE_VERSION
RUN wget --quiet --output-document=/dev/stdout https://get.jetify.com/devbox | bash -s -- -f
RUN wget --quiet --output-document=/dev/stdout https://raw.githubusercontent.com/jetify-com/devbox/main/install.sh | bash -s -- -f
RUN chown -R "${DEVBOX_USER}:${DEVBOX_USER}" /usr/local/bin/devbox

# run a devbox command to make launcher download devbox binary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ ENV PATH="/root/.nix-profile/bin:$PATH"

# Step 3: Installing devbox
ENV DEVBOX_USE_VERSION=$DEVBOX_USE_VERSION
RUN wget --quiet --output-document=/dev/stdout https://get.jetify.com/devbox | bash -s -- -f
RUN wget --quiet --output-document=/dev/stdout https://raw.githubusercontent.com/jetify-com/devbox/main/install.sh | bash -s -- -f
# run a devbox command to make launcher download devbox binary
RUN devbox version

Expand Down
60 changes: 44 additions & 16 deletions internal/launcher/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,41 +20,69 @@ import (
"go.jetify.com/devbox/internal/fileutil"
)

// These URLs are variables so that tests can serve them locally.
var (
// releasesURL serves the latest version of each channel.
releasesURL = "https://releases.jetify.com/devbox"
// downloadURL serves the release archives and checksums.
downloadURL = "https://github.com/jetify-com/devbox/releases/download"
)
// releasesURL is the GitHub releases page. It's a variable so that tests can
// serve releases locally. Everything the launcher downloads comes from here:
//
// - <releasesURL>/download/<version>/<file> is a release's archive or
// checksums.txt.
// - <releasesURL>/latest/download/checksums.txt is the checksums.txt of the
// latest stable release, which GitHub resolves to the release that's marked
// "latest" (never a draft or prerelease). Its archive names give the
// latest stable version.
var releasesURL = "https://github.com/jetify-com/devbox/releases"

// httpClient's timeout is a backstop: callers set their own deadlines.
var httpClient = &http.Client{Timeout: 10 * time.Minute}

// FetchLatestVersion returns the latest devbox version from the release
// channel, which is the stable channel unless the channel config file names
// another URL.
// channel, which is the latest stable GitHub release unless the channel config
// file names another URL.
func FetchLatestVersion(ctx context.Context) (string, error) {
url := readConfig(channelFile())
if url == "" {
url = releasesURL + "/stable/version"
url = releasesURL + "/latest/download/checksums.txt"
Comment thread
mikeland73 marked this conversation as resolved.
Resolved
}
resp, err := get(ctx, url)
if err != nil {
return "", err
}
defer resp.Body.Close()
data, err := io.ReadAll(io.LimitReader(resp.Body, 1024))
data, err := io.ReadAll(io.LimitReader(resp.Body, 64*1024))
if err != nil {
return "", fmt.Errorf("read %s: %w", url, err)
}
version := strings.TrimSpace(string(data))
if !validVersion(version) {
return "", fmt.Errorf("%s returned an invalid version %q", url, version)
version, err := parseLatestVersion(data)
if err != nil {
return "", fmt.Errorf("%s: %w", url, err)
}
return normalizeVersion(version), nil
}

// parseLatestVersion returns the version in a channel response, which is
// either a bare version (as served by older channel URLs) or a release's
// checksums.txt, whose archive for this platform names the version.
func parseLatestVersion(data []byte) (string, error) {
if version := strings.TrimSpace(string(data)); validVersion(version) {
return version, nil
}
suffix := "_" + platform() + ".tar.gz"
for _, line := range strings.Split(string(data), "\n") {
fields := strings.Fields(line)
if len(fields) != 2 {
continue
}
name, ok := strings.CutPrefix(fields[1], "devbox_")
if !ok {
continue
}
version, ok := strings.CutSuffix(name, suffix)
if ok && validVersion(version) {
return version, nil
}
}
return "", fmt.Errorf("found no devbox version for %s", platform())
}

// Install downloads devbox version unless it's already installed, and returns
// the path to its binary. It prints progress to w.
func Install(ctx context.Context, version string, w io.Writer) (string, error) {
Expand Down Expand Up @@ -98,7 +126,7 @@ func download(ctx context.Context, version, dir string) error {
}

archivePath := filepath.Join(tmp, archiveName)
gotSum, err := fetchFile(ctx, fmt.Sprintf("%s/%s/%s", downloadURL, version, archiveName), archivePath)
gotSum, err := fetchFile(ctx, fmt.Sprintf("%s/download/%s/%s", releasesURL, version, archiveName), archivePath)
if err != nil {
return err
}
Expand Down Expand Up @@ -140,7 +168,7 @@ func download(ctx context.Context, version, dir string) error {
// fetchChecksum returns the SHA-256 checksum of archiveName from the
// release's checksums.txt.
func fetchChecksum(ctx context.Context, version, archiveName string) (string, error) {
url := fmt.Sprintf("%s/%s/checksums.txt", downloadURL, version)
url := fmt.Sprintf("%s/download/%s/checksums.txt", releasesURL, version)
resp, err := get(ctx, url)
if err != nil {
return "", err
Expand Down
7 changes: 6 additions & 1 deletion internal/launcher/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ import (
// as LAUNCHER_VERSION. Bump it only when installed launchers need to change,
// because a newer version makes devbox ask users to replace their launcher,
// which can need sudo. The bash launcher's versions are all below 1.0.0.
const Version = "1.0.0"
//
// 1.1.0: the launcher's background check reads the latest version from GitHub
// releases instead of releases.jetify.com. The check runs in the front
// binary, so 1.0.0 launchers (devbox 0.19.0 and 0.20.0) keep reading
// releases.jetify.com until they're replaced.
const Version = "1.1.0"

// refreshArg is a hidden command that checks for a new devbox version. The
// launcher runs it in a detached child process so that commands don't wait
Expand Down
Loading
Loading