Skip to content

feat: add standalone CLI installation system - #247

Merged
calchiwo merged 6 commits into
mainfrom
feat/standalone-cli-installation
Oct 3, 2026
Merged

calchiwo merged 6 commits into
mainfrom
feat/standalone-cli-installation

Conversation

@calchiwo

@calchiwo calchiwo commented Oct 3, 2026

Copy link
Copy Markdown
Owner

This PR adds a complete standalone distribution path for the ExplainThisRepo CLI.

The goal is to make the native CLI installable on supported Unix-like systems without requiring the user to install Python, pip, Node.js, npm, or any project dependencies.

The resulting user experience is:

curl -fsSL https://cli.explainthisrepo.com/install.sh | sh

The installer detects the user's operating system and architecture, resolves the latest GitHub Release, downloads the matching installer archive, verifies its SHA256 checksum, extracts the native executable, and installs it to /usr/local/bin.

This builds the distribution layer on top of the native binaries that already exist.

What changed

1. Added install.sh`

A root-level install.sh is added as the standalone Unix installer.

Its responsibility is to own the installation decision-making:

Operating system
        ↓
Architecture
        ↓
Normalized target
        ↓
Latest release
        ↓
Installer archive
        ↓
SHA256 verification
        ↓
Extraction
        ↓
Executable permissions
        ↓
/usr/local/bin

The installer supports the Unix targets currently produced by the release pipeline:

linux-x64
linux-arm64
darwin-x64
darwin-arm64

It does not require Python, pip, Node.js, npm, or the ExplainThisRepo package ecosystem to already exist on the machine.

The only runtime tools required are normal Unix utilities used by the installer itself.

2. Added deterministic CLI installer archives

The release pipeline now produces a second class of release artifact specifically for automated installation.

The archive naming convention is:

explainthisrepo-v<version>-cli-installer-<target>.<format>

For example:

explainthisrepo-v0.29.0-cli-installer-linux-x64.tar.gz
explainthisrepo-v0.29.0-cli-installer-linux-arm64.tar.gz
explainthisrepo-v0.29.0-cli-installer-darwin-arm64.tar.gz
explainthisrepo-v0.29.0-cli-installer-darwin-x64.tar.gz
explainthisrepo-v0.29.0-cli-installer-win-x64.zip
explainthisrepo-v0.29.0-cli-installer-win-arm64.zip

The cli-installer portion is intentional.

The existing release binaries and the new archives serve different purposes.

The existing assets are human-facing direct binaries:

ExplainThisRepo-Linux-x64
ExplainThisRepo-Linux-ARM64
ExplainThisRepo-macOS-Apple-Silicon-arm64
ExplainThisRepo-macOS-Intel-x64
ExplainThisRepo-Windows-ARM64.exe
ExplainThisRepo-Windows-x64.exe

The new assets are machine-facing distribution packages:

explainthisrepo-v0.29.0-cli-installer-linux-x64.tar.gz

The existing human-readable asset names are therefore preserved.

They are not being replaced.

3. Archive contents are intentionally minimal

Each installer archive contains only the executable required for that target.

Unix example:

explainthisrepo

Windows example:

explainthisrepo.exe

There are no nested directories, README files, version files, or unrelated release metadata inside the archive.

This gives the installer a deterministic extraction contract:

download
    ↓
verify
    ↓
extract
    ↓
find executable at known location
    ↓
install

The installer does not need to understand the internal structure of a package.

4. Added installer archive checksums

Each installer archive receives its own SHA256 checksum.

For example:

explainthisrepo-v0.29.0-cli-installer-linux-x64.tar.gz
explainthisrepo-v0.29.0-cli-installer-linux-x64.tar.gz.sha256

The checksum is calculated over the archive itself because the installer downloads the archive.

The verification flow is therefore:

download archive
        ↓
download archive checksum
        ↓
verify archive
        ↓
extract verified archive
        ↓
install executable

This makes the integrity check apply to the exact artifact being transferred and installed.

The existing raw binary checksum generation remains part of the release pipeline for the existing release assets and package distribution.

5. Existing native build system remains the source of binaries

This change does not replace or redesign the existing PyInstaller build system.

The existing build matrix remains responsible for producing:

darwin-arm64
darwin-x64
linux-arm64
linux-x64
win-arm64
win-x64

The architecture is now:

scripts/build_pyinstaller.py
            ↓
      native binary
            ↓
   GitHub Actions build
            ↓
      release staging
            ↓
      ┌─────┴─────┐
      ↓           ↓
human-facing   installer
binary         archive
      ↓           ↓
      GitHub Release

This keeps compilation and distribution as separate concerns.

6. Existing npm and .NET distribution paths are preserved

The native binaries are still rehydrated into the existing locations:

node_version/dist/native/
dotnet_version/native/

The npm package continues to receive the native binaries.

The .NET Global Tool continues to receive the native binaries.

The standalone installer is therefore an additional distribution path rather than a replacement for the existing package ecosystems.

The overall distribution model becomes:

                         ExplainThisRepo
                              CLI
                               │
              ┌────────────────┼────────────────┐
              │                │                │
              ▼                ▼                ▼
             npm            NuGet        Standalone installer
              │                │                │
              │                │                ▼
              │                │           GitHub Release
              │                │                │
              │                │                ▼
              │                │            install.sh
              │                │                │
              │                │                ▼
              │                │          /usr/local/bin
              │                │                │
              └────────────────┴────────────────┘
                               │
                               ▼
                       explainthisrepo

7. GitHub Releases become the artifact source of truth

A version tag continues to drive the release pipeline:

git tag v0.29.0
        ↓
GitHub Actions
        ↓
build six native targets
        ↓
rehydrate native package directories
        ↓
create human-readable release binaries
        ↓
create CLI installer archives
        ↓
create checksums
        ↓
verify artifacts
        ↓
publish npm
        ↓
publish NuGet
        ↓
publish GitHub Release

The GitHub Release therefore contains both artifact classes.

Human-facing release assets

ExplainThisRepo-Linux-x64
ExplainThisRepo-Linux-ARM64
ExplainThisRepo-macOS-Apple-Silicon-arm64
ExplainThisRepo-macOS-Intel-x64
ExplainThisRepo-Windows-ARM64.exe
ExplainThisRepo-Windows-x64.exe

Installer distribution assets

explainthisrepo-v<version>-cli-installer-linux-x64.tar.gz
explainthisrepo-v<version>-cli-installer-linux-arm64.tar.gz
explainthisrepo-v<version>-cli-installer-darwin-arm64.tar.gz
explainthisrepo-v<version>-cli-installer-darwin-x64.tar.gz
explainthisrepo-v<version>-cli-installer-win-x64.zip
explainthisrepo-v<version>-cli-installer-win-arm64.zip

And their corresponding checksums.

The two artifact classes are intentionally kept separate by naming.

8. cli.explainthisrepo.com becomes the stable installation interface

The user does not need to know the GitHub repository, release tag, architecture names, or archive names.

The public installation interface is:

curl -fsSL https://cli.explainthisrepo.com/install.sh | sh

The domain provides a stable entry point while the actual release artifacts can continue to be versioned through GitHub Releases.

The installer itself determines which release artifact is appropriate.

The user does not need to manually select:

linux-x64
linux-arm64
darwin-x64
darwin-arm64

That decision belongs to the installer.

9. Why the architecture is split this way

There are three distinct naming layers in the system.

CI target identifiers

These are machine-oriented:

darwin-arm64
darwin-x64
linux-arm64
linux-x64
win-arm64
win-x64

They are used by the build matrix and installer routing.

Human-readable release binaries

These are intended for people browsing a GitHub Release:

ExplainThisRepo-Linux-x64
ExplainThisRepo-Linux-ARM64

CLI installer archives

These are intended for deterministic automated installation:

explainthisrepo-v0.29.0-cli-installer-linux-x64.tar.gz

Each naming layer therefore has a different responsibility.

This avoids forcing either the human-facing release interface or the installer to use names optimized for the other.

10. Failure handling

The installer is designed to fail rather than continue with an invalid state.

Important failure points include:

  • unsupported OS
  • unsupported architecture
  • failed release lookup
  • missing release version
  • failed archive download
  • failed checksum download
  • checksum mismatch
  • failed extraction
  • missing executable
  • failed installation

A failed installation should not result in a partially trusted executable being silently installed.

The release workflow also verifies generated native binaries before publishing the release.

11. Documentation

Two documentation layers are added.

docs/INSTALLATION.md

This documents the user-facing installation system:

  • one-command installation
  • supported platforms
  • what the installer does
  • manual installation
  • troubleshooting
  • installation paths
  • release artifact behavior

README.md

The README gets the short user-facing installation experience so a new user can immediately discover:

curl -fsSL https://cli.explainthisrepo.com/install.sh | sh

The README does not need to expose the internal release pipeline.

The detailed distribution architecture belongs in the installation and release documentation.

Result

Before this change, the project could build and publish native binaries.

After this change, those binaries are connected to a complete installation path:

source
  ↓
PyInstaller
  ↓
six native binaries
  ↓
GitHub Actions
  ↓
human-readable release binaries
+
CLI installer archives
+
SHA256 checksums
  ↓
GitHub Release
  ↓
cli.explainthisrepo.com/install.sh
  ↓
OS detection
  ↓
architecture detection
  ↓
target selection
  ↓
latest release resolution
  ↓
deterministic archive selection
  ↓
checksum verification
  ↓
extraction
  ↓
/usr/local/bin/explainthisrepo
  ↓
explainthisrepo

The important change is that the native executable is no longer the end of the distribution pipeline.

It becomes the payload of a defined installation system.

@vercel

vercel Bot commented Oct 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
explainthisrepo Ready Ready Preview Oct 3, 2026 12:57pm UTC

@calchiwo
calchiwo merged commit 04e551c into main Oct 3, 2026
7 checks passed

This branch was successfully deployed

2 active deployments
Preview – explainthisrepo — a1758bc9 Deployed Oct 3, 2026 by vercel[bot]
Preview – explain-this-repo — a1758bc9 Deployed Oct 3, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant