@oisincoveney/pipeline 3.23.0 → 3.23.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.
@@ -1,5 +1,6 @@
1
1
  import { DEFAULT_RUNNER_COMMAND_GIT_COMMITTER } from "../config/schema/catalog.js";
2
2
  import { resolveFactorySeams } from "./exec.js";
3
+ import { githubGitCredentialEnv } from "./git-credentials.js";
3
4
  import { existsSync, mkdtempSync } from "node:fs";
4
5
  import { tmpdir } from "node:os";
5
6
  import { join, resolve } from "node:path";
@@ -80,7 +81,7 @@ async function runCreateExperiment(options) {
80
81
  previews,
81
82
  ...options.templateRef ? { templateRef: options.templateRef } : {},
82
83
  templateSource
83
- }));
84
+ }), { env: githubGitCredentialEnv() });
84
85
  const stampedRegistryEntry = join(stampDir, STAMPED_REGISTRY_DIR, `${name}.yaml`);
85
86
  if (!existsSync(stampedRegistryEntry)) throw new Error(`create-experiment: stamp is missing the registry entry ${STAMPED_REGISTRY_DIR}/${name}.yaml — template contract changed?`);
86
87
  log("create-experiment: committing the stamped tree");
@@ -3,6 +3,7 @@ import { execa } from "execa";
3
3
  //#region src/factory/exec.ts
4
4
  const defaultFactoryExec = (command, args, options) => execa(command, [...args], {
5
5
  ...options?.cwd ? { cwd: options.cwd } : {},
6
+ ...options?.env ? { env: options.env } : {},
6
7
  stdin: "ignore"
7
8
  });
8
9
  const defaultFactoryGit = (cwd, args) => runAuthenticatedGit(cwd, args);
@@ -0,0 +1,37 @@
1
+ import { existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join, resolve } from "node:path";
4
+ //#region src/factory/git-credentials.ts
5
+ const DEFAULT_GIT_CREDENTIALS_DIR = "/etc/pipeline/git-credentials";
6
+ /**
7
+ * copier fetches the (private) momokaya-template by spawning its OWN git
8
+ * subprocess (`git ls-remote` / `clone`, via plumbum). `runAuthenticatedGit`
9
+ * only configures credentials per-invocation with `-c` flags on the git calls
10
+ * the lane makes itself, so copier's git sees no github.com credential and
11
+ * dies `fatal: could not read Username for 'https://github.com'`.
12
+ *
13
+ * This materializes a process-wide github.com credential from the runner's
14
+ * mounted git-credentials (username + PAT under /etc/pipeline/git-credentials)
15
+ * as `GIT_CONFIG_*` env any child (and grandchild) git process inherits, so
16
+ * copier's template fetch authenticates. The token is written to a 0600 store
17
+ * file and referenced by a `credential.helper=store` entry — it never lands in
18
+ * a URL or argv. No mounted credentials (local dev) -> empty env, so git falls
19
+ * back to ambient auth.
20
+ */
21
+ function githubGitCredentialEnv(credentialsDir = process.env.PIPELINE_GIT_CREDENTIALS_DIR ?? DEFAULT_GIT_CREDENTIALS_DIR) {
22
+ const usernamePath = resolve(credentialsDir, "username");
23
+ const passwordPath = resolve(credentialsDir, "password");
24
+ if (!(existsSync(usernamePath) && existsSync(passwordPath))) return {};
25
+ const username = readFileSync(usernamePath, "utf8").trim();
26
+ const password = readFileSync(passwordPath, "utf8").trim();
27
+ const storePath = join(mkdtempSync(join(tmpdir(), "factory-git-cred-")), ".git-credentials");
28
+ writeFileSync(storePath, `https://${encodeURIComponent(username)}:${encodeURIComponent(password)}@github.com\n`, { mode: 384 });
29
+ return {
30
+ GIT_CONFIG_COUNT: "1",
31
+ GIT_CONFIG_KEY_0: "credential.helper",
32
+ GIT_CONFIG_VALUE_0: `store --file=${storePath}`,
33
+ GIT_TERMINAL_PROMPT: "0"
34
+ };
35
+ }
36
+ //#endregion
37
+ export { githubGitCredentialEnv };
@@ -1,4 +1,5 @@
1
1
  import { resolveFactorySeams } from "./exec.js";
2
+ import { githubGitCredentialEnv } from "./git-credentials.js";
2
3
  import { committerConfigArgs } from "./create-experiment.js";
3
4
  import { isStampOf, parseCopierAnswers } from "./stamp-answers.js";
4
5
  import { parse } from "yaml";
@@ -101,7 +102,10 @@ async function updateRepo(input) {
101
102
  "--trust",
102
103
  "--defaults",
103
104
  ...input.templateRef ? ["--vcs-ref", input.templateRef] : []
104
- ], { cwd: cloneDir });
105
+ ], {
106
+ cwd: cloneDir,
107
+ env: githubGitCredentialEnv()
108
+ });
105
109
  const status = await git(cloneDir, ["status", "--porcelain"]);
106
110
  if (status.trim().length === 0) return {
107
111
  repo,
package/package.json CHANGED
@@ -146,7 +146,7 @@
146
146
  "prepack": "nub run build:cli"
147
147
  },
148
148
  "type": "module",
149
- "version": "3.23.0",
149
+ "version": "3.23.1",
150
150
  "description": "Config-driven multi-agent pipeline runner for repository work",
151
151
  "main": "./dist/index.js",
152
152
  "types": "./dist/index.d.ts",