Upload via Git Manager GUI - main.js
This commit is contained in:
136
main.js
136
main.js
@@ -41,6 +41,7 @@ const {
|
|||||||
// GitHub
|
// GitHub
|
||||||
listGithubRepos,
|
listGithubRepos,
|
||||||
getGithubCurrentUser,
|
getGithubCurrentUser,
|
||||||
|
githubRepoExists,
|
||||||
getGithubUserHeatmap,
|
getGithubUserHeatmap,
|
||||||
getGithubRepoContents,
|
getGithubRepoContents,
|
||||||
getGithubFileContent,
|
getGithubFileContent,
|
||||||
@@ -56,6 +57,7 @@ const {
|
|||||||
editGithubRelease,
|
editGithubRelease,
|
||||||
deleteGithubRelease,
|
deleteGithubRelease,
|
||||||
updateGithubRepoVisibility,
|
updateGithubRepoVisibility,
|
||||||
|
updateGithubRepoDefaultBranch,
|
||||||
updateGithubRepoTopics,
|
updateGithubRepoTopics,
|
||||||
deleteGithubRepo
|
deleteGithubRepo
|
||||||
} = require('./src/git/apiHandler.js');
|
} = require('./src/git/apiHandler.js');
|
||||||
@@ -3507,6 +3509,7 @@ ipcMain.handle('check-clone-target-collisions', async (_event, data) => {
|
|||||||
|
|
||||||
ipcMain.handle('sync-repo-to-github', async (event, data) => {
|
ipcMain.handle('sync-repo-to-github', async (event, data) => {
|
||||||
let mirrorDir = null;
|
let mirrorDir = null;
|
||||||
|
let sourceDefaultBranch = '';
|
||||||
try {
|
try {
|
||||||
const creds = readCredentials();
|
const creds = readCredentials();
|
||||||
if (!creds?.githubToken) {
|
if (!creds?.githubToken) {
|
||||||
@@ -3542,8 +3545,16 @@ ipcMain.handle('sync-repo-to-github', async (event, data) => {
|
|||||||
repoCreated = true;
|
repoCreated = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = String(e?.message || e || '');
|
const msg = String(e?.message || e || '');
|
||||||
if (!/already exists|existiert bereits|name already exists/i.test(msg)) {
|
if (!/already exists|already_exists|existiert bereits|name already exists/i.test(msg)) {
|
||||||
throw e;
|
// Falls GitHub mit einer unscharfen Meldung antwortet, explizit auf Existenz pruefen.
|
||||||
|
const exists = await githubRepoExists({
|
||||||
|
token: creds.githubToken,
|
||||||
|
owner: targetOwner,
|
||||||
|
repo
|
||||||
|
}).catch(() => false);
|
||||||
|
if (!exists) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3575,29 +3586,133 @@ ipcMain.handle('sync-repo-to-github', async (event, data) => {
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
mirrorDir = fs.mkdtempSync(ppath.join(os.tmpdir(), 'git-manager-sync-'));
|
mirrorDir = fs.mkdtempSync(ppath.join(os.tmpdir(), 'git-manager-sync-'));
|
||||||
const cloneArgs = cloneNeedsGiteaHeader
|
|
||||||
? ['-c', `http.${sourceOrigin}.extraheader=AUTHORIZATION: token ${creds.giteaToken}`, 'clone', '--mirror', sourceCloneUrl, mirrorDir]
|
|
||||||
: ['clone', '--mirror', sourceCloneUrl, mirrorDir];
|
|
||||||
|
|
||||||
|
// --bare statt --mirror: lädt alle Branches/Tags ohne mirror-Flag zu setzen,
|
||||||
|
// damit anschließende Refspec-Pushes (Force) funktionieren.
|
||||||
|
const cloneArgs = cloneNeedsGiteaHeader
|
||||||
|
? ['-c', `http.${sourceOrigin}.extraheader=AUTHORIZATION: token ${creds.giteaToken}`, 'clone', '--bare', sourceCloneUrl, mirrorDir]
|
||||||
|
: ['clone', '--bare', sourceCloneUrl, mirrorDir];
|
||||||
|
|
||||||
|
// runGit: gibt stdout zurück, loggt stderr immer (git push schreibt Status auf stderr)
|
||||||
const runGit = (args, cwd) => {
|
const runGit = (args, cwd) => {
|
||||||
const res = spawnSync('git', args, {
|
const res = spawnSync('git', args, {
|
||||||
cwd,
|
cwd,
|
||||||
encoding: 'utf8',
|
encoding: 'utf8',
|
||||||
windowsHide: true
|
windowsHide: true
|
||||||
});
|
});
|
||||||
|
const out = (res.stdout || '').trim();
|
||||||
|
const err = (res.stderr || '').trim();
|
||||||
|
if (err) console.log(`[sync-repo-to-github][git] ${err}`);
|
||||||
if (res.status !== 0) {
|
if (res.status !== 0) {
|
||||||
throw new Error((res.stderr || res.stdout || 'Git-Fehler').trim());
|
throw new Error((err || out || 'Git-Fehler').trim());
|
||||||
}
|
}
|
||||||
return (res.stdout || '').trim();
|
return out;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log(`[sync-repo-to-github] Klone Quell-Repo (bare): ${sourceCloneUrl}`);
|
||||||
runGit(cloneArgs, process.cwd());
|
runGit(cloneArgs, process.cwd());
|
||||||
|
console.log('[sync-repo-to-github] Bare-Clone abgeschlossen');
|
||||||
|
|
||||||
|
// Alle lokalen Branches auflisten
|
||||||
|
const allBranchesRaw = runGit(['for-each-ref', '--format=%(refname:short)', 'refs/heads/'], mirrorDir);
|
||||||
|
const allBranches = (allBranchesRaw || '').split('\n').map(s => s.trim()).filter(s => s && isSafeGitRef(s));
|
||||||
|
console.log(`[sync-repo-to-github] Lokale Branches: ${allBranches.join(', ') || '(keine)'}`);
|
||||||
|
|
||||||
|
// Default-Branch ermitteln
|
||||||
|
try {
|
||||||
|
const headRef = runGit(['symbolic-ref', '--short', 'HEAD'], mirrorDir);
|
||||||
|
sourceDefaultBranch = String(headRef || '').replace(/^refs\/heads\//, '').trim();
|
||||||
|
if (!isSafeGitRef(sourceDefaultBranch)) sourceDefaultBranch = '';
|
||||||
|
} catch (_) {
|
||||||
|
sourceDefaultBranch = allBranches[0] || '';
|
||||||
|
}
|
||||||
|
if (!sourceDefaultBranch && allBranches.length > 0) sourceDefaultBranch = allBranches[0];
|
||||||
|
console.log(`[sync-repo-to-github] Quell-Default-Branch: "${sourceDefaultBranch || '(unbekannt)'}"`);
|
||||||
|
|
||||||
const githubRemoteUrl = `https://github.com/${targetOwner}/${repo}.git`;
|
const githubRemoteUrl = `https://github.com/${targetOwner}/${repo}.git`;
|
||||||
const githubAuthHeader = `AUTHORIZATION: basic ${Buffer.from(`x-access-token:${creds.githubToken}`, 'utf8').toString('base64')}`;
|
const githubAuthHeader = `AUTHORIZATION: basic ${Buffer.from(`x-access-token:${creds.githubToken}`, 'utf8').toString('base64')}`;
|
||||||
|
const ghAuthArgs = ['-c', 'credential.helper=', '-c', `http.https://github.com/.extraheader=${githubAuthHeader}`];
|
||||||
|
|
||||||
runGit(['remote', 'set-url', '--push', 'origin', githubRemoteUrl], mirrorDir);
|
// GitHub als Push-Remote setzen
|
||||||
runGit(['-c', 'credential.helper=', '-c', `http.https://github.com/.extraheader=${githubAuthHeader}`, 'push', '--mirror'], mirrorDir);
|
runGit(['remote', 'set-url', 'origin', githubRemoteUrl], mirrorDir);
|
||||||
|
|
||||||
|
// Lokale Commit-SHAs loggen (= Gitea-Stand)
|
||||||
|
for (const branch of allBranches) {
|
||||||
|
try {
|
||||||
|
const localSha = runGit(['rev-parse', `refs/heads/${branch}`], mirrorDir);
|
||||||
|
console.log(`[sync-repo-to-github] Gitea ${branch}: ${localSha}`);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remote (GitHub) Commit-SHAs VOR dem Push ermitteln und vergleichen
|
||||||
|
try {
|
||||||
|
const remoteRefsRaw = runGit([...ghAuthArgs, 'ls-remote', '--heads', 'origin'], mirrorDir);
|
||||||
|
const remoteMap = {};
|
||||||
|
(remoteRefsRaw || '').split('\n').forEach(line => {
|
||||||
|
const parts = line.trim().split(/\s+/);
|
||||||
|
if (parts.length === 2) {
|
||||||
|
const branch = parts[1].replace('refs/heads/', '');
|
||||||
|
remoteMap[branch] = parts[0];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (const branch of allBranches) {
|
||||||
|
const remoteSha = remoteMap[branch] || '(nicht vorhanden)';
|
||||||
|
console.log(`[sync-repo-to-github] GitHub ${branch}: ${remoteSha}`);
|
||||||
|
}
|
||||||
|
} catch (lsErr) {
|
||||||
|
console.warn('[sync-repo-to-github] ls-remote Warnung:', lsErr?.message || lsErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jeden Branch einzeln force-pushen für maximale Zuverlässigkeit
|
||||||
|
console.log(`[sync-repo-to-github] Force-Push ${allBranches.length} Branch(es) nach GitHub: ${githubRemoteUrl}`);
|
||||||
|
let pushedCount = 0;
|
||||||
|
for (const branch of allBranches) {
|
||||||
|
console.log(`[sync-repo-to-github] Pushe Branch: ${branch}`);
|
||||||
|
runGit([...ghAuthArgs, 'push', '--force', 'origin', `refs/heads/${branch}:refs/heads/${branch}`], mirrorDir);
|
||||||
|
pushedCount++;
|
||||||
|
}
|
||||||
|
console.log(`[sync-repo-to-github] ${pushedCount} Branch(es) erfolgreich gepusht`);
|
||||||
|
|
||||||
|
// Auf GitHub nicht mehr vorhandene Branches aufräumen (prune via API, non-fatal)
|
||||||
|
try {
|
||||||
|
const remoteRefsRaw = runGit([...ghAuthArgs, 'ls-remote', '--heads', 'origin'], mirrorDir);
|
||||||
|
const remoteHeads = (remoteRefsRaw || '').split('\n')
|
||||||
|
.map(l => { const m = l.match(/refs\/heads\/(.+)$/); return m ? m[1].trim() : null; })
|
||||||
|
.filter(Boolean);
|
||||||
|
const localSet = new Set(allBranches);
|
||||||
|
for (const remoteBranch of remoteHeads) {
|
||||||
|
if (!localSet.has(remoteBranch) && isSafeGitRef(remoteBranch)) {
|
||||||
|
console.log(`[sync-repo-to-github] Lösche veralteten Remote-Branch: ${remoteBranch}`);
|
||||||
|
runGit([...ghAuthArgs, 'push', 'origin', `--delete`, remoteBranch], mirrorDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (pruneErr) {
|
||||||
|
console.warn('[sync-repo-to-github] Prune-Warnung:', pruneErr?.message || pruneErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tags mit Force-Push übertragen (non-fatal)
|
||||||
|
try {
|
||||||
|
runGit([...ghAuthArgs, 'push', 'origin', '--tags', '--force'], mirrorDir);
|
||||||
|
console.log('[sync-repo-to-github] Tags erfolgreich gepusht');
|
||||||
|
} catch (tagsErr) {
|
||||||
|
console.warn('[sync-repo-to-github] Tags-Push Warnung:', tagsErr?.message || tagsErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default-Branch auf GitHub per API angleichen
|
||||||
|
if (sourceDefaultBranch) {
|
||||||
|
console.log(`[sync-repo-to-github] Setze GitHub Default-Branch auf: ${sourceDefaultBranch}`);
|
||||||
|
try {
|
||||||
|
await updateGithubRepoDefaultBranch({
|
||||||
|
token: creds.githubToken,
|
||||||
|
owner: targetOwner,
|
||||||
|
repo,
|
||||||
|
defaultBranch: sourceDefaultBranch
|
||||||
|
});
|
||||||
|
console.log('[sync-repo-to-github] Default-Branch aktualisiert');
|
||||||
|
} catch (defaultBranchErr) {
|
||||||
|
console.warn('sync-repo-to-github: default-branch update warning', defaultBranchErr?.message || defaultBranchErr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Optional: Topics auf GitHub angleichen (nur falls übergeben)
|
// Optional: Topics auf GitHub angleichen (nur falls übergeben)
|
||||||
const topics = Array.isArray(data?.topics) ? data.topics.filter(Boolean) : [];
|
const topics = Array.isArray(data?.topics) ? data.topics.filter(Boolean) : [];
|
||||||
@@ -3617,7 +3732,8 @@ ipcMain.handle('sync-repo-to-github', async (event, data) => {
|
|||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
repoCreated,
|
repoCreated,
|
||||||
githubRepo: `${targetOwner}/${repo}`
|
githubRepo: `${targetOwner}/${repo}`,
|
||||||
|
sourceDefaultBranch: sourceDefaultBranch || null
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('sync-repo-to-github error', e);
|
console.error('sync-repo-to-github error', e);
|
||||||
|
|||||||
Reference in New Issue
Block a user