Repository navigation
Drop the release workflow; this fork ships no releases - #14
Closed
bcarlso-lt wants to merge 14 commits into
Closed
bcarlso-lt wants to merge 14 commits into
bcarlso-lt wants to merge 14 commits into
Conversation
The file backend previously encrypted tokens and client credentials with an empty passphrase, making the on-disk encryption a no-op. Now the passphrase comes from QBO_KEYRING_FILE_PASSWORD or an interactive prompt (memoized per process so multi-open commands prompt once), and headless invocations fail loudly when neither is available. --no-input suppresses the prompt. A set-but-empty env var is rejected explicitly, and a wrong passphrase surfaces as a config error naming the variable instead of 'not authenticated'. BREAKING: entries stored by older versions used the empty passphrase and can no longer be decrypted — re-run qbo auth login (and qbo auth set-client) once after upgrading. Co-authored-by: Claude Fable 5 <[email protected]>
The save path for qbo download without -o comes from the API response, which anyone with write access to the QBO company controls. Previously os.Create silently truncated whatever that name pointed at in the working directory. Now the destination is created with O_EXCL and an existing file (or planted symlink) is a usage error unless --force is passed; the existence check runs before any API calls are spent. API-supplied names are reduced to a safe basename (both separator styles) and hidden-file names like .envrc are refused outright. --force writes to a temp file and renames into place, so a mid-stream failure preserves the original and a symlinked name is replaced rather than written through. Close errors now fail the command instead of reporting a corrupt file as saved. Co-authored-by: Claude Fable 5 <[email protected]>
QBO string fields (customer names, memos, line descriptions) are often populated by external parties and were rendered verbatim in human and plain modes, letting embedded ANSI/OSC sequences rewrite or spoof terminal output — the same class git, gh, and kubectl have patched. Table cells and headers now drop C0/C1 controls and DEL, strip bidi overrides/isolates and zero-width characters, and flatten CR/LF/tab to spaces so hostile values can't forge rows or TSV columns. The stderr helpers (Hint/Success/Warn/ErrorMsg) sanitize too, covering API error bodies and API-supplied file names; message newlines are preserved. JSON mode is unchanged — encoding/json already escapes control chars. Co-authored-by: Claude Fable 5 <[email protected]>
Read /Library/Application Support/qbo/bootstrap.json (installed by device management) carrying vault URL, secret name, and Entra tenant/client ID so a first login can fetch Intuit client creds from Azure Key Vault without the binary carrying org-specific values. auth status reports the tier (bootstrap-pending/bootstrap/bootstrap-error) and now emits credential diagnostics even before a company exists; auth login names the vault on provisioned machines and fails loudly (exit 10) on a malformed bootstrap file. Keyring creds gain an origin marker for the upcoming vault self-heal. Co-Authored-By: Claude Fable 5 <[email protected]>
Check the os.Unsetenv return in token_test.go and replace a raw invisible U+009B control character in write_test.go with its Unicode escape sequence. Co-Authored-By: Claude Fable 5 <[email protected]>
Ignore local Dolt/beads state and add the bd init workflow block to AGENTS.md. The duplicate Codex-flavored block from bd setup codex was removed along with the .codex and .agents artifacts. Co-Authored-By: Claude Fable 5 <[email protected]>
On a provisioned machine with no configured client creds, qbo auth login now signs in to Entra ID (pure-Go MSAL, browser or --device-code, token cache in the qbo keyring) and fetches the Intuit client credentials from the org's Key Vault, storing them with origin=bootstrap before the Intuit OAuth flow. --no-input allows only the cached silent path (exit 4). The bearer token is only ever sent to https Azure Key Vault hosts; sign-in failures map onto the exit-code contract (6 for Conditional Access blocks and vault 403s, 4 for cancellations, 8 for network, 10 for misprovisioned secrets). set-client --clear also removes the cached Entra session. Closes qbo-cli-iyj.2. Co-Authored-By: Claude Fable 5 <[email protected]>
When a token refresh fails with invalid_client (typed RFC 6749 check, not string matching) and the credentials are vault-owned — bootstrap origin in the keyring with no env overrides — silently re-fetch them from Key Vault using the cached Entra session and retry once per invocation, preserving the stored redirect URI. User-supplied credentials are never auto-replaced; any other failure guides re-login (exit 4). The Entra browser wait now times out after 5 minutes like the Intuit flow, every MSAL network leg is bounded by a 30s HTTP client so headless self-heal can't hang, the silent path picks the cached account matching the configured tenant, and a stalled vault fetch is retryable (exit 8). Closes qbo-cli-iyj.3, qbo-cli-iyj.4. Co-Authored-By: Claude Fable 5 <[email protected]>
Intuit production apps reject http://localhost redirect URIs, so a hosted HTTPS page must receive the callback and forward it to the CLI. Route all redirect URIs through the local listener: a non-local redirect_uri is sent to Intuit (auth URL and token exchange) while the callback is still caught on localhost:8844. The --manual flag, previously declared but unused, now explicitly selects the paste-the-callback-URL flow. Skill docs updated to match. Also fix a latent race (callback handler signaled the result/error channels before writing the HTTP response, letting server.Close() kill the connection mid-response) and a listener leak on the GenerateState error path. Co-Authored-By: Claude Fable 5 <[email protected]>
Co-Authored-By: Claude Fable 5 <[email protected]>
Bash(qbo *) pre-authorized mutating commands (create/update/delete/batch) whenever the skill was active, bypassing the permission prompt. Reads stay frictionless; writes now require explicit user approval. Co-Authored-By: Claude Fable 5 <[email protected]>
Co-Authored-By: Claude Fable 5 <[email protected]>
Weekday-cron workflow that merges voska/qbo-cli upstream changes into a SHA-keyed sync branch, runs the quality gates (build/test/vet/lint), and opens a review-gated PR. Dirty merges are resolved by claude-code-action authenticated via workload identity federation (no repo secrets), running without ambient git credentials, then re-verified deterministically — including that fork history and LT customizations survived — before the PR opens. Co-Authored-By: Claude Fable 5 <[email protected]>
Distribution is a signed, notarized .pkg built by qbo-cli-deploy from a tag and delivered via Intune. A release workflow here is not just redundant, it is harmful: - `.goreleaser.yaml` is inherited from upstream and builds macOS with CGO_ENABLED=0. The Keychain backend (99designs/go-keychain) is cgo, so such a binary silently falls back to a passphrase-prompting file keyring — the exact bug qbo-cli-deploy@5de5105 fixed by building with CGO_ENABLED=1 on a macOS runner. Any Release published from here would offer a broken macOS download that is easier to find than the .pkg. - Its `brews:`/`scoops:` blocks target voska/homebrew-tap and voska/scoop-bucket using HOMEBREW_TAP_GITHUB_TOKEN, which this fork does not have. The workflow could never have completed; it has never run. Deleting only the workflow, not `.goreleaser.yaml`: the config stays byte-identical to upstream's body so it contributes no recurring merge conflicts, which matters because upstream-sync merges upstream/main on a schedule. That deletion is not self-sustaining, though. The resolve prompt's catch-all is "prefer upstream's version", so the first upstream edit to release.yml would surface a modify/delete conflict and get resolved by restoring the file. So the intent is now recorded where the resolver will read it, and asserted deterministically afterwards (`! test -f .github/workflows/release.yml`) rather than trusted. A header comment on `.goreleaser.yaml` says not to run it here — with no workflow calling it, a local `goreleaser release` would otherwise push fork-built artifacts at upstream's tap. The resolve rules call out keeping that comment when taking upstream's version of the body. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Author
|
Closing — opened against the wrong repository by mistake. This change is specific to the Lean Techniques fork ( |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Distribution is a signed, notarized
.pkgbuilt byqbo-cli-deployfrom a tag and delivered via Intune. A release workflow here isn't merely redundant — it's harmful.Why
It would publish broken macOS binaries.
.goreleaser.yamlis inherited from upstream and builds withCGO_ENABLED=0. The Keychain backend (99designs/go-keychain) is cgo, so such a binary silently falls back to a passphrase-prompting file keyring. That is the exact bugqbo-cli-deploy@5de5105fixed by buildingCGO_ENABLED=1on a macOS runner. A Release published from here would offer a broken download that's easier to find than the real.pkg.Upstream isn't wrong to use
CGO_ENABLED=0— cross-compiling darwin with cgo from an Ubuntu runner needs a toolchain goreleaser isn't set up for. That constraint is precisely why the.pkgpipeline exists, runs onmacos-latest, and useslipo.It could never have completed anyway. The
brews:/scoops:blocks targetvoska/homebrew-tapandvoska/scoop-bucketusingHOMEBREW_TAP_GITHUB_TOKEN— a secret this fork does not have (it has zero Actions secrets). Consistent with that,release.ymlhas never run, and this fork has no GitHub Releases despite carrying tagsv0.1.0–v0.8.0.Why only the workflow, not
.goreleaser.yamlupstream-syncmergesupstream/mainon a schedule. Deleting the config would create permanent divergence against a file upstream actively maintains, so every future upstream edit would conflict — a recurring tax for no extra safety. Keeping its body byte-identical to upstream costs nothing.Making the deletion durable
The deletion is not self-sustaining.
upstream-sync.yml's resolve prompt lists the LT customizations to preserve (internal/entra,internal/vault, the bootstrap tier, bouncer login) and then says "For everything else, prefer upstream's version." On the first upstream edit torelease.ymlthat becomes a modify/delete conflict, and the resolver would restore the file — after which the next fork tag would run goreleaser again, publishing Releases thatbump-versionassumes don't exist and failing on the missing secret.So this PR also:
Verify Claude's result—! test -f .github/workflows/release.yml— so it's checked, not trusted;.goreleaser.yamlwarning it must not be run here, since with no workflow calling it a localgoreleaser releasewould push fork-built artifacts at upstream's tap. The resolve rules explicitly say to keep that comment when taking upstream's version of the body.Verified
release.yml; no workflow (ci.yml,pages.yml,upstream-sync.yml) invokes goreleaser after this.qbo-cli-deployvia aDEPLOY_DISPATCH_TOKENPAT (fork beads task.17), but that was replaced by the polling model —bump-version's header records that the fork "stays org-agnostic… this repo polls… rather than the fork dispatching here."README.md/SECURITY.mdinstall instructions point at upstream's tap and Releases, which exist independently, so they remain truthful.Noted separately, not fixed here
skills/qbo/SKILL.md:13tells users tobrew install voska/tap/qbo. Upstream's newest tag is v0.6.1, while the marketplace plugin states "Requires qbo CLI v0.8.0+ (Key Vault credential bootstrap)". Anyone following the skill's own install instructions therefore gets aqbobelow the stated minimum and without the vault bootstrap. That file is what the marketplace plugin ships. Worth its own change.Companion PR: leantechniques/qbo-cli-deploy#4.
🤖 Generated with Claude Code