Skip to content

Commit

Permalink
[WIP] Add CLI for uploading template contents to templates API
Browse files Browse the repository at this point in the history
  • Loading branch information
maxwellpeterson committed Nov 21, 2024
1 parent b875a10 commit e4b814a
Show file tree
Hide file tree
Showing 10 changed files with 623 additions and 35 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
# TODO: Template API credentials
steps:
- uses: actions/checkout@v4
- name: Use Node.js
Expand All @@ -22,3 +23,4 @@ jobs:
- run: npm ci
- run: npm run check:ci
- run: npm run deploy
- run: npm run upload
3 changes: 3 additions & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Templates CLI

TODO
15 changes: 15 additions & 0 deletions cli/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import PACKAGE from "./package.json" assert { type: "json" };
import * as esbuild from "esbuild";
import fs from "node:fs";

const outfile = PACKAGE["bin"];

await esbuild.build({
entryPoints: ["src/index.ts"],
bundle: true,
sourcemap: true,
platform: "node",
outfile,
});

fs.writeFileSync(outfile, "#!/usr/bin/env node\n" + fs.readFileSync(outfile));
17 changes: 17 additions & 0 deletions cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "cli",
"description": "A handy CLI for developing templates.",
"bin": "out/cli.js",
"dependencies": {
"commander": "12.1.0"
},
"devDependencies": {
"@types/node": "22.9.1",
"esbuild": "0.24.0",
"typescript": "5.6.3"
},
"scripts": {
"build": "node build.mjs",
"check": "tsc"
}
}
19 changes: 19 additions & 0 deletions cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Command } from "commander";
import { upload } from "./upload";

const program = new Command();

program.name("cli").description("A handy CLI for developing templates.");

program
.command("upload")
.argument(
"[path-to-templates]",
"path to directory containing templates",
".",
)
.action((templateDirectory) =>
upload({ templateDirectory, apiBaseUrl: "TODO" }),
);

program.parse();
72 changes: 72 additions & 0 deletions cli/src/upload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import fs from "node:fs";
import path from "node:path";
import subprocess from "node:child_process";

const TEMPLATE_DIRECTORY_SUFFIX = "-template";

export type UploadConfig = {
templateDirectory: string;
apiBaseUrl: string;
};

export async function upload(config: UploadConfig) {
const templatePaths = fs
.readdirSync(config.templateDirectory)
.filter(
(file) =>
file.endsWith(TEMPLATE_DIRECTORY_SUFFIX) &&
fs.statSync(file).isDirectory(),
)
.map((template) => path.join(config.templateDirectory, template));
const results = await Promise.allSettled(
templatePaths.map((templatePath) => uploadTemplate(templatePath, config)),
);
results.forEach((result, i) => {
if (result.status === "rejected") {
console.error(`Upload ${templatePaths[i]} failed: ${result.reason}`);
}
});
}

export async function uploadTemplate(
templatePath: string,
config: UploadConfig,
) {
const files = collectTemplateFiles(templatePath);
console.info(`Uploading ${templatePath}:`);
const body = new FormData();
files.forEach((file) => {
console.info(` ${file.name}`);
body.set(file.name, file);
});
const url = `${config.apiBaseUrl}/upload/path`;
const response = await fetch(url, { method: "POST", body });
if (!response.ok) {
throw new Error(
`Error response from ${url} (${response.status}): ${await response.text()}`,
);
}
}

function collectTemplateFiles(templatePath: string): File[] {
return fs
.readdirSync(templatePath, { recursive: true })
.map((file) => ({
name: file.toString(),
filePath: path.join(templatePath, file.toString()),
}))
.filter(
({ filePath }) =>
!fs.statSync(filePath).isDirectory() && !gitIgnored(filePath),
)
.map(({ name, filePath }) => new File([fs.readFileSync(filePath)], name));
}

function gitIgnored(filePath: string): boolean {
try {
subprocess.execSync(`git check-ignore ${filePath}`);
return true;
} catch {
return false;
}
}
14 changes: 14 additions & 0 deletions cli/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "esnext",
"lib": ["esnext"],
"module": "nodenext",
"types": ["@types/node"],
"noEmit": true,
"isolatedModules": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"strict": true
},
"include": ["src"]
}
Loading

0 comments on commit e4b814a

Please sign in to comment.