theatre/scripts/prerelease.ts

186 lines
6 KiB
TypeScript
Raw Normal View History

/**
* This script publishes the insider packages from the CI. You can't run it locally unless you have a a valid npm access token and you store its value in the `NPM_TOKEN` environmental variable.
*/
2023-08-06 10:15:37 +02:00
import * as os from 'os'
import * as path from 'path'
import * as core from '@actions/core'
2023-08-06 10:15:37 +02:00
import {$} from '@cspotcode/zx'
const packagesToPublish = [
'@theatre/core',
'@theatre/studio',
'@theatre/dataverse',
'@theatre/react',
'@theatre/browser-bundles',
'@theatre/r3f',
'theatric',
]
/**
* Receives a version number and returns it without the tags, if there are any
*
2023-08-06 10:15:37 +02:00
* @param version - Version number
* @returns Version number without the tags
*
* @example
* ```javascript
* const version_1 = '0.4.8-dev3-ec175817'
* const version_2 = '0.4.8'
*
* stripTag(version_1) === stripTag(version_2) === '0.4.8' // returns `true`
* ```
*/
2023-08-06 10:15:37 +02:00
function stripTag(version: string) {
const regExp = /^[0-9]+\.[0-9]+\.[0-9]+/g
const matches = version.match(regExp)
if (!matches) {
throw new Error(`Version number not found in "${version}"`)
}
return matches[0]
}
/**
* Creates a version number like `0.4.8-insiders.ec175817`
*
2023-08-06 10:15:37 +02:00
* @param packageName - Name of the package
* @param commitHash - A commit hash
*/
2023-08-06 10:15:37 +02:00
function getNewVersionName(packageName: string, commitHash: string) {
// The `r3f` package has its own release schedule, so its version numbers
// are almost always different from the rest of the packages.
const pathToPackageJson =
packageName === '@theatre/r3f'
? path.resolve(__dirname, '..', 'packages', 'r3f', 'package.json')
: path.resolve(__dirname, '../', './package.json')
const jsonData = JSON.parse(
fs.readFileSync(pathToPackageJson, {encoding: 'utf-8'}),
)
const strippedVersion = stripTag(jsonData.version)
return `${strippedVersion}-insiders.${commitHash}`
}
/**
* Assigns the latest version names ({@link getNewVersionName}) to the packages' `package.json`s
*
2023-08-06 10:15:37 +02:00
* @param workspacesListObjects - An Array of objects containing information about the workspaces
* @param latestCommitHash - Hash of the latest commit
* @returns - A record of `{[packageId]: assignedVersion}`
*/
async function writeVersionsToPackageJSONs(
2023-08-06 10:15:37 +02:00
workspacesListObjects: {name: string; location: string}[],
latestCommitHash: string,
): Promise<Record<string, string>> {
const assignedVersionByPackageName: Record<string, string> = {}
for (const workspaceData of workspacesListObjects) {
const pathToPackage = path.resolve(
__dirname,
'../',
workspaceData.location,
'./package.json',
)
const original = JSON.parse(
fs.readFileSync(pathToPackage, {encoding: 'utf-8'}),
)
let {version, dependencies, peerDependencies, devDependencies} = original
// The @theatre/r3f package curently doesn't track the same version number of the other packages like @theatre/core,
// so we need to generate version numbers independently for each package
version = getNewVersionName(workspaceData.name, latestCommitHash)
assignedVersionByPackageName[workspaceData.name] = version
// Normally we don't have to override the package versions in dependencies because yarn would already convert
// all the "workspace:*" versions to a fixed version before publishing. However, packages like @theatre/studio
// have a peerDependency on @theatre/core set to "*" (meaning they would work with any version of @theatre/core).
// This is not the desired behavior in pre-release versions, so here, we'll fix those "*" versions to the set version.
for (const deps of [dependencies, peerDependencies, devDependencies]) {
if (!deps) continue
for (const wpObject of workspacesListObjects) {
if (deps[wpObject.name]) {
deps[wpObject.name] = getNewVersionName(
wpObject.name,
latestCommitHash,
)
}
}
}
const newJson = {
...original,
version,
dependencies,
peerDependencies,
devDependencies,
}
fs.writeFileSync(
path.join(pathToPackage),
JSON.stringify(newJson, undefined, 2),
{encoding: 'utf-8'},
)
await $`prettier --write ${workspaceData.location + '/package.json'}`
}
return assignedVersionByPackageName
}
2023-08-06 10:15:37 +02:00
async function prerelease() {
// @ts-ignore ignore
process.env.THEATRE_IS_PUBLISHING = true
// In the CI `git log -1` points to a fake merge commit,
// so we have to use the value of a special GitHub context variable
// through the `GITHUB_SHA` environmental variable.
// The length of the abbreviated commit hash can change, that's why we
// need the length of the fake merge commit's abbreviated hash.
const fakeMergeCommitHashLength = (await $`git log -1 --pretty=format:%h`)
.stdout.length
if (!process.env.GITHUB_SHA)
throw new Error(
'expected `process.env.GITHUB_SHA` to be defined but it was not',
)
const latestCommitHash = process.env.GITHUB_SHA.slice(
0,
fakeMergeCommitHashLength,
)
const workspacesListString = await $`yarn workspaces list --json`
const workspacesListObjects = workspacesListString.stdout
.split(os.EOL)
// strip out empty lines
.filter(Boolean)
.map((x) => JSON.parse(x))
const assignedVersionByPackageName = await writeVersionsToPackageJSONs(
workspacesListObjects,
latestCommitHash,
)
await Promise.all(
packagesToPublish.map(async (workspaceName) => {
const npmTag = 'insiders'
if (process.env.GITHUB_ACTIONS) {
await $`yarn workspace ${workspaceName} npm publish --access public --tag ${npmTag}`
}
}),
)
if (process.env.GITHUB_ACTIONS) {
const data = packagesToPublish.map((packageName) => ({
packageName,
version: assignedVersionByPackageName[packageName],
}))
// set the output for github actions.
core.setOutput('data', JSON.stringify(data))
} else {
for (const packageName of packagesToPublish) {
await $`echo ${`Published ${packageName}@${assignedVersionByPackageName[packageName]}`}`
}
}
2023-08-06 10:15:37 +02:00
}
void prerelease()