@garyr/pt-cli 0.26.0 → 0.27.0
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 +1 -1
- package/dist/commands/learnCommand.js +11 -2
- package/dist/remote.js +29 -0
- package/package.json +9 -7
- package/src/commands/learnCommand.ts +12 -2
- package/src/remote.ts +33 -0
package/README.md
CHANGED
|
@@ -3,10 +3,19 @@ import path from 'path';
|
|
|
3
3
|
import inquirer from 'inquirer';
|
|
4
4
|
import { loadConfig, saveConfig, getTemplateNames, shouldExclude, shouldIgnore, shouldExcludeFile, getDefaultPostConfig } from '../config.js';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
|
+
import { downloadAndExtract } from '../remote.js';
|
|
6
7
|
export async function learn(sourcePath, updateTemplate = null, options = {}) {
|
|
7
|
-
|
|
8
|
+
let resolvedPath;
|
|
9
|
+
// Phase 1: Remote Check
|
|
10
|
+
if (sourcePath.startsWith('http')) {
|
|
11
|
+
console.log(chalk.cyan(`Downloading remote template from: ${sourcePath}...`));
|
|
12
|
+
resolvedPath = await downloadAndExtract(sourcePath);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
resolvedPath = path.resolve(sourcePath);
|
|
16
|
+
}
|
|
8
17
|
if (!fs.existsSync(resolvedPath)) {
|
|
9
|
-
console.error(chalk.red(`Error: Path "${
|
|
18
|
+
console.error(chalk.red(`Error: Path "${resolvedPath}" does not exist.`));
|
|
10
19
|
process.exit(1);
|
|
11
20
|
}
|
|
12
21
|
const isUpdate = !!updateTemplate;
|
package/dist/remote.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/remote.ts (New File)
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { Readable } from 'stream';
|
|
6
|
+
import { finished } from 'stream/promises';
|
|
7
|
+
import { extract } from 'tar'; // You'll need: npm install tar
|
|
8
|
+
export async function downloadAndExtract(url) {
|
|
9
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pt-template-'));
|
|
10
|
+
let downloadUrl = url;
|
|
11
|
+
// Convert GitHub/Gitea URLs to Zip/Tarball endpoints
|
|
12
|
+
if (url.includes('github.com')) {
|
|
13
|
+
downloadUrl = url.replace(/\/$/, '') + '/archive/refs/heads/main.tar.gz';
|
|
14
|
+
}
|
|
15
|
+
else if (url.includes('gitea')) {
|
|
16
|
+
downloadUrl = url.replace(/\/$/, '') + '/archive/main.tar.gz';
|
|
17
|
+
}
|
|
18
|
+
const response = await fetch(downloadUrl);
|
|
19
|
+
if (!response.ok)
|
|
20
|
+
throw new Error(`Failed to fetch ${downloadUrl}: ${response.statusText}`);
|
|
21
|
+
const dest = path.join(tempDir, 'template.tar.gz');
|
|
22
|
+
const fileStream = fs.createWriteStream(dest);
|
|
23
|
+
await finished(Readable.fromWeb(response.body).pipe(fileStream));
|
|
24
|
+
// Extract tarball
|
|
25
|
+
await extract({ file: dest, cwd: tempDir });
|
|
26
|
+
// Find the actual content folder (archives usually wrap content in a folder)
|
|
27
|
+
const dirs = fs.readdirSync(tempDir).filter(f => fs.statSync(path.join(tempDir, f)).isDirectory());
|
|
28
|
+
return path.join(tempDir, dirs[0]);
|
|
29
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@garyr/pt-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Project Template CLI - Learn structures and initialize projects",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -27,16 +27,18 @@
|
|
|
27
27
|
"url": "https://github.com/garyritchie/pt-cli"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
+
"chalk": "^5.3.0",
|
|
30
31
|
"commander": "^12.1.0",
|
|
31
32
|
"inquirer": "^12.3.0",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
33
|
+
"tar": "^7.5.15",
|
|
34
|
+
"yaml": "^2.6.1"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
|
-
"typescript": "^5.6.0",
|
|
37
|
-
"@types/node": "^22.0.0",
|
|
38
37
|
"@types/inquirer": "^9.0.7",
|
|
38
|
+
"@types/node": "^22.0.0",
|
|
39
|
+
"@types/tar": "^6.1.13",
|
|
39
40
|
"ts-node": "^10.9.2",
|
|
40
|
-
"tsx": "^4.21.0"
|
|
41
|
+
"tsx": "^4.21.0",
|
|
42
|
+
"typescript": "^5.6.0"
|
|
41
43
|
}
|
|
42
|
-
}
|
|
44
|
+
}
|
|
@@ -3,6 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import inquirer from 'inquirer';
|
|
4
4
|
import { loadConfig, saveConfig, FolderNode, TemplateConfig, getTemplateNames, shouldExclude, shouldIgnore, shouldExcludeFile, PostCopyFile, TemplateVariable, CopyFileEntry, PostConfigTask, getDefaultPostConfig } from '../config.js';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
|
+
import { downloadAndExtract } from '../remote.js';
|
|
6
7
|
|
|
7
8
|
export interface LearnOptions {
|
|
8
9
|
ignore?: string;
|
|
@@ -12,11 +13,20 @@ export interface LearnOptions {
|
|
|
12
13
|
json?: boolean;
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
|
|
15
17
|
export async function learn(sourcePath: string, updateTemplate: string | null = null, options: LearnOptions = {}): Promise<void> {
|
|
16
|
-
|
|
18
|
+
let resolvedPath: string;
|
|
19
|
+
|
|
20
|
+
// Phase 1: Remote Check
|
|
21
|
+
if (sourcePath.startsWith('http')) {
|
|
22
|
+
console.log(chalk.cyan(`Downloading remote template from: ${sourcePath}...`));
|
|
23
|
+
resolvedPath = await downloadAndExtract(sourcePath);
|
|
24
|
+
} else {
|
|
25
|
+
resolvedPath = path.resolve(sourcePath);
|
|
26
|
+
}
|
|
17
27
|
|
|
18
28
|
if (!fs.existsSync(resolvedPath)) {
|
|
19
|
-
console.error(chalk.red(`Error: Path "${
|
|
29
|
+
console.error(chalk.red(`Error: Path "${resolvedPath}" does not exist.`));
|
|
20
30
|
process.exit(1);
|
|
21
31
|
}
|
|
22
32
|
|
package/src/remote.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/remote.ts (New File)
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { Readable } from 'stream';
|
|
6
|
+
import { finished } from 'stream/promises';
|
|
7
|
+
import { extract } from 'tar'; // You'll need: npm install tar
|
|
8
|
+
|
|
9
|
+
export async function downloadAndExtract(url: string): Promise<string> {
|
|
10
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pt-template-'));
|
|
11
|
+
let downloadUrl = url;
|
|
12
|
+
|
|
13
|
+
// Convert GitHub/Gitea URLs to Zip/Tarball endpoints
|
|
14
|
+
if (url.includes('github.com')) {
|
|
15
|
+
downloadUrl = url.replace(/\/$/, '') + '/archive/refs/heads/main.tar.gz';
|
|
16
|
+
} else if (url.includes('gitea')) {
|
|
17
|
+
downloadUrl = url.replace(/\/$/, '') + '/archive/main.tar.gz';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const response = await fetch(downloadUrl);
|
|
21
|
+
if (!response.ok) throw new Error(`Failed to fetch ${downloadUrl}: ${response.statusText}`);
|
|
22
|
+
|
|
23
|
+
const dest = path.join(tempDir, 'template.tar.gz');
|
|
24
|
+
const fileStream = fs.createWriteStream(dest);
|
|
25
|
+
await finished(Readable.fromWeb(response.body as any).pipe(fileStream));
|
|
26
|
+
|
|
27
|
+
// Extract tarball
|
|
28
|
+
await extract({ file: dest, cwd: tempDir });
|
|
29
|
+
|
|
30
|
+
// Find the actual content folder (archives usually wrap content in a folder)
|
|
31
|
+
const dirs = fs.readdirSync(tempDir).filter(f => fs.statSync(path.join(tempDir, f)).isDirectory());
|
|
32
|
+
return path.join(tempDir, dirs[0]);
|
|
33
|
+
}
|