Skip to content

Install the new trunk CLI instead of the legacy launcher - #301

Draft
mmatheson wants to merge 1 commit into
mainfrom
matt/trunk-22802-trunk-action-get-the-new-cli-instead-of-the-legacy-launcher
Draft

mmatheson wants to merge 1 commit into
mainfrom
matt/trunk-22802-trunk-action-get-the-new-cli-instead-of-the-legacy-launcher

Conversation

@mmatheson

Copy link
Copy Markdown
Member

Status: plan only. No code yet. This draft PR has one empty commit. I will make the change after the plan gets approval.

Linear: TRUNK-22802 — trunk-action: get the new CLI instead of the legacy launcher

Summary

Today, trunk-action downloads the legacy bash launcher from https://trunk.io/releases/trunk when the repo has no trunk. This plan changes that download: the action installs the new CLI at a pinned version with install.trunk.io. The new CLI sends check, fmt, install, upgrade and the other legacy commands to the frozen launcher, which runs the legacy binary. Thus customer behavior stays the same.

On platforms that the new CLI does not support (Windows, Intel macOS), the action continues to download the legacy launcher.

Audit: each way that trunk-action gets or finds trunk

# Path Where What it does today Proposed change
1 trunk-path input action.yaml:10, setup/action.yaml:6, upgrade/action.yaml:10; also trunkPath in payload mode (action.yaml:195) The action uses this path as TRUNK_PATH. No change. The caller selects the binary.
2 Repo-local launcher setup/locate_trunk.sh:14-19: .trunk/bin/trunk, then tools/trunk, then ./trunk The action uses the first executable file that it finds. These are usually committed legacy launchers. No change. The repo selects the binary. A committed legacy launcher continues to work as today.
3 Download (main action, setup, upgrade) setup/locate_trunk.sh:21 curl https://trunk.io/releases/trunk (mutable, latest launcher) into $TRUNK_TMPDIR. Change. On Linux x64/arm64 and macOS arm64: install the new CLI at a pinned version (see "Version pin"). On other platforms: download the legacy launcher as today.
4 Download (install action) install/get_trunk.sh:12 Always downloads the legacy launcher, adds $TRUNK_TMPDIR to GITHUB_PATH, then runs trunk tools install --ci. Change. Same as #3. Move the shared logic into one script (setup/get_trunk.sh) that both locate_trunk.sh and install/get_trunk.sh call.
5 trunk already on PATH action.yaml:273 (${TRUNK_PATH:-trunk} --ci init) locate_trunk.sh never looks at PATH. It always sets TRUNK_PATH, so the :-trunk fallback does not occur in practice. No change. If the action starts to use a trunk from PATH, the binary can change for customers with no warning. I do not propose that.
6 Warm-up call setup/locate_trunk.sh:28, install/get_trunk.sh:20 trunk version downloads the legacy binary before later steps. Change. With the new CLI, version is a built-in command, and it does not download the legacy binary. When TRUNK_PATH is the new CLI, run trunk legacy version. This downloads the launcher and the cli.version binary in this step, as today.
7 Version check in all.sh all.sh:37 trunk version gives the legacy CLI version for the --upload-id minimum check (hold-the-line path only; the web app is shut down). Change. Use trunk legacy version when TRUNK_PATH is the new CLI. Otherwise the check compares trunk 0.0.9 with 1.12.3 and fails.
8 Cache action.yaml:224-225, actions/cache at action.yaml:301 Caches ~/.cache/trunk (legacy binaries and tools). Key: trunk-<cache-key>-<runner.os>-<hash of .trunk/trunk.yaml>. No change to the path or key. The legacy binary still goes to ~/.cache/trunk/cli/<ver>-<platform>/ (I tested this, see below), so cache hits are the same as today. The new CLI binary goes into $TRUNK_TMPDIR, which is not cached. Its 14 KB launcher copy goes into ~/.cache/trunk-cli/ and is fetched one time each run. I do not add it to the cache: it is small, and a new cache path changes the cache contents for all customers.
9 Other callers of $TRUNK_PATH pull_request.sh, push.sh, all.sh, trunk_merge.sh, annotate.sh, populate_cache_only.sh, setup-env/action.yaml:166, upgrade/upgrade.sh Run check, install, check disable, upgrade, daemon shutdown. No change. All of these are legacy commands. The new CLI sends them through legacy dispatch.
10 TRUNK_LAUNCHER_QUIET action.yaml:132 Makes the legacy launcher show its download output. No change. The pinned launcher reads it as before.

Version pin

  • Add an input cli-version to the main action, setup, install and upgrade. Its default is a constant that each trunk-action release pins (start: 0.0.9, the current prod latest). This is the same model as trunk-io/login (cli-version, default 0.0.7).
  • Add an input cli-channel (default prod), as in trunk-io/login. This lets us test with the staging channel.
  • The script runs: curl -fsSL https://install.trunk.io | sh -s -- --version "${CLI_VERSION}" --channel "${CLI_CHANNEL}" --install-dir "${TRUNK_TMPDIR}/bin". Inputs go through env, never into the script text.
  • --version writes trunk.pinned, and GitHub runners set CI=true. Both stop self-update. I will also set TRUNK_NO_AUTO_UPDATE=1 in GITHUB_ENV for each later step, as a second guard.
  • The explicit --install-dir makes install.sh skip its PATH probe, so a legacy trunk on PATH does not cause a warning or a stop.
  • Payload mode gets cliVersion and cliChannel fields, with the same defaults.

Repos that pin cli.version in trunk.yaml

The new CLI sends legacy commands to its pinned launcher (1.3.4). That launcher reads .trunk/trunk.yaml cli.version and runs that exact legacy binary, as the old launcher does. I tested this on this repo (cli.version: 1.25.0) with CLI 0.0.9 and CI=true:

$ trunk version          -> trunk 0.0.9
$ trunk legacy version   -> Fetching the legacy trunk runner (one-time, ~14 KB)... 1.25.0
$ trunk check --help     -> universal code checker ...
cache: ~/.cache/trunk/cli/1.25.0-linux-x86_64/   (legacy binary, same path as today)
       ~/.cache/trunk-cli/legacy-launcher/       (new, 14 KB)

A repo with no .trunk/trunk.yaml gets trunk --ci init (action.yaml:273). init is a legacy command, so it works the same way.

Windows and Intel macOS

  • The new CLI has builds for linux-x64, linux-arm64 and darwin-arm64 only. install.sh stops on other platforms.
  • The legacy launcher supports Windows (Git Bash, mingw/msys) and Intel macOS. The action runs every step with shell: bash, so Windows runners use the bash launcher today, not trunk.ps1.
  • Plan: the new script selects the source from uname -s and uname -m before it downloads. A supported platform gets the new CLI. All other platforms get the legacy launcher from https://trunk.io/releases/trunk, as today. The script writes one ::notice:: line that tells which source it used.
  • I do not add a fallback to the legacy launcher when install.sh fails on a supported platform. A silent fallback hides real failures and makes the version hard to know. The step fails with a clear error.

Escape hatch

Add an input launcher with values new (default) and legacy. legacy keeps the old download on all platforms. A customer can use it if the new CLI causes a problem, without a pin to an old trunk-action tag. The README will document it.

Tests

  • action_tests.yaml: add a job matrix on ubuntu-latest, ubuntu-24.04-arm, macos-latest (arm64), macos-15-intel (Intel) and windows-latest. Each job runs ./setup in a fixture repo that pins cli.version, then checks:
    • which source the script used (new CLI on the first three, legacy launcher on the last two);
    • "$TRUNK_PATH" --version is trunk <cli-version> for the new CLI;
    • "$TRUNK_PATH" legacy version (or version for the launcher) equals the pinned cli.version.
  • Add a job for launcher: legacy and a job for the install action (trunk on PATH after the step).
  • The existing stub tests (TRUNK_PATH: stub.js) do not change, because they skip the download.
  • repo_tests.yaml and docker_repo_tests.yaml use ./ and will run trunk check through the new CLI on real repos. The docker job uses ubuntu:runner-24.04. The linux build is static musl, so it runs there.

README

  • Update the trunk-path text in each action.yaml and in the README: "Path to the trunk CLI".
  • Document cli-version, cli-channel and launcher, the supported platforms, and the fallback on Windows and Intel macOS.

Risk for existing customers

Risk Effect Mitigation
The v1 tag moves to the new release, so all @v1 users get the new CLI on the next run. All repos with no committed launcher change to the new CLI at one time. Behavior of legacy commands is the same (legacy dispatch runs the same launcher logic and the same cli.version binary). The launcher: legacy input is the rollback for one repo. A human decides whether to release as v1.x or as v2; see the open questions.
A new host install.trunk.io. Self-hosted runners with an egress allowlist that has trunk.io only cannot get install.sh. The README tells this. launcher: legacy works for them. Option B below removes this risk.
The first legacy command fetches the 14 KB launcher from trunk.io/releases/launcher/1.3.4/trunk. One more request to trunk.io each run. Same host as today. The core checks the sha256.
trunk version output changes to trunk x.y.z. Customer steps after setup that parse $TRUNK_PATH version get a different string. The README tells this. trunk legacy version gives the old output.
Repos with a committed launcher (.trunk/bin/trunk, tools/trunk, ./trunk). None. Not changed.
Windows and Intel macOS. None. They keep the legacy launcher.

Open questions for approval

  1. How to get the binary.
    • A (recommended, as the ticket says): pipe install.trunk.io with --version, the same as trunk-io/login.
    • B: the action downloads the tarball from trunk.io/releases/trunk-cli/<channel>/<ver>/ and checks a sha256 for each platform that is pinned in the action. This removes the new host and the mutable installer script from customer CI, but each CLI bump must update the hashes too.
  2. Release type. Release on v1 (the v1 tag moves to it), or release as v2 so that customers opt in? I recommend v1 with the launcher: legacy escape hatch, because legacy commands do not change. A human does the release in both cases.
  3. Escape hatch. Keep the launcher input (recommended), or leave it out to keep the input list small?

Out of scope

  • No merge, tag or release. A human does those.
  • No change to the legacy launcher at trunk.io/releases/trunk (decided 2026-10-24).

🤖 Generated with Claude Code

…-22802)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mmatheson

Copy link
Copy Markdown
Member Author

[coordinator] BLOCKED: plan ready for approval

The plan is in the PR description. Summary:

  • Change only the download paths (setup/locate_trunk.sh:21, install/get_trunk.sh:12). On Linux x64/arm64 and macOS arm64, install the new CLI at a pinned cli-version (default 0.0.9) with install.trunk.io --version --install-dir, as trunk-io/login does. Windows and Intel macOS keep the legacy launcher download.
  • No change to trunk-path, the repo-local launchers, PATH lookup, the cache path or the cache key.
  • cli.version pins continue to work: I tested CLI 0.0.9 on a repo with cli.version: 1.25.0. Legacy dispatch ran 1.25.0 and put it in ~/.cache/trunk/cli/, the same cached path as today.
  • Two calls of trunk version change to trunk legacy version when TRUNK_PATH is the new CLI, because version is now a built-in.
  • New inputs: cli-version, cli-channel, and an escape hatch launcher: legacy.

Questions (my recommendation first):

  1. Get the binary with A: install.trunk.io (the ticket) or B: direct tarball with a sha256 pinned in the action (no new host for allowlisted self-hosted runners).
  2. Release on v1 with the escape hatch, or as v2.
  3. Keep the launcher escape-hatch input, or leave it out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant