@theholocron/holocron-plugin-github 5.0.0-alpha.199 → 5.0.0-alpha.200

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/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { GitHubClient, GitHubClientOptions, createGitHubClient } from "@theholocron/github-client";
2
- import { Auth, AuthError, Ci, CiRun, CiRunFilter, Environment, Environments, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, PagesConfig, PullRequest, RepoRef, RepoSettings, ResolveTokenInput, Ruleset, SecretScope, Secrets, Source, TeamEntry, TeamEntry as TeamEntry$1, TeamPermission, TrackerDoctorReport, TrackerUser, VerifyTokenFailure, VerifyTokenResult, VerifyTokenResult as VerifyTokenResult$1, VerifyTokenSuccess, createFeatureResolver } from "@theholocron/cli";
2
+ import { Auth, AuthError, Ci, CiRun, CiRunFilter, Environment, Environments, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, PagesConfig, PullRequest, RepoRef, RepoSettings, ResolveTokenInput, Ruleset, SecretScope, Secrets, Source, SourceFile, SourceTeamMember, TeamEntry, TeamEntry as TeamEntry$1, TeamPermission, TrackerDoctorReport, TrackerUser, VerifyTokenFailure, VerifyTokenResult, VerifyTokenResult as VerifyTokenResult$1, VerifyTokenSuccess, createFeatureResolver } from "@theholocron/cli";
3
3
  //#region src/auth.d.ts
4
4
  declare const resolveReadToken: (input?: ResolveTokenInput) => string;
5
5
  declare const resolveIssuesToken: (input?: ResolveTokenInput) => string;
@@ -8,6 +8,7 @@ declare const resolveReleaseToken: (input?: ResolveTokenInput) => string;
8
8
  declare const resolveAdminToken: (input?: ResolveTokenInput) => string;
9
9
  declare const resolveSecretsToken: (input?: ResolveTokenInput) => string;
10
10
  declare const resolveDeployToken: (input?: ResolveTokenInput) => string;
11
+ declare const resolveUserSyncToken: (input?: ResolveTokenInput) => string;
11
12
  declare const resolveOrgToken: (input?: ResolveTokenInput) => string;
12
13
  //#endregion
13
14
  //#region src/capabilities/ci.d.ts
@@ -128,6 +129,8 @@ interface SourceOptions {
128
129
  repoRoot: string;
129
130
  /** Secondary client authenticated with HOLOCRON_ORG_TOKEN for org-scoped operations (teams, custom properties). */
130
131
  orgClient?: GitHubClient;
132
+ /** Client for the files that link GitHub accounts to other services (HOLOCRON_USER_SYNC_TOKEN); falls back to the org client. */
133
+ userSyncClient?: GitHubClient;
131
134
  /** Forwarded to a secondary client created for Pages calls (HOLOCRON_DEPLOY_TOKEN). */
132
135
  baseUrl?: string;
133
136
  fetch?: typeof fetch;
@@ -142,6 +145,7 @@ declare class GitHubSource implements Source {
142
145
  private readonly repoRoot;
143
146
  private readonly workflowDir;
144
147
  private readonly orgClient?;
148
+ private readonly userSyncClient?;
145
149
  private readonly baseUrl?;
146
150
  private readonly fetchImpl?;
147
151
  constructor(client: GitHubClient, opts: SourceOptions);
@@ -169,6 +173,13 @@ declare class GitHubSource implements Source {
169
173
  syncLabels(canonical: ReadonlyArray<LabelDef>, stale: ReadonlyArray<string>): Promise<string>;
170
174
  syncProperties(values: Record<string, string | string[]>): Promise<string>;
171
175
  syncTeams(teams: TeamEntry$1[]): Promise<string>;
176
+ teamMembers(teamSlug: string, org?: string): Promise<SourceTeamMember[]>;
177
+ readFile(path: string, repo?: string): Promise<SourceFile | null>;
178
+ writeFile(path: string, content: string, opts: {
179
+ message: string;
180
+ version?: string;
181
+ repo?: string;
182
+ }): Promise<void>;
172
183
  syncTopics(topics: string[]): Promise<string>;
173
184
  syncDescription(description: string): Promise<string>;
174
185
  syncHomepage(homepage: string): Promise<string>;
@@ -221,7 +232,7 @@ interface PluginContext {
221
232
  repo: string;
222
233
  repoRoot: string;
223
234
  }
224
- declare function source(ctx: PluginContext, orgClient?: GitHubClient): Source;
235
+ declare function source(ctx: PluginContext, orgClient?: GitHubClient, userSyncClient?: GitHubClient): Source;
225
236
  declare function secrets(ctx: PluginContext, orgClient?: GitHubClient): Secrets;
226
237
  declare function environments(ctx: PluginContext): Environments;
227
238
  declare function ci(ctx: PluginContext): Ci;
@@ -242,4 +253,4 @@ declare function createPlugin(options: GitHubPluginOptions): {
242
253
  */
243
254
  declare const AUTH_HINT: string;
244
255
  //#endregion
245
- export { AUTH_HINT, type Auth, AuthError, type CanonicalLabel, GitHubCi, type GitHubClientOptions, GitHubEnvironments, GitHubIssues, GitHubPluginOptions, GitHubSecrets, GitHubSource, type NormalizedTeamEntry, PluginContext, type ResolveTokenInput, type TeamEntry, type TeamPermission, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, ci, createFeatureResolver, createGitHubClient, createPlugin, encryptSecret, environments, issues, normalizeTeamEntry, resolveAdminToken, resolveDeployToken, resolveIssuesToken, resolveOrgToken, resolveReadToken, resolveReleaseToken, resolveSecretsToken, resolveSyncToken, secrets, source, syncLabels, syncTeams, verifyToken };
256
+ export { AUTH_HINT, type Auth, AuthError, type CanonicalLabel, GitHubCi, type GitHubClientOptions, GitHubEnvironments, GitHubIssues, GitHubPluginOptions, GitHubSecrets, GitHubSource, type NormalizedTeamEntry, PluginContext, type ResolveTokenInput, type TeamEntry, type TeamPermission, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, ci, createFeatureResolver, createGitHubClient, createPlugin, encryptSecret, environments, issues, normalizeTeamEntry, resolveAdminToken, resolveDeployToken, resolveIssuesToken, resolveOrgToken, resolveReadToken, resolveReleaseToken, resolveSecretsToken, resolveSyncToken, resolveUserSyncToken, secrets, source, syncLabels, syncTeams, verifyToken };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { createGitHubClient, createGitHubClient as createGitHubClient$1 } from "@theholocron/github-client";
3
- import { AuthError, createFeatureResolver } from "@theholocron/cli";
3
+ import { AuthError, ProviderApiError, createFeatureResolver } from "@theholocron/cli";
4
4
  import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
5
5
  import { dirname, join } from "node:path";
6
6
  import { findRootSync } from "@theholocron/fs-utils";
@@ -34,6 +34,10 @@ const resolveDeployToken = createFeatureResolver({
34
34
  envName: "HOLOCRON_DEPLOY_TOKEN",
35
35
  keyringKey: "github.deploy"
36
36
  });
37
+ const resolveUserSyncToken = createFeatureResolver({
38
+ envName: "HOLOCRON_USER_SYNC_TOKEN",
39
+ keyringKey: "github.user-sync"
40
+ });
37
41
  const resolveOrgToken = createFeatureResolver({
38
42
  envName: "HOLOCRON_ORG_TOKEN",
39
43
  keyringKey: "github.org"
@@ -422,6 +426,46 @@ function toGHScope(scope) {
422
426
  return scope;
423
427
  }
424
428
  //#endregion
429
+ //#region src/capabilities/files.ts
430
+ /** Reads one file; `null` when it does not exist. `version` is the blob sha `writeFile` needs to replace exactly this one. */
431
+ /** A fine-grained token only reaches the repositories and permissions it was given; say which one is missing. */
432
+ function noAccess(repo, action, cause) {
433
+ return new Error(`the token cannot ${action} files in ${repo} (403): give the user-sync token (HOLOCRON_USER_SYNC_TOKEN, or holocron auth set github.user-sync) access to that repository with Contents: ${action === "read" ? "read" : "read and write"}`, { cause });
434
+ }
435
+ async function readRepoFile(client, repo, path) {
436
+ try {
437
+ const file = await client.git.getContents(repo, path);
438
+ if (file.encoding !== "base64") throw new Error(`${repo}/${path}: unexpected encoding \`${file.encoding}\``);
439
+ return {
440
+ content: Buffer.from(file.content, "base64").toString("utf-8"),
441
+ version: file.sha
442
+ };
443
+ } catch (err) {
444
+ if (err instanceof ProviderApiError && err.status === 403) throw noAccess(repo, "read", err);
445
+ if (!(err instanceof ProviderApiError && err.status === 404)) throw err;
446
+ }
447
+ try {
448
+ await client.repos.getRepo(repo);
449
+ } catch (err) {
450
+ if (err instanceof ProviderApiError && err.status === 404) throw new Error(`${repo} was not found: it does not exist, or the token cannot see it`, { cause: err });
451
+ throw err;
452
+ }
453
+ return null;
454
+ }
455
+ /** Creates or replaces one file in a commit; GitHub refuses (409) a `version` that has since changed. */
456
+ async function writeRepoFile(client, repo, path, content, opts) {
457
+ try {
458
+ await client.git.putContents(repo, path, {
459
+ message: opts.message,
460
+ content,
461
+ ...opts.version ? { sha: opts.version } : {}
462
+ });
463
+ } catch (err) {
464
+ if (err instanceof ProviderApiError && err.status === 403) throw noAccess(repo, "write", err);
465
+ throw err;
466
+ }
467
+ }
468
+ //#endregion
425
469
  //#region src/capabilities/labels.ts
426
470
  /**
427
471
  * Idempotently synchronise a repo's labels to the canonical set.
@@ -505,6 +549,7 @@ var GitHubSource = class {
505
549
  repoRoot;
506
550
  workflowDir;
507
551
  orgClient;
552
+ userSyncClient;
508
553
  baseUrl;
509
554
  fetchImpl;
510
555
  constructor(client, opts) {
@@ -519,6 +564,7 @@ var GitHubSource = class {
519
564
  });
520
565
  this.workflowDir = join(this.repoRoot, ".github", "workflows");
521
566
  this.orgClient = opts.orgClient;
567
+ this.userSyncClient = opts.userSyncClient;
522
568
  this.baseUrl = opts.baseUrl;
523
569
  this.fetchImpl = opts.fetch;
524
570
  }
@@ -611,6 +657,18 @@ var GitHubSource = class {
611
657
  async syncTeams(teams) {
612
658
  return syncTeams(this.orgClient ?? this.client, this.repo, teams);
613
659
  }
660
+ async teamMembers(teamSlug, org = this.owner) {
661
+ return (await (this.orgClient ?? this.client).teams.members(org, teamSlug)).map((m) => ({
662
+ id: String(m.id),
663
+ login: m.login
664
+ }));
665
+ }
666
+ async readFile(path, repo = this.repo) {
667
+ return readRepoFile(this.userSyncClient ?? this.orgClient ?? this.client, repo, path);
668
+ }
669
+ async writeFile(path, content, opts) {
670
+ return writeRepoFile(this.userSyncClient ?? this.orgClient ?? this.client, opts.repo ?? this.repo, path, content, opts);
671
+ }
614
672
  async syncTopics(topics) {
615
673
  return syncTopics(this.client, this.repo, topics);
616
674
  }
@@ -693,11 +751,12 @@ async function verifyToken(token, opts = {}) {
693
751
  }
694
752
  //#endregion
695
753
  //#region src/index.ts
696
- function source(ctx, orgClient) {
754
+ function source(ctx, orgClient, userSyncClient) {
697
755
  return new GitHubSource(ctx.client, {
698
756
  repo: ctx.repo,
699
757
  repoRoot: ctx.repoRoot,
700
758
  orgClient,
759
+ userSyncClient,
701
760
  baseUrl: ctx.options.baseUrl,
702
761
  fetch: ctx.options.fetch
703
762
  });
@@ -776,10 +835,19 @@ function createPlugin(options) {
776
835
  return;
777
836
  }
778
837
  }
838
+ /** The token for the user-sync files; undefined when it is not configured, and the org token is used instead. */
839
+ function makeUserSyncClient() {
840
+ const token = tryResolve(resolveUserSyncToken);
841
+ return token ? createGitHubClient$1({
842
+ token,
843
+ baseUrl: options.baseUrl,
844
+ fetch: options.fetch
845
+ }) : void 0;
846
+ }
779
847
  return {
780
848
  name: "@theholocron/holocron-plugin-github",
781
849
  capabilities: {
782
- source: () => source(makeCtx(resolveAdminToken), makeOrgClient()),
850
+ source: () => source(makeCtx(resolveAdminToken), makeOrgClient(), makeUserSyncClient()),
783
851
  ci: () => ci(makeCtx(resolveReadToken)),
784
852
  secrets: () => makeSecrets(),
785
853
  environments: () => environments(makeCtx(resolveAdminToken)),
@@ -793,4 +861,4 @@ function createPlugin(options) {
793
861
  */
794
862
  const AUTH_HINT = "generate fine-grained Personal Access Tokens at https://github.com/settings/tokens (one per feature — see docs/tokens.md for required scopes per operation), then run: holocron auth set github.<feature> <PAT>";
795
863
  //#endregion
796
- export { AUTH_HINT, AuthError, GitHubCi, GitHubEnvironments, GitHubIssues, GitHubSecrets, GitHubSource, ci, createFeatureResolver, createGitHubClient, createPlugin, encryptSecret, environments, issues, normalizeTeamEntry, resolveAdminToken, resolveDeployToken, resolveIssuesToken, resolveOrgToken, resolveReadToken, resolveReleaseToken, resolveSecretsToken, resolveSyncToken, secrets, source, syncLabels, syncTeams, verifyToken };
864
+ export { AUTH_HINT, AuthError, GitHubCi, GitHubEnvironments, GitHubIssues, GitHubSecrets, GitHubSource, ci, createFeatureResolver, createGitHubClient, createPlugin, encryptSecret, environments, issues, normalizeTeamEntry, resolveAdminToken, resolveDeployToken, resolveIssuesToken, resolveOrgToken, resolveReadToken, resolveReleaseToken, resolveSecretsToken, resolveSyncToken, resolveUserSyncToken, secrets, source, syncLabels, syncTeams, verifyToken };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theholocron/holocron-plugin-github",
3
- "version": "5.0.0-alpha.199",
3
+ "version": "5.0.0-alpha.200",
4
4
  "description": "Holocron plugin for GitHub. Implements source, ci, secrets, environments, and issues capabilities against the GitHub REST API.",
5
5
  "keywords": [
6
6
  "ci",
@@ -36,7 +36,7 @@
36
36
  "devDependencies": {
37
37
  "@theholocron/eslint-config": "^8.8.2",
38
38
  "@theholocron/fs-utils": "^1.16.0",
39
- "@theholocron/github-client": "^1.43.1",
39
+ "@theholocron/github-client": "^1.47.1",
40
40
  "@theholocron/http-client": "^1.43.1",
41
41
  "@theholocron/misc-utils": "^1.16.0",
42
42
  "@theholocron/tsconfig": "^8.8.2",
@@ -52,13 +52,13 @@
52
52
  "tsx": "4.23.15",
53
53
  "typescript": "^5.9.3",
54
54
  "vitest": "^4.1.11",
55
- "@theholocron/cli": "5.0.0-alpha.199"
55
+ "@theholocron/cli": "5.0.0-alpha.200"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "@theholocron/fs-utils": "^1.16.0",
59
- "@theholocron/github-client": "^1.43.1",
59
+ "@theholocron/github-client": "^1.47.1",
60
60
  "@theholocron/misc-utils": "^1.16.0",
61
- "@theholocron/cli": "5.0.0-alpha.199"
61
+ "@theholocron/cli": "5.0.0-alpha.200"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=22"