Skip to content

Commit

Permalink
ci(github): connect npm release with more checks
Browse files Browse the repository at this point in the history
  • Loading branch information
karliatto committed Jun 6, 2024
1 parent a8bd0ae commit 5d4420d
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 150 deletions.
118 changes: 102 additions & 16 deletions .github/workflows/release-connect-npm.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,84 @@
name: "[Release] Connect NPM"
on:
workflow_dispatch:
inputs:
packages:
description: 'Array string with names of the packages to deploy. (example: ["blockchain-link-utils","blockchain-link-types","analytics"])'
required: true
type: string
deploymentType:
description: "Specifies the deployment type for the npm package. (example: canary, stable)"
required: true
type: choice
options:
- canary
- stable

jobs:
deploy-npm:
name: Deploy NPM ${{ inputs.deploymentType }}
extract-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.set-version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Number of commits to fetch. 0 indicates all history for all branches and tags.
fetch-depth: 0

- name: Setup node
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"

- name: Extract connect version
id: set-version
run: echo "version=$(node ./ci/scripts/get-connect-version.js)" >> $GITHUB_OUTPUT

sanity-check-version-match:
runs-on: ubuntu-latest
needs: [extract-version]
steps:
- uses: actions/checkout@v4

- name: Check connect version match
uses: ./.github/actions/check-connect-version-match
with:
branch_ref: "${{ github.ref }}"
extracted_version: "${{ needs.extract-version.outputs.version }}"

identify-release-packages:
runs-on: ubuntu-latest
needs: [extract-version, sanity-check-version-match]
outputs:
packagesNeedRelease: ${{ steps.set-packages-need-release.outputs.packagesNeedRelease }}
deploymentType: ${{ steps.determine-deployment-type.outputs.deploymentType }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup node
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"

- name: Install dependencies
run: yarn install

- name: Get packages that need release
id: set-packages-need-release
run: echo "packagesNeedRelease=$(yarn tsx ./ci/scripts/get-connect-dependencies-to-release.ts)" >> $GITHUB_OUTPUT

- name: Determine Deployment Type from version in branch
id: determine-deployment-type
outputs:
deploymentType: ${{ steps.determine-deployment-type.outputs.deploymentType }}
run: echo "deploymentType=$(yarn tsx ./ci/scripts/determine-deployment-type.ts ${{ needs.extract-version.outputs.version }})" >> $GITHUB_OUTPUT

- name: Sanity Check - All Packages Same Deployment Type
env:
PACKAGES: ${{ steps.set-packages-need-release.outputs.packagesNeedRelease }}
DEPLOYMENT_TYPE: ${{ steps.determine-deployment-type.outputs.deploymentType }}
run: |
yarn tsx ./ci/scripts/check-packages-same-deployment-type.ts '${{ env.PACKAGES }}' "${{ env.DEPLOYMENT_TYPE }}"
deploy-npm-connect-dependencies:
name: Deploy NPM ${{ needs.identify-release-packages.outputs.deploymentType }} ${{ matrix.package }}
needs: [extract-version, sanity-check-version-match, identify-release-packages]
environment: production-connect
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
package: ${{ fromJson(github.event.inputs.packages) }}
package: ${{ fromJson(needs.identify-release-packages.outputs.packagesNeedRelease) }}
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -30,12 +87,41 @@ jobs:
- name: Set deployment type
id: set_deployment_type
run: |
if [ "${{ github.event.inputs.deploymentType }}" == "canary" ]; then
if [ "${{ needs.identify-release-packages.outputs.deploymentType }}" == "canary" ]; then
echo "DEPLOYMENT_TYPE=beta" >> $GITHUB_ENV
else
echo "DEPLOYMENT_TYPE=latest" >> $GITHUB_ENV
fi
- name: Deploy to NPM ${{ matrix.package }}
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
uses: ./.github/actions/release-connect-npm
with:
deploymentType: ${{ env.DEPLOYMENT_TYPE }}
packageName: ${{ matrix.package }}

deploy-npm-connect:
name: Deploy NPM ${{ needs.identify-release-packages.outputs.deploymentType }} ${{ matrix.package }}
# We only deploy connect NPM once dependencies have been deployed successfully.
needs: [deploy-npm-connect-dependencies]
environment: production-connect
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
package: ["connect", "connect-web", "connect-webextension"]
steps:
- uses: actions/checkout@v4
with:
ref: develop
- name: Set deployment type
id: set_deployment_type
run: |
if [ "${{ needs.identify-release-packages.outputs.deploymentType }}" == "canary" ]; then
echo "DEPLOYMENT_TYPE=beta" >> $GITHUB_ENV
else
echo "DEPLOYMENT_TYPE=latest" >> $GITHUB_ENV
fi
- name: Deploy to NPM ${{ matrix.package }}
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
Expand Down
26 changes: 26 additions & 0 deletions ci/scripts/check-packages-same-deployment-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import semver from 'semver';

import { getLocalVersion } from './helpers';

const checkVersions = (packages: string[], deploymentType: string): void => {
const versions = packages.map(packageName => getLocalVersion(packageName));

const isCorrectType = versions.every(version => {
const isBeta = semver.prerelease(version);
return (deploymentType === 'canary' && isBeta) || (deploymentType === 'stable' && !isBeta);
});

if (!isCorrectType) {
console.error(
`Mixed deployment types detected. All versions should be either "stable" or "canary".`,
);
process.exit(1);
} else {
console.log(`All versions are of the ${deploymentType} deployment type.`);
}
};

const packages = JSON.parse(process.argv[2]);
const deploymentType = process.argv[3];

checkVersions(packages, deploymentType);
134 changes: 0 additions & 134 deletions ci/scripts/connect-release-npm-init.ts

This file was deleted.

14 changes: 14 additions & 0 deletions ci/scripts/determine-deployment-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const semver = require('semver');

const version = process.argv[2];

let deploymentType;
if (semver.prerelease(version)) {
deploymentType = 'canary';
} else if (semver.minor(version) || semver.major(version)) {
deploymentType = 'stable';
} else {
throw new Error(`Invalid version: ${version}`);
}

process.stdout.write(deploymentType);
77 changes: 77 additions & 0 deletions ci/scripts/get-connect-dependencies-to-release.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// This script should check what packages from the repository have a higher version than in NPM
// and stdout out those to be used by GitHub workflow.

import fs from 'fs';
import util from 'util';
import path from 'path';
import semver from 'semver';

import { getNpmRemoteGreatestVersion } from './helpers';

const readFile = util.promisify(fs.readFile);

const ROOT = path.join(__dirname, '..', '..');

const nonReleaseDependencies: string[] = [];

const checkNonReleasedDependencies = async (packageName: string) => {
const rawPackageJSON = await readFile(
path.join(ROOT, 'packages', packageName, 'package.json'),
'utf-8',
);

const packageJSON = JSON.parse(rawPackageJSON);
const {
version: localVersion,
dependencies,
// devDependencies // We should ignore devDependencies.
} = packageJSON;

const remoteGreatestVersion = await getNpmRemoteGreatestVersion(`@trezor/${packageName}`);

// If local version is greatest than the greatest one in NPM we add it to the release.
if (semver.gt(localVersion, remoteGreatestVersion as string)) {
const index = nonReleaseDependencies.indexOf(packageName);
if (index > -1) {
nonReleaseDependencies.splice(index, 1);
}
nonReleaseDependencies.push(packageName);
}

if (!dependencies || !Object.keys(dependencies)) {
return;
}

// eslint-disable-next-line no-restricted-syntax
for await (const [dependency] of Object.entries(dependencies)) {
// is not a dependency released from monorepo. we don't care
if (!dependency.startsWith('@trezor')) {
// eslint-disable-next-line no-continue
continue;
}
const [_prefix, name] = dependency.split('/');

await checkNonReleasedDependencies(name);
}
};

const getConnectDependenciesToRelease = async () => {
// We check what dependencies need to be released because they have version bumped locally
// and remote greatest version is lower than the local one.
await checkNonReleasedDependencies('connect');
await checkNonReleasedDependencies('connect-web');
await checkNonReleasedDependencies('connect-webextension');

// We do not want to include `connect`, `connect-web` and `connect-webextension` since we want
// to release those separately and we always want to release them.
const onlyDependenciesToRelease = nonReleaseDependencies.filter(item => {
return !['connect', 'connect-web', 'connect-webextension'].includes(item);
});

// We use `onlyDependenciesToRelease` to trigger NPM releases
const dependenciesToRelease = JSON.stringify(onlyDependenciesToRelease);

process.stdout.write(dependenciesToRelease);
};

getConnectDependenciesToRelease();
Loading

0 comments on commit 5d4420d

Please sign in to comment.