@gitpagedocs/cli 1.1.62 → 1.1.64
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/README.md +193 -193
- package/ai/application/ai-command.ts +24 -24
- package/ai/application/ai-provider-factory.ts +22 -22
- package/ai/application/docs-pattern.ts +68 -68
- package/ai/application/run-ai-cli-command.ts +167 -167
- package/ai/config.ts +50 -50
- package/ai/core/models/ai-cli-config.ts +33 -33
- package/ai/core/models/parse-ai-cli-config.ts +78 -78
- package/ai/core/ports/illm-provider.ts +32 -32
- package/ai/infrastructure/ai-config-file.ts +101 -101
- package/ai/infrastructure/file-system-adapter.ts +85 -85
- package/ai/infrastructure/llm/tools-llm-provider.ts +54 -54
- package/ai/infrastructure/version-docs-writer.ts +143 -143
- package/ai/presentation/ai-prompts.ts +239 -239
- package/application/ports/cli-runtime-ports.ts +91 -91
- package/application/report/config-only-reporter.mjs +64 -64
- package/application/use-cases/dispatch-mode.ts +18 -18
- package/builders/config-orchestrator.mjs +34 -34
- package/builders/project-links.mjs +50 -50
- package/builders/root-config-builder.mjs +61 -61
- package/builders/route-builders.mjs +136 -136
- package/builders/theme-template.mjs +76 -76
- package/builders/version-config-builder.mjs +186 -186
- package/content/docs-en.mjs +549 -549
- package/content/docs-es.mjs +462 -462
- package/content/docs-pt.mjs +462 -462
- package/contracts/doc-versions.mjs +3 -3
- package/data/default-site-config.mjs +321 -321
- package/data/i18n-langmenu.mjs +347 -347
- package/data/layouts.mjs +436 -436
- package/data/path-mappings.mjs +26 -26
- package/data/route-metas.mjs +93 -93
- package/data/theme-colors.mjs +443 -443
- package/data/urls.mjs +12 -12
- package/domain/models/cli-options.ts +33 -33
- package/home/constants.mjs +4 -4
- package/index.mjs +37 -37
- package/infrastructure/gitpagedocs-config-file.ts +47 -47
- package/infrastructure/home/runtime.mjs +37 -37
- package/package.json +3 -3
- package/presentation/commands/ai-info.ts +57 -57
- package/presentation/commands/config-clear.ts +23 -23
- package/presentation/commands/config-info.ts +24 -24
- package/presentation/commands/diagnostics.ts +164 -164
- package/presentation/commands/docs.ts +37 -37
- package/presentation/commands/mcp.ts +20 -20
- package/presentation/commands/pages.ts +101 -101
- package/presentation/commands/password.ts +62 -62
- package/presentation/commands/run-command.ts +70 -70
- package/presentation/index.ts +124 -124
- package/presentation/options/parser.ts +119 -119
- package/presentation/options/resolver.ts +46 -46
- package/presentation/ui/clack.ts +91 -91
- package/presentation/ui/prompts.ts +146 -146
- package/runtime/git-ops.mjs +110 -110
- package/runtime/output.mjs +159 -159
- package/runtime/workflow.mjs +129 -129
- package/start.mjs +12 -12
- package/ui/logger.mjs +9 -9
|
@@ -1,146 +1,146 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { execSync } from "node:child_process";
|
|
4
|
-
import type { CliOptions } from "../../domain/models/cli-options";
|
|
5
|
-
import { DEFAULTS } from "../options/schema";
|
|
6
|
-
import { askText, askConfirm, note } from "./clack";
|
|
7
|
-
import { interactivePromptsAvailable, promptingEnabled } from "./tty";
|
|
8
|
-
import { askLayoutsSource, askLayoutsDir } from "./layouts-prompts";
|
|
9
|
-
|
|
10
|
-
export { interactivePromptsAvailable };
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Every command prompts when it runs in a real terminal, so a bare invocation
|
|
14
|
-
* reflects what the user actually needs instead of silently taking defaults.
|
|
15
|
-
* CI, a piped stdin, and an explicit `--no-interactive` / `--yes` / `-y` opt out;
|
|
16
|
-
* `--interactive` / `-i` remains accepted and reads the same either way.
|
|
17
|
-
*/
|
|
18
|
-
export function shouldRunInteractive(argv: string[]): boolean {
|
|
19
|
-
return promptingEnabled(argv.slice(2));
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export async function promptHomeOptions(parsed: CliOptions): Promise<CliOptions> {
|
|
23
|
-
const outputDir = await askText({
|
|
24
|
-
message: "Output directory:",
|
|
25
|
-
defaultValue: parsed.outputDir ?? DEFAULTS.home.outputDir,
|
|
26
|
-
validate: (v) => (v && v.trim() ? undefined : "Required"),
|
|
27
|
-
});
|
|
28
|
-
const repositorySearch = await askConfirm(
|
|
29
|
-
"Enable repository search home?",
|
|
30
|
-
parsed.repositorySearch ?? DEFAULTS.repositorySearch,
|
|
31
|
-
);
|
|
32
|
-
const basePath = await askText({
|
|
33
|
-
message: "Base path (e.g. git-page-docs, empty for root):",
|
|
34
|
-
defaultValue: parsed.basePath ?? DEFAULTS.basePath,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
...parsed,
|
|
39
|
-
outputDir: outputDir.trim(),
|
|
40
|
-
repositorySearch,
|
|
41
|
-
basePath: basePath?.trim() ?? "",
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Deploy/--push needs a GitHub owner + repo. When they are missing, prompt for
|
|
47
|
-
* them (pre-filled from the git `origin` remote when available) instead of
|
|
48
|
-
* crashing with "`--push` requires owner and repo".
|
|
49
|
-
*/
|
|
50
|
-
export async function askOwnerRepo(
|
|
51
|
-
detected: { owner: string; repo: string } | null,
|
|
52
|
-
defaults?: { owner?: string; repo?: string },
|
|
53
|
-
): Promise<{ owner: string; repo: string }> {
|
|
54
|
-
const owner = await askText({
|
|
55
|
-
message: "GitHub owner (user or organization):",
|
|
56
|
-
defaultValue: defaults?.owner || detected?.owner || "",
|
|
57
|
-
validate: (v) => (v && v.trim() ? undefined : "Owner is required."),
|
|
58
|
-
});
|
|
59
|
-
const repo = await askText({
|
|
60
|
-
message: "GitHub repository name:",
|
|
61
|
-
defaultValue: defaults?.repo || detected?.repo || "",
|
|
62
|
-
validate: (v) => (v && v.trim() ? undefined : "Repository is required."),
|
|
63
|
-
});
|
|
64
|
-
return { owner: owner.trim(), repo: repo.trim() };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export async function promptDeployOptions(
|
|
68
|
-
parsed: CliOptions,
|
|
69
|
-
detected: { owner: string; repo: string } | null,
|
|
70
|
-
): Promise<CliOptions> {
|
|
71
|
-
note(
|
|
72
|
-
"Deploy publishes gitpagedocs to GitHub Pages and needs the target\nrepository (owner/repo). Press Enter to accept a detected value.",
|
|
73
|
-
"GitHub Pages deploy",
|
|
74
|
-
);
|
|
75
|
-
const { owner, repo } = await askOwnerRepo(detected, {
|
|
76
|
-
owner: parsed.githubOwner,
|
|
77
|
-
repo: parsed.githubRepo,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
...parsed,
|
|
82
|
-
githubOwner: owner,
|
|
83
|
-
githubRepo: repo,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Deploy/--push needs a git repository in the current folder. When there isn't
|
|
89
|
-
* one, offer to run `git init` instead of crashing with "not a git repository".
|
|
90
|
-
* In CI/non-TTY this is a no-op so the downstream guard still reports clearly.
|
|
91
|
-
*/
|
|
92
|
-
export async function ensureGitRepoInteractive(root: string): Promise<void> {
|
|
93
|
-
if (existsSync(path.join(root, ".git"))) return;
|
|
94
|
-
if (!interactivePromptsAvailable()) return;
|
|
95
|
-
const init = await askConfirm(
|
|
96
|
-
"This folder is not a git repository yet. Initialize one here (git init)?",
|
|
97
|
-
true,
|
|
98
|
-
);
|
|
99
|
-
if (!init) {
|
|
100
|
-
note("Deploy needs a git repository. Run `git init`, then retry.", "Heads up");
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
try {
|
|
104
|
-
execSync("git init", { cwd: root, stdio: "ignore" });
|
|
105
|
-
note("Initialized an empty git repository.", "git");
|
|
106
|
-
} catch {
|
|
107
|
-
note("Could not run `git init`. Initialize git manually, then retry.", "git");
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Ask only for what the command line did not already answer, so an explicit
|
|
113
|
-
* flag is never second-guessed by a prompt.
|
|
114
|
-
*/
|
|
115
|
-
export async function promptConfigOnlyOptions(parsed: CliOptions): Promise<CliOptions> {
|
|
116
|
-
const useLocalLayoutConfig = parsed.explicit.useLocalLayoutConfig
|
|
117
|
-
? parsed.useLocalLayoutConfig
|
|
118
|
-
: await askLayoutsSource(parsed.useLocalLayoutConfig ?? false);
|
|
119
|
-
|
|
120
|
-
const layoutsDir =
|
|
121
|
-
useLocalLayoutConfig && !parsed.explicit.layoutsDir
|
|
122
|
-
? await askLayoutsDir(parsed.layoutsDir)
|
|
123
|
-
: parsed.layoutsDir;
|
|
124
|
-
|
|
125
|
-
const githubOwner = parsed.explicit.githubOwner
|
|
126
|
-
? parsed.githubOwner
|
|
127
|
-
: await askText({
|
|
128
|
-
message: "GitHub owner (optional):",
|
|
129
|
-
defaultValue: parsed.githubOwner ?? "",
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const githubRepo = parsed.explicit.githubRepo
|
|
133
|
-
? parsed.githubRepo
|
|
134
|
-
: await askText({
|
|
135
|
-
message: "GitHub repo (optional):",
|
|
136
|
-
defaultValue: parsed.githubRepo ?? "",
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
return {
|
|
140
|
-
...parsed,
|
|
141
|
-
useLocalLayoutConfig,
|
|
142
|
-
layoutsDir,
|
|
143
|
-
githubOwner: githubOwner?.trim() ?? "",
|
|
144
|
-
githubRepo: githubRepo?.trim() ?? "",
|
|
145
|
-
};
|
|
146
|
-
}
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import type { CliOptions } from "../../domain/models/cli-options";
|
|
5
|
+
import { DEFAULTS } from "../options/schema";
|
|
6
|
+
import { askText, askConfirm, note } from "./clack";
|
|
7
|
+
import { interactivePromptsAvailable, promptingEnabled } from "./tty";
|
|
8
|
+
import { askLayoutsSource, askLayoutsDir } from "./layouts-prompts";
|
|
9
|
+
|
|
10
|
+
export { interactivePromptsAvailable };
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Every command prompts when it runs in a real terminal, so a bare invocation
|
|
14
|
+
* reflects what the user actually needs instead of silently taking defaults.
|
|
15
|
+
* CI, a piped stdin, and an explicit `--no-interactive` / `--yes` / `-y` opt out;
|
|
16
|
+
* `--interactive` / `-i` remains accepted and reads the same either way.
|
|
17
|
+
*/
|
|
18
|
+
export function shouldRunInteractive(argv: string[]): boolean {
|
|
19
|
+
return promptingEnabled(argv.slice(2));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function promptHomeOptions(parsed: CliOptions): Promise<CliOptions> {
|
|
23
|
+
const outputDir = await askText({
|
|
24
|
+
message: "Output directory:",
|
|
25
|
+
defaultValue: parsed.outputDir ?? DEFAULTS.home.outputDir,
|
|
26
|
+
validate: (v) => (v && v.trim() ? undefined : "Required"),
|
|
27
|
+
});
|
|
28
|
+
const repositorySearch = await askConfirm(
|
|
29
|
+
"Enable repository search home?",
|
|
30
|
+
parsed.repositorySearch ?? DEFAULTS.repositorySearch,
|
|
31
|
+
);
|
|
32
|
+
const basePath = await askText({
|
|
33
|
+
message: "Base path (e.g. git-page-docs, empty for root):",
|
|
34
|
+
defaultValue: parsed.basePath ?? DEFAULTS.basePath,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
...parsed,
|
|
39
|
+
outputDir: outputDir.trim(),
|
|
40
|
+
repositorySearch,
|
|
41
|
+
basePath: basePath?.trim() ?? "",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Deploy/--push needs a GitHub owner + repo. When they are missing, prompt for
|
|
47
|
+
* them (pre-filled from the git `origin` remote when available) instead of
|
|
48
|
+
* crashing with "`--push` requires owner and repo".
|
|
49
|
+
*/
|
|
50
|
+
export async function askOwnerRepo(
|
|
51
|
+
detected: { owner: string; repo: string } | null,
|
|
52
|
+
defaults?: { owner?: string; repo?: string },
|
|
53
|
+
): Promise<{ owner: string; repo: string }> {
|
|
54
|
+
const owner = await askText({
|
|
55
|
+
message: "GitHub owner (user or organization):",
|
|
56
|
+
defaultValue: defaults?.owner || detected?.owner || "",
|
|
57
|
+
validate: (v) => (v && v.trim() ? undefined : "Owner is required."),
|
|
58
|
+
});
|
|
59
|
+
const repo = await askText({
|
|
60
|
+
message: "GitHub repository name:",
|
|
61
|
+
defaultValue: defaults?.repo || detected?.repo || "",
|
|
62
|
+
validate: (v) => (v && v.trim() ? undefined : "Repository is required."),
|
|
63
|
+
});
|
|
64
|
+
return { owner: owner.trim(), repo: repo.trim() };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function promptDeployOptions(
|
|
68
|
+
parsed: CliOptions,
|
|
69
|
+
detected: { owner: string; repo: string } | null,
|
|
70
|
+
): Promise<CliOptions> {
|
|
71
|
+
note(
|
|
72
|
+
"Deploy publishes gitpagedocs to GitHub Pages and needs the target\nrepository (owner/repo). Press Enter to accept a detected value.",
|
|
73
|
+
"GitHub Pages deploy",
|
|
74
|
+
);
|
|
75
|
+
const { owner, repo } = await askOwnerRepo(detected, {
|
|
76
|
+
owner: parsed.githubOwner,
|
|
77
|
+
repo: parsed.githubRepo,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
...parsed,
|
|
82
|
+
githubOwner: owner,
|
|
83
|
+
githubRepo: repo,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Deploy/--push needs a git repository in the current folder. When there isn't
|
|
89
|
+
* one, offer to run `git init` instead of crashing with "not a git repository".
|
|
90
|
+
* In CI/non-TTY this is a no-op so the downstream guard still reports clearly.
|
|
91
|
+
*/
|
|
92
|
+
export async function ensureGitRepoInteractive(root: string): Promise<void> {
|
|
93
|
+
if (existsSync(path.join(root, ".git"))) return;
|
|
94
|
+
if (!interactivePromptsAvailable()) return;
|
|
95
|
+
const init = await askConfirm(
|
|
96
|
+
"This folder is not a git repository yet. Initialize one here (git init)?",
|
|
97
|
+
true,
|
|
98
|
+
);
|
|
99
|
+
if (!init) {
|
|
100
|
+
note("Deploy needs a git repository. Run `git init`, then retry.", "Heads up");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
execSync("git init", { cwd: root, stdio: "ignore" });
|
|
105
|
+
note("Initialized an empty git repository.", "git");
|
|
106
|
+
} catch {
|
|
107
|
+
note("Could not run `git init`. Initialize git manually, then retry.", "git");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Ask only for what the command line did not already answer, so an explicit
|
|
113
|
+
* flag is never second-guessed by a prompt.
|
|
114
|
+
*/
|
|
115
|
+
export async function promptConfigOnlyOptions(parsed: CliOptions): Promise<CliOptions> {
|
|
116
|
+
const useLocalLayoutConfig = parsed.explicit.useLocalLayoutConfig
|
|
117
|
+
? parsed.useLocalLayoutConfig
|
|
118
|
+
: await askLayoutsSource(parsed.useLocalLayoutConfig ?? false);
|
|
119
|
+
|
|
120
|
+
const layoutsDir =
|
|
121
|
+
useLocalLayoutConfig && !parsed.explicit.layoutsDir
|
|
122
|
+
? await askLayoutsDir(parsed.layoutsDir)
|
|
123
|
+
: parsed.layoutsDir;
|
|
124
|
+
|
|
125
|
+
const githubOwner = parsed.explicit.githubOwner
|
|
126
|
+
? parsed.githubOwner
|
|
127
|
+
: await askText({
|
|
128
|
+
message: "GitHub owner (optional):",
|
|
129
|
+
defaultValue: parsed.githubOwner ?? "",
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const githubRepo = parsed.explicit.githubRepo
|
|
133
|
+
? parsed.githubRepo
|
|
134
|
+
: await askText({
|
|
135
|
+
message: "GitHub repo (optional):",
|
|
136
|
+
defaultValue: parsed.githubRepo ?? "",
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
...parsed,
|
|
141
|
+
useLocalLayoutConfig,
|
|
142
|
+
layoutsDir,
|
|
143
|
+
githubOwner: githubOwner?.trim() ?? "",
|
|
144
|
+
githubRepo: githubRepo?.trim() ?? "",
|
|
145
|
+
};
|
|
146
|
+
}
|
package/runtime/git-ops.mjs
CHANGED
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { execSync } from "node:child_process";
|
|
4
|
-
|
|
5
|
-
/** Detect { owner, repo } from the git `origin` remote URL, or null. */
|
|
6
|
-
export function detectRepoFromGit(root) {
|
|
7
|
-
try {
|
|
8
|
-
const url = execSync("git remote get-url origin", { cwd: root, stdio: "pipe" }).toString().trim();
|
|
9
|
-
const match = url.match(/[:/]([^/:]+)\/([^/]+?)(?:\.git)?$/);
|
|
10
|
-
if (match) return { owner: match[1], repo: match[2] };
|
|
11
|
-
} catch {
|
|
12
|
-
// no remote / not a repo
|
|
13
|
-
}
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function tryConfigurePagesToGitHubActions(owner, repo, branch, root) {
|
|
18
|
-
try {
|
|
19
|
-
execSync("gh --version", { cwd: root, stdio: "ignore" });
|
|
20
|
-
} catch {
|
|
21
|
-
console.warn(
|
|
22
|
-
"GitHub CLI (gh) not found. Could not auto-configure Pages source. Set GitHub Pages source to 'GitHub Actions' manually in repository settings.",
|
|
23
|
-
);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const candidates = [
|
|
28
|
-
`gh api -X PUT "repos/${owner}/${repo}/pages" -f build_type=workflow`,
|
|
29
|
-
`gh api -X POST "repos/${owner}/${repo}/pages" -f build_type=workflow`,
|
|
30
|
-
`gh api -X POST "repos/${owner}/${repo}/pages" -f source[branch]=${branch} -f source[path]=/`,
|
|
31
|
-
`gh api -X PUT "repos/${owner}/${repo}/pages" -f source[branch]=${branch} -f source[path]=/ -f build_type=workflow`,
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
for (const command of candidates) {
|
|
35
|
-
try {
|
|
36
|
-
execSync(command, { cwd: root, stdio: "ignore" });
|
|
37
|
-
console.log("GitHub Pages source configured for GitHub Actions.");
|
|
38
|
-
return;
|
|
39
|
-
} catch {
|
|
40
|
-
// Try next candidate endpoint.
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
console.warn(
|
|
45
|
-
"Could not auto-configure Pages source through GitHub API. Set repository Pages source to 'GitHub Actions' manually.",
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function getCurrentGitBranch(root) {
|
|
50
|
-
try {
|
|
51
|
-
const branch = execSync("git branch --show-current", { cwd: root, stdio: "pipe" }).toString().trim();
|
|
52
|
-
return branch || "main";
|
|
53
|
-
} catch {
|
|
54
|
-
return "main";
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function runGitPushForGeneratedArtifacts(options, root, sanitizeSegment) {
|
|
59
|
-
const owner = sanitizeSegment(options.githubOwner);
|
|
60
|
-
const repo = sanitizeSegment(options.githubRepo);
|
|
61
|
-
if (!owner || !repo) {
|
|
62
|
-
throw new Error("`--push` requires owner and repo. Use `--owner <owner> --repo <repo>` or `--<owner> --<repo>`.");
|
|
63
|
-
}
|
|
64
|
-
if (!existsSync(path.join(root, ".git"))) {
|
|
65
|
-
throw new Error("Current directory is not a git repository. Initialize git before using --push.");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const repoUrl = `https://github.com/${owner}/${repo}.git`;
|
|
69
|
-
try {
|
|
70
|
-
execSync("git remote get-url origin", { cwd: root, stdio: "ignore" });
|
|
71
|
-
} catch {
|
|
72
|
-
execSync(`git remote add origin "${repoUrl}"`, { cwd: root, stdio: "inherit" });
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
execSync('git add "gitpagedocs" ".github/workflows/gitpagedocs-pages.yml"', { cwd: root, stdio: "inherit" });
|
|
76
|
-
|
|
77
|
-
let hasStagedChanges = false;
|
|
78
|
-
try {
|
|
79
|
-
execSync("git diff --cached --quiet", { cwd: root, stdio: "ignore" });
|
|
80
|
-
hasStagedChanges = false;
|
|
81
|
-
} catch {
|
|
82
|
-
hasStagedChanges = true;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (!hasStagedChanges) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
execSync('git commit -m "chore: setup gitpagedocs pages workflow"', { cwd: root, stdio: "inherit" });
|
|
90
|
-
|
|
91
|
-
const currentBranch = getCurrentGitBranch(root);
|
|
92
|
-
try {
|
|
93
|
-
execSync(`git push -u origin ${currentBranch}`, { cwd: root, stdio: "inherit" });
|
|
94
|
-
tryConfigurePagesToGitHubActions(owner, repo, currentBranch, root);
|
|
95
|
-
return;
|
|
96
|
-
} catch {
|
|
97
|
-
console.warn("Initial push failed. Trying automatic rebase with remote branch...");
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
execSync(`git pull --rebase origin ${currentBranch}`, { cwd: root, stdio: "inherit" });
|
|
102
|
-
execSync(`git push -u origin ${currentBranch}`, { cwd: root, stdio: "inherit" });
|
|
103
|
-
} catch {
|
|
104
|
-
throw new Error(
|
|
105
|
-
`Failed to push after automatic rebase on branch '${currentBranch}'. Resolve conflicts and run 'git push -u origin ${currentBranch}' manually.`,
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
tryConfigurePagesToGitHubActions(owner, repo, currentBranch, root);
|
|
110
|
-
}
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
/** Detect { owner, repo } from the git `origin` remote URL, or null. */
|
|
6
|
+
export function detectRepoFromGit(root) {
|
|
7
|
+
try {
|
|
8
|
+
const url = execSync("git remote get-url origin", { cwd: root, stdio: "pipe" }).toString().trim();
|
|
9
|
+
const match = url.match(/[:/]([^/:]+)\/([^/]+?)(?:\.git)?$/);
|
|
10
|
+
if (match) return { owner: match[1], repo: match[2] };
|
|
11
|
+
} catch {
|
|
12
|
+
// no remote / not a repo
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function tryConfigurePagesToGitHubActions(owner, repo, branch, root) {
|
|
18
|
+
try {
|
|
19
|
+
execSync("gh --version", { cwd: root, stdio: "ignore" });
|
|
20
|
+
} catch {
|
|
21
|
+
console.warn(
|
|
22
|
+
"GitHub CLI (gh) not found. Could not auto-configure Pages source. Set GitHub Pages source to 'GitHub Actions' manually in repository settings.",
|
|
23
|
+
);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const candidates = [
|
|
28
|
+
`gh api -X PUT "repos/${owner}/${repo}/pages" -f build_type=workflow`,
|
|
29
|
+
`gh api -X POST "repos/${owner}/${repo}/pages" -f build_type=workflow`,
|
|
30
|
+
`gh api -X POST "repos/${owner}/${repo}/pages" -f source[branch]=${branch} -f source[path]=/`,
|
|
31
|
+
`gh api -X PUT "repos/${owner}/${repo}/pages" -f source[branch]=${branch} -f source[path]=/ -f build_type=workflow`,
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
for (const command of candidates) {
|
|
35
|
+
try {
|
|
36
|
+
execSync(command, { cwd: root, stdio: "ignore" });
|
|
37
|
+
console.log("GitHub Pages source configured for GitHub Actions.");
|
|
38
|
+
return;
|
|
39
|
+
} catch {
|
|
40
|
+
// Try next candidate endpoint.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.warn(
|
|
45
|
+
"Could not auto-configure Pages source through GitHub API. Set repository Pages source to 'GitHub Actions' manually.",
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function getCurrentGitBranch(root) {
|
|
50
|
+
try {
|
|
51
|
+
const branch = execSync("git branch --show-current", { cwd: root, stdio: "pipe" }).toString().trim();
|
|
52
|
+
return branch || "main";
|
|
53
|
+
} catch {
|
|
54
|
+
return "main";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function runGitPushForGeneratedArtifacts(options, root, sanitizeSegment) {
|
|
59
|
+
const owner = sanitizeSegment(options.githubOwner);
|
|
60
|
+
const repo = sanitizeSegment(options.githubRepo);
|
|
61
|
+
if (!owner || !repo) {
|
|
62
|
+
throw new Error("`--push` requires owner and repo. Use `--owner <owner> --repo <repo>` or `--<owner> --<repo>`.");
|
|
63
|
+
}
|
|
64
|
+
if (!existsSync(path.join(root, ".git"))) {
|
|
65
|
+
throw new Error("Current directory is not a git repository. Initialize git before using --push.");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const repoUrl = `https://github.com/${owner}/${repo}.git`;
|
|
69
|
+
try {
|
|
70
|
+
execSync("git remote get-url origin", { cwd: root, stdio: "ignore" });
|
|
71
|
+
} catch {
|
|
72
|
+
execSync(`git remote add origin "${repoUrl}"`, { cwd: root, stdio: "inherit" });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
execSync('git add "gitpagedocs" ".github/workflows/gitpagedocs-pages.yml"', { cwd: root, stdio: "inherit" });
|
|
76
|
+
|
|
77
|
+
let hasStagedChanges = false;
|
|
78
|
+
try {
|
|
79
|
+
execSync("git diff --cached --quiet", { cwd: root, stdio: "ignore" });
|
|
80
|
+
hasStagedChanges = false;
|
|
81
|
+
} catch {
|
|
82
|
+
hasStagedChanges = true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!hasStagedChanges) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
execSync('git commit -m "chore: setup gitpagedocs pages workflow"', { cwd: root, stdio: "inherit" });
|
|
90
|
+
|
|
91
|
+
const currentBranch = getCurrentGitBranch(root);
|
|
92
|
+
try {
|
|
93
|
+
execSync(`git push -u origin ${currentBranch}`, { cwd: root, stdio: "inherit" });
|
|
94
|
+
tryConfigurePagesToGitHubActions(owner, repo, currentBranch, root);
|
|
95
|
+
return;
|
|
96
|
+
} catch {
|
|
97
|
+
console.warn("Initial push failed. Trying automatic rebase with remote branch...");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
execSync(`git pull --rebase origin ${currentBranch}`, { cwd: root, stdio: "inherit" });
|
|
102
|
+
execSync(`git push -u origin ${currentBranch}`, { cwd: root, stdio: "inherit" });
|
|
103
|
+
} catch {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Failed to push after automatic rebase on branch '${currentBranch}'. Resolve conflicts and run 'git push -u origin ${currentBranch}' manually.`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
tryConfigurePagesToGitHubActions(owner, repo, currentBranch, root);
|
|
110
|
+
}
|