@bussolabs/closeyourit-cli 0.6.0 → 0.7.1

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.
@@ -10,6 +10,7 @@ export default class ProjectsGithubUpdate extends BaseCommand {
10
10
  'sync-enabled': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
11
  'tag-binding-enabled': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
12
  'autoclose-on-merge': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
13
+ 'sync-secrets': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
13
14
  project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
14
15
  };
15
16
  run(): Promise<unknown>;
@@ -9,6 +9,7 @@ class ProjectsGithubUpdate extends base_1.BaseCommand {
9
9
  '<%= config.bin %> projects github-update -p LBRA --production-environment-id <env-uuid>',
10
10
  '<%= config.bin %> projects github-update -p LBRA --no-tag-binding-enabled',
11
11
  '<%= config.bin %> projects github-update -p LBRA --default-branch main --autoclose-on-merge',
12
+ '<%= config.bin %> projects github-update -p LBRA --sync-secrets',
12
13
  ];
13
14
  static flags = {
14
15
  ...base_1.projectFlag,
@@ -28,6 +29,10 @@ class ProjectsGithubUpdate extends base_1.BaseCommand {
28
29
  allowNo: true,
29
30
  description: 'Enable/disable moving the ticket to done on PR merge',
30
31
  }),
32
+ 'sync-secrets': core_1.Flags.boolean({
33
+ allowNo: true,
34
+ description: 'Enable/disable pushing vault secrets to the GitHub Environment secrets (SECRETS_JSON)',
35
+ }),
31
36
  };
32
37
  async run() {
33
38
  const { flags } = await this.parse(ProjectsGithubUpdate);
@@ -47,6 +52,8 @@ class ProjectsGithubUpdate extends base_1.BaseCommand {
47
52
  body.tag_binding_enabled = flags['tag-binding-enabled'];
48
53
  if (flags['autoclose-on-merge'] !== undefined)
49
54
  body.autoclose_on_merge = flags['autoclose-on-merge'];
55
+ if (flags['sync-secrets'] !== undefined)
56
+ body.sync_secrets = flags['sync-secrets'];
50
57
  // The GitHub action responds to both PATCH and PUT (Rails singleton `resource` update); the CLI
51
58
  // transport speaks PUT — the backend applies only the keys present in the body either way.
52
59
  const res = await this.api.put(`/cli/v1/projects/${encodeURIComponent(projectId)}/github`, body);
@@ -2,8 +2,6 @@ import { BaseCommand } from '../../../base';
2
2
  export default class SecretsAssetsList extends BaseCommand {
3
3
  static description: string;
4
4
  static flags: {
5
- page: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
6
- per: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
7
5
  project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
6
  };
9
7
  run(): Promise<unknown>;
@@ -1,18 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const core_1 = require("@oclif/core");
4
3
  const base_1 = require("../../../base");
5
4
  const output_1 = require("../../../lib/output");
5
+ const paginate_1 = require("../../../lib/paginate");
6
6
  class SecretsAssetsList extends base_1.BaseCommand {
7
7
  static description = 'List encrypted secret files visible to a project (metadata only)';
8
- static flags = { ...base_1.projectFlag, page: core_1.Flags.integer({ default: 1 }), per: core_1.Flags.integer({ default: 50 }) };
8
+ static flags = { ...base_1.projectFlag };
9
9
  async run() {
10
10
  const { flags } = await this.parse(SecretsAssetsList);
11
11
  const projectId = await this.resolveProjectId(flags.project);
12
- const res = await this.api.get(`/cli/v1/projects/${encodeURIComponent(projectId)}/secret_assets?page=${flags.page}&per=${flags.per}`);
12
+ const data = await (0, paginate_1.fetchAllPages)(this.api, `/cli/v1/projects/${encodeURIComponent(projectId)}/secret_assets`);
13
13
  if (!this.jsonEnabled())
14
- this.log((0, output_1.renderTable)(['ID', 'NAME', 'TYPE', 'ENV', 'VERSION', 'BYTES'], (res.data ?? []).map((a) => [a.id, a.name, a.asset_type, a.environment?.code ?? 'all', a.current_version?.number ?? '', a.current_version?.byte_size ?? ''])));
15
- return res;
14
+ this.log((0, output_1.renderTable)(['ID', 'NAME', 'TYPE', 'ENV', 'VERSION', 'BYTES'], data.map((a) => [a.id, a.name, a.asset_type, a.environment?.code ?? 'all', a.current_version?.number ?? '', a.current_version?.byte_size ?? ''])));
15
+ return { data };
16
16
  }
17
17
  }
18
18
  exports.default = SecretsAssetsList;
@@ -2,14 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const base_1 = require("../../../base");
4
4
  const output_1 = require("../../../lib/output");
5
+ const paginate_1 = require("../../../lib/paginate");
5
6
  class SecretsAssetsSharedList extends base_1.BaseCommand {
6
7
  static description = 'List organization-level encrypted secret files and delegated projects';
7
8
  async run() {
8
9
  await this.parse(SecretsAssetsSharedList);
9
- const res = await this.api.get('/cli/v1/shared_secret_assets?per=100');
10
+ const data = await (0, paginate_1.fetchAllPages)(this.api, '/cli/v1/shared_secret_assets');
10
11
  if (!this.jsonEnabled())
11
- this.log((0, output_1.renderTable)(['ID', 'NAME', 'TYPE', 'ENV'], (res.data ?? []).map((a) => [a.id, a.name, a.asset_type, a.environment?.code ?? 'all'])));
12
- return res;
12
+ this.log((0, output_1.renderTable)(['ID', 'NAME', 'TYPE', 'ENV'], data.map((a) => [a.id, a.name, a.asset_type, a.environment?.code ?? 'all'])));
13
+ return { data };
13
14
  }
14
15
  }
15
16
  exports.default = SecretsAssetsSharedList;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const base_1 = require("../../base");
5
5
  const output_1 = require("../../lib/output");
6
+ const paginate_1 = require("../../lib/paginate");
6
7
  class SecretsList extends base_1.BaseCommand {
7
8
  static description = 'List secret names for a project (optionally filtered by environment). Never prints values.';
8
9
  static examples = [
@@ -16,17 +17,17 @@ class SecretsList extends base_1.BaseCommand {
16
17
  async run() {
17
18
  const { flags } = await this.parse(SecretsList);
18
19
  const projectId = await this.resolveProjectId(flags.project);
19
- const query = flags.environment ? `?environment=${encodeURIComponent(flags.environment)}` : '';
20
- const res = await this.api.get(`/cli/v1/projects/${encodeURIComponent(projectId)}/secrets${query}`);
20
+ const params = flags.environment ? { environment: flags.environment } : {};
21
+ const data = await (0, paginate_1.fetchAllPages)(this.api, `/cli/v1/projects/${encodeURIComponent(projectId)}/secrets`, params);
21
22
  if (!this.jsonEnabled()) {
22
- const rows = (res.data ?? []).map((secret) => [
23
+ const rows = data.map((secret) => [
23
24
  String(secret.name ?? ''),
24
25
  String(secret.environment?.code ?? ''),
25
26
  String(secret.description ?? ''),
26
27
  ]);
27
28
  this.log((0, output_1.renderTable)(['NAME', 'ENVIRONMENT', 'DESCRIPTION'], rows));
28
29
  }
29
- return res;
30
+ return { data };
30
31
  }
31
32
  }
32
33
  exports.default = SecretsList;
@@ -0,0 +1,9 @@
1
+ import type { CliApi } from './api';
2
+ /**
3
+ * Read every page of a paginated collection endpoint and return the aggregated rows.
4
+ *
5
+ * Bounded collections (secrets, secret assets) must be listed in full, not just page 1 — reading
6
+ * only `res.data` silently truncated to the backend page size (CYCL: `cyi secrets list` showed 10).
7
+ * A backend that does not paginate returns everything on page 1 (`meta` absent) → single fetch.
8
+ */
9
+ export declare function fetchAllPages<T = Record<string, unknown>>(api: CliApi, basePath: string, params?: Record<string, string>): Promise<T[]>;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchAllPages = fetchAllPages;
4
+ /**
5
+ * Read every page of a paginated collection endpoint and return the aggregated rows.
6
+ *
7
+ * Bounded collections (secrets, secret assets) must be listed in full, not just page 1 — reading
8
+ * only `res.data` silently truncated to the backend page size (CYCL: `cyi secrets list` showed 10).
9
+ * A backend that does not paginate returns everything on page 1 (`meta` absent) → single fetch.
10
+ */
11
+ async function fetchAllPages(api, basePath, params = {}) {
12
+ const all = [];
13
+ let page = 1;
14
+ let totalPages = 1;
15
+ do {
16
+ const qs = new URLSearchParams({ ...params, page: String(page), per: '100' }).toString();
17
+ // eslint-disable-next-line no-await-in-loop
18
+ const res = await api.get(`${basePath}?${qs}`);
19
+ all.push(...(Array.isArray(res.data) ? res.data : []));
20
+ totalPages = Number(res.meta?.total_pages ?? 1);
21
+ page += 1;
22
+ } while (page <= totalPages);
23
+ return all;
24
+ }