Skip to content

Releases: mpostument/bootstrap

bootstrap v2026.09.28-3

Choose a tag to compare

@github-actions github-actions released this 28 Sep 11:20

Windows 1.48.0

Added

  • ILSpy is back in the dev group, dropped in 1.47.0: decompiles any .NET
    assembly to C# - a NuGet package with no source, a Unity plugin, a build's
    Assembly-CSharp.dll. From winget, icsharpcode.ILSpy. On a machine with a
    saved -Select pick it is new to the manifest, so the next manual run asks
    whether to install it; the scheduled run leaves it out until then.

Linux 1.41.0

Added

  • --select: choose which packages this machine has, the same design as
    macOS 1.43.0 and Windows 1.47.0: a menu of every apt package, flatpak and uv
    tool by group, plus a releases section for the release binaries, which
    belong to no group. Unticked and installed is uninstalled after one
    confirmation - apt-get remove through sudo, flatpak uninstall, uv tool
    uninstall, or the binary deleted from ~/.local/bin.
  • apt removals are simulated first. apt-get remove takes everything that
    depends on a package with it - python3 would take half the system - so a
    removal that would take anything but the package itself is not run, and the
    result names what would have gone.
  • The pick lives in /var/lib/bootstrap-linux/selection, not under $HOME:
    the systemd timer runs as root and has to read the pick a person made. An
    interactive run writes it through sudo, which it already has for apt.
    REQUIRED in packages.conf - zsh, git and uv - is always installed and locked
    in the menu. New packages are asked about once on a manual run; the timer
    installs none of them and reports each as deselected, and a section with
    nothing ticked counts as switched off.
  • The run history records a removal as -pkg, and the summary counts
    removed, would-remove and deselected.

macOS 1.44.0

Added

  • ILSpy is back in the dev group, dropped in 1.43.0: decompiles any .NET
    assembly to C# - a NuGet package with no source, a Unity plugin, a build's
    Assembly-CSharp.dll. From the Homebrew cask. Linux still has none - its
    build is an amd64-only .deb. On a machine with a saved --select pick it is
    new to the manifest, so the next manual run asks whether to install it; the
    scheduled run leaves it out until then.

Fixed

  • The summary counts what --select did. 1.43.0 added the removed,
    would-remove and deselected results but left them out of the summary's list,
    so a run that uninstalled something reported nothing of it at the end.

bootstrap v2026.09.28-2

Choose a tag to compare

@github-actions github-actions released this 28 Sep 07:17

Windows 1.47.0

Added

  • A release archive is a whole install now. It used to hold the platform
    directory alone, without the configs the script deploys from the repo root -
    starship.toml, the tool themes, mise/, tools/cli-parity.conf - so an install
    from it could not finish its shell and theme phases. Each archive now
    unpacks to one bootstrap/ directory laid out as the repo: the platform's own
    directory and every top-level entry but the other two platforms. It also
    carries a RELEASE file naming the tag and the repo.
  • An archive install updates itself like a checkout: when a newer release
    is out, a run you are sitting at offers it, downloads that release's zip,
    checks it against the published .sha256, unpacks it into a temp directory
    and copies it over the install - the same directory, so the daily task still
    finds the script. A download that fails or does not match leaves the install
    as it was. An archive from before this release has no RELEASE file; unpack
    this one once by hand and it updates from then on.
  • Update from a run: when preflight finds a newer release, a run you are
    sitting at asks Update to vX and rerun? [y/N]. Yes fetches, moves the
    checkout to the tag - a branch fast-forwards, a detached checkout of a tag
    moves to the new one - and reruns the new script with the same arguments and
    -SkipUpdateCheck. Local changes, or a branch with commits the release
    lacks, and it refuses and says why rather than guessing. The daily task,
    -WhatIf and -Doctor only print the line, as before.
  • -Select: choose which packages this machine has. A menu of every
    winget package and uv tool in the manifest, drawn by the script itself: a
    section per group with its packages beneath, each with a [x] or [ ] box,
    the installed version and the current pick pre-ticked. Space on a package
    ticks it; on a section it ticks all of it - or clears it, when all of it was
    ticked - and the section shows [x], [-] or [ ] for all, some or none.
    Not fzf: fzf marks only ticked lines, with no empty box for the rest, and a
    whole-section toggle from inside it takes a shell command per keypress. It
    needs no fzf, so it works on a first run too. Ticked is installed; unticked
    and installed is uninstalled, after one confirmation listing all of it.
    Declining abandons the whole pick rather than saving it without the
    removals, which would leave those packages installed but unwanted, and
    never offered for removal again.
  • The pick is saved to %LOCALAPPDATA%\windows-bootstrap\selection.json,
    beside last-run, and every later run follows it, the daily task included.
    It keeps two lists: Selected, what was ticked, and Known, everything the
    menu offered - so an unticked package can be told from one the manifest
    gained since. With no file every package is wanted, exactly as before.
  • Required in packages.psd1: ids that are always installed and show
    locked in the menu - pwsh, git, uv and mise, which other phases stand on.
    Optional, so an older manifest still loads; an id no group lists fails the
    run up front.
  • New packages are asked about, not assumed. A manual run asks once per
    package the manifest gained since the last pick and remembers the answer
    either way. An unattended run installs none of them and reports each as
    deselected, so nothing arrives on the machine that nobody chose. A
    section with nothing ticked counts as switched off: its newcomers are left
    out without asking.
  • User-scope packages uninstall from an elevated run. winget refuses
    (0x8A15007D) to remove a package installed per user - most portable CLIs -
    from an administrator process. That one call is rerun un-elevated as the
    same user, through a throwaway scheduled task with RunLevel Limited that is
    removed when it finishes.
  • What the menu offers to remove follows the machine: anything unticked
    and still installed that a menu has offered before. A removal that failed
    is offered again on the next -Select instead of being stranded as
    unticked-but-installed.
  • Result actions removed, would-remove and deselected, and the run
    history now records a removal as -id.

Removed

  • ILSpy, added in 1.46.0: dropped before anyone depended on it. Taking it
    out of the manifest stops installs and upgrades; a copy already installed
    stays until winget uninstall icsharpcode.ILSpy.
  • Sysinternals Suite, added in 1.46.0: winget's manifest pins the hash of
    SysinternalsSuite.zip, which Microsoft replaces in place at the same URL
    without a new version, so the install fails the hash check - and an
    elevated winget rightly refuses to skip it - every time the zip moves until
    the manifest catches up.

Linux 1.40.0

Added

  • A release archive is a whole install now. It used to hold the platform
    directory alone, without the configs the script deploys from the repo root -
    starship.toml, the tool themes, mise/, tools/cli-parity.conf - so an install
    from it could not finish its shell and theme phases. Each archive now
    unpacks to one bootstrap/ directory laid out as the repo: the platform's own
    directory and every top-level entry but the other two platforms. It also
    carries a RELEASE file naming the tag and the repo.
  • An archive install updates itself like a checkout: when a newer release
    is out, a run you are sitting at offers it, downloads that release's
    tarball, checks it against the published .sha256, unpacks it into a temp
    directory and copies it over the install - the same directory, so the
    systemd timer still finds the script. A download that fails or does not
    match leaves the install as it was. An archive from before this release has
    no RELEASE file; unpack this one once by hand and it updates from then on.
  • Update from a run: when preflight finds a newer release, a run you are
    sitting at asks Update to vX and rerun? [y/N]. Yes fetches, moves the
    checkout to the tag - a branch fast-forwards, a detached checkout of a tag
    moves to the new one - and reruns the new script with the same arguments and
    --skip-update-check. Local changes, or a branch with commits the release
    lacks, and it refuses and says why rather than guessing. The systemd timer,
    --dry-run, --doctor and a run as root only print the line: git writing
    into your checkout as root would leave files you could not change. The rerun
    is an exec: bash reads a script as it runs, so the old process must not go
    on over a file that just changed under it.

macOS 1.43.0

Added

  • A release archive is a whole install now. It used to hold the platform
    directory alone, without the configs the script deploys from the repo root -
    starship.toml, the tool themes, mise/, tools/cli-parity.conf - so an install
    from it could not finish its shell and theme phases. Each archive now
    unpacks to one bootstrap/ directory laid out as the repo: the platform's own
    directory and every top-level entry but the other two platforms. It also
    carries a RELEASE file naming the tag and the repo.
  • An archive install updates itself like a checkout: when a newer release
    is out, a run you are sitting at offers it, downloads that release's
    tarball, checks it against the published .sha256, unpacks it into a temp
    directory and copies it over the install - the same directory, so the
    launchd agent still finds the script. A download that fails or does not
    match leaves the install as it was. An archive from before this release has
    no RELEASE file; unpack this one once by hand and it updates from then on.
  • Update from a run: when preflight finds a newer release, a run you are
    sitting at asks Update to vX and rerun? [y/N]. Yes fetches, moves the
    checkout to the tag - a branch fast-forwards, a detached checkout of a tag
    moves to the new one - and reruns the new script with the same arguments and
    --skip-update-check. Local changes, or a branch with commits the release
    lacks, and it refuses and says why rather than guessing. The launchd agent,
    --dry-run and --doctor only print the line. The rerun is an exec: bash
    reads a script as it runs, so the old process must not go on over a file
    that just changed under it.
  • --select: choose which packages this Mac has, the same design as
    Windows 1.47.0. A menu of every formula and cask in the manifest, drawn by
    the script: a section per group with its packages beneath, each with a
    [x] or [ ] box and its installed version, the current pick pre-ticked.
    Space on a section ticks all of it, or clears it when all of it was ticked,
    and the section shows [x], [-] or [ ]. Keys are read from /dev/tty
    a byte at a time; everything else - the rows, a toggle, a frame - is plain
    functions over indexed arrays, since bash 3.2 has no associative ones.
  • Unticked and installed is uninstalled with brew uninstall, after one
    confirmation listing all of it; declining keeps everything and saves
    nothing. What is offered follows the machine - unticked, installed, offered
    before - so a formula brew refused to remove because another depends on it
    is offered again next time.
  • The pick is saved to ~/.local/state/bootstrap-macos/selection as plain
    yes/no lines, and every later run follows it, the launchd agent
    included. A line that is neither makes the file untrusted, and every package
    is wanted - a garbled file never unticks everything. No file, no change.
  • REQUIRED in packages.conf - git, mise and uv - always installed and
    locked in the menu. Optional, so an older manifest still loads; an id no
    group lists fails the run up front.
  • New packages are asked about once on a manual run and the answer kept;
    the agent installs none of them and reports each as deselected. A section
    with nothing ticked counts as switched off, and its newcomers are left out
    without ...
Read more

bootstrap v2026.09.27-2

Choose a tag to compare

@github-actions github-actions released this 27 Sep 15:36

Windows 1.46.0

Added

  • Sysinternals Suite in the apps group - Process Explorer for what holds a
    file or DLL open, Autoruns for everything that starts with the system,
    Procmon for every file and registry access a process makes. winget versions
    it by date (2026-07-09) rather than semver; it upgrades like any other
    package, since winget compares its own version strings.
  • UniGetUI, beside it - one window for what is outdated across winget,
    pip, npm and dotnet tools. It can update packages on its own schedule too;
    leave that off if the daily bootstrap task is meant to be the only updater.
  • HWiNFO - hardware and sensor inspection: temperatures, clocks, voltages
    and WHEA memory errors, with a sensor log for a run under load.
  • CrystalDiskInfo - SMART health for SSDs and HDDs, wear and reallocated
    sectors behind a Good / Caution / Bad verdict.
  • Bulk Crap Uninstaller - everything installed, including orphans with no
    uninstaller and what a removal left behind, and removes them in bulk.
  • Portmaster - which app connects where: domains, countries and
    connection counts per process, with a per-app block list. It installs a
    network filter driver and answers DNS itself, so if WSL, Rancher Desktop or
    a VPN loses name resolution after install, that is the first place to look.
  • ILSpy in the dev group, beside the .NET SDK - decompiles any .NET
    assembly back to C#: a NuGet package with no source, a Unity plugin, a
    build's Assembly-CSharp.dll, without opening a solution in Rider first.
  • mitmproxy in the dev group - an HTTP(S) proxy that shows every request
    and response an app makes: Bruno sends requests, mitmproxy watches what your
    app, game or CLI sends. mitmproxy (TUI), mitmweb (browser UI) and
    mitmdump. HTTPS needs its CA certificate trusted, which stays a manual
    step - visit mitm.it through the proxy - since a bootstrap that silently
    trusts an interception CA is the wrong default. mitmproxy.mitmproxy.

Not added

  • popeye and k8sgpt, which Linux 1.39.0 and macOS 1.42.0 gained: neither
    publishes a winget package. packages.psd1 says so beside the cloud group.

Linux 1.39.0

Added

  • popeye, in the cloud group next to k9s - same author, other half of the
    job: k9s is for looking at a cluster, popeye scans one and grades it -
    orphaned ConfigMaps and Secrets, pods without limits, services with no
    endpoints. A release binary; it answers only popeye version,
    which binary_version already tries last, so it needs no VERSION_ARGS.
  • k8sgpt, beside it - the other question: popeye grades what is sloppy,
    k8sgpt lists what is broken right now (CrashLoopBackOff, Pending pods,
    unbound PVCs, an Ingress pointing at nothing). The scan is local; only
    --explain sends findings to an LLM backend, and --anonymize masks
    resource names before they leave the machine. A release binary,
    with the same version-only answer as popeye.
  • Windows gets neither: no winget package exists for either tool.
  • mitmproxy in the dev group - an HTTP(S) proxy that shows every request
    and response an app makes: Bruno sends requests, mitmproxy watches what your
    app, game or CLI sends. mitmproxy (TUI), mitmweb (browser UI) and
    mitmdump. HTTPS needs its CA certificate trusted, which stays a manual
    step - visit mitm.it through the proxy - since a bootstrap that silently
    trusts an interception CA is the wrong default. Through uv tool, in a
    new GROUP_dev_UV: the archive's package trails upstream by majors.

Not added

  • ILSpy, which macOS 1.42.0 and Windows 1.46.0 gained: upstream's Linux
    build is an amd64-only .deb, and there is no general installer for a .deb
    from a release yet - only ghostty's own block.

macOS 1.42.0

Added

  • popeye, in the cloud group next to k9s - same author, other half of the
    job: k9s is for looking at a cluster, popeye scans one and grades it -
    orphaned ConfigMaps and Secrets, pods without limits, services with no
    endpoints. From Homebrew core.
  • k8sgpt, beside it - the other question: popeye grades what is sloppy,
    k8sgpt lists what is broken right now (CrashLoopBackOff, Pending pods,
    unbound PVCs, an Ingress pointing at nothing). The scan is local; only
    --explain sends findings to an LLM backend, and --anonymize masks
    resource names before they leave the machine. From Homebrew core.
  • Windows gets neither: no winget package exists for either tool.
  • mitmproxy in the dev group - an HTTP(S) proxy that shows every request
    and response an app makes: Bruno sends requests, mitmproxy watches what your
    app, game or CLI sends. mitmproxy (TUI), mitmweb (browser UI) and
    mitmdump. HTTPS needs its CA certificate trusted, which stays a manual
    step - visit mitm.it through the proxy - since a bootstrap that silently
    trusts an interception CA is the wrong default. From the Homebrew cask;
    there is no formula.
  • ILSpy in the dev group - decompiles any .NET assembly back to C#: a
    NuGet package with no source, a Unity plugin, a build's
    Assembly-CSharp.dll. Cross-platform since 11.0; from the Homebrew cask.

bootstrap v2026.09.17-3

Choose a tag to compare

@github-actions github-actions released this 17 Sep 17:19

Windows 1.41.1

Fixed

  • -Doctor stopped at its first check. ok and broken, the two
    verdicts the doctor exists to give, were never added to the ValidateSet
    on Add-Result, so the probe shell's own ok threw before a single tool
    was looked at. Both are in the set now, green and red.

  • starship prompt reported "no prompt function" with the profile
    loaded.
    A function's Definition starts with a newline, and the probe
    answers one key=value per line - so fn:prompt= always arrived empty.
    The definition is flattened onto one line before it is sent.

  • The mise checks looked in the Linux data directory. Windows mise keeps
    its shims under %LOCALAPPDATA%\mise\shims, which is what the mise phase
    puts on the user PATH; the doctor looked in ~\.local\share\mise\shims
    and reported it missing on every machine. And node, go and java
    resolving to mise\installs\... is mise activate working as intended -
    it puts those ahead of the shims - so that is ok now, not a note. Only a
    runtime from outside mise is flagged.

  • Housekeeping threw on an empty download cache. On PowerShell 7,
    Measure-Object emits nothing at all for empty input, so
    (... | Measure-Object -Sum).Sum is $null.Sum, which strict mode rejects.
    That ran before the "nothing to prune" check, so a clean cache - the usual
    case - was enough. Sizes are summed by Format-Size instead, which is
    0.0 MB for no files.

Linux 1.34.2

Fixed

  • Housekeeping still stopped mid-phase, past the journal fix in 1.34.1.
    The actual point of failure was the very next line: flatpak uninstall --unused --dry-run. uninstall has never had a --dry-run - that flag
    exists only on flatpak prune, for an unrelated purpose (pruning the
    OSTree object store, not listing installed-but-unused runtime refs) - so
    the command errored with Unknown option --dry-run every time, and the
    same pipefail/set -e/2>/dev/null combination silenced it. There is
    no safe read-only substitute: --noninteractive implies --assumeyes on
    --unused, so scripting around the missing flag risked doing the removal
    this phase promises never to do. The check is gone; flatpak uninstall --unused by hand is still there to run and review before answering yes.

  • --doctor misjudged three things that were actually fine. 7zip's
    expected command was hardcoded to 7zz - that name is macOS's; apt's own
    7zip package puts 7z on PATH here. ~/.dotnet/tools was checked
    against PATH unconditionally, when the shell fragment itself only adds it
    once dotnet tool install -g has created the directory - its absence is
    the fragment working as intended, not broken. And carapace's completer
    was checked for a function named _carapace; carapace 1.7 defines
    _carapace_completer instead, so a working completer was reported as
    never initialised.

  • @releases tools disappeared under WSL and other NAT'd or shared
    addresses.
    Each one calls GitHub's API once, unauthenticated, to read
    its latest tag - 60 requests/hour per source IP, easy to spend across the
    20-plus tools this platform installs that way, and every one queued
    behind the first failure came back failed and was never installed, not
    just delayed. GITHUB_TOKEN/GH_TOKEN (read the same as gh itself
    reads them) or a gh auth login raise that to 5000/hour; either is now
    picked up automatically, computed once per run rather than per tool.

macOS 1.37.1

Fixed

  • github_latest_tag was unauthenticated. GitHub's API allows 60
    requests/hour per source IP without a token, shared with anything else on
    the same address; every release-tracked tool this call is used for is
    one more request against that budget. GITHUB_TOKEN/GH_TOKEN (read the
    same as gh itself reads them) or a gh auth login raise that to
    5000/hour; either is now picked up automatically, computed once per run
    rather than per tool.

  • --doctor's carapace check looked for the wrong function. carapace
    1.7 defines _carapace_completer, not _carapace - the fragment
    registers it with compdef _carapace_completer <every command it covers>. A working completer was reported as never initialised.

bootstrap v2026.09.17-2

Choose a tag to compare

@github-actions github-actions released this 17 Sep 16:20

Windows 1.41.1

Fixed

  • -Doctor stopped at its first check. ok and broken, the two
    verdicts the doctor exists to give, were never added to the ValidateSet
    on Add-Result, so the probe shell's own ok threw before a single tool
    was looked at. Both are in the set now, green and red.

  • starship prompt reported "no prompt function" with the profile
    loaded.
    A function's Definition starts with a newline, and the probe
    answers one key=value per line - so fn:prompt= always arrived empty.
    The definition is flattened onto one line before it is sent.

  • The mise checks looked in the Linux data directory. Windows mise keeps
    its shims under %LOCALAPPDATA%\mise\shims, which is what the mise phase
    puts on the user PATH; the doctor looked in ~\.local\share\mise\shims
    and reported it missing on every machine. And node, go and java
    resolving to mise\installs\... is mise activate working as intended -
    it puts those ahead of the shims - so that is ok now, not a note. Only a
    runtime from outside mise is flagged.

  • Housekeeping threw on an empty download cache. On PowerShell 7,
    Measure-Object emits nothing at all for empty input, so
    (... | Measure-Object -Sum).Sum is $null.Sum, which strict mode rejects.
    That ran before the "nothing to prune" check, so a clean cache - the usual
    case - was enough. Sizes are summed by Format-Size instead, which is
    0.0 MB for no files.

Linux 1.34.1

Fixed

  • Housekeeping stopped mid-phase, without a word, on WSL and anywhere else
    the journal is not persistent.
    The disk-usage check piped
    journalctl --disk-usage straight into sed, and journalctl exits
    non-zero when there is no /var/log/journal to report on - which
    pipefail turned into the whole pipeline failing, and set -e then
    ended the run right there. 2>/dev/null on the same line hid the only
    clue. Guarded with || true, the same fix already applied to the stale
    .deb count just above it: no journal to report on is not a failure.

macOS 1.37.0

Removed

  • docker-desktop. Docker Desktop's own installer owns it on this machine -
    every run found /Applications/Docker.app already there and reported it as
    installed by something else, which is a line about software this manifest
    does not manage. Out of the apps group, and out of the README passages that
    used it as the example of a cask.

  • flameshot. Homebrew disabled the cask: it does not pass the macOS
    Gatekeeper check, so brew install --cask flameshot now fails outright and
    every run reported it as a failed step. Screenshots are cmd-shift-5 in the
    meantime. The winget package on Windows is unaffected and stays.

Added

  • --doctor: is it actually in effect? Every phase in this script asserts
    that a package is installed. Nothing asserted that it works, and the
    difference is where this changelog's bugs live: ~/go/bin missing from
    PATH, ~/.dotnet/tools missing from PATH, the mise shims missing from
    PATH, a leftover apt fd-find shadowing the real fd, format and
    right_format in starship.toml never in effect, Tab never opening fzf.
    Every one of those was found by somebody noticing, months later, because a
    phase that installs a package has no idea whether the shell you type into can
    see it.

    So --doctor asks the only environment that can answer: a real login shell.
    One zsh -lic probe emits every fact at once - what each cli tool resolves
    to, which widget Tab and the up arrow are bound to, whether starship, atuin,
    carapace, zoxide and compinit initialised, what node, go and java
    resolve to, what is on PATH, whether the deployed config still matches the
    repo, whether the launchd agent is loaded. About a second for all of it; a shell
    per check would take a minute to say the same thing.

    Three verdicts, not two. broken is wrong, ok is right, and present is a
    judgement call left to the reader: a mise shim in front of a binary runs the
    same program through one more exec, and a config file that differs from the
    repo is only going to be replaced by the next run. Making those broken
    would have meant a doctor that cries wolf, which is a doctor nobody runs.

    It changes nothing and exits 1 if anything is broken, so it works as a check.
    Running it on the machine it was written on found a stale mise shim for yq
    sitting in front of Homebrew's, and a launchd agent that had never been
    installed.

  • --history: and what did it move? --status answers "did last night's
    run work". This answers the other half. A snapshot of brew list --versions, formulae and casks both is taken
    before the packages phase and another after the upgrades, and the difference -
    node 24.20.0>24.21.0, +ripgrep 14.1.1 - goes into the run record and into
    one line per run in history, next to it.

    The snapshot is allowed to fail, in pieces. A package listing can exit
    non-zero for reasons that have nothing to do with this run, and one that
    fails prints nothing at all rather than a shorter list - so taking the
    formulae and the casks together meant losing both. Worse, under set -e a
    snapshot taken for the history could end the run before it installed
    anything, which is exactly what it did on the machine this was written on,
    between the taps phase and the first package. Each listing now runs
    separately and each may fail; what still answers is recorded, and the run
    carries on either way.

    That is what actually moved, rather than what scrolled past in the output:
    Homebrew keeps no log of what it upgraded. One line per run, oldest first, capped at 200 lines, which is
    eight months of nightly runs. --history=n for a narrower window; unattended
    runs are marked, because those are the ones nobody watched.

bootstrap v2026.09.17

Choose a tag to compare

@github-actions github-actions released this 17 Sep 16:13

Windows 1.41.1

Fixed

  • -Doctor stopped at its first check. ok and broken, the two
    verdicts the doctor exists to give, were never added to the ValidateSet
    on Add-Result, so the probe shell's own ok threw before a single tool
    was looked at. Both are in the set now, green and red.

  • starship prompt reported "no prompt function" with the profile
    loaded.
    A function's Definition starts with a newline, and the probe
    answers one key=value per line - so fn:prompt= always arrived empty.
    The definition is flattened onto one line before it is sent.

  • The mise checks looked in the Linux data directory. Windows mise keeps
    its shims under %LOCALAPPDATA%\mise\shims, which is what the mise phase
    puts on the user PATH; the doctor looked in ~\.local\share\mise\shims
    and reported it missing on every machine. And node, go and java
    resolving to mise\installs\... is mise activate working as intended -
    it puts those ahead of the shims - so that is ok now, not a note. Only a
    runtime from outside mise is flagged.

  • Housekeeping threw on an empty download cache. On PowerShell 7,
    Measure-Object emits nothing at all for empty input, so
    (... | Measure-Object -Sum).Sum is $null.Sum, which strict mode rejects.
    That ran before the "nothing to prune" check, so a clean cache - the usual
    case - was enough. Sizes are summed by Format-Size instead, which is
    0.0 MB for no files.

Linux 1.34.0

Added

  • --doctor: is it actually in effect? Every phase in this script asserts
    that a package is installed. Nothing asserted that it works, and the
    difference is where this changelog's bugs live: ~/go/bin missing from
    PATH, ~/.dotnet/tools missing from PATH, the mise shims missing from
    PATH, a leftover apt fd-find shadowing the real fd, format and
    right_format in starship.toml never in effect, Tab never opening fzf.
    Every one of those was found by somebody noticing, months later, because a
    phase that installs a package has no idea whether the shell you type into can
    see it.

    So --doctor asks the only environment that can answer: a real login shell.
    One zsh -lic probe emits every fact at once - what each cli tool resolves
    to, which widget Tab and the up arrow are bound to, whether starship, atuin,
    carapace, zoxide and compinit initialised, what node, go and java
    resolve to, what is on PATH, whether the deployed config still matches the
    repo, whether the systemd timer is loaded. About a second for all of it; a shell
    per check would take a minute to say the same thing.

    Three verdicts, not two. broken is wrong, ok is right, and present is a
    judgement call left to the reader: a mise shim in front of a binary runs the
    same program through one more exec, and a config file that differs from the
    repo is only going to be replaced by the next run. Making those broken
    would have meant a doctor that cries wolf, which is a doctor nobody runs.

    It changes nothing and exits 1 if anything is broken, so it works as a check.
    Running it on the machine it was written on found a stale mise shim for yq
    sitting in front of Homebrew's, and a launchd agent that had never been
    installed.

  • --history: and what did it move? --status answers "did last night's
    run work". This answers the other half. A snapshot of every dpkg package and every flatpak is taken
    before the packages phase and another after the upgrades, and the difference -
    node 24.20.0>24.21.0, +ripgrep 14.1.1 - goes into the run record and into
    one line per run in history, next to it.

    The snapshot is allowed to fail, in pieces. flatpak is not on every
    machine, and a command that is missing entirely exits 127 - which, under
    set -e and a pipeline, would end a run over a snapshot taken for the
    history. Each listing now runs separately and each may fail; what still
    answers is recorded, and the run carries on either way.

    That is what actually moved, rather than what scrolled past in the output:
    and it is every dpkg package, not only the ones this manifest names, because apt-get upgrade moves plenty this script never mentions. A dist-upgrade that moves a hundred packages is summarised rather than printed in full. One line per run, oldest first, capped at 200 lines, which is
    eight months of nightly runs. --history=n for a narrower window; unattended
    runs are marked, because those are the ones nobody watched.

macOS 1.37.0

Removed

  • docker-desktop. Docker Desktop's own installer owns it on this machine -
    every run found /Applications/Docker.app already there and reported it as
    installed by something else, which is a line about software this manifest
    does not manage. Out of the apps group, and out of the README passages that
    used it as the example of a cask.

  • flameshot. Homebrew disabled the cask: it does not pass the macOS
    Gatekeeper check, so brew install --cask flameshot now fails outright and
    every run reported it as a failed step. Screenshots are cmd-shift-5 in the
    meantime. The winget package on Windows is unaffected and stays.

Added

  • --doctor: is it actually in effect? Every phase in this script asserts
    that a package is installed. Nothing asserted that it works, and the
    difference is where this changelog's bugs live: ~/go/bin missing from
    PATH, ~/.dotnet/tools missing from PATH, the mise shims missing from
    PATH, a leftover apt fd-find shadowing the real fd, format and
    right_format in starship.toml never in effect, Tab never opening fzf.
    Every one of those was found by somebody noticing, months later, because a
    phase that installs a package has no idea whether the shell you type into can
    see it.

    So --doctor asks the only environment that can answer: a real login shell.
    One zsh -lic probe emits every fact at once - what each cli tool resolves
    to, which widget Tab and the up arrow are bound to, whether starship, atuin,
    carapace, zoxide and compinit initialised, what node, go and java
    resolve to, what is on PATH, whether the deployed config still matches the
    repo, whether the launchd agent is loaded. About a second for all of it; a shell
    per check would take a minute to say the same thing.

    Three verdicts, not two. broken is wrong, ok is right, and present is a
    judgement call left to the reader: a mise shim in front of a binary runs the
    same program through one more exec, and a config file that differs from the
    repo is only going to be replaced by the next run. Making those broken
    would have meant a doctor that cries wolf, which is a doctor nobody runs.

    It changes nothing and exits 1 if anything is broken, so it works as a check.
    Running it on the machine it was written on found a stale mise shim for yq
    sitting in front of Homebrew's, and a launchd agent that had never been
    installed.

  • --history: and what did it move? --status answers "did last night's
    run work". This answers the other half. A snapshot of brew list --versions, formulae and casks both is taken
    before the packages phase and another after the upgrades, and the difference -
    node 24.20.0>24.21.0, +ripgrep 14.1.1 - goes into the run record and into
    one line per run in history, next to it.

    The snapshot is allowed to fail, in pieces. A package listing can exit
    non-zero for reasons that have nothing to do with this run, and one that
    fails prints nothing at all rather than a shorter list - so taking the
    formulae and the casks together meant losing both. Worse, under set -e a
    snapshot taken for the history could end the run before it installed
    anything, which is exactly what it did on the machine this was written on,
    between the taps phase and the first package. Each listing now runs
    separately and each may fail; what still answers is recorded, and the run
    carries on either way.

    That is what actually moved, rather than what scrolled past in the output:
    Homebrew keeps no log of what it upgraded. One line per run, oldest first, capped at 200 lines, which is
    eight months of nightly runs. --history=n for a narrower window; unattended
    runs are marked, because those are the ones nobody watched.

bootstrap v2026.09.16

Choose a tag to compare

@github-actions github-actions released this 16 Sep 13:05

Windows 1.39.0

Added

  • TerminalSetPwshDefault in the Shell manifest (default $true).
    merge-terminal-settings.ps1 now writes Windows Terminal's
    defaultProfile to the PowerShell 7 profile guid it already creates, so
    a new terminal tab opens PowerShell 7 instead of whatever Windows
    Terminal picked on its own.

  • F2 flips PSReadLine predictions to ListView for the current line -
    see Changed below for why InlineView is now the default. F2 runs
    SwitchPredictionView, so the full dropdown of matches is still one key
    away without it appearing unprompted.

Changed

  • PSReadLine PredictionViewStyle: ListView → InlineView. ListView
    rendered a dropdown of every history/plugin match for whatever was in the
    buffer, typed or pasted - pasting a multi-line command flooded the
    terminal with a page of unrelated history entries. InlineView shows one
    greyed suggestion on the current line instead, the same shape as zsh's
    zsh-autosuggestions on the other two platforms.

  • Prompt renders on one line. starship.toml (shared by all three
    platforms) had a hardcoded leading newline in format, on top of
    add_newline = false, forcing a blank line above every prompt regardless
    of that setting. Removed, so the prompt is one dense line like a typical
    Mac/Linux shell.

Linux 1.32.0

Fixed

  • Temp files no longer survive an interrupted run. Every mktemp went
    through a bare command substitution and was cleaned up only on the happy
    path, so a Ctrl-C between the mktemp and the mv left the half-written
    file behind - two ~/.zshrc.bootstrap.XXXXXX files from interrupted runs
    were sitting in $HOME on this machine when this was found. Every temp path
    now goes through mktemp_tracked, and trap on EXIT, INT and TERM removes
    whatever is still there; INT and TERM re-exit with 130 and 143 rather than
    resuming where they were interrupted.

    mktemp_tracked assigns to a variable named by its first argument instead of
    printing the path: a command substitution would run the array append in a
    subshell, and the parent would forget the path - which is the whole point.

Added

  • ~/go/bin on PATH. go install and the VS Code Go extension put
    gopls, dlv and staticcheck there, and nothing was adding it - the
    tools installed fine and then were not on PATH. Unrelated to where go
    itself comes from; it was missing before this release too.

  • ~/.dotnet/tools on PATH, next to the existing DOTNET_ROOT line.
    dotnet tool install -g installs there. macOS gets this from the SDK
    installer's /etc/paths.d entry and Windows from the installer's user PATH;
    only Linux was missing it.

  • node, go and java come from mise on all three platforms, declared once in
    mise/tools.conf at the repo root. They used to come from three different
    places - brew on macOS, winget on Windows, and nothing at all on Linux,
    where grep -cE "nodejs|golang-go|openjdk" packages.conf returned 0 and a
    fresh machine simply had no Node until somebody ran mise use -g by hand.
    mise was installed everywhere and managing nothing: mise ls -g was empty.

    mise use -g merges into the user's global config rather than replacing it,
    so a tool added by hand survives a bootstrap run. Removing the packages from
    the manifests does not uninstall them - brew uninstall node go,
    brew uninstall --cask microsoft-openjdk@21 and the winget equivalents are
    yours to run when you want the disk back.

    Python is deliberately not in the list: OS packages link against the system
    python3, and a mise shim in front of it breaks them.

  • The mise shims directory on PATH in the zsh fragment. mise activate
    covers interactive shells, JAVA_HOME included, but never sees a
    non-interactive ssh host command, a cron job, or a Makefile an IDE runs.
    Activation still takes precedence where both apply.

  • Seven git defaults, set the same way the delta and difftastic keys
    already are - only where the key is unset, so an existing value stays:
    push.autoSetupRemote (push a new branch without the --set-upstream
    dance), fetch.prune (stop completing months of deleted origin/*),
    diff.algorithm=histogram (better on moved and reindented code, and it is
    what delta and difftastic then render), rebase.autoStash,
    column.ui=auto, merge.conflictStyle=zdiff3 (the common ancestor in a
    conflict, not just the two endings) and tag.sort=-version:refname
    (v1.10.0 above v1.9.0).

    The tag field is version:refname; a plain -version is rejected at use
    with fatal: unknown field name: version.

  • lazygit in RELEASES. A git TUI: stage hunks, rebase, stash and
    branch without leaving the terminal. A release binary, not an apt package -
    Debian's trails upstream. Its assets are linux_x86_64/linux_arm64, so the
    entry uses {GORELEASER_ARCH} rather than the {ARCH} that expands to
    amd64.

  • Catppuccin Mocha for bat, delta and fzf. The palette was already deployed
    for starship, ghostty and atuin, and stopped at the three tools you read
    output in: bat rendered in its own default theme, delta in bat's, and
    fzf in its own colours.

    • bat/config and bat/themes/Catppuccin Mocha.tmTheme at the repo root,
      deployed to bat's config directory by a new bat config phase - the
      same shared-file shape starship.toml and atuin/ already use. bat since
      0.24 reads the themes directory at startup, so the bat cache --build
      fallback is a no-op on anything current.
    • delta.syntax-theme=Catppuccin Mocha in the git config phase, set only
      when bat is installed: delta highlights through bat's theme store, so a
      diff and a bat of the same file now match.
    • FZF_DEFAULT_OPTS with the Catppuccin colours. fzf-tab shells out to fzf
      and inherits them.
  • MANPAGER through bat, so man pages get the same theme. col -bx
    strips the overstrike backspaces groff emits for bold and underline, which
    bat would otherwise render literally. Set for batcat as well as
    bat, the same way the cat alias above it already is - Debian's apt
    package installs the binary under that name.

macOS 1.34.0

Fixed

  • Temp files no longer survive an interrupted run. Every mktemp went
    through a bare command substitution and was cleaned up only on the happy
    path, so a Ctrl-C between the mktemp and the mv left the half-written
    file behind - two ~/.zshrc.bootstrap.XXXXXX files from interrupted runs
    were sitting in $HOME on this machine when this was found. Every temp path
    now goes through mktemp_tracked, and trap on EXIT, INT and TERM removes
    whatever is still there; INT and TERM re-exit with 130 and 143 rather than
    resuming where they were interrupted.

    mktemp_tracked assigns to a variable named by its first argument instead of
    printing the path: a command substitution would run the array append in a
    subshell, and the parent would forget the path - which is the whole point.

Added

  • macOS note on the mise JDK: it is not registered with
    /usr/libexec/java_home, so the JetBrains IDEs, Android Studio and anything
    else that auto-detects a JDK need the path once -
    ~/.local/share/mise/installs/java/temurin-21, a symlink mise maintains
    across patch bumps. Shells are unaffected: mise activate exports
    JAVA_HOME. Registering it properly would mean a sudo symlink into
    /Library/Java/JavaVirtualMachines, and this script does not use sudo.

  • ~/go/bin on PATH. go install and the VS Code Go extension put
    gopls, dlv and staticcheck there, and nothing was adding it - the
    tools installed fine and then were not on PATH. Unrelated to where go
    itself comes from; it was missing before this release too.

  • node, go and java come from mise on all three platforms, declared once in
    mise/tools.conf at the repo root. They used to come from three different
    places - brew on macOS, winget on Windows, and nothing at all on Linux,
    where grep -cE "nodejs|golang-go|openjdk" packages.conf returned 0 and a
    fresh machine simply had no Node until somebody ran mise use -g by hand.
    mise was installed everywhere and managing nothing: mise ls -g was empty.

    mise use -g merges into the user's global config rather than replacing it,
    so a tool added by hand survives a bootstrap run. Removing the packages from
    the manifests does not uninstall them - brew uninstall node go,
    brew uninstall --cask microsoft-openjdk@21 and the winget equivalents are
    yours to run when you want the disk back.

    Python is deliberately not in the list: OS packages link against the system
    python3, and a mise shim in front of it breaks them.

  • The mise shims directory on PATH in the zsh fragment. mise activate
    covers interactive shells, JAVA_HOME included, but never sees a
    non-interactive ssh host command, a cron job, or a Makefile an IDE runs.
    Activation still takes precedence where both apply.

  • Seven git defaults, set the same way the delta and difftastic keys
    already are - only where the key is unset, so an existing value stays:
    push.autoSetupRemote (push a new branch without the --set-upstream
    dance), fetch.prune (stop completing months of deleted origin/*),
    diff.algorithm=histogram (better on moved and reindented code, and it is
    what delta and difftastic then render), rebase.autoStash,
    column.ui=auto, merge.conflictStyle=zdiff3 (the common ancestor in a
    conflict, not just the two endings) and tag.sort=-version:refname
    (v1.10.0 above v1.9.0).

    The tag field is version:refname; a plain -version is rejected at use
    with fatal: unknown field name: version.

  • lazygit in the cli group. A git TUI: stage hunks, rebase, stash and
    branch without leaving the terminal. Same tool on all three platform...

Read more

bootstrap v2026.09.15

Choose a tag to compare

@github-actions github-actions released this 15 Sep 18:12

Windows 1.37.0

Removed

  • vim.vim from cli. 1.36.0 added it to close a documented parity GAP,
    but the winget package installs to %LOCALAPPDATA%\Programs\Vim and never
    puts itself on PATH, so vim was never actually runnable after a fresh
    bootstrap. Pulled back out until there's a phase to fix its PATH (or a
    package that does it itself). tools/cli-parity.conf carries the GAP note
    again.

Linux 1.30.0

Changed

  • The Manual - reported only phase is skipped when MANUAL is empty,
    instead of printing a header with nothing under it in every run. MANUAL
    has been empty here since before this entry, so the phase never had anything
    to say.

Added

  • sd, xh and doggo in RELEASES. A sed replacement that takes
    literal strings, an HTTP client that speaks JSON without a row of curl
    flags, and a dig that prints a readable table - on a fresh Debian or
    Ubuntu box doggo is the only DNS client here, since dnsutils is not in
    the apt list. All three are release binaries: none is in the archive. The
    zsh fragment aliases dig to doggo and adds http/https for xh
    (upstream ships xhs as a symlink inside the tarball, which
    install_release_bins does not copy); sd deliberately gets no sed
    alias, since its pattern syntax is not sed's and anything pasted from a
    script would quietly do something else.

macOS 1.32.0

Changed

  • The Manual - reported only phase is skipped when MANUAL is empty,
    instead of printing a header with nothing under it in every run.

  • MANUAL is empty. mas and colima were the only entries; neither is
    wanted on this machine, and both were reported missing on every run.

Added

  • jetbrains-toolbox in the dev group, and rider/datagrip in
    HELD.
    Toolbox installs and self-updates both IDEs, so a rider or
    datagrip cask would fight it - the same reasoning the Managed section of
    windows/packages.psd1 already carried for Toolbox, Rider and Android
    Studio. HELD was empty until now, which made the omission look like an
    oversight rather than a decision.

  • sd, xh and doggo in cli. A sed replacement that takes literal
    strings, an HTTP client that speaks JSON without a row of curl flags, and a
    dig that prints a readable table. The zsh fragment aliases dig to
    doggo and adds http/https for xh; sd deliberately gets no sed
    alias, since its pattern syntax is not sed's and anything pasted from a
    script would quietly do something else.

bootstrap v2026.09.11

Choose a tag to compare

@github-actions github-actions released this 13 Sep 13:30

Windows 1.32.0

Added

  • Bitwarden.CLI in the apps group, beside the desktop app. bw reads
    and writes the vault from a terminal or a script - bw get password <item>,
    or bw unlock once and a session token for the rest. Requested directly;
    1.31.0 had left it out until secrets were being copied into a terminal by
    hand.

Linux 1.27.0

Added

  • GROUP_<name>_UV - Python CLIs installed with uv tool install, each in
    its own environment, and kept current with uv tool upgrade. Entries are
    name or name|extra arguments. The phase runs after the release binaries,
    which bring uv, and honours --groups, --dry-run and --skip-upgrade.
  • GHOSTTY_DEB_REPO - where Ghostty's .deb comes from; see Changed.
  • The Bitwarden CLI (bw) as a release binary, for x86_64 and aarch64.
    In neither archive. Requested directly; 1.26.0 had left it out.
  • RELEASE_<name>_TAG_PREFIX - for a repository that releases several
    products under one "latest". bitwarden/clients tags web, desktop, browser
    and cli releases side by side, and its latest is whichever shipped last; with
    cli-v the newest CLI tag is taken instead. verify-manifests.yml does the
    same.
  • RELEASE_<name>_ASSET_<arch> - an asset name for one architecture, by
    dpkg --print-architecture, over RELEASE_<name>_ASSET. Bitwarden names its
    x86_64 zip without an architecture and its arm64 zip with one, which no
    placeholder spells for both.

Changed

  • atuin and trippy are release binaries, not apt packages. Ubuntu 24.04
    carries neither, so on a noble host (WSL included) both were reported
    missing and never installed. On Debian the archive lagged: trixie's atuin is
    18.6.1 against the self-hosted sync server's 18.22.0, and its trippy 0.12.2.
    Both now come from upstream's musl builds, for x86_64 and aarch64, into
    ~/.local/bin like starship and uv.

    A copy apt installed earlier is not removed, and ~/.local/bin is ahead of
    it on PATH in the zsh fragment; sudo apt remove atuin trippy tidies it.

  • The modern CLI bundle is release binaries too: bat, delta, eza, fd,
    ripgrep, zoxide, fzf, jq, yt-dlp, btop, procs, dust, duf, glow and lnav, plus
    gping and shellcheck. Ubuntu 24.04 has no procs, dust or glow, and trails
    upstream on the rest by one to three years - yt-dlp from April 2024, which
    YouTube has long since broken, and fzf 0.44, from before fzf --zsh. trixie
    is closer, never current. musl builds where upstream makes them for both
    architectures; eza and delta are gnu, needing glibc 2.18 and 2.34 on x86_64.
    yt-dlp is upstream's zipapp, one file for every architecture, run by
    python3. tools/cli-parity.conf marks them @releases.

    Tags such as jq-1.8.2 and gping-v1.21.0 resolve to their version, here
    and in verify-manifests.yml; ${tag#v} alone would have reinstalled both
    on every run.

  • gh comes from GitHub's own apt repository (REPO_githubcli): 2.100.0,
    where Ubuntu 24.04 has 2.45 and trixie 2.46. Its key is a binary keyring,
    which gpg --dearmor rejects, so setup_repo now converts only
    ASCII-armoured keys and installs binary ones as they are.

  • Node and Go leave the apt list for mise, as OpenTofu did on Windows:
    Ubuntu 24.04 ships Node 18, past end of life, and Go 1.22. Run once:
    mise use -g node@lts go@latest.

  • ansible, ansible-lint, pre-commit and yamllint are uv tool installs in
    the infra group, which keeps its name: Ubuntu 24.04 has ansible-lint 6.17
    against 26.8 upstream. ansible brings ansible-core's commands through
    --with-executables-from ansible-core.

  • Ghostty comes from the community .deb that ghostty.org's Debian and
    Ubuntu instructions point to, mkasberg/ghostty-ubuntu. It sat in the
    apps apt list, which neither archive carries before Ubuntu 26.04, so it
    never installed. The .deb is chosen as that project's install.sh chooses -
    Ubuntu by version, Debian by codename - and handed to apt, instead of piping
    the script into bash. Releases it does not build for, bookworm among them,
    are reported missing.

  • Nothing apt installed earlier is removed. ~/.local/bin is ahead of
    /usr/bin on a login's PATH, so the new copies win. To tidy up:

    sudo apt remove bat eza git-delta fd-find ripgrep zoxide fzf jq yt-dlp btop \
      procs du-dust duf glow lnav gping shellcheck nodejs npm golang-go \
      ansible ansible-lint pre-commit yamllint
    

Fixed

  • trip would have failed as a release binary. The alias was
    sudo trip, and sudo's secure_path leaves out ~/.local/bin. It now
    resolves the full path when the alias is defined.
  • The script puts ~/.local/bin on its own PATH. It never had, so a step
    that looked for a release binary by name - delta for the git config, atuin
    and carapace for theirs - missed one installed earlier in the same run, or
    under the timer, and skipped. The one-off RELEASE_BIN_DIR checks that
    worked around it are gone.
  • A leftover apt fd-find shadowed the real fd. The fragment aliased
    fd to fdfind whenever fdfind existed; it now does so only when there is
    no fd.

macOS 1.29.0

Added

  • A network group: trippy, gping and nmap. Per-hop loss and
    latency in one live view, a ping graph for one host or several, and what a
    host exposes. The zsh fragment aliases trip to trip -u: macOS is the one
    platform where trippy traces without root.

  • The flameshot cask in apps. Screenshots annotated before they are
    copied: arrows, boxes, numbered markers, blur. macOS asks for the Screen
    Recording permission on first capture.

  • kubecolor in cloud, uv in dev, carapace in shell.

    • kubectl is now an alias for kubecolor, which passes every argument
      through and only adds colour, and k one for kubectl.
    • carapace completes the CLIs zsh has nothing for. git is excluded
      (CARAPACE_EXCLUDES=git): zsh's own completion for it is better.
    • uv handles Python packages and venvs; mise still picks the Python version.
  • difftastic in cli, and git settings for it. A structural diff: it
    compares syntax, so a reformat is not a change. delta stays the pager for
    git diff, show, log and add -p; difftastic is asked for per command:

    • git difftool or git dft (diff.tool=difftastic, no prompt, paged)
    • git ddiff, git dshow <rev>, git dlog - difftastic in place of the
      patch

    Never diff.external globally: its output is not a patch, so
    git diff > x.patch and git apply would stop working. delta passes
    difftastic's output through byte for byte, so the pager needs no exception.
    Like delta's, each key is set only when unset.

  • git sdiff - the delta view side by side
    (-c core.pager='delta --side-by-side' diff), set with delta's keys.

  • Completion for the CLIs carapace has no completer for.

    • stern and yq: carapace specs in carapace/specs/, deployed to
      ~/Library/Application Support/carapace/specs - Go's config directory on
      macOS, which carapace uses unless $XDG_CONFIG_HOME is an absolute path.
      Both are Cobra apps, so each spec is one line handing Tab to the tool's
      own __complete.
    • mise: mise completion zsh in the fragment - small, and it asks mise.
    • uv: its zsh script is ~570 KB, so the fragment registers a stub that
      loads it on the first Tab after uv and replaces itself with the real
      _uv; a new shell pays nothing for it.
  • The onlyoffice cask in apps. An office suite built around .docx,
    .xlsx and .pptx, so files from Microsoft Office keep their layout more often
    than in LibreOffice. AGPL-3.0: free for personal and company use.

Changed

  • atuin/config.toml points sync at the self-hosted server
    (sync_address = "http://192.168.50.6:8087") instead of atuin's own
    api.atuin.sh default. That is roles/atuin on raspberrypi_master in
    mpostument/raspberry-setup, on the house LAN only - 8087 because the
    OpenTelemetry collector holds 8888 there.

    Sync is still off until you run atuin register (or atuin login with the
    key atuin key prints): this says where, not whether. Off that network a
    machine records locally and syncs when it is back.

bootstrap v2026.09.10

Choose a tag to compare

@github-actions github-actions released this 13 Sep 12:18

Windows 1.31.0

Added

  • Bitwarden.Bitwarden in the apps group. The desktop app: the vault
    outside the browser, and what lets the browser extension unlock with
    Windows Hello rather than the master password. Autofill itself comes from
    the extension, which winget cannot install - it comes from the Chrome Web
    Store.

    Bitwarden.CLI is deliberately not added: it only pays off once secrets
    are being copied into a terminal by hand, and it is one line away when
    that happens.

  • A network group: FujiApple.Trippy and orf.gping. Per-hop loss and
    latency in one live view, and a ping graph for one host or several. Tracing
    needs an elevated shell on Windows, so the profile runs trip through
    gsudo.

    nmap is deliberately absent: winget's Insecure.Nmap is stuck at 7.80, from
    2019.

  • Flameshot.Flameshot in the creative group, beside ScreenToGif.
    Screenshots annotated before they are copied: arrows, boxes, numbered
    markers, blur, or pinned on screen. Windows 11 still hands the Print Screen
    key to Snipping Tool until that is switched off in Settings > Accessibility

    Keyboard.

  • rsteube.Carapace in shell. Completion for 1000+ CLIs through one
    engine - flags and subcommands for kubectl, gh, az, helm and the rest, where
    PowerShell otherwise offers file names. git is excluded
    (CARAPACE_EXCLUDES=git) and stays with posh-git. Colour is off
    (CARAPACE_COLOR=0): PSFzf's Tab list prints completion labels verbatim,
    and carapace's colour codes showed up there as raw escape sequences.

    The trailing space carapace puts on each value is off
    (CARAPACE_NOSPACE=*): PSFzf quotes any completion containing whitespace and
    adds a space of its own, so picking get from kubectl <Tab> inserted
    kubectl "get " and kubectl answered unknown command "get ".

    PowerShell 7 only. Windows PowerShell 5.1 drops empty arguments to native
    programs, including the '' carapace's completer passes for the word under
    the cursor; carapace answers [], its script throws on that, and 5.1 fell
    back to file names after a wasted carapace run on every Tab.

  • astral-sh.uv in dev. Python packages and venvs; mise still picks the
    Python version.

  • k for kubectl in the profile. kubecolor, which the Linux and macOS
    fragments alias kubectl to, has no winget package.

  • Wilfred.difftastic in cli, and git settings for it. A structural
    diff: it compares syntax, so a reformat is not a change. delta stays the
    pager for git diff, show, log and add -p; difftastic is asked for
    per command:

    • git difftool or git dft (diff.tool=difftastic, no prompt, paged)
    • git ddiff, git dshow <rev>, git dlog - difftastic in place of the
      patch

    Never diff.external globally: its output is not a patch, so
    git diff > x.patch and git apply would stop working. delta passes
    difftastic's output through byte for byte, so the pager needs no exception.
    Like delta's, each key is set only when unset, and Git.DifftasticEnabled
    turns them off. tools lists it, from its new row in tools/cli-parity.conf.

  • git sdiff - the delta view side by side
    (-c core.pager='delta --side-by-side' diff), set with delta's keys.

  • Completion for the CLIs carapace has no completer for.

    • stern and yq: carapace specs in carapace/specs/, deployed to
      %APPDATA%\carapace\specs (or an absolute XDG_CONFIG_HOME) through
      Deploy-ManagedFile. Both are Cobra apps, so each spec is one line handing
      Tab to the tool's own __complete.
    • uv: its PowerShell script is ~750 KB and took ~200 ms to load, so the
      profile registers a stub that loads it on the first Tab after uv and
      answers through it from then on (8 ms a Tab afterwards). The real completer
      is captured by shadowing Register-ArgumentCompleter while the script runs,
      because a completer cannot re-run completion from inside itself.
    • mise: loaded the same way, with a fix. mise 2026.9's script cuts the
      command at the cursor with Extent.Text, which ends at the last word, so
      mise <Tab> completed the word mise again and offered file names. The
      profile pads the text out to the cursor first; mise <Tab> now lists its
      99 subcommands.
  • ONLYOFFICE.DesktopEditors in apps. An office suite built around
    .docx, .xlsx and .pptx, so files from Microsoft Office keep their layout more
    often than in LibreOffice. AGPL-3.0: free for personal and company use.

Changed

  • atuin/config.toml points sync at the self-hosted server
    (sync_address = "http://192.168.50.6:8087") instead of atuin's own
    api.atuin.sh default. That is roles/atuin on raspberrypi_master in
    mpostument/raspberry-setup, on the house LAN only - 8087 because the
    OpenTelemetry collector holds 8888 there.

    Sync is still off until you run atuin register (or atuin login with the
    key atuin key prints): this says where, not whether. Off that network a
    machine records locally and syncs when it is back.

Removed

  • OpenTofu.Tofu from the dev group - mise owns OpenTofu, as on Linux and
    macOS.
    Terraform projects pin a required version, and a winget tofu
    upgraded on every daily run drifts from it; mise switches version per
    directory. The other winget runtimes stay: Windows tooling looks for
    Python, Node and Java where their installers put them.

    Run once: mise use -g opentofu@latest. The bootstrap installs what the
    manifest lists and never uninstalls, so an existing copy stays until
    winget uninstall OpenTofu.Tofu.

Fixed

  • Tab never opened fzf. Set-PsFzfOption -TabExpansion only reaches git,
    through PowerShell's legacy TabExpansion hook; it does not bind the key.
    Tab stayed on MenuComplete, which prints a flat, unselectable list once the
    menu outgrows the window - kubectl <Tab>, 46 described subcommands, did.
    Tab is now bound to Invoke-FzfTabCompletion: an fzf list for every command,
    a single match inserted directly. Ctrl+Space is still MenuComplete.

  • git settings with double quotes in them lost the quotes under Windows
    PowerShell 5.1
    , which hands " inside an argument to a native program
    unescaped. mergetool.unityyamlmerge.cmd was stored as
    ... merge -p $BASE $REMOTE $LOCAL $MERGED, never matched what the script
    wanted, and was rewritten on every run. Quotes are now escaped for git's
    parser on 5.1 and 7.0-7.2; 7.3+ already passes them intact.

Linux 1.26.0

Changed

  • atuin/config.toml points sync at the self-hosted server
    (sync_address = "http://192.168.50.6:8087") instead of atuin's own
    api.atuin.sh default. That is roles/atuin on raspberrypi_master in
    mpostument/raspberry-setup, on the house LAN only - 8087 because the
    OpenTelemetry collector holds 8888 there.

    Sync is still off until you run atuin register (or atuin login with the
    key atuin key prints): this says where, not whether. Off that network a
    machine records locally and syncs when it is back.

Added

  • com.bitwarden.desktop from Flathub in the apps group. Not in the
    Debian archive, so Flathub it is, beside Obsidian, Bruno and DBeaver. The
    desktop app is the vault outside the browser; autofill itself comes from the
    browser extension. Like the rest of apps, it is skipped on a headless
    machine.

    The CLI is deliberately not added: it only pays off once secrets are being
    copied into a terminal by hand.

  • A network group: trippy, gping and nmap. Per-hop loss and
    latency in one live view, a ping graph for one host or several, and what a
    host exposes. trippy and gping arrive with trixie; on bookworm they are
    reported missing, as eza already is. The zsh fragment aliases trip to
    sudo trip: trippy needs raw sockets and has no unprivileged mode on Linux.

  • org.flameshot.Flameshot from Flathub in the creative group, beside
    OBS. Screenshots annotated before they are copied: arrows, boxes, numbered
    markers, blur. Flathub rather than apt because Debian's is 12.1.0 and
    Flathub's 14.0.0, and the Wayland fixes are in between. It does not take the
    Print key by itself: on GNOME, bind a custom shortcut to
    flatpak run org.flameshot.Flameshot gui.

  • kubecolor, uv and carapace as release binaries. Debian's kubecolor
    is 0.0.20, from before the kubecolor/kubecolor fork; uv and carapace are not
    in the archive at all.

    • kubectl is now an alias for kubecolor, which passes every argument
      through and only adds colour, and k one for kubectl.
    • carapace completes the CLIs zsh has nothing for. git is excluded
      (CARAPACE_EXCLUDES=git): zsh's own completion for it is better.
    • uv handles Python packages and venvs; mise still picks the Python version.
  • difftastic as a release binary, and git settings for it. A structural
    diff: it compares syntax, so a reformat is not a change. Not in the Debian
    archive; the gnu build, because upstream's musl build is x86_64 only. delta
    stays the pager for git diff, show, log and add -p; difftastic is
    asked for per command:

    • git difftool or git dft (diff.tool=difftastic, no prompt, paged)
    • git ddiff, git dshow <rev>, git dlog - difftastic in place of the
      patch

    Never diff.external globally: its output is not a patch, so
    git diff > x.patch and git apply would stop working. delta passes
    difftastic's output through byte for byte, so the pager needs no exception.
    Like delta's, each key is set only when unset.

  • git sdiff - the delta view side by side
    (-c core.pager='delta --side-by-side' diff), set with delta's keys.

  • org.onlyoffice.desktopeditors from Flathub in apps. An office suite
    built around .docx, .xlsx and .pptx, so files from Microsoft Office keep
    their layout more often than in LibreOffice. Not in the Debian archive.
    AGPL-3.0: free for...

Read more