diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 052c366..7257bfd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -123,14 +123,14 @@ jobs: tarball_url="https://github.com/K1-R1/smoosh/archive/refs/tags/${TAG_NAME}.tar.gz" # Compute SHA256 from the release tarball. - curl -fsSL "${tarball_url}" -o /tmp/smoosh-src.tar.gz - sha256="$(sha256sum /tmp/smoosh-src.tar.gz | awk '{print $1}')" + curl -fsSL "${tarball_url}" -o "$RUNNER_TEMP/smoosh-src.tar.gz" + sha256="$(sha256sum "$RUNNER_TEMP/smoosh-src.tar.gz" | awk '{print $1}')" # Clone, patch, push. git clone \ "https://x-access-token:${COMMITTER_TOKEN}@github.com/K1-R1/homebrew-tap.git" \ - /tmp/homebrew-tap - cd /tmp/homebrew-tap + "$RUNNER_TEMP/homebrew-tap" + cd "$RUNNER_TEMP/homebrew-tap" git config user.email "github-actions[bot]@users.noreply.github.com" git config user.name "github-actions[bot]" diff --git a/CHANGELOG.md b/CHANGELOG.md index 975b16f..7e4424f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] +## [1.0.2] - 2026-03-17 + +### Fixed + +- `.gitignore` auto-update no longer crashes on read-only repos when + `--output-dir` points outside the repository root. The `.gitignore` write + is now skipped (with a warning) since an external absolute path in + `.gitignore` would be meaningless anyway. +- README and `install.sh` header comment no longer pin curl install URLs to + `v1.0.0` — now reference `main` branch so users always get the latest + install script. + +### Security + +- `.gitignore` auto-update skips symlinks with a warning, preventing the + `cp` + `mv` pattern from overwriting the symlink target. +- `install.sh` now warns before falling back to `sudo` for installation, + making privilege escalation visible in `curl | bash` contexts. +- README manual install instructions now include SHA256 checksum + verification step. +- Release workflow uses `$RUNNER_TEMP` instead of world-readable `/tmp` for + intermediate files. + +### Added + +- Table of contents in README for quick navigation. +- Uninstall section in README (Homebrew and manual paths, shadow binary + note). +- Agent/CI usage section in README with JSON schema example, recommended + commands, and automation flags table. +- `--no-color` flag documented in README configuration reference table. +- Colour environment variable precedence documented (`--no-color` > + `NO_COLOR` > `FORCE_COLOR` > `CLICOLOR` > TTY auto-detect). +- `install.sh` environment variables documented in README (`SMOOSH_VERSION`, + `SMOOSH_INSTALL_DIR`, `SMOOSH_NO_CONFIRM`, `SMOOSH_NO_VERIFY`). +- Agent/CI example added to `--help` output. +- 3 new bats tests: external `--output-dir` skips `.gitignore`, symlink + `.gitignore` is not followed, `--help` includes agent example. + ## [1.0.1] - 2026-03-15 ### Fixed @@ -64,6 +103,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/). - Output path shown relative to repo root - Demo recordings: interactive mode flow and power-user flags -[Unreleased]: https://github.com/K1-R1/smoosh/compare/v1.0.1...HEAD +[Unreleased]: https://github.com/K1-R1/smoosh/compare/v1.0.2...HEAD +[1.0.2]: https://github.com/K1-R1/smoosh/compare/v1.0.1...v1.0.2 [1.0.1]: https://github.com/K1-R1/smoosh/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/K1-R1/smoosh/releases/tag/v1.0.0 diff --git a/README.md b/README.md index 171907b..8c5ebe1 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ ChatGPT, or your own RAG pipeline. Pure bash, zero dependencies. +**[Quick Start](#quick-start)** · **[Why smoosh?](#why-smoosh)** · **[Features](#features)** · **[Installation](#installation)** · **[Uninstall](#uninstall)** · **[Usage](#usage)** · **[AI Tools](#using-smoosh-with-ai-tools)** · **[Agent / CI](#agents-and-ci-pipelines)** · **[Config Reference](#configuration-reference)** · **[FAQ](#faq)** + ## Quick Start ```bash @@ -94,24 +96,48 @@ brew install K1-R1/tap/smoosh ### curl (macOS / Linux / Git Bash) ```bash -curl -fsSL https://raw.githubusercontent.com/K1-R1/smoosh/v1.0.0/install.sh | bash +curl -fsSL https://raw.githubusercontent.com/K1-R1/smoosh/main/install.sh | bash ``` Installs to `/usr/local/bin`. Override with: ```bash SMOOSH_INSTALL_DIR="$HOME/.local/bin" \ - curl -fsSL https://raw.githubusercontent.com/K1-R1/smoosh/v1.0.0/install.sh | bash + curl -fsSL https://raw.githubusercontent.com/K1-R1/smoosh/main/install.sh | bash ``` +The installer supports these environment variables: + +| Variable | Default | Description | +| --- | --- | --- | +| `SMOOSH_INSTALL_DIR` | `/usr/local/bin` | Installation directory | +| `SMOOSH_VERSION` | latest | Pin a specific version (e.g. `1.0.1`) | +| `SMOOSH_NO_CONFIRM` | `0` | Set to `1` to skip confirmation prompt | +| `SMOOSH_NO_VERIFY` | `0` | Set to `1` to skip checksum verification (unsafe) | + ### Manual ```bash curl -fsSL https://github.com/K1-R1/smoosh/releases/latest/download/smoosh -o smoosh +curl -fsSL https://github.com/K1-R1/smoosh/releases/latest/download/smoosh.sha256 -o smoosh.sha256 +sha256sum -c smoosh.sha256 chmod +x smoosh sudo mv smoosh /usr/local/bin/ ``` +### Uninstall + +```bash +# Homebrew +brew uninstall smoosh + +# curl / manual +rm "$(which smoosh)" +``` + +If you installed via both methods, check `which smoosh` after removing one — a +second copy may remain in a different location. + ## Usage ### Basics @@ -242,6 +268,49 @@ grounded in your actual code. Works with any ChatGPT plan that supports file uploads. +### Agents and CI pipelines + +smoosh is designed to be called by AI agents and CI pipelines, not just humans. + +**Pre-flight check** — estimate size before generating output: + +```bash +smoosh --json --dry-run --all . +``` + +```json +{ + "dry_run": true, + "repo": "my-project", + "files": [ + {"path": "README.md", "words": 194, "chunk": 1}, + {"path": "src/main.py", "words": 312, "chunk": 1} + ], + "total_words": 506, + "estimated_tokens": 658, + "estimated_chunks": 1 +} +``` + +**Generate output:** + +```bash +smoosh --no-interactive --json --all . +``` + +**Key flags for automation:** + +| Flag | Purpose | +| --- | --- | +| `--no-interactive` | Skip TTY detection and prompts | +| `--json` | Structured JSON to stdout (status messages go to stderr) | +| `--quiet` | Output file paths only, one per line | +| `--dry-run` | Preview without writing files | +| `--no-color` | Disable colour escape codes | + +Exit codes 0–7 are differentiated for programmatic decision-making — see +[Configuration Reference](#configuration-reference) below. + ## Configuration Reference | Flag | Default | Description | @@ -262,10 +331,14 @@ Works with any ChatGPT plan that supports file uploads. | `--quiet` | — | Print output paths only (stdout) | | `--json` | — | Structured JSON to stdout | | `--no-interactive` | — | Skip interactive mode even in a TTY | +| `--no-color` | — | Disable colour output | | `--no-check-secrets` | — | Skip the basic secrets scan | | `--version` | — | Print version and exit 0 | | `--help` | — | Print full usage and exit 0 | +**Colour control:** `--no-color` flag > `NO_COLOR` env var > `FORCE_COLOR` > +`CLICOLOR` > TTY auto-detect. See [no-color.org](https://no-color.org/). + **Exit codes:** | Code | Meaning | diff --git a/docs/plans/2026-03-17-001-fix-acceptance-test-findings-plan.md b/docs/plans/2026-03-17-001-fix-acceptance-test-findings-plan.md new file mode 100644 index 0000000..cc0cc86 --- /dev/null +++ b/docs/plans/2026-03-17-001-fix-acceptance-test-findings-plan.md @@ -0,0 +1,248 @@ +--- +title: "fix: Resolve acceptance test findings" +type: fix +status: completed +date: 2026-03-17 +--- + +# fix: Resolve acceptance test findings + +## Overview + +Acceptance testing (human walkthrough + agent walkthrough) surfaced 2 bugs, +security hardening items, and documentation gaps. A follow-up audit expanded the +raw findings to ~25. This plan resolves all actionable findings to best practices +before promotional activity begins. + +## Triage + +Each dismissed finding was re-evaluated with justification: + +**Excluded (with rationale):** + +| Finding | Verdict | Justification | +|---------|---------|---------------| +| `--output-dir` path traversal | Dismissed | User controls all inputs on a local CLI tool. Rejecting `..` would break legitimate `--output-dir ../shared-output`. No privilege boundary crossed. | +| `mktemp` explicit error handling | Dismissed | `set -euo pipefail` (line 6) already propagates `mktemp` failures from `$()` substitution. Adding `\|\| die` to 8 callsites is redundant. | +| Remote clone symlink race condition | Dismissed | Two-pass remove+verify is sound. The "race" requires attacker write access to a `mktemp -d` dir — meaning they already have the user's UID. | +| `sed` injection in release.yml | Dismissed | `tarball_url` comes from `github.ref_name` (git tags can't contain `&` or `\`). `sha256` is hex from `sha256sum`. Neither can contain sed specials. | +| Token in git clone URL | Dismissed | Standard cross-repo GH Actions pattern. Fine-grained PAT, scoped to one repo. GitHub auto-masks secrets in logs. | +| Pre-release binary validation | Dismissed | smoosh is a bash script. Suggested `file \| grep ELF` check would actually fail. SHA256 checksum IS the integrity check. | +| Troubleshooting section | Dismissed | No user reports yet. Shadow binary issue covered by uninstall section (D2). Add when real issues arrive. | +| `LC_ALL=C` documentation | Dismissed | Set internally by smoosh for consistent sorting. Not consumed from user's environment. Documenting it would confuse. | + +**Included:** everything below. + +## Bugs + +### B1. `.gitignore` write crashes on read-only repos + +- **File:** `smoosh:710-745` +- **Symptom:** When an agent uses `--output-dir /external/path` on a local repo + it has read-only access to, `mktemp` in `REPO_ROOT` fails with exit 1. +- **Root cause:** Line 710-711 warns when output-dir is outside the repo, but + execution falls through to line 733 which creates a temp file in REPO_ROOT. + The `.gitignore` entry would be meaningless anyway — it would contain an + absolute external path. +- **Fix:** After the "outside repo" warning on line 711, add `return 0`. +- **Test:** New bats test — run smoosh with `--output-dir` pointing outside the + repo fixture. Assert success. Assert `.gitignore` in the repo is unchanged. + (Existing test `smoosh_edge_cases.bats:251` asserts only the warning message; + it doesn't verify that `.gitignore` is left untouched.) + +### B2. README curl install pinned to v1.0.0 + +- **File:** `README.md:97,104` +- **Symptom:** New users following README install v1.0.0 instead of latest. +- **Fix:** Change from tag-pinned URL to `main` branch: + ``` + https://raw.githubusercontent.com/K1-R1/smoosh/main/install.sh + ``` + The install.sh script already resolves the latest release version at runtime + via GitHub API — the tag in the URL only controls which version of + `install.sh` itself is fetched, not which smoosh version is installed. + Using `main` means users always get the latest install script. +- **Test:** Not testable in bats (documentation). + +### B2b. install.sh header comment pinned to v1.0.0 + +- **File:** `install.sh:5` +- **Fix:** Update to match README (use `main`). +- **Test:** Not testable in bats (documentation comment). + +## Security hardening + +### S1. `.gitignore` symlink check + +- **File:** `smoosh:722-743` +- **Issue:** `.gitignore` modification doesn't verify it's a regular file. If + `.gitignore` is a symlink, the `cp` + `mv` pattern would overwrite the + symlink target. +- **Fix:** Add before line 730: + ```bash + if [[ -L "${gitignore}" ]]; then + warn ".gitignore is a symlink — skipping auto-update" + return 0 + fi + ``` +- **Test:** New bats test — create a repo fixture with `.gitignore` as a + symlink to another file. Run smoosh. Assert the symlink target is unchanged. + Assert warning message is emitted. + +### S2. README manual install lacks checksum verification + +- **File:** `README.md:109-113` +- **Issue:** Manual install downloads binary without integrity check. The + `install.sh` path has SHA256 verification, but the "manual" shortcut bypasses + it entirely. +- **Fix:** Add checksum step to the manual install instructions: + ```bash + curl -fsSL https://github.com/K1-R1/smoosh/releases/latest/download/smoosh -o smoosh + curl -fsSL https://github.com/K1-R1/smoosh/releases/latest/download/smoosh.sha256 -o smoosh.sha256 + sha256sum -c smoosh.sha256 + chmod +x smoosh + sudo mv smoosh /usr/local/bin/ + ``` +- **Test:** Not testable in bats (documentation). + +### S3. release.yml uses /tmp instead of $RUNNER_TEMP + +- **File:** `.github/workflows/release.yml:126,132` +- **Issue:** Files written to world-readable `/tmp`. GitHub provides + `$RUNNER_TEMP` as a per-job temp directory. +- **Fix:** Replace `/tmp/smoosh-src.tar.gz` with `"$RUNNER_TEMP/smoosh-src.tar.gz"` + and `/tmp/homebrew-tap` with `"$RUNNER_TEMP/homebrew-tap"`. +- **Test:** Not testable in bats (CI workflow). + +### S4. install.sh sudo escalation without notice + +- **File:** `install.sh:158-166` +- **Issue:** Script tries `install -m 755` silently, then falls back to `sudo` + without notice. On systems with NOPASSWD sudo, this escalates without any + user-visible indication. In the `curl | bash` context, the user can't inspect + what happens before execution. +- **Fix:** Add `warn` before the sudo fallback: + ```bash + if install -m 755 "${tmp_bin}" "${INSTALL_DIR}/${BINARY_NAME}" 2>/dev/null; then + : # success without sudo + else + warn "Writing to ${INSTALL_DIR} requires elevated privileges (sudo)." + if sudo install -m 755 "${tmp_bin}" "${INSTALL_DIR}/${BINARY_NAME}"; then + : # success with sudo + else + die "Installation failed. Try: SMOOSH_INSTALL_DIR=\$HOME/.local/bin bash install.sh" + fi + fi + ``` +- **Test:** Not deterministically testable without mocking filesystem + permissions. The fix is a single `warn` line — low risk, no test needed. + +## Documentation improvements + +### D1. Add table of contents to README + +- **File:** `README.md` (top, after logo/tagline) +- **Content:** Linked section headings for Quick Start, Why smoosh, Features, + Installation, Usage, AI Tools, Config Reference, FAQ, Contributing, Licence. + +### D2. Add uninstall instructions + +- **File:** `README.md` (new section after Installation) +- **Content:** + - Homebrew: `brew uninstall smoosh` + - curl/manual: `rm /usr/local/bin/smoosh` (or `which smoosh` to find it) + - Note: if you installed via both methods, check `which smoosh` after removing + one — a shadow binary may remain. + +### D3. Add `--no-color` to README config reference + +- **File:** `README.md:247-267` +- **Issue:** Flag exists in `--help` and code but missing from README table. +- **Fix:** Add row: `--no-color | — | Disable colour output` + +### D4. Document colour environment variables + +- **File:** `README.md` (in or near config reference) +- **Content:** Precedence: `--no-color` flag > `NO_COLOR` > `FORCE_COLOR` > + `CLICOLOR` > TTY auto-detect. Link to https://no-color.org/. + +### D5. Add agent/CI usage section + +- **File:** `README.md` (expand "Using smoosh with AI tools" or new subsection) +- **Content:** + - Recommended agent pre-flight: `smoosh --json --dry-run --all .` + - Recommended agent execution: `smoosh --no-interactive --json --all .` + - JSON output schema example (show actual structure from dry-run) + - Exit codes for programmatic decision-making + - Note: `--json` goes to stdout, status to stderr — safe to pipe + +### D6. Add agent example to `--help` + +- **File:** `smoosh` (help text, Examples section) +- **Add:** + ``` + smoosh --no-interactive --json . # agent / CI pipeline + ``` +- **Test:** Assert `--help` output contains `--no-interactive --json`. + +### D7. Document install.sh env vars in README + +- **File:** `README.md` (near curl install section) +- **Content:** Brief mention of `SMOOSH_VERSION`, `SMOOSH_INSTALL_DIR`, + `SMOOSH_NO_CONFIRM`, `SMOOSH_NO_VERIFY` with one-line descriptions. + +## New tests summary + +| Test | File | What it asserts | +|------|------|-----------------| +| External output-dir skips .gitignore | `smoosh_edge_cases.bats` | `--output-dir /tmp/ext` succeeds; repo `.gitignore` unchanged | +| Symlink .gitignore skipped with warning | `smoosh_edge_cases.bats` | `.gitignore` symlink target unchanged; warning emitted | +| `--help` includes agent example | `smoosh_args.bats` | Output contains `--no-interactive --json` | + +## Implementation phases + +### Phase 1: Bug fixes + security hardening (smoosh script) + +1. Fix B1 — add early return in `ensure_output_dir` when output is outside repo +2. Fix S1 — add symlink check before `.gitignore` modification +3. Fix S4 — add sudo warning in install.sh +4. Add agent example to `--help` (D6) +5. Write 3 new bats tests +6. Run: `shellcheck smoosh && shfmt -d -i 2 smoosh && bats test/*.bats` + +### Phase 2: Version pinning + CI fixes + +7. Fix B2 — update README curl URLs from `v1.0.0` to `main` +8. Fix B2b — update install.sh header comment +9. Fix S2 — add checksum verification to README manual install +10. Fix S3 — use `$RUNNER_TEMP` in release.yml + +### Phase 3: README documentation + +11. Add table of contents (D1) +12. Add uninstall section (D2) +13. Add `--no-color` to config reference (D3) +14. Document colour env vars (D4) +15. Add agent/CI usage section with JSON schema example (D5) +16. Document install.sh env vars (D7) + +### Phase 4: Verify + +17. `shellcheck smoosh` +18. `shfmt -d -i 2 smoosh` +19. `bats test/*.bats` +20. `prek run` + +## Acceptance criteria + +- [x] `smoosh --output-dir /tmp/ext --no-interactive .` succeeds on a repo + without crashing (B1 fix + test) +- [x] `.gitignore` symlink is not followed (S1 fix + test) +- [x] `--help` output contains agent example (D6 + test) +- [x] README curl install references `main` branch, not a version tag +- [x] README manual install includes SHA256 verification step +- [x] install.sh warns before sudo escalation +- [x] release.yml uses `$RUNNER_TEMP` +- [x] README has table of contents, uninstall section, `--no-color` in config + table, colour env vars, agent usage section, install.sh env vars +- [x] All checks pass: shellcheck, shfmt, bats, prek diff --git a/install.sh b/install.sh index 7700cb3..81c7643 100644 --- a/install.sh +++ b/install.sh @@ -2,7 +2,7 @@ # smoosh installer — https://github.com/K1-R1/smoosh # # Usage: -# curl -fsSL https://raw.githubusercontent.com/K1-R1/smoosh/v1.0.0/install.sh | bash +# curl -fsSL https://raw.githubusercontent.com/K1-R1/smoosh/main/install.sh | bash # # Options (set via environment variables): # SMOOSH_INSTALL_DIR — installation directory (default: /usr/local/bin) @@ -45,7 +45,10 @@ fi info() { printf '%s\n' "${GREEN}info${RESET} ${*}"; } warn() { printf '%s\n' "${YELLOW}warn${RESET} ${*}" >&2; } error() { printf '%s\n' "${RED}error${RESET} ${*}" >&2; } -die() { error "${@}"; exit 1; } +die() { + error "${@}" + exit 1 +} need_cmd() { command -v "${1}" >/dev/null 2>&1 || die "Required command not found: ${1}" @@ -159,10 +162,13 @@ download_and_install() { info "Installing to ${INSTALL_DIR}/${BINARY_NAME}..." if install -m 755 "${tmp_bin}" "${INSTALL_DIR}/${BINARY_NAME}" 2>/dev/null; then : # success without sudo - elif sudo install -m 755 "${tmp_bin}" "${INSTALL_DIR}/${BINARY_NAME}"; then - : # success with sudo else - die "Installation failed. Try: SMOOSH_INSTALL_DIR=\$HOME/.local/bin bash install.sh" + warn "Writing to ${INSTALL_DIR} requires elevated privileges (sudo)." + if sudo install -m 755 "${tmp_bin}" "${INSTALL_DIR}/${BINARY_NAME}"; then + : # success with sudo + else + die "Installation failed. Try: SMOOSH_INSTALL_DIR=\$HOME/.local/bin bash install.sh" + fi fi } @@ -199,7 +205,10 @@ main() { read -r answer case "${answer}" in [yY] | [yY][eE][sS]) ;; - *) printf 'Installation cancelled.\n'; exit 0 ;; + *) + printf 'Installation cancelled.\n' + exit 0 + ;; esac printf '\n' fi diff --git a/smoosh b/smoosh index fceb178..9b287ca 100755 --- a/smoosh +++ b/smoosh @@ -5,7 +5,7 @@ set -euo pipefail -readonly VERSION="1.0.1" +readonly VERSION="1.0.2" # --------------------------------------------------------------------------- # Globals — argument variables (set by parse_args, read everywhere) @@ -352,6 +352,7 @@ Examples: smoosh --code # docs + code files smoosh --code --toc # with table of contents smoosh https://github.com/u/r # remote repository + smoosh --no-interactive --json . # agent / CI pipeline EOF } @@ -707,8 +708,12 @@ ensure_output_dir() { OUTPUT_DIR="${dir}" # Warn if output directory is outside the repository root (local repos only). + # Skip .gitignore update — an external path in .gitignore is meaningless, and + # writing to REPO_ROOT would fail on read-only repos (e.g. sandboxed agents). if [[ "${REMOTE_REPO}" != "true" ]] && [[ "${OUTPUT_DIR}" != "${REPO_ROOT}/"* ]]; then warn "--output-dir is outside the repository root; output will not be auto-gitignored: ${OUTPUT_DIR}" + mkdir -p "${OUTPUT_DIR}" || die 7 "Cannot create output directory: ${OUTPUT_DIR}" + return 0 fi if [[ ! -d "${OUTPUT_DIR}" ]]; then @@ -718,15 +723,18 @@ ensure_output_dir() { # Remote repos: skip .gitignore (the clone is a temp dir, not the user's repo). [[ "${REMOTE_REPO}" == "true" ]] && return 0 - # Compute repo-root-relative gitignore entry (skip for paths outside the repo). + # Compute repo-root-relative gitignore entry. local gitignore="${REPO_ROOT}/.gitignore" - local entry - if [[ "${OUTPUT_DIR}" == "${REPO_ROOT}/"* ]]; then - entry="${OUTPUT_DIR#"${REPO_ROOT}/"}" - entry="${entry}/" - else - entry="${OUTPUT_DIR}/" + + # Guard against symlink .gitignore — cp + mv would overwrite the target. + if [[ -L "${gitignore}" ]]; then + warn ".gitignore is a symlink — skipping auto-update" + return 0 fi + + local entry + entry="${OUTPUT_DIR#"${REPO_ROOT}/"}" + entry="${entry}/" if ! grep -qF -- "${entry}" "${gitignore}" 2>/dev/null; then local tmp # Create temp in REPO_ROOT so mv to .gitignore is atomic (same filesystem). diff --git a/test/golden/expected/include-hidden-md.golden b/test/golden/expected/include-hidden-md.golden index 9ce57ac..ef7895c 100644 --- a/test/golden/expected/include-hidden-md.golden +++ b/test/golden/expected/include-hidden-md.golden @@ -28,14 +28,6 @@ jobs: ---- -### File: .gitignore ---- - -OUTPUT_DIR/ - - - --- ### File: Makefile --- diff --git a/test/smoosh_args.bats b/test/smoosh_args.bats index 6b2b539..d4e7698 100644 --- a/test/smoosh_args.bats +++ b/test/smoosh_args.bats @@ -41,6 +41,11 @@ load 'test_helper/common-setup' assert_output --partial "--output-dir" } +@test "--help includes agent/CI example" { + run smoosh --help + assert_output --partial "--no-interactive --json" +} + # --------------------------------------------------------------------------- # --version # --------------------------------------------------------------------------- diff --git a/test/smoosh_edge_cases.bats b/test/smoosh_edge_cases.bats index dfd119f..35fc74f 100644 --- a/test/smoosh_edge_cases.bats +++ b/test/smoosh_edge_cases.bats @@ -256,6 +256,42 @@ setup() { assert_output --partial "outside the repository root" } +@test "--output-dir outside repo does not modify .gitignore" { + local ext_dir + ext_dir="$(mktemp -d)" + local before="" + [[ -f "${EDGE_REPO}/.gitignore" ]] && before="$(cat "${EDGE_REPO}/.gitignore")" + run smoosh --output-dir "${ext_dir}" "${EDGE_REPO}" + assert_success + local after="" + [[ -f "${EDGE_REPO}/.gitignore" ]] && after="$(cat "${EDGE_REPO}/.gitignore")" + [[ "${before}" == "${after}" ]] + rm -rf "${ext_dir}" +} + +@test "symlink .gitignore is not followed during auto-update" { + local sym_repo + sym_repo="$(mktemp -d)" + printf '# test\n' >"${sym_repo}/readme.md" + local target="${sym_repo}/real-gitignore" + printf 'existing\n' >"${target}" + ln -sf "real-gitignore" "${sym_repo}/.gitignore" + git -C "${sym_repo}" init -q + git -C "${sym_repo}" config user.email "test@example.com" + git -C "${sym_repo}" config user.name "Test" + git -C "${sym_repo}" add -A + git -C "${sym_repo}" commit -q -m "init" + local before + before="$(cat "${target}")" + run smoosh "${sym_repo}" + assert_success + assert_output --partial ".gitignore is a symlink" + local after + after="$(cat "${target}")" + [[ "${before}" == "${after}" ]] + rm -rf "${sym_repo}" +} + # --------------------------------------------------------------------------- # Unreadable file during processing # ---------------------------------------------------------------------------