diff --git a/README.ts b/README.ts index 6addd0d..c0e729e 100755 --- a/README.ts +++ b/README.ts @@ -1,81 +1,120 @@ -#!/usr/bin/env -S deno run --allow-read --allow-run=bash,git,cargo --allow-env --allow-sys +#!/usr/bin/env -S deno run --allow-read --allow-run=bash,git,cargo --allow-net=docs.rs:443 --allow-env --allow-sys import * as zx from "npm:zx" -import { z, ZodSchema } from "https://deno.land/x/zod@v3.23.8/mod.ts" -import { assertEquals } from "https://jsr.io/@std/assert/1.0.0/equals.ts" -import { assert } from "https://jsr.io/@std/assert/1.0.0/assert.ts" - -const CargoToml = z.object({ - package: z.object({ - name: z.string().min(1), - description: z.string().min(1), - repository: z.string().url().min(1), - metadata: z.object({ - details: z.object({ - title: z.string().min(1), - tagline: z.string(), - summary: z.string(), - }), +import { z, ZodSchema, ZodTypeDef } from "https://deno.land/x/zod@v3.23.8/mod.ts" +import { assert, assertEquals } from "jsr:@std/assert@1.0.0" + +const CargoTomlSchema = z.object({ + package: z.object({ + name: z.string().min(1), + description: z.string().min(1), + repository: z.string().url().min(1), + metadata: z.object({ + details: z.object({ + title: z.string().min(1).optional(), + tagline: z.string().optional(), + summary: z.string().optional(), + peers: z.array(z.string()).default([]).describe("Packages that should be installed alongside this package"), + }).default({}), + }).default({}), }), - }), }) -type CargoToml = z.infer +type CargoToml = z.infer const CargoMetadataSchema = z.object({ - packages: z.array(z.object({ - name: z.string(), - targets: z.array(z.object({ - name: z.string(), + packages: z.array(z.object({ + name: z.string(), + targets: z.array(z.object({ + name: z.string(), + })), })), - })), }) type CargoMetadata = z.infer -const Repo = z.object({ - url: z.string().url(), +const RepoSchema = z.object({ + url: z.string().url(), }) -type Repo = z.infer +type Repo = z.infer -const $ = zx.$({ - cwd: import.meta.dirname, +const BadgeSchema = z.object({ + name: z.string().min(1), + image: z.string().url(), + url: z.string().url(), }) -const parse = (schema: ZodSchema, input: zx.ProcessOutput) => schema.parse(JSON.parse(input.stdout)) -const renderMarkdownList = (items: string[]) => items.map((bin) => `* ${bin}`).join("\n") +type Badge = z.infer + +const badge = (name: string, image: string, url: string): Badge => BadgeSchema.parse({ name, url, image }) -const theCargoToml: CargoToml = parse(CargoToml, await $`yj -t < Cargo.toml`) -const { package: { name, metadata: { details: { title } } } } = theCargoToml -const bin = name -const help = await $`cargo run --quiet --bin ${bin} -- --help` +const dirname = import.meta.dirname +if (!dirname) throw new Error("Cannot determine the current script dirname") + +const $ = zx.$({ cwd: dirname }) + +// deno-lint-ignore no-explicit-any +const parse = (schema: ZodSchema, input: zx.ProcessOutput) => schema.parse(JSON.parse(input.stdout)) + +const theCargoToml: CargoToml = parse(CargoTomlSchema, await $`yj -t < Cargo.toml`) +const { package: { name, description, metadata: { details } } } = theCargoToml +const title = details.title || description +const peers = details.peers const theCargoMetadata: CargoMetadata = parse(CargoMetadataSchema, await $`cargo metadata --format-version 1`) const thePackageMetadata = theCargoMetadata.packages.find((p) => p.name == name) assert(thePackageMetadata, "Could not find package metadata") const target = thePackageMetadata.targets[0] assert(target, "Could not find package first target") -const doc = await $`cargo doc2readme --template README.jl --target-name ${target.name} --out -` -const repo: Repo = parse(Repo, await $`gh repo view --json url`) -const extraBins = (await $`find src/bin/*.rs -type f -exec basename {} .rs \\;`).valueOf().split("\n") +const docsUrl = `https://docs.rs/${name}` + +// launch multiple promises in parallel +const doc2ReadmePromise = $`cargo doc2readme --template README.jl --target-name ${target.name} --out -` +const ghRepoPromise = $`gh repo view --json url` +const docsUrlPromise = fetch(docsUrl, { method: "HEAD" }) +const helpPromise = $`cargo run --quiet --bin ${name} -- --help` +const extraBinsPromise = $`find src/bin/*.rs -type f -exec basename {} .rs \\;` +const doc = await doc2ReadmePromise +const docStr = doc.stdout.trim() + +const repo: Repo = parse(RepoSchema, await ghRepoPromise) assertEquals(repo.url, theCargoToml.package.repository) +const docsUrlHead = await docsUrlPromise +const docsUrlIs200 = docsUrlHead.status === 200 + +const badges: Badge[] = [ + badge("Build", `${repo.url}/actions/workflows/ci.yml/badge.svg`, repo.url), +] +if (docsUrlIs200) { + badges.push(badge("Documentation", `https://docs.rs/${name}/badge.svg`, docsUrl)) +} +const badgesStr = badges.map(({ name, image, url }) => `[![${name}](${image})](${url})`).join("\n") + +const titleSection = [ + badgesStr, + docStr, +].filter((s) => s.length) + +const cargoAddPackages = [name, ...peers] + const autogenerated = ` `.trim() +const help = await helpPromise +const extraBins = (await extraBinsPromise).valueOf().split("\n") +const renderMarkdownList = (items: string[]) => items.map((bin) => `* ${bin}`).join("\n") + console.info(` ${autogenerated} # ${title} -[![Build](${repo.url}/actions/workflows/ci.yml/badge.svg)](${repo.url}) -[![Documentation](https://docs.rs/${name}/badge.svg)](https://docs.rs/${name}) - -${doc.stdout.trim()} +${titleSection.join("\n\n")} ## Installation diff --git a/commitlint.config.mjs b/commitlint.config.mjs index 93c0d81..f763166 100644 --- a/commitlint.config.mjs +++ b/commitlint.config.mjs @@ -1,14 +1,12 @@ -const severity = { - disable: 0, - warn: 1, - error: 2, -} +const disable = 0 +const warn = 1 +const error = 2 export default { extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [ - severity.error, + error, 'always', [ 'build', @@ -26,5 +24,6 @@ export default { 'test', ], ], + 'subject-case': [disable] }, } diff --git a/deno.lock b/deno.lock index 5a45070..d6dab02 100644 --- a/deno.lock +++ b/deno.lock @@ -2,6 +2,7 @@ "version": "3", "packages": { "specifiers": { + "jsr:@std/assert@1.0.0": "jsr:@std/assert@1.0.0", "jsr:@std/internal@^1.0.1": "jsr:@std/internal@1.0.1", "npm:zx": "npm:zx@8.1.4" },