import { ArtifactStore } from './artifacts'; import { GitTransport } from './git-transport'; import { GitMetadataReader } from './git-metadata-reader'; import { publicGitSource } from './git-source'; import { connectedGitObjects } from './git-connectivity'; import { resolveDid } from './identity'; import { registry } from './state'; import { accountDid, branchName, fail, type RepoRecord } from './types'; /** Imports only a verified source branch's objects and pins the submitted head on * a private ref. Contributors cannot choose a destination ref or overwrite code. */ export async function retainCurrentPatch( env: Env, target: RepoRecord, input: Record, keep: string, ) { const sourceDid = accountDid(input.sourceRepo), sourceBranch = branchName(input.sourceBranch ?? 'main'); const head = (input.head as { oid?: string } | undefined)?.oid, base = (input.base as { oid?: string } | undefined)?.oid; if ( !head || !base || !/^[0-9a-f]{40}$/.test(head) || !/^[0-9a-f]{40}$/.test(base) ) fail(400, 'InvalidRequest', 'base and head must name SHA-1 commits'); const destinationReader = new GitMetadataReader( new ArtifactStore(env, target.branch), target.artifact, ); if (!(await destinationReader.publicMatch(base))) fail(400, 'InvalidRequest', 'patch base is not in the target repository'); const local = await registry(env).get(sourceDid, true); let source: GitTransport; if (local?.state === 'ready') source = new GitTransport( new ArtifactStore(env, local.branch), local.artifact, ); else { const document = await resolveDid(env, sourceDid), service = document.service?.find( (s) => s.id === '#tangled_knot' || s.id === sourceDid + '#tangled_knot', ); if (!service || typeof service.serviceEndpoint !== 'string') fail(400, 'InvalidRequest', 'source DID has no knot service'); const origin = new URL(service.serviceEndpoint); if (origin.protocol !== 'https:' || origin.username || origin.password) fail(400, 'InvalidRequest', 'invalid source knot endpoint'); source = publicGitSource(origin.origin + '/' + sourceDid); } const graph = await source.fetchRefs(['refs/heads/' + sourceBranch]); const reachable = connectedGitObjects(graph.objects, [ graph.refs['refs/heads/' + sourceBranch], ]); if ( !reachable.some((object) => object.oid === head && object.type === 'commit') ) fail( 400, 'InvalidRequest', 'patch head is not reachable from the source branch', ); const objects = connectedGitObjects(graph.objects, [head]); await new GitTransport( new ArtifactStore(env, target.branch), target.artifact, ).compareAndSwap( 'refs/hidden/current-patches/' + keep, '0'.repeat(40), head, objects, ); }