diff --git a/apps/api/src/services/githubPusher.ts b/apps/api/src/services/githubPusher.ts index ec038f6..3d4d211 100644 --- a/apps/api/src/services/githubPusher.ts +++ b/apps/api/src/services/githubPusher.ts @@ -72,36 +72,19 @@ async function emitProgress( } async function ensureMainBranchExists(octokit: Octokit, owner: string, repo: string): Promise { - try { - await octokit.repos.getBranch({ - owner, - repo, - branch: "main", - }); - return; - } catch { - // Branch does not exist yet. Initialize it below. + // With auto_init the repo is created with an initial commit on `main`, but + // GitHub populates it asynchronously. Poll briefly until the branch is + // visible before pushing files. + for (let attempt = 0; attempt < 10; attempt++) { + try { + await octokit.repos.getBranch({ owner, repo, branch: "main" }); + return; + } catch { + await wait(500); + } } - const tree = await octokit.git.createTree({ - owner, - repo, - tree: [], - }); - - const commit = await octokit.git.createCommit({ - owner, - repo, - message: "chore: initialize main branch", - tree: tree.data.sha, - }); - - await octokit.git.createRef({ - owner, - repo, - ref: "refs/heads/main", - sha: commit.data.sha, - }); + throw new Error("Timed out waiting for the 'main' branch to be initialized"); } export async function pushToGitHub( @@ -124,7 +107,7 @@ export async function pushToGitHub( name: repoName, description: "Generated by StackForge", private: false, - auto_init: false, + auto_init: true, }); const owner = repo.data.owner?.login ?? authUser.data.login; @@ -148,6 +131,23 @@ export async function pushToGitHub( } for (const [filePath, content] of entries) { + // The repo was auto-initialized with a README, so updating an existing + // file requires its current blob sha. Look it up; absent for new files. + let existingSha: string | undefined; + try { + const existing = await octokit.repos.getContent({ + owner, + repo: repoName, + path: filePath, + ref: "main", + }); + if (!Array.isArray(existing.data) && "sha" in existing.data) { + existingSha = existing.data.sha; + } + } catch { + // File does not exist yet; create it fresh. + } + await octokit.repos.createOrUpdateFileContents({ owner, repo: repoName, @@ -155,6 +155,7 @@ export async function pushToGitHub( message: "feat: initial project generated by StackForge", content: Buffer.from(content, "utf8").toString("base64"), branch: "main", + ...(existingSha !== undefined ? { sha: existingSha } : {}), }); await emitProgress(onProgress, {