Skip to content

feat: Install the Flagsmith CLI and authenticate it via OIDC - #1

Merged
khvn26 merged 56 commits into
mainfrom
feat/setup-cli-action
Aug 20, 2026
Merged

feat: Install the Flagsmith CLI and authenticate it via OIDC#1
khvn26 merged 56 commits into
mainfrom
feat/setup-cli-action

Conversation

@khvn26

@khvn26 khvn26 commented Aug 18, 2026

Copy link
Copy Markdown
Member

In this PR, we implement the Flagsmith/setup-cli action.

Here's what it does:

  1. Grabs the installer script for the runner's platform, and requested (or latest) version.
  2. Resolves the actual CLI version via installer's --dry-run mode.
  3. Installs the CLI 🎉
  4. Puts the installed binary in GitHub Actions' tool cache.
  5. Detects whether the static credentials were set for the requested API URL.
  6. If no static credentials detected and the action is not run against a fork PR, performs the OIDC exchange against the requested API URL.
  7. Warns if something went wrong during the OIDC exchange.

E2E-tested in CI against staging.

khvn26 added 30 commits August 18, 2026 15:07
Adds the composite action from Flagsmith/actions#9: resolve a CLI version,
run the release's own install.sh (or install.ps1 on Windows), then exchange
the job's GitHub OIDC token at POST /api/v1/auth/oidc/token/.

The minted token is exported under the CLI's host-scoped credential name,
because the CLI trusts unscoped FLAGSMITH_ACCESS_TOKEN only for its default
host — a self-hosted api-url would otherwise silently go unauthenticated.

Jobs without id-token: write install the CLI and warn, so the action stays
usable with a Master API key from secrets.
Replaces the action's reimplementation of the release layout with a call to
install.sh/install.ps1, pinned to the version being installed and run with
--bin-dir and --no-modify-path. Platform detection, archive naming and
checksum verification move back to the repository that publishes the
releases, where the CLI's own CI exercises them on every platform; this
action keeps only the installer's flags, which fail loudly rather than
silently when they change. The binary is still cached by version and
architecture, and PATH is still ours to set.

Also, from reading depot/setup-action:

- Skip the exchange when the job already carries a credential the CLI would
  use for this api-url, following the CLI's own precedence, so a workflow
  with a Master API key neither pays for nor fails an exchange it never
  asked for.
- Name the fork pull request case explicitly. GitHub withholds an OIDC
  identity from those runs, so the generic advice to add id-token: write
  sent people to change something that could not help.
Everything they carried is already available from the CLI the action just
installed: `flagsmith --version`, `flagsmith auth status`, and `flagsmith
auth token` for scripts that need the raw credential. The access token in
particular is better left out of the outputs, where it would invite copying
a credential between steps and jobs for no gain.

`authenticated` and `expires-in` existed largely so the workflow had
something to assert; the tests now assert behaviour instead, which is a
stronger check: `flagsmith auth status` for the authenticated path, and the
absence of an exported token for the bring-your-own-credential path.
installerScript, binaryName and installerInvocation all branched on the same
platform check, so they are one platformInstaller now. ExchangedToken.tokenType
was set and never read.
install.sh already fails with its own message when neither is on PATH, so the
pre-flight check was 23 lines of nicer wording plus its own test suite.
They were one-liners exported only so their own tests could reach them.
isForkPullRequest took a readEvent parameter no caller ever passed; the tests
point GITHUB_EVENT_PATH at a tmp file now.
Trim comments to the ones that carry information the code does not.
The JSON-only reading suppressed exactly the bodies that carry a diagnosis
this action cannot produce itself. A proxy demanding authentication, a
captive portal or a load balancer with no backend answers in HTML, and
Flagsmith never sees the request at all; dropping that body left the user
with a hint about trust relationships and nothing about the proxy.

Collapsed to one line and truncated, so the annotation stays readable.
The parser already strips surrounding whitespace and lowercases the host, so
urlHost needs no bare-host fallback and only a single trailing slash is left to
trim.
An unpinned install now runs the installer from main, whose default version is
the latest release, instead of resolving the tag through the GitHub API. Only a
pinned version is cached, so the cache key can never be a moving target.
The dry run reports the version it would install without downloading anything,
so an unpinned install is cached under the same concrete tag a pinned one is.
index.ts exists to start run() and nothing else, so main.ts has no module-level
side effect and needs no test-environment guard.
fetchOk owns the status check, the user agent and the body snippet; callers
supply the first line of the failure.
tsconfig extends @tsconfig/node24 and now checks the test files too, which the
excluded config never did.
@khvn26
khvn26 requested a review from matthewelwell August 19, 2026 13:26
@khvn26
khvn26 force-pushed the feat/setup-cli-action branch from b3dd6e8 to 8adfee8 Compare August 19, 2026 14:17

@matthewelwell matthewelwell left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Responding to all remaining threads, but I'm still looking at install.ts to try and understand why my view is so far off from where we're at.

Comment thread README.md
Comment thread src/credential-name.ts Outdated
Comment thread src/credential-name.ts Outdated
Comment thread src/http.ts Outdated
Comment thread src/install.ts
@khvn26
khvn26 force-pushed the feat/setup-cli-action branch from 70ccd33 to 467d113 Compare August 19, 2026 15:19

@matthewelwell matthewelwell left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, added a bunch of suggestions in an attempt to make the code more readable for someone who's eyes don't read typescript well...

Comment thread src/install.ts Outdated
Comment thread src/install.ts Outdated
Comment thread src/install.ts Outdated
Comment thread src/install.ts Outdated
Comment thread src/install.ts Outdated
Comment thread src/install.ts Outdated
Comment thread src/install.ts Outdated
Comment thread src/install.ts Outdated
Comment thread src/install.ts Outdated
Comment thread src/credential-name.ts Outdated

@matthewelwell matthewelwell left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few final comments regarding the tests, but happy to approve without changes if we think that's the best way forwards.

Comment thread src/auth.test.ts Outdated
Comment thread src/auth.test.ts Outdated
Comment thread src/auth.test.ts Outdated
khvn26 added 2 commits August 20, 2026 12:09
Addresses review: the 401 integration test stays as the one wiring
check (real HTTP response body flowing into the error message), while
the hint-per-status mapping is now unit tested directly for 400, 401,
403, 404, 429 and the fallback.
400, 403, 429 and the unmapped-status fallback join the existing 401
and 404 cases, so the hint table is covered directly rather than only
through the mock-server integration path.
Comment thread .github/workflows/release.yml
The mock HTTP server only re-stated what we told it to say, and the
reviewer flagged the drift risk. The smoke job (from the test/smoke
branch) now runs in pull-request.yml: it installs the CLI, performs a
real OIDC exchange against staging, and lists flags. The pure unit
tests for parsing, hints and OIDC detection remain.
@khvn26
khvn26 force-pushed the feat/setup-cli-action branch from 00dd495 to eb2f097 Compare August 20, 2026 11:29
@khvn26
khvn26 requested a review from matthewelwell August 20, 2026 11:49
@khvn26
khvn26 merged commit 32ae142 into main Aug 20, 2026
4 checks passed
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.

2 participants