feat: add safe Gitea sync and signed updates
ForgeFlow quality gate / secret-scan (push) Failing after 32s
ForgeFlow quality gate / quality (push) Failing after 0s

This commit is contained in:
NuklearRabbit
2026-08-27 00:38:58 +02:00
parent cb9bdcd713
commit d47c7b5e41
46 changed files with 1658 additions and 246 deletions
+145 -1
View File
@@ -5,7 +5,7 @@ function createMockRepositoryBridge(context) {
await wait(80);
snapshot();
return {
appVersion: "0.10.12-demo",
appVersion: "0.10.13-demo",
platform: "win32",
state: clone(state),
git: { available: true, version: "git version 2.47.3" },
@@ -427,6 +427,71 @@ function createMockRepositoryBridge(context) {
emitRepositories();
return { output: "Fast-forwarded.", status: clone(repo.localStatus) };
},
async previewWorkspaceSync(localPath) {
await wait(260);
const repo = findRepo(localPath);
const status = repo.localStatus;
const targetSha = status.branch.behind ? "f".repeat(40) : status.head;
return {
id: `demo-${String(status.head).slice(0, 7)}-${status.branch.ahead}-${status.branch.behind}`.padEnd(64, "0").slice(0, 64),
branch: status.branch.head,
upstream: status.branch.upstream || `origin/${status.branch.head}`,
currentSha: status.head,
targetSha,
needsSync: !status.clean || status.head !== targetSha || status.branch.ahead > 0,
blockers: [],
summary: {
resultingTrackedChanges: status.branch.behind ? 3 : 0,
added: status.branch.behind ? 1 : 0,
modified: status.branch.behind ? 1 : 0,
deleted: status.branch.behind ? 1 : 0,
renamed: 0,
localFilesToStash: status.counts.changed,
untrackedFilesToStash: status.counts.untracked,
localCommitsToProtect: status.branch.ahead,
incomingCommits: status.branch.behind,
},
changes: status.branch.behind
? [
{ code: "A", status: "added", path: "src/remote-feature.js" },
{ code: "M", status: "modified", path: "README.md" },
{ code: "D", status: "deleted", path: "docs/obsolete.md" },
]
: [],
localFiles: clone(status.files),
incomingCommits: [],
localCommits: [],
recovery: {
safetyBranch: status.branch.ahead > 0,
stash: status.counts.changed > 0,
untrackedCleanup: status.counts.untracked > 0,
ignoredFilesPreserved: true,
},
};
},
async applyWorkspaceSync(localPath, expectedPlanId) {
const plan = await this.previewWorkspaceSync(localPath);
if (plan.id !== expectedPlanId) throw new Error("The workspace sync preview is stale.");
const repo = findRepo(localPath);
const hadChanges = repo.localStatus.counts.changed > 0;
repo.localStatus.head = plan.targetSha;
repo.localStatus.shortHead = plan.targetSha.slice(0, 7);
repo.localStatus.files = [];
repo.localStatus.branch.ahead = 0;
repo.localStatus.branch.behind = 0;
recompute(repo);
emitRepositories();
return {
applied: plan.needsSync,
unchanged: !plan.needsSync,
plan,
status: clone(repo.localStatus),
backupBranch: plan.summary.localCommitsToProtect ? `forgeflow/recovery-${plan.branch}-demo` : null,
stash: hadChanges ? { ref: "stash@{0}", shortSha: "demo123", subject: "ForgeFlow workspace sync" } : null,
ignoredFilesPreserved: true,
cleaned: [],
};
},
async history() {
await wait(100);
return clone(commitHistory);
@@ -556,6 +621,85 @@ function createMockRepositoryBridge(context) {
stashes: clone(list),
};
},
async gitRecoveryStatus(localPath) {
const repo = findRepo(localPath);
const status = clone(repo.localStatus);
const upstream = status.branch?.upstream;
const recommendations = [
{
id: "fetch",
label: "Fetch and recalculate remote state",
action: "fetch",
safe: true,
},
];
if (
status.clean &&
status.branch.behind > 0 &&
status.branch.ahead === 0 &&
upstream
) {
recommendations.push({
id: "pull",
label: `Fast-forward from ${upstream}`,
action: "fast-forward",
safe: true,
});
}
if (
status.branch.ahead > 0 &&
status.branch.behind === 0 &&
upstream
) {
recommendations.push({
id: "push",
label: `Push ${status.branch.ahead} local commit(s)`,
action: "push",
safe: true,
});
}
return {
status,
lockReport: {
root: localPath,
gitDir: `${localPath}\\.git`,
locks: [],
processes: { available: true, active: [] },
},
recommendations,
};
},
async reconcileRepository(localPath) {
await wait(160);
return this.gitRecoveryStatus(localPath);
},
async repairGitLocks(localPath) {
return {
...(await this.gitRecoveryStatus(localPath)).lockReport,
removed: [],
skipped: [],
repaired: false,
};
},
async repairRepositorySync(localPath, strategy) {
const repo = findRepo(localPath);
if (strategy === "fast-forward") {
repo.localStatus.branch.behind = 0;
repo.localStatus.head = "f".repeat(40);
} else if (strategy === "push") {
repo.localStatus.branch.ahead = 0;
} else if (strategy !== "fetch") {
throw new Error("Unsupported demo synchronization strategy.");
}
recompute(repo);
emitRepositories();
return {
strategy,
backupBranch: null,
status: clone(repo.localStatus),
lockReport: (await this.gitRecoveryStatus(localPath)).lockReport,
};
},
async indexLockInfo() {
return { exists: false, ageMs: 0 };
},