mirror of
https://github.com/better-auth/better-auth.git
synced 2026-10-19 23:53:17 -05:00
refactor(cli): limit identity assessment to migration state
This commit is contained in:
@@ -43,7 +43,7 @@ npx auth@latest migrate plan
|
||||
npx auth@latest migrate plan --json > better-auth-migration-plan.json
|
||||
```
|
||||
|
||||
The JSON plan includes the adapter, dialect, selected and detected account identity strategies, account counts, issuer-resolution coverage, projected collisions, sorted schema changes, blockers, and any release migration actions. A blocked plan exits with status `1`.
|
||||
The JSON plan includes the adapter, dialect, selected and detected account identity strategies, sorted schema changes, blockers, and any release migration actions. A blocked plan exits with status `1`.
|
||||
|
||||
### Apply a migration
|
||||
|
||||
|
||||
@@ -144,7 +144,6 @@ it("plans a default provider-scoped upgrade against Drizzle snake_case columns",
|
||||
expect(inspection.accountIdentity).toMatchObject({
|
||||
selectedStrategy: "provider-id",
|
||||
detectedStrategy: "provider-id",
|
||||
totalAccounts: 1,
|
||||
});
|
||||
expect(inspection.migrationBlockers).not.toContainEqual(
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -583,11 +583,6 @@ export interface AccountIdentityMigrationAssessment {
|
||||
}
|
||||
| undefined;
|
||||
migrationRequired: boolean;
|
||||
totalAccounts: number;
|
||||
externalAccounts: number;
|
||||
automaticIssuerResolution?: { resolved: number; total: number } | undefined;
|
||||
projectedCollisions: number;
|
||||
manualReviewProviders: string[];
|
||||
}
|
||||
|
||||
/** Inspects the configured account-identity path without changing the database. */
|
||||
@@ -606,10 +601,6 @@ export async function inspectAccountIdentityMigration(
|
||||
detectedStrategy: "empty",
|
||||
physicalSchema,
|
||||
migrationRequired: false,
|
||||
totalAccounts: 0,
|
||||
externalAccounts: 0,
|
||||
projectedCollisions: 0,
|
||||
manualReviewProviders: [],
|
||||
});
|
||||
if (!accountSchema) return createEmptyAssessment();
|
||||
const physicalSchema = {
|
||||
@@ -656,42 +647,6 @@ export async function inspectAccountIdentityMigration(
|
||||
: accountsWithIssuer === accounts.length
|
||||
? "issuer"
|
||||
: "mixed";
|
||||
const configuredIssuers = await resolveConfiguredIssuerState(config);
|
||||
const isExternal = (providerId: string) =>
|
||||
(configuredIssuers.providerKinds[providerId] ??
|
||||
(providerId === "credential" || providerId === "siwe"
|
||||
? "local"
|
||||
: "external")) === "external";
|
||||
const externalAccounts = accounts.filter((account) =>
|
||||
isExternal(account.providerId),
|
||||
);
|
||||
const manualReviewProviders =
|
||||
selectedStrategy === "issuer"
|
||||
? [
|
||||
...new Set(
|
||||
externalAccounts
|
||||
.filter(
|
||||
(account) =>
|
||||
!readStoredIssuer(account.issuer) &&
|
||||
!configuredIssuers.issuers[account.providerId],
|
||||
)
|
||||
.map((account) => account.providerId),
|
||||
),
|
||||
].sort()
|
||||
: [];
|
||||
const projectedIdentities = new Set<string>();
|
||||
let projectedCollisions = 0;
|
||||
for (const account of accounts) {
|
||||
const namespace =
|
||||
selectedStrategy === "provider-id"
|
||||
? account.providerId
|
||||
: readStoredIssuer(account.issuer) ||
|
||||
configuredIssuers.issuers[account.providerId];
|
||||
if (!namespace) continue;
|
||||
const identity = JSON.stringify([namespace, account.providerAccountId]);
|
||||
if (projectedIdentities.has(identity)) projectedCollisions += 1;
|
||||
else projectedIdentities.add(identity);
|
||||
}
|
||||
return {
|
||||
selectedStrategy,
|
||||
detectedStrategy,
|
||||
@@ -700,22 +655,6 @@ export async function inspectAccountIdentityMigration(
|
||||
selectedStrategy === "issuer"
|
||||
? detectedStrategy !== "issuer"
|
||||
: detectedStrategy === "issuer" || detectedStrategy === "mixed",
|
||||
totalAccounts: accounts.length,
|
||||
externalAccounts: externalAccounts.length,
|
||||
...(selectedStrategy === "issuer"
|
||||
? {
|
||||
automaticIssuerResolution: {
|
||||
resolved: externalAccounts.filter(
|
||||
(account) =>
|
||||
Boolean(readStoredIssuer(account.issuer)) ||
|
||||
Boolean(configuredIssuers.issuers[account.providerId]),
|
||||
).length,
|
||||
total: externalAccounts.length,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
projectedCollisions,
|
||||
manualReviewProviders,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -198,20 +198,6 @@ export function printHumanMigrationPlan(
|
||||
console.log(
|
||||
`Account identity: ${migrationPlan.accountIdentity.selectedStrategy} (database: ${migrationPlan.accountIdentity.detectedStrategy})`,
|
||||
);
|
||||
console.log(
|
||||
`Accounts: ${migrationPlan.accountIdentity.totalAccounts} total, ${migrationPlan.accountIdentity.externalAccounts} external`,
|
||||
);
|
||||
if (migrationPlan.accountIdentity.automaticIssuerResolution) {
|
||||
console.log(
|
||||
`Automatic issuer resolution: ${migrationPlan.accountIdentity.automaticIssuerResolution.resolved}/${migrationPlan.accountIdentity.automaticIssuerResolution.total}`,
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
`Projected collisions: ${migrationPlan.accountIdentity.projectedCollisions}`,
|
||||
);
|
||||
console.log(
|
||||
`Providers requiring manual issuer review: ${migrationPlan.accountIdentity.manualReviewProviders.join(", ") || "none"}`,
|
||||
);
|
||||
console.log(
|
||||
`Blockers: ${migrationPlan.blockers.map(({ code }) => code).join(", ") || "none"}`,
|
||||
);
|
||||
|
||||
@@ -183,7 +183,7 @@ export function describeIrreversibleReleaseActions(
|
||||
if (
|
||||
accountIdentity.selectedStrategy === "issuer" &&
|
||||
accountIdentity.migrationRequired &&
|
||||
accountIdentity.totalAccounts > 0
|
||||
accountIdentity.detectedStrategy !== "empty"
|
||||
) {
|
||||
actions.push(
|
||||
"write the 1.7 account identity onto every existing account row",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { printHumanMigrationPlan } from "./migrate";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createMigrationPlan } from "./migration-plan";
|
||||
|
||||
describe("createMigrationPlan", () => {
|
||||
@@ -9,10 +8,6 @@ describe("createMigrationPlan", () => {
|
||||
selectedStrategy: "provider-id",
|
||||
detectedStrategy: "issuer",
|
||||
migrationRequired: true,
|
||||
totalAccounts: 4,
|
||||
externalAccounts: 3,
|
||||
projectedCollisions: 0,
|
||||
manualReviewProviders: [],
|
||||
},
|
||||
hasChanges: false,
|
||||
migrationBlockers: [
|
||||
@@ -39,44 +34,4 @@ describe("createMigrationPlan", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("prints issuer readiness and manual-review providers before applying", () => {
|
||||
const plan = createMigrationPlan({
|
||||
accountIdentity: {
|
||||
selectedStrategy: "issuer",
|
||||
detectedStrategy: "provider-id",
|
||||
migrationRequired: true,
|
||||
totalAccounts: 17,
|
||||
externalAccounts: 3,
|
||||
automaticIssuerResolution: { resolved: 2, total: 3 },
|
||||
projectedCollisions: 1,
|
||||
manualReviewProviders: ["custom-oidc"],
|
||||
},
|
||||
hasChanges: true,
|
||||
migrationBlockers: [],
|
||||
migrationTarget: { adapter: "prisma", dialect: "postgres" },
|
||||
toBeAdded: [],
|
||||
toBeAddedIndexes: [],
|
||||
toBeCreated: [],
|
||||
});
|
||||
const log = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
|
||||
try {
|
||||
printHumanMigrationPlan(plan, [], [], []);
|
||||
const output = log.mock.calls
|
||||
.map((parts) => parts.map(String).join(" "))
|
||||
.join("\n");
|
||||
expect(output).toContain(
|
||||
"Account identity: issuer (database: provider-id)",
|
||||
);
|
||||
expect(output).toContain("Automatic issuer resolution: 2/3");
|
||||
expect(output).toContain("Projected collisions: 1");
|
||||
expect(output).toContain(
|
||||
"Providers requiring manual issuer review: custom-oidc",
|
||||
);
|
||||
expect(output).toContain("No database changes were applied.");
|
||||
} finally {
|
||||
log.mockRestore();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -321,10 +321,6 @@ describe("migration plan status", () => {
|
||||
selectedStrategy: "provider-id",
|
||||
detectedStrategy: "provider-id",
|
||||
migrationRequired: false,
|
||||
totalAccounts: 1,
|
||||
externalAccounts: 0,
|
||||
projectedCollisions: 0,
|
||||
manualReviewProviders: [],
|
||||
},
|
||||
hasChanges: false,
|
||||
migrationBlockers: [],
|
||||
@@ -347,10 +343,6 @@ describe("migration plan status", () => {
|
||||
selectedStrategy: "issuer",
|
||||
detectedStrategy: "provider-id",
|
||||
migrationRequired: true,
|
||||
totalAccounts: 1,
|
||||
externalAccounts: 0,
|
||||
projectedCollisions: 0,
|
||||
manualReviewProviders: [],
|
||||
},
|
||||
hasChanges: true,
|
||||
migrationBlockers: [
|
||||
|
||||
Reference in New Issue
Block a user