diff --git a/.github/workflows/branches.yaml b/.github/workflows/branches.yaml index c543a1f6..07e3d8a8 100644 --- a/.github/workflows/branches.yaml +++ b/.github/workflows/branches.yaml @@ -18,4 +18,6 @@ jobs: with: node-version: "20.x" - run: npm ci + - run: npm run build:cli + - run: npm ci - run: npm run check:ci diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 4915ddb8..02ac41d2 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -21,6 +21,8 @@ jobs: with: node-version: "20.x" - run: npm ci + - run: npm run build:cli + - run: npm ci - run: npm run check:ci - run: npm run deploy - run: npm run upload diff --git a/cli/src/index.ts b/cli/src/index.ts index d4215b80..db689ec4 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -1,12 +1,14 @@ import { Command } from "commander"; import { upload } from "./upload"; +import { lint } from "./lint"; const program = new Command(); -program.name("cli").description("A handy CLI for developing templates."); +program.name("cli").description("a handy CLI for developing templates"); program .command("upload") + .description("upload templates to the templates API") .argument( "[path-to-templates]", "path to directory containing templates", @@ -16,4 +18,17 @@ program upload({ templateDirectory, apiBaseUrl: "TODO" }), ); +program + .command("lint") + .description("find and fix template style problems") + .argument( + "[path-to-templates]", + "path to directory containing templates", + ".", + ) + .option("--fix", "fix problems that can be automatically fixed") + .action((templateDirectory, options) => + lint({ templateDirectory, fix: options.fix }), + ); + program.parse(); diff --git a/cli/src/lint.ts b/cli/src/lint.ts new file mode 100644 index 00000000..44c4d97c --- /dev/null +++ b/cli/src/lint.ts @@ -0,0 +1,50 @@ +import path from "node:path"; +import { getTemplatePaths, readJSON, writeJSON } from "./util"; + +export type LintConfig = { + templateDirectory: string; + fix: boolean; +}; + +export function lint(config: LintConfig) { + const templatePaths = getTemplatePaths(config.templateDirectory); + const results = templatePaths.flatMap((templatePath) => + lintTemplate(templatePath, config.fix), + ); + if (results.length > 0) { + results.forEach(({ filePath, problems }) => { + console.error(`Problems with ${filePath}`); + problems.forEach((problem) => console.log(` - ${problem}`)); + }); + process.exit(1); + } +} +const RULES = { + "wrangler.json": [lintCompatibilityDate], +}; +const TARGET_COMPATIBILITY_DATE = "2024-11-01"; + +type FileDiagnostic = { + filePath: string; + problems: string[]; +}; + +function lintTemplate(templatePath: string, fix: boolean): FileDiagnostic[] { + return Object.entries(RULES).flatMap(([file, linters]) => { + const filePath = path.join(templatePath, file); + const problems = linters.flatMap((linter) => linter(filePath, fix)); + return problems.length > 0 ? [{ filePath, problems }] : []; + }); +} + +function lintCompatibilityDate(filePath: string, fix: boolean): string[] { + const wrangler = readJSON<{ compatibility_date?: string }>(filePath); + if (wrangler["compatibility_date"] !== TARGET_COMPATIBILITY_DATE && !fix) { + return [ + `"compatibility_date" should be set to "${TARGET_COMPATIBILITY_DATE}"`, + ]; + } + wrangler["compatibility_date"] = TARGET_COMPATIBILITY_DATE; + writeJSON(filePath, wrangler); + return []; +} diff --git a/cli/src/upload.ts b/cli/src/upload.ts index 70bedb7e..7ac40ed3 100644 --- a/cli/src/upload.ts +++ b/cli/src/upload.ts @@ -1,8 +1,7 @@ import fs from "node:fs"; import path from "node:path"; import subprocess from "node:child_process"; - -const TEMPLATE_DIRECTORY_SUFFIX = "-template"; +import { getTemplatePaths } from "./util"; export type UploadConfig = { templateDirectory: string; @@ -10,14 +9,7 @@ export type UploadConfig = { }; 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 templatePaths = getTemplatePaths(config.templateDirectory); const results = await Promise.allSettled( templatePaths.map((templatePath) => uploadTemplate(templatePath, config)), ); diff --git a/cli/src/util.ts b/cli/src/util.ts new file mode 100644 index 00000000..93d190f1 --- /dev/null +++ b/cli/src/util.ts @@ -0,0 +1,23 @@ +import fs from "node:fs"; +import path from "node:path"; + +const TEMPLATE_DIRECTORY_SUFFIX = "-template"; + +export function getTemplatePaths(templateDirectory: string): string[] { + return fs + .readdirSync(templateDirectory) + .filter( + (file) => + file.endsWith(TEMPLATE_DIRECTORY_SUFFIX) && + fs.statSync(file).isDirectory(), + ) + .map((template) => path.join(templateDirectory, template)); +} + +export function readJSON(filePath: string): T { + return JSON.parse(fs.readFileSync(filePath, { encoding: "utf-8" })); +} + +export function writeJSON(filePath: string, object: T) { + fs.writeFileSync(filePath, JSON.stringify(object, undefined, 2) + "\n"); +} diff --git a/d1-template/wrangler.json b/d1-template/wrangler.json index f750c6cc..f0c1621a 100644 --- a/d1-template/wrangler.json +++ b/d1-template/wrangler.json @@ -1,5 +1,5 @@ { - "compatibility_date": "2024-11-15", + "compatibility_date": "2024-11-01", "main": "src/index.ts", "name": "d1-template", "upload_source_maps": true, diff --git a/image-classification-template/wrangler.json b/image-classification-template/wrangler.json index 6c610507..4ef09b54 100644 --- a/image-classification-template/wrangler.json +++ b/image-classification-template/wrangler.json @@ -1,5 +1,5 @@ { - "compatibility_date": "2024-11-15", + "compatibility_date": "2024-11-01", "main": "src/index.ts", "name": "image-classification-template", "upload_source_maps": true, diff --git a/llm-template/wrangler.json b/llm-template/wrangler.json index 20606c07..b9556987 100644 --- a/llm-template/wrangler.json +++ b/llm-template/wrangler.json @@ -1,5 +1,5 @@ { - "compatibility_date": "2024-11-15", + "compatibility_date": "2024-11-01", "main": "src/index.ts", "name": "llm-template", "upload_source_maps": true, diff --git a/package.json b/package.json index 6cf07e73..ebcc18b5 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,10 @@ }, "packageManager": "npm@10.2.0", "scripts": { - "check:ci": "prettier . --check && syncpack lint && turbo run build check types && git diff --exit-code", + "build:cli": "turbo run build --filter=cli", + "check:ci": "cli lint . && prettier . --check && syncpack lint && turbo run build check types && git diff --exit-code", "deploy": "turbo run deploy", - "fix:ci": "prettier . --write && syncpack format && syncpack fix-mismatches && turbo run types", - "preupload": "turbo run build --filter=cli", + "fix:ci": "cli lint . --fix && prettier . --write && syncpack format && syncpack fix-mismatches && turbo run types", "upload": "cli upload ." }, "workspaces": [ diff --git a/speech-to-text-template/wrangler.json b/speech-to-text-template/wrangler.json index 964ffa75..4bcd8340 100644 --- a/speech-to-text-template/wrangler.json +++ b/speech-to-text-template/wrangler.json @@ -1,5 +1,5 @@ { - "compatibility_date": "2024-11-15", + "compatibility_date": "2024-11-01", "main": "src/index.ts", "name": "speech-to-text-template", "upload_source_maps": true, diff --git a/text-classification-template/wrangler.json b/text-classification-template/wrangler.json index 50804e6c..ddfc39cb 100644 --- a/text-classification-template/wrangler.json +++ b/text-classification-template/wrangler.json @@ -1,5 +1,5 @@ { - "compatibility_date": "2024-11-15", + "compatibility_date": "2024-11-01", "main": "src/index.ts", "name": "text-classification-template", "upload_source_maps": true, diff --git a/text-to-image-template/wrangler.json b/text-to-image-template/wrangler.json index 0d67b389..dd015276 100644 --- a/text-to-image-template/wrangler.json +++ b/text-to-image-template/wrangler.json @@ -1,5 +1,5 @@ { - "compatibility_date": "2024-11-15", + "compatibility_date": "2024-11-01", "main": "src/index.ts", "name": "text-to-image-template", "upload_source_maps": true, diff --git a/translation-template/wrangler.json b/translation-template/wrangler.json index 112f234a..befe69ee 100644 --- a/translation-template/wrangler.json +++ b/translation-template/wrangler.json @@ -1,5 +1,5 @@ { - "compatibility_date": "2024-11-15", + "compatibility_date": "2024-11-01", "main": "src/index.ts", "name": "translation-template", "upload_source_maps": true, diff --git a/vector-embedding-template/wrangler.json b/vector-embedding-template/wrangler.json index e7791ba7..9f0b9f06 100644 --- a/vector-embedding-template/wrangler.json +++ b/vector-embedding-template/wrangler.json @@ -1,5 +1,5 @@ { - "compatibility_date": "2024-11-15", + "compatibility_date": "2024-11-01", "main": "src/index.ts", "name": "vector-embedding-template", "upload_source_maps": true,