Merge branch 'fix/init-second-machine-guard' (v0.2.1.0)
Second-machine init guard, store-protection self-heal (gitignore + hook + key untrack, post-pull ordering), and the rekey dotfile data-loss fix.
This commit is contained in:
commit
6ab22c2b95
6 changed files with 306 additions and 37 deletions
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to a four-digit MAJOR.MINOR.PATCH.MICRO version scheme.
|
||||
|
||||
## [0.2.1.0] - 2026-06-05
|
||||
|
||||
### Fixed
|
||||
|
||||
- **`secrets rekey` no longer bricks dotenv stores.** The re-encrypt loop used a bare `"$dir"*` glob, which never matches dotfiles — so `.env` blobs were decrypted to the temp dir but never re-encrypted, leaving them on the **old** key while the new key overwrote `key.txt`. After a rotation, every dotenv file in the store was undecryptable. The glob now mirrors the decrypt loop (`"$dir"* "$dir".*`), and a round-trip test (push → rekey → pull) pins it. If you ran `rekey` on an earlier version and `pull` now fails with `no identity matched any of the recipients`, your blobs are on a pre-rotation key — recover with an old `key.txt` from another machine.
|
||||
- **`secrets init` on a second machine now fails helpfully instead of half-initializing.** Copying `key.txt` into `~/.secrets` and then running `init` (instead of cloning your secrets repo) used to run `git init`, crash on the existing key, and leave a store with no `.gitignore` — a state where a later `push` would commit the private key. The guard now fires *before* `git init`, leaves the key untouched, and prints the exact `git clone` command to run — using the real remote URL when your `.secrets-store` file declares one.
|
||||
|
||||
### Security
|
||||
|
||||
- **The private key can no longer be committed by a store missing its `.gitignore`.** `push`, `pull`, and `rekey` now self-heal store protections immediately before any `git add -A`: a missing *or corrupted* `.gitignore` (one without the `key.txt` line) is rewritten, and the pre-commit hook is reinstalled if absent. The heal runs *after* the fast-forward pull, closing a window where remote history without a `.gitignore` could strip protection mid-push.
|
||||
- **An already-tracked `key.txt` is now untracked automatically.** `.gitignore` can't untrack a file that was committed in the past; the heal now removes a tracked key from the index with a warning that history may need scrubbing and the key may warrant rotation.
|
||||
|
||||
### Changed
|
||||
|
||||
- Project `CLAUDE.md` gained agent skill-routing guidance and an updated test-suite count (126 bats tests, up from 113).
|
||||
|
||||
## [0.2.0.0] - 2026-05-26
|
||||
|
||||
### Added
|
||||
|
|
|
|||
21
CLAUDE.md
21
CLAUDE.md
|
|
@ -37,7 +37,7 @@ Single bash script (`secrets`) with subcommands: init, push, pull, list, rm, rek
|
|||
secrets # CLI script (~600 lines bash)
|
||||
hooks/pre-commit # Pre-commit hook template
|
||||
test/
|
||||
secrets.bats # bats-core test suite (113 tests)
|
||||
secrets.bats # bats-core test suite (126 tests)
|
||||
test_helper.bash # Shared setup/teardown
|
||||
README.md # User-facing documentation
|
||||
CLAUDE.md # This file
|
||||
|
|
@ -85,3 +85,22 @@ Key design decisions (all driven by /autoplan review):
|
|||
## Environment variable
|
||||
|
||||
`SECRETS_DIR` overrides the default `~/.secrets` location (useful for testing). Per-project bindings via `.secrets-store` file beat this env var; use `--store <dir>` for one-shot overrides that beat everything.
|
||||
|
||||
## Skill routing
|
||||
|
||||
When the user's request matches an available skill, invoke it via the Skill tool. When in doubt, invoke the skill.
|
||||
|
||||
Key routing rules:
|
||||
- Product ideas/brainstorming → invoke /office-hours
|
||||
- Strategy/scope → invoke /plan-ceo-review
|
||||
- Architecture → invoke /plan-eng-review
|
||||
- Design system/plan review → invoke /design-consultation or /plan-design-review
|
||||
- Full review pipeline → invoke /autoplan
|
||||
- Bugs/errors → invoke /investigate
|
||||
- QA/testing site behavior → invoke /qa or /qa-only
|
||||
- Code review/diff check → invoke /review
|
||||
- Visual polish → invoke /design-review
|
||||
- Ship/deploy/PR → invoke /ship or /land-and-deploy
|
||||
- Save progress → invoke /context-save
|
||||
- Resume context → invoke /context-restore
|
||||
- Author a backlog-ready spec/issue → invoke /spec
|
||||
|
|
|
|||
|
|
@ -404,13 +404,15 @@ If you suspect your key has been compromised, or a teammate leaves the team:
|
|||
secrets rekey
|
||||
```
|
||||
|
||||
This generates a new key and re-encrypts all secrets. After rekeying:
|
||||
This generates a new key and re-encrypts all secrets (including `.env` and other dotfiles). After rekeying:
|
||||
|
||||
1. Copy the new `~/.secrets/key.txt` to every machine and teammate
|
||||
2. Old encrypted files remain in git history (encrypted with the old key, which should be discarded)
|
||||
|
||||
For complete rotation with no historical exposure, create a fresh `~/.secrets/` repo.
|
||||
|
||||
> **Recovering from a broken rekey (pre-0.2.1.0):** Older versions of `rekey` skipped dotfiles (`.env`, `.dev.vars`) when re-encrypting, leaving their blobs on the *old* key while `key.txt` was replaced. If `secrets pull` now fails with `no identity matched any of the recipients`, those blobs are still encrypted to a key you no longer have. Restore the **old** `key.txt` from another machine that hasn't rekeyed, `secrets pull` to recover the plaintext, then `secrets rekey` again on 0.2.1.0 or later.
|
||||
|
||||
## Environment variables
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
|
|
@ -423,6 +425,8 @@ For complete rotation with no historical exposure, create a fresh `~/.secrets/`
|
|||
|
||||
**"Not initialized"** — Run `secrets init` to create the `~/.secrets/` directory.
|
||||
|
||||
**"Found an existing key ... but no repo"** — You copied `key.txt` into `~/.secrets` and then ran `secrets init`. On a second machine you should *clone* your existing secrets repo, not re-initialize it (`init` is only for the very first machine). The error prints the exact `git clone` command to run — copy-paste it, or see [Additional machines](#additional-machines). When your project's `.secrets-store` file declares a remote URL, the command is filled in with the real URL.
|
||||
|
||||
**"No secret files found"** — You're in a directory that doesn't have `.env`, `.env.*`, or `.dev.vars` files. Make sure you're in the right project directory.
|
||||
|
||||
**"Project not found"** — The project name doesn't match anything in `~/.secrets/`. Run `secrets list` to see what's stored. The name is usually derived from your directory name or git remote.
|
||||
|
|
@ -432,7 +436,7 @@ For complete rotation with no historical exposure, create a fresh `~/.secrets/`
|
|||
## Development
|
||||
|
||||
```bash
|
||||
# Run the test suite (113 tests)
|
||||
# Run the test suite (126 tests)
|
||||
brew install bats-core
|
||||
bats test/secrets.bats
|
||||
```
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.2.0.0
|
||||
0.2.1.0
|
||||
|
|
|
|||
115
secrets
115
secrets
|
|
@ -701,26 +701,9 @@ HOOKEOF
|
|||
fi
|
||||
}
|
||||
|
||||
# ─── Subcommands ───────────────────────────────────────────────────────
|
||||
|
||||
cmd_init() {
|
||||
check_cmd age
|
||||
check_cmd git
|
||||
resolve_store
|
||||
|
||||
if [ -d "$SECRETS_DIR/.git" ]; then
|
||||
die "Already initialized at $SECRETS_DIR. Key file preserved."
|
||||
fi
|
||||
|
||||
info "Initializing secrets repo at $SECRETS_DIR"
|
||||
mkdir -p "$SECRETS_DIR"
|
||||
git init "$SECRETS_DIR" >/dev/null
|
||||
|
||||
# Generate age key pair
|
||||
info "Generating age key pair"
|
||||
age-keygen -o "$KEY_FILE" 2>&1
|
||||
|
||||
# Write .gitignore
|
||||
# Write the store-level .gitignore. Critical: the `key.txt` line is what
|
||||
# keeps the private key out of `git add -A` during push/rekey.
|
||||
write_store_gitignore() {
|
||||
cat > "$SECRETS_DIR/.gitignore" << 'EOF'
|
||||
# Never commit the private key
|
||||
key.txt
|
||||
|
|
@ -735,6 +718,70 @@ key.txt
|
|||
!**/.env.*.age
|
||||
!**/.dev.vars.age
|
||||
EOF
|
||||
}
|
||||
|
||||
# Restore store-level protections if missing. A cloned store has no
|
||||
# pre-commit hook (hooks aren't cloned), and a half-initialized store may
|
||||
# lack .gitignore — without it, `git add -A` would commit key.txt.
|
||||
ensure_store_protections() {
|
||||
# Content-aware: a present-but-corrupted .gitignore missing the key.txt
|
||||
# line is just as dangerous as a missing one.
|
||||
if [ ! -f "$SECRETS_DIR/.gitignore" ] || ! grep -qx 'key.txt' "$SECRETS_DIR/.gitignore"; then
|
||||
write_store_gitignore
|
||||
info "Restored store .gitignore"
|
||||
fi
|
||||
# .gitignore can't untrack an already-tracked key (legacy damage, or a
|
||||
# past window where .gitignore was missing). Remove it from the index so
|
||||
# the next commit drops it from the tip.
|
||||
if git -C "$SECRETS_DIR" ls-files --error-unmatch key.txt >/dev/null 2>&1; then
|
||||
git -C "$SECRETS_DIR" rm --cached --quiet key.txt
|
||||
echo "WARNING: key.txt was tracked in the store repo — untracked it now." >&2
|
||||
echo "It may still exist in git history; consider 'secrets rekey' and scrubbing history." >&2
|
||||
fi
|
||||
if [ ! -x "$SECRETS_DIR/.git/hooks/pre-commit" ]; then
|
||||
mkdir -p "$SECRETS_DIR/.git/hooks"
|
||||
install_hook
|
||||
info "Reinstalled pre-commit hook"
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Subcommands ───────────────────────────────────────────────────────
|
||||
|
||||
cmd_init() {
|
||||
check_cmd age
|
||||
check_cmd git
|
||||
resolve_store
|
||||
|
||||
if [ -d "$SECRETS_DIR/.git" ]; then
|
||||
die "Already initialized at $SECRETS_DIR. Key file preserved."
|
||||
fi
|
||||
|
||||
# Second-machine trap: a copied key.txt without a repo means the user
|
||||
# should clone their existing secrets repo, not init a fresh one.
|
||||
# Catch it BEFORE git init so we don't leave a half-initialized store.
|
||||
if [ -f "$KEY_FILE" ]; then
|
||||
# Render a runnable clone command when .secrets-store carried a remote
|
||||
# URL (already sanitized by resolve_store), mirroring check_initialized.
|
||||
local clone_src="<your-secrets-remote>"
|
||||
[ -n "${_REMOTE_URL:-}" ] && clone_src="$_REMOTE_URL"
|
||||
die "Found an existing key at $KEY_FILE but no repo at $SECRETS_DIR.
|
||||
If this is a second machine, don't run 'secrets init' — clone your existing secrets repo instead:
|
||||
|
||||
git clone $clone_src $SECRETS_DIR
|
||||
|
||||
Your key file has been left untouched."
|
||||
fi
|
||||
|
||||
info "Initializing secrets repo at $SECRETS_DIR"
|
||||
mkdir -p "$SECRETS_DIR"
|
||||
git init "$SECRETS_DIR" >/dev/null
|
||||
|
||||
# Generate age key pair
|
||||
info "Generating age key pair"
|
||||
age-keygen -o "$KEY_FILE" 2>&1
|
||||
|
||||
# Write .gitignore
|
||||
write_store_gitignore
|
||||
|
||||
# Install pre-commit hook
|
||||
mkdir -p "$SECRETS_DIR/.git/hooks"
|
||||
|
|
@ -787,6 +834,11 @@ commit_and_push_secrets() {
|
|||
fi
|
||||
fi
|
||||
|
||||
# Must run AFTER the pull and immediately before `git add -A`: the pull
|
||||
# can remove or alter .gitignore (remote history that lacks it), and a
|
||||
# store missing the key.txt line would stage and push the private key.
|
||||
ensure_store_protections
|
||||
|
||||
git -C "$SECRETS_DIR" add -A
|
||||
if git -C "$SECRETS_DIR" diff --cached --quiet 2>/dev/null; then
|
||||
info "No changes to push (secrets unchanged)"
|
||||
|
|
@ -917,11 +969,8 @@ cmd_pull() {
|
|||
# Merge any external files (.secrets-files) declared in this project.
|
||||
pull_external_files "$PWD" "$project"
|
||||
|
||||
# Reinstall hook if missing
|
||||
if [ ! -x "$SECRETS_DIR/.git/hooks/pre-commit" ]; then
|
||||
install_hook
|
||||
info "Reinstalled pre-commit hook"
|
||||
fi
|
||||
# Reinstall hook / store .gitignore if missing
|
||||
ensure_store_protections
|
||||
}
|
||||
|
||||
# Pull and decrypt .age files from a project path into a target directory.
|
||||
|
|
@ -1004,11 +1053,8 @@ cmd_pull_workspaces() {
|
|||
|
||||
info "Decrypted $total file(s) total"
|
||||
|
||||
# Reinstall hook if missing
|
||||
if [ ! -x "$SECRETS_DIR/.git/hooks/pre-commit" ]; then
|
||||
install_hook
|
||||
info "Reinstalled pre-commit hook"
|
||||
fi
|
||||
# Reinstall hook / store .gitignore if missing
|
||||
ensure_store_protections
|
||||
}
|
||||
|
||||
cmd_list() {
|
||||
|
|
@ -1139,13 +1185,15 @@ cmd_rekey() {
|
|||
|
||||
info "Re-encrypting all files with new key..."
|
||||
|
||||
# Re-encrypt all files
|
||||
# Re-encrypt all files. The ".*" glob is required: dotenv files decrypt
|
||||
# to dotfiles ("$tmpdir/p/.env") that a bare "*" would silently skip,
|
||||
# leaving their blobs on the old key (undecryptable after rotation).
|
||||
for dir in "$tmpdir"/*/; do
|
||||
[ -d "$dir" ] || continue
|
||||
local project
|
||||
project=$(basename "$dir")
|
||||
mkdir -p "$SECRETS_DIR/$project"
|
||||
for f in "$dir"*; do
|
||||
for f in "$dir"* "$dir".*; do
|
||||
[ -f "$f" ] || continue
|
||||
local name
|
||||
name=$(basename "$f")
|
||||
|
|
@ -1162,7 +1210,8 @@ cmd_rekey() {
|
|||
fi
|
||||
done
|
||||
|
||||
# Commit and push
|
||||
# Commit and push (heal .gitignore first so add -A can't stage key.txt)
|
||||
ensure_store_protections
|
||||
git -C "$SECRETS_DIR" add -A
|
||||
git -C "$SECRETS_DIR" commit -m "rekey all secrets" >/dev/null
|
||||
if git -C "$SECRETS_DIR" remote get-url origin >/dev/null 2>&1; then
|
||||
|
|
|
|||
|
|
@ -1476,3 +1476,184 @@ gradle_project() {
|
|||
# definition must, and the '!' comment must be ignored.
|
||||
grep -q '^beaconClerkPkTest=realkey$' "$HOME/.gradle/gradle.properties"
|
||||
}
|
||||
|
||||
# ─── init second-machine guard + store .gitignore self-heal ────────────
|
||||
|
||||
@test "init with existing key but no repo dies with clone guidance" {
|
||||
# Second-machine trap: user copies key.txt into ~/.secrets, then runs
|
||||
# `secrets init` instead of cloning their secrets repo.
|
||||
mkdir -p "$SECRETS_DIR"
|
||||
age-keygen -o "$SECRETS_DIR/key.txt" 2>/dev/null
|
||||
# Guard against a vacuous '' = '' comparison if age-keygen failed
|
||||
[ -s "$SECRETS_DIR/key.txt" ]
|
||||
local key_before
|
||||
key_before=$(cat "$SECRETS_DIR/key.txt")
|
||||
|
||||
run "$SECRETS_BIN" init
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"git clone"* ]]
|
||||
# Must not leave a half-initialized store behind
|
||||
[ ! -d "$SECRETS_DIR/.git" ]
|
||||
# Key untouched
|
||||
[ "$(cat "$SECRETS_DIR/key.txt")" = "$key_before" ]
|
||||
}
|
||||
|
||||
@test "push restores missing store .gitignore and never commits key.txt" {
|
||||
init_with_remote
|
||||
rm "$SECRETS_DIR/.gitignore"
|
||||
create_project_dir
|
||||
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Restored store .gitignore"* ]]
|
||||
[ -f "$SECRETS_DIR/.gitignore" ]
|
||||
grep -q "key.txt" "$SECRETS_DIR/.gitignore"
|
||||
# key.txt must never be tracked (push does `git add -A` in the store)
|
||||
run git -C "$SECRETS_DIR" ls-files
|
||||
[[ "$output" != *"key.txt"* ]]
|
||||
}
|
||||
|
||||
@test "pull restores missing store .gitignore" {
|
||||
init_with_remote
|
||||
create_project_dir
|
||||
"$SECRETS_BIN" push >/dev/null 2>&1
|
||||
rm "$SECRETS_DIR/.gitignore"
|
||||
rm .env .env.staging
|
||||
|
||||
run "$SECRETS_BIN" pull
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$SECRETS_DIR/.gitignore" ]
|
||||
}
|
||||
|
||||
@test "rekey restores missing store .gitignore and never commits key.txt" {
|
||||
init_with_remote
|
||||
create_project_dir
|
||||
"$SECRETS_BIN" push >/dev/null 2>&1
|
||||
rm "$SECRETS_DIR/.gitignore"
|
||||
|
||||
run "$SECRETS_BIN" rekey
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$SECRETS_DIR/.gitignore" ]
|
||||
run git -C "$SECRETS_DIR" ls-files
|
||||
[[ "$output" != *"key.txt"* ]]
|
||||
}
|
||||
|
||||
@test "rekey re-encrypts dotenv blobs (round-trip survives key rotation)" {
|
||||
# The decrypt loop matches dotfiles (".*.age") but a re-encrypt glob of
|
||||
# "$dir"* would silently skip them — leaving .env.age on the OLD key
|
||||
# after rotation, i.e. undecryptable. Guard the full round-trip.
|
||||
init_with_remote
|
||||
create_project_dir
|
||||
"$SECRETS_BIN" push >/dev/null 2>&1
|
||||
|
||||
run "$SECRETS_BIN" rekey
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
rm .env .env.staging
|
||||
run "$SECRETS_BIN" pull
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(cat .env)" = "SECRET_KEY=abc123" ]
|
||||
[ "$(cat .env.staging)" = "DB_HOST=staging.db.example.com" ]
|
||||
}
|
||||
|
||||
@test "push reinstalls missing pre-commit hook" {
|
||||
init_with_remote
|
||||
rm "$SECRETS_DIR/.git/hooks/pre-commit"
|
||||
create_project_dir
|
||||
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Reinstalled pre-commit hook"* ]]
|
||||
[ -x "$SECRETS_DIR/.git/hooks/pre-commit" ]
|
||||
}
|
||||
|
||||
@test "rekey reinstalls missing pre-commit hook" {
|
||||
init_with_remote
|
||||
create_project_dir
|
||||
"$SECRETS_BIN" push >/dev/null 2>&1
|
||||
rm "$SECRETS_DIR/.git/hooks/pre-commit"
|
||||
|
||||
run "$SECRETS_BIN" rekey
|
||||
[ "$status" -eq 0 ]
|
||||
[ -x "$SECRETS_DIR/.git/hooks/pre-commit" ]
|
||||
}
|
||||
|
||||
@test "store protections heal is a silent no-op when nothing is missing" {
|
||||
init_with_remote
|
||||
create_project_dir
|
||||
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" != *"Restored store .gitignore"* ]]
|
||||
[[ "$output" != *"Reinstalled pre-commit hook"* ]]
|
||||
}
|
||||
|
||||
@test "restored store .gitignore carries the full block/allow globs" {
|
||||
init_with_remote
|
||||
rm "$SECRETS_DIR/.gitignore"
|
||||
create_project_dir
|
||||
"$SECRETS_BIN" push >/dev/null 2>&1
|
||||
|
||||
grep -q "^key.txt$" "$SECRETS_DIR/.gitignore"
|
||||
grep -qF '**/.env' "$SECRETS_DIR/.gitignore"
|
||||
grep -qF '**/.dev.vars' "$SECRETS_DIR/.gitignore"
|
||||
grep -qF '!**/.env.age' "$SECRETS_DIR/.gitignore"
|
||||
grep -qF '!**/.env.*.age' "$SECRETS_DIR/.gitignore"
|
||||
grep -qF '!**/.dev.vars.age' "$SECRETS_DIR/.gitignore"
|
||||
}
|
||||
|
||||
@test "push heals .gitignore removed by remote history before staging (key never pushed)" {
|
||||
init_with_remote
|
||||
create_project_dir
|
||||
"$SECRETS_BIN" push >/dev/null 2>&1
|
||||
# Remote history drops .gitignore (e.g. an old machine committed without it)
|
||||
git clone -q "$REMOTE_DIR" "$TEST_TMPDIR/other"
|
||||
git -C "$TEST_TMPDIR/other" rm -q .gitignore
|
||||
git -C "$TEST_TMPDIR/other" -c user.email=t@t -c user.name=t commit -qm "drop gitignore"
|
||||
git -C "$TEST_TMPDIR/other" push -q
|
||||
|
||||
echo "B=2" >> .env
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$SECRETS_DIR/.gitignore" ]
|
||||
run git -C "$SECRETS_DIR" ls-files
|
||||
[[ "$output" != *"key.txt"* ]]
|
||||
}
|
||||
|
||||
@test "push untracks a previously committed key.txt with a warning" {
|
||||
init_with_remote
|
||||
# Simulate legacy damage: key.txt got committed in the past
|
||||
git -C "$SECRETS_DIR" add -f key.txt
|
||||
git -C "$SECRETS_DIR" -c user.email=t@t -c user.name=t commit -qm "oops"
|
||||
create_project_dir
|
||||
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"key.txt was tracked"* ]]
|
||||
run git -C "$SECRETS_DIR" ls-files
|
||||
[[ "$output" != *"key.txt"* ]]
|
||||
}
|
||||
|
||||
@test "push rewrites a store .gitignore that is missing the key.txt line" {
|
||||
init_with_remote
|
||||
printf '%s\n' '**/.env' > "$SECRETS_DIR/.gitignore"
|
||||
create_project_dir
|
||||
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
grep -qx 'key.txt' "$SECRETS_DIR/.gitignore"
|
||||
run git -C "$SECRETS_DIR" ls-files
|
||||
[[ "$output" != *"key.txt"* ]]
|
||||
}
|
||||
|
||||
@test "init guard renders the real clone URL when .secrets-store carries a remote" {
|
||||
mkdir -p "$HOME/.secrets-work"
|
||||
age-keygen -o "$HOME/.secrets-work/key.txt" 2>/dev/null
|
||||
[ -s "$HOME/.secrets-work/key.txt" ]
|
||||
cd "$WORK_DIR"
|
||||
echo "work git@example.com:me/secrets-work.git" > .secrets-store
|
||||
|
||||
run "$SECRETS_BIN" init
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"git clone git@example.com:me/secrets-work.git"* ]]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue