| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- import { mkdtempSync, rmSync } from 'node:fs'
- import { tmpdir } from 'node:os'
- import { join } from 'node:path'
- import { spawnSync } from 'node:child_process'
- import { normalizeReleaseError } from './release-error-normalizer.mjs'
- function runGit(args, options = {}) {
- const result = spawnSync('git', args, {
- cwd: options.cwd,
- encoding: 'utf8',
- stdio: 'pipe',
- timeout: options.timeout ?? 20_000
- })
- const error = result.error instanceof Error ? result.error.message : ''
- return {
- ok: result.status === 0,
- status: result.status,
- signal: result.signal || '',
- stdout: (result.stdout || '').trim(),
- stderr: (result.stderr || '').trim(),
- error
- }
- }
- function commandError(result) {
- return normalizeReleaseError(result.error || result.stderr || result.stdout || result.signal || 'unknown error')
- }
- export function localHeadTree(repoDir) {
- const head = runGit(['rev-parse', 'HEAD'], { cwd: repoDir })
- const tree = runGit(['rev-parse', 'HEAD^{tree}'], { cwd: repoDir })
- return {
- ok: head.ok && tree.ok && Boolean(head.stdout) && Boolean(tree.stdout),
- hash: head.stdout,
- tree: tree.stdout,
- error: head.ok && tree.ok ? '' : commandError(head.ok ? tree : head)
- }
- }
- export function remoteHeadTree(url, branch = 'main', options = {}) {
- const workdir = mkdtempSync(join(tmpdir(), 'file-viewer-remote-tree-'))
- try {
- const init = runGit(['init', '-q'], { cwd: workdir, timeout: options.timeout })
- if (!init.ok) {
- return {
- ok: false,
- tree: '',
- error: commandError(init)
- }
- }
- const fetch = runGit(
- [
- '-c',
- 'protocol.version=2',
- 'fetch',
- '--quiet',
- '--depth=1',
- '--filter=blob:none',
- '--no-tags',
- url,
- `refs/heads/${branch}`
- ],
- { cwd: workdir, timeout: options.timeout }
- )
- if (!fetch.ok) {
- return {
- ok: false,
- tree: '',
- error: commandError(fetch)
- }
- }
- const tree = runGit(['rev-parse', 'FETCH_HEAD^{tree}'], { cwd: workdir, timeout: options.timeout })
- return {
- ok: tree.ok && Boolean(tree.stdout),
- tree: tree.stdout,
- error: tree.ok && tree.stdout ? '' : commandError(tree)
- }
- } finally {
- rmSync(workdir, { recursive: true, force: true })
- }
- }
- export function comparePublicMirrorTrees({ publicRepoDir, githubHead, giteeHead, giteeUrl, branch = 'main', timeout }) {
- if (!githubHead.ok || !giteeHead.ok) {
- return {
- inSync: false,
- mode: 'missing',
- checked: false,
- referenceTree: '',
- giteeTree: '',
- error: 'missing public repository head'
- }
- }
- if (githubHead.hash === giteeHead.hash) {
- return {
- inSync: true,
- mode: 'commit',
- checked: false,
- referenceTree: '',
- giteeTree: '',
- error: ''
- }
- }
- const reference = localHeadTree(publicRepoDir)
- if (!reference.ok || reference.hash !== githubHead.hash) {
- return {
- inSync: false,
- mode: 'unchecked',
- checked: false,
- referenceTree: reference.tree,
- giteeTree: '',
- error: reference.ok
- ? `local public repository ${reference.hash.slice(0, 12)} does not match GitHub ${githubHead.hash.slice(0, 12)}`
- : reference.error
- }
- }
- const gitee = remoteHeadTree(giteeUrl, branch, { timeout })
- if (!gitee.ok) {
- return {
- inSync: false,
- mode: 'unchecked',
- checked: false,
- referenceTree: reference.tree,
- giteeTree: '',
- error: gitee.error
- }
- }
- return {
- inSync: reference.tree === gitee.tree,
- mode: reference.tree === gitee.tree ? 'tree' : 'stale',
- checked: true,
- referenceTree: reference.tree,
- giteeTree: gitee.tree,
- error: ''
- }
- }
|