Skip to content

[codex] fix simple-git unsafe env handling#4602

Merged
Kitenite merged 1 commit into
mainfrom
codex/simple-git-user-env-root-cause
May 15, 2026
Merged

[codex] fix simple-git unsafe env handling#4602
Kitenite merged 1 commit into
mainfrom
codex/simple-git-user-env-root-cause

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented May 15, 2026

Fixes #4599
Supersedes #4600

What changed

  • Replaces the shell-env PAGER/GIT_PAGER stripping workaround with a simple-git configuration that explicitly allows the user's Git environment/config hooks.
  • Centralizes the simple-git unsafe allow-list in @superset/shared/simple-git-options so desktop and host-service use the same policy.
  • Routes host-service production simple-git construction through one wrapper and adds a lint guard to prevent future direct production simpleGit(...) usage outside approved wrappers.
  • Adds regression coverage proving default simple-git rejects the blocked env while our configured client accepts it.

Why

The allowUnsafeEditor / allowUnsafePager failures are thrown by simple-git@3.36.0, not by Git itself. Superset is a local Git client, so honoring the user's local Git environment is expected. Deleting individual env vars treated one symptom and changed user Git semantics.

Validation

  • bun test packages/shared/src/simple-git-options.test.ts apps/desktop/src/lib/trpc/routers/workspaces/utils/git-client.test.ts packages/host-service/src/runtime/git/simple-git.test.ts apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.test.ts
  • bun run lint
  • bun run typecheck in packages/shared
  • bun run typecheck in packages/host-service
  • bun run typecheck in apps/desktop
  • Mutation-checked the regression tests by temporarily removing allowUnsafeEditor and allowUnsafeAlias; both cases failed as expected before restoration.

Summary by cubic

Fixes simple-git unsafe env errors by allowing user Git env/config and centralizing options to honor local Git behavior. Removes the PAGER stripping workaround and routes production Git usage through approved wrappers.

  • Bug Fixes

    • Enable all simple-git@3.36 unsafe flags via @superset/shared/simple-git-options so editor/pager/hooks work as expected.
    • Preserve PAGER/GIT_PAGER in desktop shell env.
    • Add tests covering the explicit unsafe flag list and acceptance of user env vs default rejection.
  • Refactors

    • Add createUserSimpleGit wrapper (host-service) and use shared options in desktop and host-service.
    • Replace direct simple-git usage with wrappers in production paths.
    • Add scripts/check-simple-git-usage.sh and run it in scripts/lint.sh to forbid direct imports/constructors outside tests and approved wrappers.

Written for commit cdfe2fc. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • New Features

    • Improved Git integration with centralized configuration for safer, more reliable Git operations across the application.
    • Enhanced environment variable preservation for Git-related settings (editor, pager configurations).
  • Bug Fixes

    • Resolved issues with Git hook handling by implementing consistent Git client initialization.
  • Tests

    • Added comprehensive test coverage for Git client configuration and environment variable handling.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1ac53402-c504-4f04-94c2-e23777fb3ebd

📥 Commits

Reviewing files that changed from the base of the PR and between d96fdc8 and cdfe2fc.

📒 Files selected for processing (16)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git-client.test.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git-client.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.test.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.ts
  • packages/host-service/src/runtime/git/git.ts
  • packages/host-service/src/runtime/git/simple-git.test.ts
  • packages/host-service/src/runtime/git/simple-git.ts
  • packages/host-service/src/trpc/router/git/utils/git-helpers.ts
  • packages/host-service/src/trpc/router/project/project.ts
  • packages/host-service/src/trpc/router/project/utils/resolve-repo.ts
  • packages/host-service/src/trpc/router/workspace-creation/shared/project-helpers.ts
  • packages/shared/package.json
  • packages/shared/src/simple-git-options.test.ts
  • packages/shared/src/simple-git-options.ts
  • scripts/check-simple-git-usage.sh
  • scripts/lint.sh

📝 Walkthrough

Walkthrough

This PR centralizes simple-git initialization across the codebase by introducing a shared factory function that applies unsafe options needed for Git hooks and editor/pager support. It updates all git client instantiations to use this factory, stops removing paging variables from the environment, and adds enforcement rules to prevent future direct simple-git usage.

Changes

Centralized Simple-Git Factory and Integration

Layer / File(s) Summary
Centralized unsafe options definition and validation
packages/shared/src/simple-git-options.ts, packages/shared/src/simple-git-options.test.ts, packages/shared/package.json
Defines SIMPLE_GIT_UNSAFE_OPTION_FLAGS constant tuple, SimpleGitUnsafeOptionFlag type union, and USER_GIT_ENV_SIMPLE_GIT_OPTIONS configuration object that maps each unsafe flag to true. Tests verify the list is complete and correctly enabled.
Simple-git factory implementation
packages/host-service/src/runtime/git/simple-git.ts, packages/host-service/src/runtime/git/simple-git.test.ts
Introduces createUserSimpleGit(baseDir?: string) factory that applies shared unsafe options when creating simple-git instances. Tests verify factory accepts blocked Git environments while direct simple-git calls reject them using helper functions makeBlockedGitEnv and expectUnsafeEnvRejected.
Environment setup to preserve Git-related variables
apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.ts, apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.test.ts
Stops deleting PAGER and GIT_PAGER from derived environment in getProcessEnvWithShellPath. Tests verify EDITOR, GIT_EDITOR, PAGER, GIT_PAGER are preserved in returned environment.
Desktop git-client integration with factory
apps/desktop/src/lib/trpc/routers/workspaces/utils/git-client.ts, apps/desktop/src/lib/trpc/routers/workspaces/utils/git-client.test.ts
Updates getSimpleGitWithShellPath to use createUserSimpleGit factory instead of direct simpleGit() calls. Tests verify factory allows Git operations when blocked environment variables are set and validates unsafe option flag list.
Host-service git operations using factory
packages/host-service/src/runtime/git/git.ts, packages/host-service/src/trpc/router/git/utils/git-helpers.ts, packages/host-service/src/trpc/router/project/project.ts, packages/host-service/src/trpc/router/project/utils/resolve-repo.ts, packages/host-service/src/trpc/router/workspace-creation/shared/project-helpers.ts
Replaces all direct simpleGit() imports and calls with createUserSimpleGit factory throughout git-related modules: credential factory, diff helpers for rename/copy detection, project discovery, repository initialization and cloning, and workspace setup helpers.
Enforcement rules to prevent direct simple-git usage
scripts/check-simple-git-usage.sh, scripts/lint.sh
Adds bash script that enforces repository-wide simple-git usage rules by detecting forbidden direct imports, requires, and instantiations outside approved tests and wrappers. Integrates into lint.sh workflow.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #4596: The PR directly addresses the "EDITOR is not permitted without enabling allowUnsafeEditor" error by introducing USER_GIT_ENV_SIMPLE_GIT_OPTIONS that enables the allowUnsafeEditor flag and preserving EDITOR/GIT_EDITOR environment variables in shell environment setup.

Possibly related PRs

  • superset-sh/superset#4568: Both PRs modify getProcessEnvWithShellPath in shell-env.ts around PAGER/GIT_PAGER handling, with this PR preserving them while the related PR removes them—they represent divergent approaches to the same code region.

Poem

🐰 A factory springs forth to guide the git,
With unsafe flags now bundled, bit by bit,
No more shall pagers vanish in the night,
The editor and hooks dance into light,
From desktop to the host, all paths unite!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/simple-git-user-env-root-cause

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 2026

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) View Branch
Vercel API (Vercel) Open Preview
Vercel Web (Vercel) Open Preview
Vercel Marketing (Vercel) Open Preview
Vercel Admin (Vercel) Open Preview
Vercel Docs (Vercel) Open Preview

Preview updates automatically with new commits

@Kitenite Kitenite force-pushed the codex/simple-git-user-env-root-cause branch from b0a4c9c to cdfe2fc Compare May 15, 2026 16:31
@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 15, 2026

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 16 files

Re-trigger cubic

@Kitenite Kitenite merged commit 4bf08f6 into main May 15, 2026
14 checks passed
@Kitenite Kitenite deleted the codex/simple-git-user-env-root-cause branch May 15, 2026 16:33
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 15, 2026

Greptile Summary

This PR fixes simple-git@3.36.0 allowUnsafe* errors by centralizing all unsafe compatibility flags in @superset/shared/simple-git-options and routing every production simpleGit(...) call through thin wrappers (createUserSimpleGit) in both desktop and host-service. The previous workaround of deleting PAGER/GIT_PAGER from the environment is removed in favour of this explicit allow-list, preserving the user's local Git semantics.

  • Centralized options: packages/shared/src/simple-git-options.ts exports USER_GIT_ENV_SIMPLE_GIT_OPTIONS with all 20 allowUnsafe* flags enabled; wrapper files verify type compatibility via satisfies Partial<SimpleGitOptions>.
  • Lint guard added: scripts/check-simple-git-usage.sh uses ripgrep to block direct simpleGit(...) construction or imports outside the two approved wrappers, but is wired into lint.sh without exit-code propagation, so violations currently won't block CI.
  • Regression tests: Both packages add integration tests that confirm the default simpleGit rejects a blocked env while the wrapped client accepts it.

Confidence Score: 3/5

The core bug fix and wrapper centralization are correct, but the new lint guard is wired into lint.sh in a way that makes it a no-op for CI enforcement.

check-simple-git-usage.sh correctly exits 1 on violations, but lint.sh captures biome's exit code before the custom checks run and exits with that stored value — a failing guard never propagates to the caller. The fix the PR was specifically designed to enforce (blocking future direct simpleGit() usage) is therefore not actually enforced in CI today.

scripts/lint.sh — needs || exit 1 (or equivalent) after each custom check script call to make the guards effective.

Important Files Changed

Filename Overview
packages/shared/src/simple-git-options.ts New shared module exporting all simple-git unsafe option flags and the OPTIONS object; uses an as cast instead of importing SimpleGitOptions, so flag correctness is only checked in consumer files via satisfies.
scripts/lint.sh Adds bash ./scripts/check-simple-git-usage.sh to lint, but lint.sh lacks set -e and exits with biome's stored exit code, so a failure in the new guard never blocks the lint run.
scripts/check-simple-git-usage.sh New lint guard using ripgrep to detect direct simpleGit imports/calls outside approved wrappers; exits 1 on violations but is not effectively wired up in lint.sh.
packages/host-service/src/runtime/git/simple-git.ts New host-service wrapper that constructs simple-git instances with the shared unsafe options; clean and minimal.
apps/desktop/src/lib/trpc/routers/workspaces/utils/git-client.ts Replaces bare simpleGit() calls with a local createUserSimpleGit wrapper using the shared unsafe options; removes PAGER stripping.
apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.ts Removes the PAGER/GIT_PAGER deletion workaround; user env values are now preserved and handled via simple-git's unsafe allow-list instead.
packages/host-service/src/trpc/router/project/utils/resolve-repo.ts All six bare simpleGit() calls replaced with createUserSimpleGit(); no logic changes.
packages/shared/src/simple-git-options.test.ts Unit tests verifying the flags list and that every flag is enabled in the options object.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["@superset/shared\nsimple-git-options.ts\nUSER_GIT_ENV_SIMPLE_GIT_OPTIONS\n(all allowUnsafe* flags)"] --> B
    A --> C
    B["apps/desktop\ngit-client.ts\ncreateUserSimpleGit()"]
    C["packages/host-service\nruntime/git/simple-git.ts\ncreateUserSimpleGit()"]
    B --> D["getSimpleGitWithShellPath()\n(desktop callers)"]
    C --> E["git.ts / git-helpers.ts\nproject.ts / resolve-repo.ts\nproject-helpers.ts"]
    F["scripts/check-simple-git-usage.sh\n(lint guard)"] -.->|"wired via lint.sh\n(exit code not propagated)"| B
    F -.-> C
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
scripts/lint.sh:16
**Lint guard exit code not propagated**

`check-simple-git-usage.sh` calls `exit 1` when it finds a violation, but `lint.sh` has no `set -e` and ends with `exit $exit_code` (which holds biome's exit code, captured before the custom checks run). A violation in `check-simple-git-usage.sh` will print its output but will never cause `lint.sh` to exit non-zero on its own, so CI won't be blocked. The same is true for the two pre-existing `check-*.sh` calls above it. Adding `|| exit 1` after the script call (or adding `set -e` to `lint.sh`) would make the guard effective.

### Issue 2 of 3
packages/shared/src/simple-git-options.ts:27-33
**Type assertion bypasses `simple-git` type checking in the shared package**

`Object.fromEntries(...).map(...)` is widened to `Record<string, boolean>` by TypeScript, then forced to `Record<SimpleGitUnsafeOptionFlag, true>` via an `as` cast. Nothing in this file imports `SimpleGitOptions` from `simple-git`, so if `simple-git` renames or removes a flag, this module will not produce a compile error. The safety net lives entirely in the two consumer files (via `satisfies Partial<SimpleGitOptions>`), meaning a consumer that skips the `satisfies` constraint would silently carry stale flags.

### Issue 3 of 3
apps/desktop/src/lib/trpc/routers/workspaces/utils/git-client.test.ts:11-50
**Duplicate test helpers across two test files**

`makeBlockedGitEnv` and `expectUnsafeEnvRejected` are copied verbatim in both `git-client.test.ts` and `packages/host-service/src/runtime/git/simple-git.test.ts`. If the blocked env list changes, both copies need to be updated. Since `@superset/shared` already centralizes the options, a shared test utility would keep these in sync.

Reviews (1): Last reviewed commit: "fix simple-git unsafe env handling" | Re-trigger Greptile

Comment thread scripts/lint.sh

./scripts/check-desktop-git-env.sh
./scripts/check-git-ref-strings.sh
bash ./scripts/check-simple-git-usage.sh
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Lint guard exit code not propagated

check-simple-git-usage.sh calls exit 1 when it finds a violation, but lint.sh has no set -e and ends with exit $exit_code (which holds biome's exit code, captured before the custom checks run). A violation in check-simple-git-usage.sh will print its output but will never cause lint.sh to exit non-zero on its own, so CI won't be blocked. The same is true for the two pre-existing check-*.sh calls above it. Adding || exit 1 after the script call (or adding set -e to lint.sh) would make the guard effective.

Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/lint.sh
Line: 16

Comment:
**Lint guard exit code not propagated**

`check-simple-git-usage.sh` calls `exit 1` when it finds a violation, but `lint.sh` has no `set -e` and ends with `exit $exit_code` (which holds biome's exit code, captured before the custom checks run). A violation in `check-simple-git-usage.sh` will print its output but will never cause `lint.sh` to exit non-zero on its own, so CI won't be blocked. The same is true for the two pre-existing `check-*.sh` calls above it. Adding `|| exit 1` after the script call (or adding `set -e` to `lint.sh`) would make the guard effective.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +27 to +33
export const USER_GIT_ENV_SIMPLE_GIT_OPTIONS = {
unsafe: Object.fromEntries(
SIMPLE_GIT_UNSAFE_OPTION_FLAGS.map((flag) => [flag, true]),
),
} as {
unsafe: Record<SimpleGitUnsafeOptionFlag, true>;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Type assertion bypasses simple-git type checking in the shared package

Object.fromEntries(...).map(...) is widened to Record<string, boolean> by TypeScript, then forced to Record<SimpleGitUnsafeOptionFlag, true> via an as cast. Nothing in this file imports SimpleGitOptions from simple-git, so if simple-git renames or removes a flag, this module will not produce a compile error. The safety net lives entirely in the two consumer files (via satisfies Partial<SimpleGitOptions>), meaning a consumer that skips the satisfies constraint would silently carry stale flags.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/shared/src/simple-git-options.ts
Line: 27-33

Comment:
**Type assertion bypasses `simple-git` type checking in the shared package**

`Object.fromEntries(...).map(...)` is widened to `Record<string, boolean>` by TypeScript, then forced to `Record<SimpleGitUnsafeOptionFlag, true>` via an `as` cast. Nothing in this file imports `SimpleGitOptions` from `simple-git`, so if `simple-git` renames or removes a flag, this module will not produce a compile error. The safety net lives entirely in the two consumer files (via `satisfies Partial<SimpleGitOptions>`), meaning a consumer that skips the `satisfies` constraint would silently carry stale flags.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +11 to +50

function makeBlockedGitEnv(workRoot: string): Record<string, string> {
const globalConfig = join(workRoot, "global.gitconfig");
const systemConfig = join(workRoot, "system.gitconfig");
const configFile = join(workRoot, "gitconfig");
const templateDir = join(workRoot, "template");
mkdirSync(templateDir);
writeFileSync(globalConfig, "");
writeFileSync(systemConfig, "");
writeFileSync(configFile, "");

return {
EDITOR: "true",
GIT_ASKPASS: "/bin/echo",
GIT_CONFIG: configFile,
GIT_CONFIG_COUNT: "0",
GIT_CONFIG_GLOBAL: globalConfig,
GIT_CONFIG_SYSTEM: systemConfig,
GIT_EDITOR: "true",
GIT_EXEC_PATH: execSync("git --exec-path", { encoding: "utf8" }).trim(),
GIT_EXTERNAL_DIFF: "true",
GIT_PAGER: "cat",
GIT_PROXY_COMMAND: "true",
GIT_SEQUENCE_EDITOR: "true",
GIT_SSH: "ssh",
GIT_SSH_COMMAND: "ssh",
GIT_TEMPLATE_DIR: templateDir,
PAGER: "cat",
PREFIX: workRoot,
SSH_ASKPASS: "/bin/echo",
};
}

async function expectUnsafeEnvRejected(git: SimpleGit): Promise<void> {
try {
await git.raw(["status", "--short"]);
} catch (err) {
expect(String(err)).toContain("not permitted without enabling allowUnsafe");
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Duplicate test helpers across two test files

makeBlockedGitEnv and expectUnsafeEnvRejected are copied verbatim in both git-client.test.ts and packages/host-service/src/runtime/git/simple-git.test.ts. If the blocked env list changes, both copies need to be updated. Since @superset/shared already centralizes the options, a shared test utility would keep these in sync.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/lib/trpc/routers/workspaces/utils/git-client.test.ts
Line: 11-50

Comment:
**Duplicate test helpers across two test files**

`makeBlockedGitEnv` and `expectUnsafeEnvRejected` are copied verbatim in both `git-client.test.ts` and `packages/host-service/src/runtime/git/simple-git.test.ts`. If the blocked env list changes, both copies need to be updated. Since `@superset/shared` already centralizes the options, a shared test utility would keep these in sync.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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.

Unable to spawn new workspaces. Getting error:

1 participant