Skip to content

feat(cli): derive the repo URL from the git remote [ship] - #1500

Open
martzoukos wants to merge 2 commits into
mainfrom
martzoukos/derive-repo-url-from-git
Open

martzoukos wants to merge 2 commits into
mainfrom
martzoukos/derive-repo-url-from-git

Conversation

@martzoukos

Copy link
Copy Markdown

Affected Components

  • CLI
  • Create CLI
  • Test
  • Docs
  • Examples
  • Other

Summary

Projects created by checkly init or import plan often have no repoUrl. Export to code can't sync bound resources for those projects.

  • Deploy and test sessions: the repoInfo.repoUrl fallback chain is now CHECKLY_REPO_URL → CHECKLY_TEST_REPO_URL → config repoUrl → CHECKLY_GITHUB_REPOSITORY → GITHUB_SERVER_URL + GITHUB_REPOSITORY → the origin remote.
    • Credentials are stripped from the remote.
    • git@host:x/y and ssh:// remotes become https://host/x/y.
    • .git is dropped.
  • Repos with no commits still send their URL, and commitId is left out. commitId is already optional in the backend's repoInfoSchema. That schema is shared by /v1/test-sessions/run, /next/test-sessions/trigger and deploy.
  • checkly init writes repoUrl into the generated config. With no remote, it writes a commented placeholder. The agent onboarding instructions (initialize.md, context.ts) now tell the agent to set repoUrl from the remote.
  • import plan falls back to the git remote when it creates a new project.

Notes for the Reviewer

  • The derived URL only ever goes into repoInfo.repoUrl, never into project.repoUrl. The backend lets a derived URL fill a project only when the project has no URL, so forks and mirrors can't overwrite a declared URL.
  • The companion monorepo PR makes bound sync recover when a project has no repo. It doesn't depend on this PR.

Tests

  • util.spec.ts: SSH, ssh:// with a port, https with a token, no remote, no repo, no commits, and the fallback order.
  • boilerplate.spec.ts: createConfig with and without a remote.

🤖 Generated with Claude Code

Projects created by `checkly init` or `import plan` often end up without a
repoUrl, which blocks Export to code from syncing bound resources.

- getGitInformation now falls back to GitHub Actions' built-in env vars and
  the `origin` remote, with credentials stripped and SSH remotes converted to
  https. The derived URL only goes into repoInfo.repoUrl, never project.repoUrl.
- Repositories without commits still send their URL; commitId is omitted.
- `checkly init` writes repoUrl into the generated config, and the agent
  onboarding instructions tell the agent to set it from the remote.
- `import plan` falls back to the git remote when creating a new project.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@martzoukos

Copy link
Copy Markdown
Author

Review Summary

Verdict: Approve, but fix the Important issues before merging. There are no Critical issues.

Intent (as understood): When a project has no repoUrl, fill repoInfo.repoUrl from the origin git remote as a last resort, so export-to-code can sync bound resources.

Overview: The backend already accepts this (see Verification). The code that writes the generated config file and the code that turns SSH remotes into web URLs need work.

Triage

  • Diff: 8 files, +263 / −20, reviewed at 159a705a.
  • Reviewers: Generalist, Correctness (new parsing logic and fewer null returns), Security (tokens in remote URLs, a git subprocess, codegen), Architecture (CLI→backend payload contract).

Critical Issues

None.

Important Issues

  1. packages/cli/src/helpers/onboarding/boilerplate.ts:97: the URL is written into checkly.config.ts without escaping.

    • new URL(...) leaves a ' in the path untouched, and the scp-style branch keeps the path as-is. A remote like git@h:o/r');require(...) becomes live code in the generated config.
    • The URL is also passed as the replacement string to replaceAll, where $' has a special meaning and gets expanded.
    • Both confirmed in node.
    • Fix: `repoUrl: ${JSON.stringify(repoUrl)}` and .replaceAll(slot, () => repoUrlProperty).
  2. packages/cli/src/services/util.ts:82: the scp-style branch trusts any host name. These produce wrong web URLs:

    • SSH host aliases: git@github-work:acme/app → https://github-work/acme/app.
    • Azure DevOps: git@ssh.dev.azure.com:v3/org/proj/repo is not a web path.
    • Bitbucket Server: ssh://git@host:7999/PROJ/repo is not the web path either.

    A wrong URL sticks: the backend only fills an empty project URL, so a later derived URL never replaces it, and import plan creates new projects with it. Fix: return undefined for hosts without a dot and for known non-web SSH hosts, and add these cases to the tests.

  3. packages/cli/src/commands/trigger.ts:167: trigger calls getGitInformation() without the config's repoUrl. For trigger, the git remote wins over the declared URL; test and deploy use the declared one. checklyConfig is already loaded a few lines above. Fix: getGitInformation(checklyConfig?.repoUrl).

Suggestions

  1. Credentials can survive in the scp-style branch (util.ts:82): x-access-token:TOKEN@github.com/o/r and git@h:o/r?token=x keep the token. Return undefined when the path contains @, ? or #.
  2. import plan has its own resolution (plan.ts:1320): it skips the CHECKLY_REPO_URL and GitHub env steps. Use getGitInformation(config.repoUrl, configDirectory)?.repoUrl, and resolve it below the check for an existing project.
  3. Duplicated helper (util.ts:46-64): getGitHubActionsRepositoryUrl nearly copies getGitHubRepositoryUrl. Make them one helper that takes the env var pair.
  4. Empty SHA env var (util.ts:215): CHECKLY_REPO_SHA='' still hides the real SHA, and can now make the function return null, which drops branch and commit message too. Use || in the SHA chain.
  5. Leftover placeholders: add a test in boilerplate.spec.ts that the generated config contains no {{.

What's Done Well

  • The ?? chain is lazy: git only runs when every declared source is empty.
  • execFileSync runs with fixed arguments and no shell. git config --get does not run hooks or fsmonitor.
  • The tests clear GITHUB_* env vars, so the CI runner can't leak into them.
  • Nothing in the CLI depends on repoInfo === null or on commitId being set.

Verification

  • Backend: repoInfoSchema already has commitId optional. In ProjectDeployService.ts on main, a declared URL always wins and a derived URL only fills an empty one. GitHub reporting is gated on github.reporting, not on whether repoInfo is present.
  • Tests: read, not run during review.

Open Questions

  1. checkly init writes the derived URL as a declared value. Deploy sends a declared URL as the project's URL and it always wins, so from a fork it overwrites the stored URL on every deploy. That goes against the note that forks and mirrors "can't overwrite a declared URL". Either say in the description that the user is expected to review the generated URL, or always write the commented placeholder and let the first deploy fill the project.
  2. GitHub Actions env beats origin (util.ts:225): when a workflow checks out a different repo with actions/checkout repository:, GITHUB_REPOSITORY is the wrong answer. Is that order intended?

🤖 Generated with Claude Code

…fig repoUrl in trigger

- `checkly init` quotes the git-derived URL as a string literal and uses a
  function replacer, so quotes or `$'` in a remote can't break or inject
  code into checkly.config.ts.
- SSH remotes whose host has no dot (SSH config aliases like `github-work`)
  or whose web path layout differs (Azure DevOps) no longer produce a URL.
- `checkly trigger` passes the config repoUrl, so a declared URL wins over
  the git remote there too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@martzoukos martzoukos changed the title feat(cli): derive the repo URL from the git remote feat(cli): derive the repo URL from the git remote [ship] Sep 30, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Auto-approved: ship/show PR from a same-repo branch.

This branch has not been deployed

No deployments
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.

1 participant