tldraw/scripts/lib/didAnyPackageChange.ts
David Sheldrick 44a3ea7363
[docs] Sync docs deploy with npm deploy (#3153)
This PR makes it so that our docs deployment process is tied to, and
mirrors, the npm deployment process.

From here on:

- Commits to main get deployed to staging.tldraw.dev
- Commits to a special protected branch called `docs-production` get
deployed to www.tldraw.dev
- Whenever we create a new npm 'latest' release we reset the HEAD of
docs-production to point to the tagged commit for that release.
- If we make a docs change that we want to appear on tldraw.dev ASAP
without waiting for the next npm release, we'll have to follow the same
process as for creating a patch release i.e merge a cherry-pick PR
targeting the latest release branch e.g. `v2.0.x`. This will not cause
another npm patch release unless the cherry-picked changes touch source
files, e.g. updating TSDoc comments.


### Change Type


- [x] `docs` — Changes to the documentation, examples, or templates.
- [x] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
2024-03-14 14:43:32 +00:00

84 lines
2.2 KiB
TypeScript

import { writeFileSync } from 'fs'
import tar from 'tar'
import tmp from 'tmp'
import { exec } from './exec'
import { PackageDetails, getAllPackageDetails } from './publishing'
async function hasPackageChanged(pkg: PackageDetails) {
const dir = tmp.dirSync({ unsafeCleanup: true })
const dirPath = dir.name
try {
const version = pkg.version
const unscopedName = pkg.name.replace('@tldraw/', '')
const url = `https://registry.npmjs.org/${pkg.name}/-/${unscopedName}-${version}.tgz`
const res = await fetch(url)
if (res.status >= 400) {
throw new Error(`Package not found at url ${url}: ${res.status}`)
}
const publishedTarballPath = `${dirPath}/published-package.tgz`
writeFileSync(publishedTarballPath, Buffer.from(await res.arrayBuffer()))
const publishedManifest = await getTarballManifest(publishedTarballPath)
const localTarballPath = `${dirPath}/local-package.tgz`
await exec('yarn', ['pack', '--out', localTarballPath], { pwd: pkg.dir })
const localManifest = await getTarballManifest(localTarballPath)
return !manifestsAreEqual(publishedManifest, localManifest)
} finally {
dir.removeCallback()
}
}
function manifestsAreEqual(a: Record<string, Buffer>, b: Record<string, Buffer>) {
const aKeys = Object.keys(a)
const bKeys = Object.keys(b)
if (aKeys.length !== bKeys.length) {
return false
}
for (const key of aKeys) {
if (!bKeys.includes(key)) {
return false
}
if (!a[key].equals(b[key])) {
return false
}
}
return true
}
function getTarballManifest(tarballPath: string): Promise<Record<string, Buffer>> {
const manifest: Record<string, Buffer> = {}
return new Promise((resolve, reject) =>
tar.list(
{
// @ts-expect-error bad typings
file: tarballPath,
onentry: (entry) => {
entry.on('data', (data) => {
// we could hash these to reduce memory but it's probably fine
manifest[entry.path] = data
})
},
},
(err: any) => {
if (err) {
reject(err)
} else {
resolve(manifest)
}
}
)
)
}
export async function didAnyPackageChange() {
const details = getAllPackageDetails()
for (const pkg of Object.values(details)) {
if (await hasPackageChanged(pkg)) {
return true
}
}
return false
}