(params: GitBundleSyncParams)
| 93 | } |
| 94 | |
| 95 | export async function syncProjectViaGitBundle(params: GitBundleSyncParams): Promise<void> { |
| 96 | const { |
| 97 | projectPath, |
| 98 | workspacePath, |
| 99 | remoteTmpDir, |
| 100 | remoteBundlePath, |
| 101 | exec, |
| 102 | quoteRemotePath, |
| 103 | initLogger, |
| 104 | logOriginErrors, |
| 105 | abortSignal, |
| 106 | createRemoteBundle, |
| 107 | cloneStep, |
| 108 | } = params; |
| 109 | |
| 110 | if (abortSignal?.aborted) { |
| 111 | throw new Error("Sync operation aborted before starting"); |
| 112 | } |
| 113 | |
| 114 | const { originUrl } = await getOriginUrlForBundle( |
| 115 | projectPath, |
| 116 | initLogger, |
| 117 | logOriginErrors ?? false |
| 118 | ); |
| 119 | |
| 120 | // Ensure the bundle exists on the remote runtime. |
| 121 | initLogger.logStep("Creating git bundle..."); |
| 122 | let createResult: Awaited<ReturnType<GitBundleSyncParams["createRemoteBundle"]>>; |
| 123 | try { |
| 124 | createResult = await createRemoteBundle({ remoteBundlePath, initLogger, abortSignal }); |
| 125 | } catch (error) { |
| 126 | // Best-effort cleanup (remote bundle may have been partially written). |
| 127 | try { |
| 128 | const rmStream = await exec(`rm -f ${quoteRemotePath(remoteBundlePath)}`, { |
| 129 | cwd: remoteTmpDir, |
| 130 | timeout: 10, |
| 131 | abortSignal, |
| 132 | }); |
| 133 | await rmStream.exitCode; |
| 134 | } catch { |
| 135 | // Ignore cleanup errors. |
| 136 | } |
| 137 | |
| 138 | throw error; |
| 139 | } |
| 140 | |
| 141 | try { |
| 142 | // Clone from the bundle on the remote runtime. |
| 143 | initLogger.logStep(cloneStep); |
| 144 | const cloneStream = await exec( |
| 145 | `${gitNoHooksPrefix(params.trusted)}git clone --quiet ${quoteRemotePath(remoteBundlePath)} ${quoteRemotePath(workspacePath)}`, |
| 146 | { |
| 147 | cwd: remoteTmpDir, |
| 148 | timeout: 300, |
| 149 | abortSignal, |
| 150 | } |
| 151 | ); |
| 152 |
no test coverage detected