Automatically publish prereleases to npm (#155)

This commit creates `x.x.x-insiders.COMMIT_HASH` builds of all of our public packages, and publishes them to npm with the `insiders` tag, so one could say `npm install @theatre/core@0.4.8-insiders.bsdf387` and use the package at that particular commit.

Co-authored-by: Aria Minaei <aria.minaei@gmail.com>
This commit is contained in:
Fülöp 2022-05-18 12:09:45 +02:00 committed by Aria Minaei
parent 3d10325873
commit 8520c74116
5 changed files with 196 additions and 3 deletions

10
.github/.yarnrc.publish.yml vendored Normal file
View file

@ -0,0 +1,10 @@
# Auth config for publishing to npm registry.
# It's put in /.github so it's only picked up by
# github actions.
npmPublishRegistry: 'https://registry.npmjs.org'
npmRegistries:
//registry.npmjs.org:
npmAlwaysAuth: true
npmAuthToken: ${NODE_AUTH_TOKEN}

View file

@ -7,7 +7,7 @@ on:
branches: [main] branches: [main]
jobs: jobs:
build: Test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:

View file

@ -0,0 +1,34 @@
name: 'Publish Prerelease'
on:
workflow_dispatch:
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '16.x'
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn install
- name: Build the theatre packages
run: yarn build
- name: Update .yarnrc.yml with the auth config for the npmPublishRegistry
run: cat .github/.yarnrc.publish.yml >> .yarnrc.yml
- name: Publish the theatre packages
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# LATEST_COMMIT_HASH: ${{ github.event.pull_request.head.sha }}
run: yarn zx scripts/prerelease.mjs

View file

@ -2,8 +2,8 @@ nodeLinker: node-modules
plugins: plugins:
- path: .yarn/plugins/@yarnpkg/plugin-compat.cjs - path: .yarn/plugins/@yarnpkg/plugin-compat.cjs
spec: "@yarnpkg/plugin-compat" spec: '@yarnpkg/plugin-compat'
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools" spec: '@yarnpkg/plugin-interactive-tools'
yarnPath: .yarn/releases/yarn-3.2.0.cjs yarnPath: .yarn/releases/yarn-3.2.0.cjs

149
scripts/prerelease.mjs Normal file
View file

@ -0,0 +1,149 @@
/**
* 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.
*/
import os from 'os'
import path from 'path'
const packagesToPublish = [
'@theatre/core',
'@theatre/studio',
'@theatre/dataverse',
'@theatre/react',
'@theatre/browser-bundles',
'@theatre/r3f',
]
/**
* Receives a version number and returns it without the tags, if there are any
*
* @param {string} 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`
* ```
*/
function stripTag(version) {
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`
*
* @param {string} packageName - Name of the package
* @param {string} commitHash - A commit hash
*/
function getNewVersionName(packageName, commitHash) {
// 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 new versions to the packages
*
* @param {{name: string, location: string}[]} workspacesListObjects - An Array of objects containing information about the workspaces
* @param {string} latestCommitHash - Hash of the latest commit
*/
async function assignVersions(workspacesListObjects, latestCommitHash) {
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)
// 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'}`
}
}
;(async function () {
// @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 lenght of the fake merge commit's abbreviated hash.
const fakeMergeCommitHashLength = (await $`git log -1 --pretty=format:%h`)
.stdout.length
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))
await assignVersions(workspacesListObjects, latestCommitHash)
await Promise.all(
packagesToPublish.map((workspaceName) => {
const npmTag = 'insiders'
return $`yarn workspace ${workspaceName} npm publish --access public --tag ${npmTag}`
}),
)
})()