Files
orca/src/shared/plugin-git-download-binary-compatibility.test-cases.ts
T
Neil 584e672df8 fix(plugins): accept plain repo, release and tag page URLs in Git install (#36525)
* fix(plugins): accept plain repo, release and tag page URLs in Git install

The Git URL install dialog refused anything without a #ref suffix, so pasting a
repository or GitHub release link failed with "Add a #tag or #commit". It now
reads owner/repo, repository URLs with or without .git, SSH URLs and GitHub /
GitLab release, tag, tree and commit pages, pins the newest version tag (or the
default branch's commit when there is none) with git ls-remote, shows that
version and commit, and installs only that exact commit.

* fix(plugins): let the Git install dialog close while a version check runs
2026-10-30 19:10:28 -07:00

81 lines
3.8 KiB
TypeScript

import { mkdir, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import { expect, it } from 'vitest'
import {
pluginGitCloneArgs,
pluginGitConfigArgs,
pluginGitFetchCommitArgs,
pluginGitListRefsArgs,
pluginGitRemoteHeadArgs,
pluginGitTreeArgs
} from './plugins/installation/plugin-git-arguments'
export function registerPluginGitDownloadBinaryCompatibilityCase(
runGit: (
args: string[],
env?: NodeJS.ProcessEnv,
stdin?: string
) => Promise<{ stdout: string; stderr: string }>,
getRepoPath: () => string
): void {
// Why pin this: plugin downloads pass every `-c` key and flag below to the user's Git, and a
// local path counts as the file transport, so the protocol policy is observable offline.
it('downloads a plugin commit with the exact no-checkout arguments', async () => {
const source = 'compat-plugin-source'
await mkdir(join(getRepoPath(), source, 'bin'), { recursive: true })
await writeFile(join(getRepoPath(), source, 'bin', 'run'), 'plugin\n', { mode: 0o755 })
await runGit(['init', '-q', source])
await runGit(['-C', source, 'add', '--all'])
// Why: Windows ignores the 0o755 file mode, so record the executable bit in the index directly.
await runGit(['-C', source, 'update-index', '--chmod=+x', 'bin/run'])
// Why inline identity: a fresh repository does not inherit the fixture repository's config.
const identity = [
'-c',
'user.name=Compatibility Test',
'-c',
'user.email=compatibility@example.invalid'
]
await runGit(['-C', source, ...identity, 'commit', '-qm', 'plugin'])
await runGit(['-C', source, 'tag', 'v1'])
const commit = (await runGit(['-C', source, 'rev-parse', 'HEAD'])).stdout.trim()
const config = pluginGitConfigArgs('compat-no-hooks')
const allowLocal = ['-c', 'protocol.file.allow=always']
await expect(
runGit([...config, ...pluginGitCloneArgs(source, 'v1', 'refused')])
).rejects.toThrow("transport 'file' not allowed")
// Why: the install dialog pins a version with these before anything downloads.
await runGit(['-C', source, ...identity, 'tag', '-a', '-m', 'release', 'v2'])
const refs = await runGit([...config, ...allowLocal, ...pluginGitListRefsArgs(source)])
expect(refs.stdout).toContain(`${commit}\trefs/tags/v1\n`)
expect(refs.stdout).toContain(`${commit}\trefs/tags/v2^{}\n`)
expect(refs.stdout).toMatch(new RegExp(`${commit}\trefs/heads/\\S+\n`))
const head = await runGit([...config, ...allowLocal, ...pluginGitRemoteHeadArgs(source)])
expect(head.stdout).toBe(`${commit}\tHEAD\n`)
await runGit([...config, ...allowLocal, ...pluginGitCloneArgs(source, 'v1', 'compat-clone')])
await runGit([...config, 'init', '--quiet', '--template=', 'compat-fetch'])
await runGit(['-C', 'compat-fetch', ...config, 'remote', 'add', 'origin', `../${source}`])
await runGit([
'-C',
'compat-fetch',
...config,
...allowLocal,
...pluginGitFetchCommitArgs(commit)
])
for (const repository of ['compat-clone', 'compat-fetch']) {
const tree = await runGit(['-C', repository, ...pluginGitTreeArgs(commit)])
const [directory, file] = tree.stdout.split('\0')
expect(directory).toMatch(/^040000 tree [0-9a-f]{40} +-\tbin$/)
expect(file).toMatch(/^100755 blob [0-9a-f]{40} +7\tbin\/run$/)
const object = file?.slice(12, 52) ?? ''
const blob = await runGit(['-C', repository, 'cat-file', '--batch'], {}, `${object}\n`)
expect(blob.stdout).toBe(`${object} blob 7\nplugin\n\n`)
// Why: a catalog `path` lists one directory, named relative to it, through `<commit>:<path>`.
const subtree = await runGit(['-C', repository, ...pluginGitTreeArgs(commit, 'bin')])
expect(subtree.stdout).toBe(`100755 blob ${object} 7\trun\0`)
}
})
}