mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 16:27:18 +02:00
fix(github): retain initial import across interrupted credential storage
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
82a6c5b7bb
commit
b2f0c27bc8
3 files changed
+47
-7
No files matched your search
@@ -106,7 +106,7 @@ enrollment handoff passed. The test did not authorize Cloud enrollment or
|
||||
create a provider App.
|
||||
|
||||
The focused wizard and retained clipboard/recovery UI suites pass (37 tests).
|
||||
GitHub backend coverage passes (192 cases), including both owner types,
|
||||
GitHub backend coverage passes (193 cases), including both owner types,
|
||||
interrupted registration and vault storage, empty saved repository selections,
|
||||
localhost configuration, concurrent identity attempts, enrolled-origin recovery,
|
||||
tenant binding, and legacy callbacks. The complete OpenAPI suite passes (13
|
||||
@@ -123,7 +123,9 @@ covered by the updated focused checks. The full GitHub fixture also verifies
|
||||
installation permissions and preserved legacy tool authority. The browser
|
||||
report attaches screenshots of both wizard screens. Fresh manual App imports
|
||||
initialize repository access and tools without a registration row; interrupted
|
||||
imports resume those defaults while explicit restrictions stay preserved.
|
||||
imports resume those defaults while explicit restrictions stay preserved. A real
|
||||
vault-write interruption stays in recovery without completing an empty import;
|
||||
retrying the credentials discovers and imports the actual repositories.
|
||||
|
||||
Cloud's final standard `npm test` passes: 2,561 tests and 73 environment-dependent
|
||||
skips. Dedicated-App regressions cover normalized concurrent starts, stale
|
||||
|
||||
@@ -1877,6 +1877,7 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
|
||||
useVerifiedAppId = false,
|
||||
registerWithManifest = false,
|
||||
connectExistingApp = false,
|
||||
interruptAppVault = false,
|
||||
) {
|
||||
let setupComplete = false;
|
||||
const deferredSchedule = overrides.scheduleDeferredWork;
|
||||
@@ -2052,10 +2053,26 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
|
||||
);
|
||||
if (registerWithManifest)
|
||||
await context.service.startGitHubRegistration(endpoint.id, "owner-user", "Maya");
|
||||
if (connectExistingApp)
|
||||
await context.service.storeGitHubApp(endpoint.id, "owner-user", {
|
||||
appId: String(appRegistrationId), privateKey, webhookSecret,
|
||||
});
|
||||
if (connectExistingApp) {
|
||||
const credentials = { appId: String(appRegistrationId), privateKey, webhookSecret };
|
||||
if (interruptAppVault) {
|
||||
// Fail the real vault write after App identity has been durably claimed.
|
||||
await db.execute(sql.raw("CREATE FUNCTION github_wizard_fail_vault() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION 'Simulated App vault failure'; END $$"));
|
||||
await db.execute(sql.raw("CREATE TRIGGER github_wizard_fail_vault BEFORE INSERT ON company_secrets FOR EACH ROW WHEN (lower(NEW.key) LIKE '%privatekey%') EXECUTE FUNCTION github_wizard_fail_vault()"));
|
||||
try {
|
||||
await expect(context.service.storeGitHubApp(endpoint.id, "owner-user", credentials)).rejects.toThrow();
|
||||
expect(await context.service.githubWizard.advance(endpoint.id, "owner-user")).toMatchObject({ state: "recovery" });
|
||||
expect((await context.service.get(endpoint.id)).setup.github).toMatchObject({ initialSetupPending: true, initialRepositoryImportPending: true });
|
||||
expect((await context.service.get(endpoint.id)).setup.github?.initialRepositoriesImported).not.toBe(true);
|
||||
expect(await context.service.listResources(endpoint.id)).toHaveLength(0);
|
||||
expect((await githubChatManagementService(db, providerFetch).configuration(endpoint.id, "owner-user")).revision).toBe(0);
|
||||
} finally {
|
||||
await db.execute(sql.raw("DROP TRIGGER github_wizard_fail_vault ON company_secrets"));
|
||||
await db.execute(sql.raw("DROP FUNCTION github_wizard_fail_vault()"));
|
||||
}
|
||||
}
|
||||
await context.service.storeGitHubApp(endpoint.id, "owner-user", credentials);
|
||||
}
|
||||
await context.service.configure(
|
||||
endpoint.id,
|
||||
{
|
||||
@@ -2936,6 +2953,14 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
|
||||
)?.enabled,
|
||||
).toBe(false);
|
||||
});
|
||||
it("manual App credential retry imports repositories after an interrupted vault write", async () => {
|
||||
const f = await seedCompany();
|
||||
const context = await configuredGitHubEndpoint(f, {}, true, false, true, true);
|
||||
await context.service.githubWizard.advance(context.endpoint.id, "owner-user");
|
||||
expect((await context.service.listResources(context.endpoint.id)).map(resource => resource.enabled)).toEqual([true]);
|
||||
expect((await githubChatManagementService(db, context.providerFetch).configuration(context.endpoint.id, "owner-user")).configuration.toolsEnabled).toBe(true);
|
||||
expect((await context.service.get(context.endpoint.id)).setup.github).toMatchObject({ initialRepositoriesImported: true, initialSetupPending: false });
|
||||
});
|
||||
it("manual App setup resumes tool initialization after an interrupted repository import", async () => {
|
||||
const f = await seedCompany();
|
||||
const context = await configuredGitHubEndpoint(f, {}, true, false, true);
|
||||
|
||||
@@ -701,7 +701,20 @@ export function githubChatWizardService(
|
||||
}
|
||||
return { endpointId: id, state: "create" };
|
||||
}
|
||||
if (!bot.providerAccountId || bot.setup.github?.stage === "install") {
|
||||
if (bot.setup.github?.initialSetupPending) {
|
||||
try {
|
||||
const stored = await githubBotCredentials(db, bot.companyId, id);
|
||||
if (!stored.credentials.webhookSecret)
|
||||
throw conflict("The App webhook secret is missing");
|
||||
} catch {
|
||||
return {
|
||||
endpointId: id,
|
||||
state: "recovery",
|
||||
message: "This App's credentials were not completely saved. Recover its existing credentials before continuing.",
|
||||
};
|
||||
}
|
||||
}
|
||||
if (!bot.providerAccountId || bot.setup.github?.stage === "install" || bot.setup.github?.initialSetupPending) {
|
||||
try {
|
||||
await options.refreshRepositories(id, userId);
|
||||
} catch (error) {
|
||||
|
||||
Reference in new issue
Block a user