Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ jobs:
- name: Build
run: bun build --compile --target=${{ matrix.bun_target }} cli/src/index.ts --outfile cvlt

- name: Test version
shell: bash
run: |
binary=./cvlt
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
binary=./cvlt.exe
fi
test "$("$binary" --version)" = "${{ needs.version.outputs.version }}"

- name: Re-sign darwin binary
if: startsWith(matrix.bun_target, 'bun-darwin')
run: |
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,15 @@ cvlt item delete DB_PASSWORD --vault github.com/acme/api

**Scope: personal by default, except `vlt://`.** General commands and `op://` references target your personal account unless you select an org with `--org <slug>` (or `CRCL_ORG`). A `vlt://github.com/<owner>/...` read automatically targets `<owner>` when it is an org accessible to the current Circles credential; otherwise it keeps the personal fallback. An explicit org must match the reference owner, and `run`/`inject` resolve mixed owners independently. CI via GitHub OIDC already has its org fixed by `OP_CONNECT_HOST`.

To exercise two accessible owners against a live development Vault without printing either value, provide distinct fixture references and run:

```bash
CVLT_E2E_PROFILE=dev-profile \
CVLT_E2E_REF_A=vlt://github.com/example-org-a/example-repo/MIXED_OWNER_PROBE \
CVLT_E2E_REF_B=vlt://github.com/example-org-b/example-repo/MIXED_OWNER_PROBE \
npm run test:e2e:mixed-owners
```

### Registering repos for CI access (operator-only)

`cvlt vault create <coordinate>` creates the op:// vault that stores the secrets; for a **repo** coordinate it also records the OIDC grant that lets that repo's CI read it (**creating it is the consent**). Grants are org-scoped, so pass `--org <owner>`. Once per repo:
Expand Down
2 changes: 2 additions & 0 deletions cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env bun

import { defineCommand, runMain } from "citty"
import pkg from "../package.json"
import {
api,
apiOptional,
Expand Down Expand Up @@ -1282,6 +1283,7 @@ export const main = defineCommand({
meta: {
name: "cvlt",
description: "1Password-style secrets CLI for Circles Vault",
version: (pkg as { version?: string }).version || "dev",
},
args: {
profile: { type: "string" as const, description: "Circles profile to use (default: shared current profile)" },
Expand Down
18 changes: 18 additions & 0 deletions cli/src/version.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, it } from "bun:test"
import { spawnSync } from "node:child_process"
import { join } from "node:path"
import pkg from "../package.json"

describe("CLI version", () => {
it("prints the package version without showing help", () => {
const result = spawnSync("bun", ["run", "cli/src/index.ts", "--version"], {
cwd: join(import.meta.dir, "../.."),
env: { ...process.env, CI: "1" },
encoding: "utf8",
})

expect(result.status).toBe(0)
expect(result.stderr).toBe("")
expect(result.stdout.trim()).toBe((pkg as { version?: string }).version || "dev")
})
})
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"build": "bun build --compile cli/src/index.ts --outfile cvlt",
"build:sdk": "npm run build --workspace=@circlesac/vault",
"dev": "bun run cli/src/index.ts",
"test": "bun test node/src/ cli/src/"
"test": "bun test node/src/ cli/src/",
"test:e2e:mixed-owners": "bash scripts/e2e-vlt-mixed-owners.sh"
},
"devDependencies": {
"@types/bun": "latest",
Expand Down
37 changes: 37 additions & 0 deletions scripts/e2e-vlt-mixed-owners.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
set -euo pipefail

: "${CVLT_E2E_REF_A:?Set CVLT_E2E_REF_A to the first vlt:// reference}"
: "${CVLT_E2E_REF_B:?Set CVLT_E2E_REF_B to the second vlt:// reference}"

cvlt_bin=${CVLT_BIN:-cvlt}
profile_args=()
if [[ -n ${CVLT_E2E_PROFILE:-} ]]; then
profile_args=(--profile "$CVLT_E2E_PROFILE")
fi

work_dir=$(mktemp -d)
trap 'rm -rf "$work_dir"' EXIT

"$cvlt_bin" "${profile_args[@]}" read "$CVLT_E2E_REF_A" --out-file "$work_dir/a"
"$cvlt_bin" "${profile_args[@]}" read "$CVLT_E2E_REF_B" --out-file "$work_dir/b"
if cmp -s "$work_dir/a" "$work_dir/b"; then
echo "Mixed-owner fixtures must contain distinct values" >&2
exit 1
fi

printf '{{%s}}\n---CVLT-MIXED-OWNER---\n{{%s}}' "$CVLT_E2E_REF_A" "$CVLT_E2E_REF_B" > "$work_dir/template"
{
cat "$work_dir/a"
printf '\n---CVLT-MIXED-OWNER---\n'
cat "$work_dir/b"
} > "$work_dir/expected"
"$cvlt_bin" "${profile_args[@]}" inject --in-file "$work_dir/template" --out-file "$work_dir/injected"
cmp "$work_dir/expected" "$work_dir/injected"

printf 'CVLT_E2E_A=%s\nCVLT_E2E_B=%s\n' "$CVLT_E2E_REF_A" "$CVLT_E2E_REF_B" > "$work_dir/env"
CVLT_E2E_A_FILE="$work_dir/a" CVLT_E2E_B_FILE="$work_dir/b" \
"$cvlt_bin" "${profile_args[@]}" run --env-file "$work_dir/env" -- sh -c \
'printf %s "$CVLT_E2E_A" | cmp - "$CVLT_E2E_A_FILE" && printf %s "$CVLT_E2E_B" | cmp - "$CVLT_E2E_B_FILE"'

echo "Mixed-owner read, inject, and run passed"
Loading