@dosu/cli 0.11.0-alpha.2 → 0.11.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/dosu.js +129 -21
- package/package.json +1 -1
package/bin/dosu.js
CHANGED
|
@@ -4341,7 +4341,7 @@ function getSupabaseAnonKey() {
|
|
|
4341
4341
|
function getVersionString() {
|
|
4342
4342
|
return `v${VERSION}`;
|
|
4343
4343
|
}
|
|
4344
|
-
var VERSION = "0.11.0-alpha.
|
|
4344
|
+
var VERSION = "0.11.0-alpha.4";
|
|
4345
4345
|
|
|
4346
4346
|
// src/debug/logger.ts
|
|
4347
4347
|
import {
|
|
@@ -7148,6 +7148,7 @@ var init_open = __esm(() => {
|
|
|
7148
7148
|
// src/setup/github-step.ts
|
|
7149
7149
|
var exports_github_step = {};
|
|
7150
7150
|
__export(exports_github_step, {
|
|
7151
|
+
verifyDataSourcesPersist: () => verifyDataSourcesPersist,
|
|
7151
7152
|
stepConnectGitHubRepo: () => stepConnectGitHubRepo,
|
|
7152
7153
|
detectGitRepo: () => detectGitRepo
|
|
7153
7154
|
});
|
|
@@ -7175,24 +7176,32 @@ function detectGitRepo(cwd = process.cwd()) {
|
|
|
7175
7176
|
}
|
|
7176
7177
|
async function fetchListForOrg(trpc, orgID) {
|
|
7177
7178
|
try {
|
|
7178
|
-
|
|
7179
|
+
const repos = await trpc.githubRepository.listForOrg.query({
|
|
7179
7180
|
org_id: orgID
|
|
7180
7181
|
});
|
|
7182
|
+
return sortReposByRecency(repos);
|
|
7181
7183
|
} catch (err) {
|
|
7182
7184
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7183
7185
|
logger.warn("setup", `listForOrg failed: ${msg}`);
|
|
7184
7186
|
return [];
|
|
7185
7187
|
}
|
|
7186
7188
|
}
|
|
7189
|
+
function sortReposByRecency(repos) {
|
|
7190
|
+
return [...repos].sort((a, b3) => {
|
|
7191
|
+
const ta = Date.parse(a.created_at ?? "") || 0;
|
|
7192
|
+
const tb = Date.parse(b3.created_at ?? "") || 0;
|
|
7193
|
+
return tb - ta;
|
|
7194
|
+
});
|
|
7195
|
+
}
|
|
7187
7196
|
function buildPromptOptions(repos) {
|
|
7188
7197
|
return [
|
|
7189
|
-
...repos.map((r) => ({ kind: "repo", label: r.slug, value: r.slug })),
|
|
7190
7198
|
{
|
|
7191
7199
|
kind: "action",
|
|
7192
7200
|
label: "Add repositories...",
|
|
7193
7201
|
value: ADD_REPOSITORIES_VALUE,
|
|
7194
7202
|
hint: "Open GitHub and refresh this list"
|
|
7195
|
-
}
|
|
7203
|
+
},
|
|
7204
|
+
...repos.map((r) => ({ kind: "repo", label: r.slug, value: r.slug }))
|
|
7196
7205
|
];
|
|
7197
7206
|
}
|
|
7198
7207
|
function hasNewVisibleRepository(previousRepos, nextRepos) {
|
|
@@ -7291,19 +7300,65 @@ async function createDeploymentForRepo(trpc, orgID, spaceID, repo) {
|
|
|
7291
7300
|
logger.warn("setup", `dataSource.create returned no data_source for ${repo.slug}`);
|
|
7292
7301
|
return { deployment_id: deployment.deployment_id };
|
|
7293
7302
|
}
|
|
7303
|
+
await trpc.dataSource.syncDataSource.mutate(dataSource.data_source_id);
|
|
7294
7304
|
const spaceDeployments = await trpc.workspaces.listForSpace.query(spaceID);
|
|
7295
7305
|
await Promise.all(spaceDeployments.map((d3) => trpc.deploymentDataSource.create.mutate({
|
|
7296
7306
|
deployment_id: d3.deployment_id,
|
|
7297
7307
|
data_source_id: dataSource.data_source_id
|
|
7298
7308
|
})));
|
|
7299
|
-
return {
|
|
7309
|
+
return {
|
|
7310
|
+
deployment_id: deployment.deployment_id,
|
|
7311
|
+
data_source_id: dataSource.data_source_id
|
|
7312
|
+
};
|
|
7300
7313
|
} catch (err) {
|
|
7301
7314
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7302
7315
|
logger.error("setup", `Failed to wire up ${repo.slug}: ${msg}`);
|
|
7303
7316
|
return null;
|
|
7304
7317
|
}
|
|
7305
7318
|
}
|
|
7306
|
-
async function
|
|
7319
|
+
async function verifyDataSourcesPersist(trpc, orgID, expectedDataSourceIds, opts = {}) {
|
|
7320
|
+
const expected = new Set(expectedDataSourceIds);
|
|
7321
|
+
if (expected.size === 0) {
|
|
7322
|
+
return { alive: new Set, dropped: new Set };
|
|
7323
|
+
}
|
|
7324
|
+
const timeoutMs = opts.timeoutMs ?? DATA_SOURCE_VERIFY_POLL_TIMEOUT_MS;
|
|
7325
|
+
const intervalMs = opts.intervalMs ?? DATA_SOURCE_VERIFY_POLL_INTERVAL_MS;
|
|
7326
|
+
const startedAt = Date.now();
|
|
7327
|
+
let alive = new Set;
|
|
7328
|
+
let dropped = new Set;
|
|
7329
|
+
let firstIteration = true;
|
|
7330
|
+
while (firstIteration || Date.now() - startedAt < timeoutMs) {
|
|
7331
|
+
firstIteration = false;
|
|
7332
|
+
let listed = [];
|
|
7333
|
+
try {
|
|
7334
|
+
listed = await trpc.dataSource.list.query({
|
|
7335
|
+
org_id: orgID,
|
|
7336
|
+
excluded_provider_slugs: []
|
|
7337
|
+
});
|
|
7338
|
+
} catch (err) {
|
|
7339
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7340
|
+
logger.warn("setup", `dataSource.list during verify failed: ${msg}`);
|
|
7341
|
+
}
|
|
7342
|
+
const presentNow = new Set(listed.map((d3) => d3.data_source_id).filter((id) => Boolean(id)));
|
|
7343
|
+
alive = new Set([...expected].filter((id) => presentNow.has(id)));
|
|
7344
|
+
dropped = new Set([...expected].filter((id) => !presentNow.has(id)));
|
|
7345
|
+
if (dropped.size > 0)
|
|
7346
|
+
return { alive, dropped };
|
|
7347
|
+
if (timeoutMs === 0)
|
|
7348
|
+
break;
|
|
7349
|
+
await sleep2(intervalMs);
|
|
7350
|
+
}
|
|
7351
|
+
return { alive, dropped };
|
|
7352
|
+
}
|
|
7353
|
+
async function deleteOrphanDeployment(trpc, deploymentID, slug) {
|
|
7354
|
+
try {
|
|
7355
|
+
await trpc.workspaces.delete.mutate(deploymentID);
|
|
7356
|
+
} catch (err) {
|
|
7357
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7358
|
+
logger.warn("setup", `Failed to revert orphan deployment for ${slug}: ${msg}`);
|
|
7359
|
+
}
|
|
7360
|
+
}
|
|
7361
|
+
async function stepConnectGitHubRepo(cfg, detected = detectGitRepo(), opts = {}) {
|
|
7307
7362
|
logger.info("setup", "Step: connect GitHub repo(s)");
|
|
7308
7363
|
if (!cfg.org_id || !cfg.space_id) {
|
|
7309
7364
|
M2.warn("Cannot connect GitHub: your Dosu workspace is missing org/space context. Re-run `dosu setup` from a fresh state.");
|
|
@@ -7326,9 +7381,10 @@ async function stepConnectGitHubRepo(cfg, detected = detectGitRepo()) {
|
|
|
7326
7381
|
${lines}`);
|
|
7327
7382
|
}
|
|
7328
7383
|
const selected = await promptGitHubRepositories({
|
|
7329
|
-
message:
|
|
7384
|
+
message: `Select repositories to connect ${dim(`(${undeployed.length} available)`)}`,
|
|
7330
7385
|
options: buildPromptOptions(undeployed),
|
|
7331
|
-
initialValues: []
|
|
7386
|
+
initialValues: [],
|
|
7387
|
+
maxItems: REPO_MULTISELECT_MAX_ITEMS
|
|
7332
7388
|
});
|
|
7333
7389
|
if (pD(selected)) {
|
|
7334
7390
|
logger.info("setup", "Repository selection cancelled");
|
|
@@ -7359,29 +7415,55 @@ ${lines}`);
|
|
|
7359
7415
|
continue;
|
|
7360
7416
|
const result = await createDeploymentForRepo(trpc, orgID, spaceID, repo);
|
|
7361
7417
|
if (result) {
|
|
7362
|
-
created.push({
|
|
7418
|
+
created.push({
|
|
7419
|
+
deployment_id: result.deployment_id,
|
|
7420
|
+
data_source_id: result.data_source_id,
|
|
7421
|
+
slug
|
|
7422
|
+
});
|
|
7423
|
+
}
|
|
7424
|
+
}
|
|
7425
|
+
const expectedDsIds = created.map((c) => c.data_source_id).filter((id) => Boolean(id));
|
|
7426
|
+
const survivors = await verifyDataSourcesPersist(trpc, orgID, expectedDsIds, opts.verify);
|
|
7427
|
+
const reverted = [];
|
|
7428
|
+
if (survivors.dropped.size > 0) {
|
|
7429
|
+
const droppedEntries = created.filter((c) => c.data_source_id && survivors.dropped.has(c.data_source_id));
|
|
7430
|
+
for (const entry of droppedEntries) {
|
|
7431
|
+
await deleteOrphanDeployment(trpc, entry.deployment_id, entry.slug);
|
|
7432
|
+
reverted.push({ slug: entry.slug, deployment_id: entry.deployment_id });
|
|
7363
7433
|
}
|
|
7364
7434
|
}
|
|
7365
|
-
|
|
7435
|
+
const survived = created.filter((c) => !c.data_source_id || survivors.alive.has(c.data_source_id));
|
|
7436
|
+
if (survived.length === 0) {
|
|
7366
7437
|
s.stop("Failed");
|
|
7367
|
-
|
|
7438
|
+
if (reverted.length > 0) {
|
|
7439
|
+
M2.error(`Couldn't sync any repos — Dosu doesn't have GitHub access to: ${reverted.map((r) => r.slug).join(", ")}.`);
|
|
7440
|
+
} else {
|
|
7441
|
+
M2.error("Could not connect any repos. Check `dosu logs --tail 50` for details.");
|
|
7442
|
+
}
|
|
7368
7443
|
return { advance: false, has_connected_repo: deployed.length > 0 };
|
|
7369
7444
|
}
|
|
7370
|
-
|
|
7371
|
-
|
|
7445
|
+
if (reverted.length > 0) {
|
|
7446
|
+
s.stop(`Connected ${survived.length} repo${survived.length === 1 ? "" : "s"} · ${reverted.length} skipped`);
|
|
7447
|
+
M2.warn(`Dosu couldn't sync ${reverted.length} repo${reverted.length === 1 ? "" : "s"} (GitHub App has no access): ${reverted.map((r) => r.slug).join(", ")}`);
|
|
7448
|
+
} else {
|
|
7449
|
+
s.stop(`Connected ${survived.length} repo${survived.length === 1 ? "" : "s"}`);
|
|
7450
|
+
}
|
|
7451
|
+
for (const { slug, deployment_id } of survived) {
|
|
7372
7452
|
M2.success(`${slug}
|
|
7373
7453
|
${dim(`deployment ${deployment_id}`)}`);
|
|
7374
7454
|
}
|
|
7375
|
-
const primary = (detected &&
|
|
7455
|
+
const primary = (detected && survived.find((c) => c.slug === detected.slug)) ?? survived[0];
|
|
7456
|
+
const survivingDataSourceIds = survived.map((c) => c.data_source_id).filter((id) => Boolean(id));
|
|
7376
7457
|
return {
|
|
7377
7458
|
advance: true,
|
|
7378
7459
|
has_connected_repo: true,
|
|
7379
7460
|
deployment_id: primary.deployment_id,
|
|
7380
|
-
space_id: cfg.space_id
|
|
7461
|
+
space_id: cfg.space_id,
|
|
7462
|
+
created_data_source_ids: survivingDataSourceIds
|
|
7381
7463
|
};
|
|
7382
7464
|
}
|
|
7383
7465
|
}
|
|
7384
|
-
var INSTALLATION_TIMEOUT_MS, REPO_REFRESH_POLL_INTERVAL_MS = 500, REPO_REFRESH_POLL_TIMEOUT_MS = 1e4;
|
|
7466
|
+
var INSTALLATION_TIMEOUT_MS, REPO_REFRESH_POLL_INTERVAL_MS = 500, REPO_REFRESH_POLL_TIMEOUT_MS = 1e4, REPO_MULTISELECT_MAX_ITEMS = 10, DATA_SOURCE_VERIFY_POLL_INTERVAL_MS = 1000, DATA_SOURCE_VERIFY_POLL_TIMEOUT_MS = 1e4;
|
|
7385
7467
|
var init_github_step = __esm(() => {
|
|
7386
7468
|
init_dist6();
|
|
7387
7469
|
init_trpc();
|
|
@@ -7770,7 +7852,7 @@ async function stepImportGitHubDocs(cfg, opts = {}) {
|
|
|
7770
7852
|
return { advance: false };
|
|
7771
7853
|
}
|
|
7772
7854
|
const trpc = createTypedClient(cfg);
|
|
7773
|
-
const files = opts.waitForFreshDocs ? await waitForImportableGithubFiles(trpc, cfg.org_id, cfg.space_id) : await fetchImportableGithubFiles(trpc, cfg.space_id);
|
|
7855
|
+
const files = opts.waitForFreshDocs ? await waitForImportableGithubFiles(trpc, cfg.org_id, cfg.space_id, opts.expectedDataSourceIds) : await fetchImportableGithubFiles(trpc, cfg.space_id);
|
|
7774
7856
|
if (files === null) {
|
|
7775
7857
|
return { advance: false };
|
|
7776
7858
|
}
|
|
@@ -7890,7 +7972,7 @@ Your GitHub docs are ready, and onboarding is complete.`);
|
|
|
7890
7972
|
M2.warn(`Imported ${processed - failed} of ${total} doc${total === 1 ? "" : "s"}; ${failed} failed.
|
|
7891
7973
|
You can review the failed docs later, but onboarding is complete.`);
|
|
7892
7974
|
}
|
|
7893
|
-
async function waitForImportableGithubFiles(trpc, orgID, spaceID) {
|
|
7975
|
+
async function waitForImportableGithubFiles(trpc, orgID, spaceID, expectedDataSourceIds) {
|
|
7894
7976
|
while (true) {
|
|
7895
7977
|
const spinner = Y2();
|
|
7896
7978
|
spinner.start("Scanning repositories for markdown documents...");
|
|
@@ -7904,7 +7986,7 @@ async function waitForImportableGithubFiles(trpc, orgID, spaceID) {
|
|
|
7904
7986
|
return latestFiles;
|
|
7905
7987
|
}
|
|
7906
7988
|
const dataSources = await fetchGitHubDataSources(trpc, orgID);
|
|
7907
|
-
if (dataSources
|
|
7989
|
+
if (isScanComplete(dataSources, expectedDataSourceIds)) {
|
|
7908
7990
|
spinner.stop("No markdown docs found");
|
|
7909
7991
|
return [];
|
|
7910
7992
|
}
|
|
@@ -7927,6 +8009,24 @@ async function waitForImportableGithubFiles(trpc, orgID, spaceID) {
|
|
|
7927
8009
|
}
|
|
7928
8010
|
}
|
|
7929
8011
|
}
|
|
8012
|
+
function isScanComplete(dataSources, expectedDataSourceIds) {
|
|
8013
|
+
if (expectedDataSourceIds && expectedDataSourceIds.length > 0) {
|
|
8014
|
+
const byId = new Map;
|
|
8015
|
+
for (const ds of dataSources) {
|
|
8016
|
+
if (ds.data_source_id)
|
|
8017
|
+
byId.set(ds.data_source_id, ds);
|
|
8018
|
+
}
|
|
8019
|
+
for (const id of expectedDataSourceIds) {
|
|
8020
|
+
const ds = byId.get(id);
|
|
8021
|
+
if (!ds)
|
|
8022
|
+
continue;
|
|
8023
|
+
if (ds.is_indexed !== true)
|
|
8024
|
+
return false;
|
|
8025
|
+
}
|
|
8026
|
+
return true;
|
|
8027
|
+
}
|
|
8028
|
+
return dataSources.length > 0 && dataSources.every((ds) => ds.is_indexed === true);
|
|
8029
|
+
}
|
|
7930
8030
|
async function fetchGitHubDataSources(trpc, orgID) {
|
|
7931
8031
|
try {
|
|
7932
8032
|
const dataSources = await trpc.dataSource.list.query({
|
|
@@ -8274,9 +8374,14 @@ async function runSetup(opts = {}) {
|
|
|
8274
8374
|
const apiClient = new Client(cfg);
|
|
8275
8375
|
let cloudSetupContext = null;
|
|
8276
8376
|
if (cfg.mode !== MODE_OSS) {
|
|
8377
|
+
const s = Y2();
|
|
8378
|
+
s.start("Loading your workspace...");
|
|
8277
8379
|
cloudSetupContext = await resolveCloudSetupContext(cfg);
|
|
8278
|
-
if (!cloudSetupContext)
|
|
8380
|
+
if (!cloudSetupContext) {
|
|
8381
|
+
s.stop("Workspace load failed");
|
|
8279
8382
|
return;
|
|
8383
|
+
}
|
|
8384
|
+
s.stop("Workspace loaded");
|
|
8280
8385
|
}
|
|
8281
8386
|
if (cfg.mode !== MODE_OSS && cloudSetupContext?.kind === "onboarding") {
|
|
8282
8387
|
const ok = await bindOnboardingDeployment(apiClient, cfg, cloudSetupContext.targetOrg ?? null);
|
|
@@ -8319,7 +8424,8 @@ async function runSetup(opts = {}) {
|
|
|
8319
8424
|
}
|
|
8320
8425
|
const { stepImportGitHubDocs: stepImportGitHubDocs2 } = await Promise.resolve().then(() => (init_github_doc_import_step(), exports_github_doc_import_step));
|
|
8321
8426
|
const importResult = await stepImportGitHubDocs2(cfg, {
|
|
8322
|
-
waitForFreshDocs: Boolean(connectResult.deployment_id)
|
|
8427
|
+
waitForFreshDocs: Boolean(connectResult.deployment_id),
|
|
8428
|
+
expectedDataSourceIds: connectResult.created_data_source_ids
|
|
8323
8429
|
});
|
|
8324
8430
|
if (!importResult.advance)
|
|
8325
8431
|
return;
|
|
@@ -8536,6 +8642,8 @@ async function bindOnboardingDeployment(apiClient, cfg, targetOrg) {
|
|
|
8536
8642
|
cfg.org_id = deployment.org_id;
|
|
8537
8643
|
cfg.space_id = deployment.space_id;
|
|
8538
8644
|
logger.info("setup", `Bound onboarding context org=${targetOrg.org_id} deployment=${deployment.deployment_id}`);
|
|
8645
|
+
M2.success(`Organization
|
|
8646
|
+
${dim(targetOrg.name)}`);
|
|
8539
8647
|
return true;
|
|
8540
8648
|
}
|
|
8541
8649
|
async function resolveOnboardingDeployment(apiClient, targetOrg) {
|