diff --git a/deploy/createTypesPackages.mjs b/deploy/createTypesPackages.mjs index 857389878..7f6507f13 100644 --- a/deploy/createTypesPackages.mjs +++ b/deploy/createTypesPackages.mjs @@ -3,7 +3,7 @@ // node deploy/createTypesPackages.mjs // prettier-ignore -const packages = [ +export const packages = [ { name: "@types/web", description: "Types for the DOM, and other web technologies in browsers", @@ -71,8 +71,6 @@ const go = async () => { } }; -go(); - async function updatePackageJSON(packagePath, pkg, gitSha) { const pkgJSONPath = join(packagePath, "package.json"); const packageText = fs.readFileSync(pkgJSONPath, "utf8"); @@ -131,3 +129,7 @@ function copyREADME(pkg, pkgJSON, writePath) { fs.writeFileSync(writePath, readme); } + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + await go(); +} diff --git a/deploy/deployChangedPackages.mjs b/deploy/deployChangedPackages.mjs index cba5fca87..7333a44f5 100644 --- a/deploy/deployChangedPackages.mjs +++ b/deploy/deployChangedPackages.mjs @@ -6,110 +6,124 @@ // ones which have changed. import * as fs from "fs"; -import { join, dirname } from "path"; +import { join, dirname, basename } from "path"; import { fileURLToPath } from "url"; -import fetch from "node-fetch"; -import { spawnSync } from "child_process"; +import { spawnSync, execSync } from "child_process"; import { Octokit } from "@octokit/core"; import printDiff from "print-diff"; +import { generateChangelogFrom } from "../lib/changelog.js"; +import { packages } from "./createTypesPackages.mjs"; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -const verify = () => { - const authToken = process.env.GITHUB_TOKEN || process.env.GITHUB_API_TOKEN; - if (!authToken) - throw new Error( - "There isn't an ENV var set up for creating a GitHub release, expected GITHUB_TOKEN." +verify(); + +const uploaded = []; + +// Loop through generated packages, deploying versions for anything which has different +// .d.ts files from the version available on npm. +const generatedDir = join(__dirname, "generated"); +for (const dirName of fs.readdirSync(generatedDir)) { + console.log(`Looking at ${dirName}`); + const localPackageJSONPath = join(generatedDir, dirName, "package.json"); + const newTSConfig = fs.readFileSync(localPackageJSONPath, "utf-8"); + const pkgJSON = JSON.parse(newTSConfig); + + // We'll need to map back from the filename in the npm package to the + // generated file in baselines inside the git tag + const thisPackageMeta = packages.find((p) => p.name === pkgJSON.name); + + const dtsFiles = fs + .readdirSync(join(generatedDir, dirName)) + .filter((f) => f.endsWith(".d.ts")); + + /** @type {string[]} */ + let releaseNotes = []; + + // Look through each .d.ts file included in a package to + // determine if anything has changed + let upload = false; + for (const file of dtsFiles) { + const originalFilename = basename( + thisPackageMeta.files.find((f) => f.to === file).from ); -}; - -const go = async () => { - verify(); - - const uploaded = []; - - // Loop through generated packages, deploying versions for anything which has different - // .d.ts files from the version available on npm. - const generatedDir = join(__dirname, "generated"); - for (const dirName of fs.readdirSync(generatedDir)) { - console.log(`Looking at ${dirName}`); - const localPackageJSONPath = join(generatedDir, dirName, "package.json"); - const newTSConfig = fs.readFileSync(localPackageJSONPath, "utf-8"); - const pkgJSON = JSON.parse(newTSConfig); - - const dtsFiles = fs - .readdirSync(join(generatedDir, dirName)) - .filter((f) => f.endsWith(".d.ts")); - - // Look through each .d.ts file included in a package to - // determine if anything has changed - let upload = false; - for (const file of dtsFiles) { - const generatedDTSPath = join(generatedDir, dirName, file); - const generatedDTSContent = fs.readFileSync(generatedDTSPath, "utf8"); - const unpkgURL = `https://unpkg.com/${pkgJSON.name}/${file}`; - try { - const npmDTSReq = await fetch(unpkgURL); - const npmDTSText = await npmDTSReq.text(); - console.log(`Comparing ${file} from unpkg, to generated version:`); - printDiff(npmDTSText, generatedDTSContent); - - upload = upload || npmDTSText !== generatedDTSContent; - } catch (error) { - // Could not find a previous build - console.log(` -Could not get the file ${file} inside the npm package ${pkgJSON.name} from unpkg at ${unpkgURL} + + const generatedDTSPath = join(generatedDir, dirName, file); + const generatedDTSContent = fs.readFileSync(generatedDTSPath, "utf8"); + + // This assumes we'll only _ever_ ship patches, which may change in the + // future someday. + const [maj, min, patch] = pkgJSON.version.split("."); + const olderVersion = `${maj}.${min}.${patch - 1}`; + + try { + const oldFile = gitShowFile( + `${pkgJSON.name}@${olderVersion}`, + `baselines/${originalFilename}` + ); + console.log(`Comparing ${file} from ${olderVersion}, to now:`); + printDiff(oldFile, generatedDTSContent); + + const title = `\n## \`${file}\`\n`; + const notes = generateChangelogFrom(oldFile, generatedDTSContent); + releaseNotes.push(title); + releaseNotes.push(notes.trim() === "" ? "No changes" : notes); + + upload = upload || oldFile !== generatedDTSContent; + } catch (error) { + // Could not find a previous build + console.log(` +Could not get the file ${file} inside the npm package ${pkgJSON.name} from tag ${olderVersion}. Assuming that this means we need to upload this package.`); - upload = true; - } + upload = true; } + } - // Publish via npm - if (upload) { - if (process.env.NODE_AUTH_TOKEN) { - const publish = spawnSync("npm", ["publish", "--access", "public"], { - cwd: join(generatedDir, dirName), - stdio: "inherit", - }); - - if (publish.status) { - console.log(publish.stdout?.toString()); - console.log(publish.stderr?.toString()); - process.exit(publish.status); - } else { - console.log(publish.stdout?.toString()); - - await createRelease(`${pkgJSON.name}@${pkgJSON.version}`); - } + // Publish via npm + if (upload) { + if (process.env.NODE_AUTH_TOKEN) { + const publish = spawnSync("npm", ["publish", "--access", "public"], { + cwd: join(generatedDir, dirName), + stdio: "inherit", + }); + + if (publish.status) { + console.log(publish.stdout?.toString()); + console.log(publish.stderr?.toString()); + process.exit(publish.status); } else { - console.log( - "Wanting to run: 'npm publish --access public' in " + - join(generatedDir, dirName) - ); - } + console.log(publish.stdout?.toString()); - uploaded.push(dirName); + await createRelease(`${pkgJSON.name}@${pkgJSON.version}`); + } + } else { + console.log( + "Wanting to run: 'npm publish --access public' in " + + join(generatedDir, dirName) + ); } - } - // Warn if we did a dry run. - if (!process.env.NODE_AUTH_TOKEN) { - console.log( - "Did a dry run because process.env.NODE_AUTH_TOKEN is not set." - ); + uploaded.push(dirName); } - if (uploaded.length) { - console.log("Uploaded: ", uploaded.join(", ")); - } else { - console.log("No uploads"); - } -}; + console.log("\n# Release notes:"); + console.log(releaseNotes.join("\n"), "\n\n"); +} +// Warn if we did a dry run. +if (!process.env.NODE_AUTH_TOKEN) { + console.log("Did a dry run because process.env.NODE_AUTH_TOKEN is not set."); +} + +if (uploaded.length) { + console.log("Uploaded: ", uploaded.join(", ")); +} else { + console.log("No uploads"); +} -async function createRelease(tag) { +async function createRelease(tag, body) { const authToken = process.env.GITHUB_TOKEN || process.env.GITHUB_API_TOKEN; const octokit = new Octokit({ auth: authToken }); @@ -119,6 +133,8 @@ async function createRelease(tag) { repo: "TypeScript-DOM-lib-generator", tag_name: tag, target_commitish: process.env.GITHUB_SHA, + name: tag, + body, }); } catch (error) { console.error( @@ -127,4 +143,14 @@ async function createRelease(tag) { } } -go(); +function verify() { + const authToken = process.env.GITHUB_TOKEN || process.env.GITHUB_API_TOKEN; + if (!authToken) + throw new Error( + "There isn't an ENV var set up for creating a GitHub release, expected GITHUB_TOKEN." + ); +} + +function gitShowFile(commitish, path) { + return execSync(`git show "${commitish}":${path}`, { encoding: "utf-8" }); +} diff --git a/src/changelog.ts b/src/changelog.ts index 880aed8fa..194012775 100644 --- a/src/changelog.ts +++ b/src/changelog.ts @@ -137,10 +137,21 @@ function writeAddedRemovedInline(added: Set, removed: Set) { const dom = "baselines/dom.generated.d.ts"; -export function generate(): string { +export function generateDefaultFromRecentTag(): string { const [base = gitLatestTag(), head = "HEAD"] = process.argv.slice(2); const previous = gitShowFile(base, dom); const current = gitShowFile(head, dom); + const changelog = generateChangelogFrom(previous, current); + if (!changelog.length) { + throw new Error(`No change reported between ${base} and ${head}.`); + } + return changelog; +} + +export function generateChangelogFrom( + previous: string, + current: string +): string { const { interfaces: { added, removed, modified }, others, @@ -150,6 +161,7 @@ export function generate(): string { if (added.size || removed.size) { outputs.push(writeAddedRemoved(added, removed)); } + if (modified.size) { const modifiedOutput = [`## Modified\n`]; for (const [key, value] of modified.entries()) { @@ -169,14 +181,9 @@ export function generate(): string { } const output = outputs.join("\n\n"); - - if (!output.length) { - throw new Error(`No change reported between ${base} and ${head}.`); - } - return output; } if (process.argv[1] === fileURLToPath(import.meta.url)) { - console.log(generate()); + console.log(generateDefaultFromRecentTag()); } diff --git a/src/version.ts b/src/version.ts index 04556224f..261f14a4e 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,8 +1,8 @@ import { execSync } from "child_process"; import { readFile, writeFile } from "fs/promises"; -import { generate } from "./changelog.js"; +import { generateDefaultFromRecentTag } from "./changelog.js"; -const output = generate(); +const output = generateDefaultFromRecentTag(); const path = new URL("../CHANGELOG.md", import.meta.url);