Skip to content

fix: accept Azure DevOps Git URLs without .git suffix in plugin marketplace#299576

Merged
connor4312 merged 3 commits intomainfrom
copilot/fix-azure-devops-url-rejection
Mar 6, 2026
Merged

fix: accept Azure DevOps Git URLs without .git suffix in plugin marketplace#299576
connor4312 merged 3 commits intomainfrom
copilot/fix-azure-devops-url-rejection

Conversation

Copy link
Contributor

Copilot AI commented Mar 5, 2026

parseMarketplaceReference silently rejected Azure DevOps HTTPS clone URLs (https://dev.azure.com/org/project/_git/repo) because normalizeGitRepoPath required a .git suffix. Users had to manually append .git to the canonical URL provided by Azure DevOps.

Changes

  • normalizeGitRepoPath: Removes the hard .git requirement. Path depth validation now strips .git only when present before checking for at least two segments. Uses a gitSuffix constant instead of the magic number -4 for maintainability.
  • parseUriMarketplaceReference: Handles both forms using a gitSuffix constant:
    • cacheSegments strip .git when present
    • canonicalId always normalizes to include .git, so …/repo and …/repo.git deduplicate to the same entry
  • Tests: Updated the test that expected HTTPS/SSH URLs without .git to be rejected; added tests for Azure DevOps-style URLs and cross-suffix deduplication. SCP-style (git@host:path) still requires .git.

Example — this now parses correctly:

https://dev.azure.com/org/project/_git/repo
→ canonicalId: git:dev.azure.com/org/project/_git/repo.git
→ cacheSegments: ['dev.azure.com', 'org', 'project', '_git', 'repo']

And deduplicates with https://dev.azure.com/org/project/_git/repo.git to a single entry.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…tplace

- Remove the `.git` suffix requirement from `normalizeGitRepoPath`; the function
  now accepts any URL path that has at least two segments.
- Update `parseUriMarketplaceReference` to handle paths with and without `.git`:
  - `cacheSegments` are built without the suffix in both cases.
  - `canonicalId` is always normalized to include `.git` so that the same repo
    specified with and without the suffix deduplicates correctly.
- Add a JSDoc comment on `normalizeGitRepoPath` explaining the new semantics.
- Update the test that expected HTTPS/SSH URLs without `.git` to be rejected;
  these are now accepted. SCP-style (`git@host:path`) still requires `.git`.
- Add tests for Azure DevOps-style URLs and cross-suffix deduplication.

Co-authored-by: connor4312 <2230985+connor4312@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix Azure DevOps Git URL rejection in marketplace fix: accept Azure DevOps Git URLs without .git suffix in plugin marketplace Mar 5, 2026
@connor4312 connor4312 marked this pull request as ready for review March 5, 2026 20:06
Copilot AI review requested due to automatic review settings March 5, 2026 20:06
@connor4312 connor4312 enabled auto-merge (squash) March 5, 2026 20:06
@vs-code-engineering vs-code-engineering bot added this to the 1.111.0 milestone Mar 5, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates marketplace Git URL parsing to accept Azure DevOps (and general HTTPS/SSH) clone URLs that omit the .git suffix, while normalizing canonical IDs to include .git for deduplication.

Changes:

  • Relaxes normalizeGitRepoPath by removing the hard .git requirement while still validating minimum path depth.
  • Updates URI parsing to strip .git for cache segments but always include .git in canonicalId for deduplication.
  • Updates/adds tests for Azure DevOps URLs and cross-suffix deduplication behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/vs/workbench/contrib/chat/common/plugins/pluginMarketplaceService.ts Adjusts Git URL normalization and URI parsing to accept URLs without .git while keeping canonical IDs stable.
src/vs/workbench/contrib/chat/test/common/plugins/pluginMarketplaceService.test.ts Updates/extends tests to cover non-.git URLs (including Azure DevOps) and deduplication.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +643 to +647
const pathHasGitSuffix = normalizedPath.toLowerCase().endsWith('.git');
const pathWithoutGit = pathHasGitSuffix ? normalizedPath.slice(1, -4) : normalizedPath.slice(1);
const pathSegments = pathWithoutGit.split('/').map(sanitizePathSegment);
// Always normalize the canonical path to include .git so that URLs with and without the suffix deduplicate.
const canonicalPath = pathHasGitSuffix ? normalizedPath.slice(1).toLowerCase() : `${normalizedPath.slice(1).toLowerCase()}.git`;
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

The .git suffix is handled via repeated string literals and the magic number -4. Consider introducing a single constant (e.g., const gitSuffix = '.git') and using gitSuffix.length in slice() calls. This reduces the risk of future inconsistencies and makes the intent clearer.

See below for a potential fix:

	const gitSuffix = '.git';
	const sanitizedAuthority = sanitizePathSegment(uri.authority.toLowerCase());
	const pathHasGitSuffix = normalizedPath.toLowerCase().endsWith(gitSuffix);
	const pathWithoutGit = pathHasGitSuffix ? normalizedPath.slice(1, normalizedPath.length - gitSuffix.length) : normalizedPath.slice(1);
	const pathSegments = pathWithoutGit.split('/').map(sanitizePathSegment);
	// Always normalize the canonical path to include .git so that URLs with and without the suffix deduplicate.
	const canonicalPath = pathHasGitSuffix ? normalizedPath.slice(1).toLowerCase() : `${normalizedPath.slice(1).toLowerCase()}${gitSuffix}`;

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 5c6077a — introduced a gitSuffix constant in both parseUriMarketplaceReference and normalizeGitRepoPath, replacing all -4 magic numbers and repeated '.git' literals.

Comment on lines +691 to +694
// Strip .git suffix (if present) only for the purposes of validating path depth.
const pathWithoutGit = withLeadingSlash.toLowerCase().endsWith('.git')
? withLeadingSlash.slice(1, -4)
: withLeadingSlash.slice(1);
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

Same maintainability concern here: '.git' and -4 are duplicated in suffix handling. Reusing a shared gitSuffix constant and slicing with -gitSuffix.length would make this easier to maintain and less error-prone.

Copilot uses AI. Check for mistakes.
Co-authored-by: connor4312 <2230985+connor4312@users.noreply.github.com>
@connor4312 connor4312 merged commit 6bb50a9 into main Mar 6, 2026
20 checks passed
@connor4312 connor4312 deleted the copilot/fix-azure-devops-url-rejection branch March 6, 2026 17:16
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.

4 participants