duoops 0.2.6 → 0.2.8

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.
@@ -1,5 +1,5 @@
1
1
  import { BigQuery } from '@google-cloud/bigquery';
2
- import { confirm, input, password, select } from '@inquirer/prompts';
2
+ import { checkbox, confirm, input, password, select } from '@inquirer/prompts';
3
3
  import { Command } from '@oclif/core';
4
4
  import axios from 'axios';
5
5
  import { bold, gray, green, yellow } from 'kleur/colors';
@@ -46,6 +46,21 @@ const detectGitRemotePath = () => {
46
46
  }
47
47
  catch { /* ignore */ }
48
48
  };
49
+ const resolveProjectId = async (auth, pathOrId) => {
50
+ const base = normalizeGitLabUrl(auth.baseUrl);
51
+ const encoded = encodeURIComponent(normalizeProjectPath(pathOrId));
52
+ try {
53
+ const res = await axios.get(`${base}/api/v4/projects/${encoded}`, {
54
+ headers: gitlabAuthHeaders(auth.token),
55
+ timeout: 5000,
56
+ validateStatus: () => true,
57
+ });
58
+ if (res.status === 200 && res.data?.id) {
59
+ return { id: res.data.id, name: res.data.name_with_namespace, path: res.data.path_with_namespace };
60
+ }
61
+ }
62
+ catch { /* ignore */ }
63
+ };
49
64
  const normalizeProjectPath = (project) => project.replace(/^\/+/, '');
50
65
  const normalizeGitLabUrl = (url) => url.replace(/\/+$/, '');
51
66
  const setGitlabVariable = async (auth, projectPath, variable) => {
@@ -295,9 +310,16 @@ export default class Init extends Command {
295
310
  message: 'GitLab Personal Access Token',
296
311
  });
297
312
  }
298
- const enableMeasure = await confirm({
299
- message: 'Enable Measure/Sustainability Tracking (BigQuery)?',
313
+ const features = await checkbox({
314
+ choices: [
315
+ { checked: true, name: 'Carbon measurement (BigQuery)', value: 'measure' },
316
+ { checked: true, name: 'Duo agents and flows', value: 'agents' },
317
+ { checked: true, name: 'MCP server (Cloud Run)', value: 'mcp' },
318
+ { checked: true, name: 'CI project wiring (variables + runner)', value: 'ci' },
319
+ ],
320
+ message: 'What would you like to set up?',
300
321
  });
322
+ const enableMeasure = features.includes('measure');
301
323
  let bigqueryDataset;
302
324
  let bigqueryTable;
303
325
  let googleProjectId;
@@ -369,11 +391,7 @@ export default class Init extends Command {
369
391
  };
370
392
  configManager.set(config);
371
393
  this.log(green('Configuration saved.'));
372
- const setupAgents = await confirm({
373
- default: true,
374
- message: 'Set up DuoOps agents and flows in this project?',
375
- });
376
- if (setupAgents) {
394
+ if (features.includes('agents')) {
377
395
  const { scaffoldProject } = await import('../lib/scaffold.js');
378
396
  const result = scaffoldProject(process.cwd());
379
397
  for (const f of result.created) {
@@ -384,30 +402,20 @@ export default class Init extends Command {
384
402
  }
385
403
  }
386
404
  this.log(`\nTry '${this.config.bin} pipelines:list <project>' to verify GitLab access.\n`);
387
- if (enableMeasure) {
388
- const deployMcp = await confirm({
389
- default: true,
390
- message: 'Deploy the DuoOps MCP Server to Cloud Run now?',
391
- });
392
- if (deployMcp && googleProjectId && bigqueryDataset && bigqueryTable) {
393
- try {
394
- await McpDeploy.run([
395
- '--gcp-project', googleProjectId,
396
- '--bq-dataset', bigqueryDataset,
397
- '--bq-table', bigqueryTable,
398
- '--gitlab-url', gitlabUrl,
399
- ]);
400
- }
401
- catch (error) {
402
- this.warn(`MCP Deployment failed: ${error}`);
403
- }
405
+ if (features.includes('mcp') && googleProjectId && bigqueryDataset && bigqueryTable) {
406
+ try {
407
+ await McpDeploy.run([
408
+ '--gcp-project', googleProjectId,
409
+ '--bq-dataset', bigqueryDataset,
410
+ '--bq-table', bigqueryTable,
411
+ '--gitlab-url', gitlabUrl,
412
+ ]);
413
+ }
414
+ catch (error) {
415
+ this.warn(`MCP Deployment failed: ${error}`);
404
416
  }
405
417
  }
406
- const configureProject = await confirm({
407
- default: true,
408
- message: 'Configure a GitLab project (CI variables + optional GCP runner) now?',
409
- });
410
- if (!configureProject) {
418
+ if (!features.includes('ci')) {
411
419
  return;
412
420
  }
413
421
  const updatedDefaultProject = await this.configureGitLabProject({ baseUrl: gitlabUrl, token: gitlabToken }, config.defaultProjectId);
@@ -447,13 +455,44 @@ export default class Init extends Command {
447
455
  this.warn('GitLab credentials missing, skipping project configuration.');
448
456
  return;
449
457
  }
450
- const projectPath = await input({
451
- default: currentDefault ?? detectGitRemotePath(),
452
- message: 'GitLab project path (e.g., group/project)',
453
- });
458
+ const detectedPath = detectGitRemotePath();
459
+ let projectPath;
460
+ let resolvedProject;
461
+ if (detectedPath) {
462
+ resolvedProject = await resolveProjectId(auth, detectedPath);
463
+ }
464
+ if (resolvedProject) {
465
+ const useDetected = await confirm({
466
+ default: true,
467
+ message: `Detected project: ${resolvedProject.name} (ID: ${resolvedProject.id}). Use it?`,
468
+ });
469
+ if (useDetected) {
470
+ projectPath = resolvedProject.path;
471
+ }
472
+ else {
473
+ projectPath = await input({
474
+ default: currentDefault,
475
+ message: 'GitLab project path or URL',
476
+ });
477
+ resolvedProject = await resolveProjectId(auth, projectPath);
478
+ }
479
+ }
480
+ else {
481
+ projectPath = await input({
482
+ default: currentDefault ?? detectedPath,
483
+ message: 'GitLab project path or URL',
484
+ });
485
+ resolvedProject = await resolveProjectId(auth, projectPath);
486
+ }
454
487
  if (!projectPath) {
455
488
  this.warn('Project path is required to configure CI variables.');
456
489
  }
490
+ if (resolvedProject) {
491
+ this.log(gray(` Project: ${resolvedProject.name} (ID: ${resolvedProject.id})`));
492
+ }
493
+ else if (projectPath) {
494
+ this.warn(`Could not resolve project "${projectPath}". Continuing with path as-is.`);
495
+ }
457
496
  const setAsDefault = await confirm({
458
497
  default: !currentDefault,
459
498
  message: 'Use this project as the default for DuoOps commands?',
@@ -669,5 +669,5 @@
669
669
  ]
670
670
  }
671
671
  },
672
- "version": "0.2.6"
672
+ "version": "0.2.8"
673
673
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "duoops",
3
3
  "description": "Toolset for Explainable and Sustainable CI on Gitlab.",
4
- "version": "0.2.6",
4
+ "version": "0.2.8",
5
5
  "author": "Younes Laaroussi",
6
6
  "bin": {
7
7
  "duoops": "./bin/run.js"