in projenrc/release.ts [15:249]
public constructor(private readonly project: typescript.TypeScriptProject) {
new ReleaseTask(project);
new TagReleaseTask(project);
let release = project.github!.addWorkflow('release');
release.runName = 'Release ${{ github.ref_name }}';
release.on({ push: { tags: ['v*.*.*'] } });
const nodeVersion = project.minNodeVersion?.split('.', 1).at(0) ?? 'lts/*';
const releasePackageName = 'release-package';
const publishTarget = 'publish-target';
const federateToAwsStep: github.workflows.JobStep = {
name: 'Federate to AWS',
uses: 'aws-actions/configure-aws-credentials@v1',
with: {
'aws-region': 'us-east-1',
'role-to-assume': '${{ secrets.AWS_ROLE_TO_ASSUME }}',
'role-session-name': 'GHA-aws-jsii-compiler@${{ github.ref_name }}',
},
};
release.addJob('build', {
name: 'Build release package',
env: {
CI: 'true',
},
outputs: {
[PublishTargetOutput.DIST_TAG]: { stepId: publishTarget, outputName: PublishTargetOutput.DIST_TAG },
[PublishTargetOutput.IS_LATEST]: { stepId: publishTarget, outputName: PublishTargetOutput.IS_LATEST },
[PublishTargetOutput.GITHUB_RELEASE]: { stepId: publishTarget, outputName: PublishTargetOutput.GITHUB_RELEASE },
[PublishTargetOutput.IS_PRERELEASE]: { stepId: publishTarget, outputName: PublishTargetOutput.IS_PRERELEASE },
},
permissions: {
idToken: github.workflows.JobPermission.WRITE,
contents: github.workflows.JobPermission.READ,
},
runsOn: ['ubuntu-latest'],
steps: [
ACTIONS_CHECKOUT(),
ACTIONS_SETUP_NODE(nodeVersion),
YARN_INSTALL(),
{
name: 'Prepare Release',
run: 'yarn release ${{ github.ref_name }}',
},
{
name: 'Determine Target',
id: publishTarget,
run: 'yarn ts-node projenrc/publish-target.ts ${{ github.ref_name }}',
env: {
// A GitHub token is required to list GitHub Releases, so we can tell if the `latest` dist-tag is needed.
GITHUB_TOKEN: '${{ github.token }}',
},
},
{
...federateToAwsStep,
// Only necessary if we're going to be publishing assets to GitHub Releases.
if: `fromJSON(steps.publish-target.outputs.${PublishTargetOutput.GITHUB_RELEASE})`,
},
{
name: 'Sign Tarball',
if: `fromJSON(steps.publish-target.outputs.${PublishTargetOutput.GITHUB_RELEASE})`,
run: [
'set -eo pipefail',
// First, we're going to be configuring GPG "correctly"
'export GNUPGHOME=$(mktemp -d)',
'echo "charset utf-8" > ${GNUPGHOME}/gpg.conf',
'echo "no-comments" >> ${GNUPGHOME}/gpg.conf',
'echo "no-emit-version" >> ${GNUPGHOME}/gpg.conf',
'echo "no-greeting" >> ${GNUPGHOME}/gpg.conf',
// Now, we need to import the OpenPGP private key into the keystore
'secret=$(aws secretsmanager get-secret-value --secret-id=${{ secrets.OPEN_PGP_KEY_ARN }} --query=SecretString --output=text)',
'privatekey=$(node -p "(${secret}).PrivateKey")',
'passphrase=$(node -p "(${secret}).Passphrase")',
'echo "::add-mask::${passphrase}"', // !!! IMPORTANT !!! (Ensures the value does not leak into public logs)
'unset secret',
'echo ${passphrase} | gpg --batch --yes --import --armor --passphrase-fd=0 <(echo "${privatekey}")',
'unset privatekey',
// Now we can actually detach-sign the artifacts
'for file in $(find dist -type f -not -iname "*.asc"); do',
` echo \${passphrase} | gpg --batch --yes --local-user=${JSON.stringify(
CODE_SIGNING_USER_ID,
)} --detach-sign --armor --pinentry-mode=loopback --passphrase-fd=0 \${file}`,
'done',
'unset passphrase',
// Clean up the GnuPG home directory (secure-wipe)
'find ${GNUPGHOME} -type f -exec shred --remove {} \\;',
].join('\n'),
},
{
name: 'Upload artifact',
uses: 'actions/upload-artifact@v4.3.6',
with: {
name: releasePackageName,
path: '${{ github.workspace }}/dist',
overwrite: true,
},
},
],
});
const downloadArtifactStep: github.workflows.JobStep = {
name: 'Download artifact',
uses: 'actions/download-artifact@v4',
with: {
name: releasePackageName,
},
};
release.addJob('release-to-github', {
name: 'Create GitHub Release',
env: {
CI: 'true',
},
if: `fromJSON(needs.build.outputs.${PublishTargetOutput.GITHUB_RELEASE})`,
needs: ['build'],
permissions: {
contents: github.workflows.JobPermission.WRITE,
},
runsOn: ['ubuntu-latest'],
steps: [
downloadArtifactStep,
{
id: 'release-exists',
name: 'Verify if release exists',
run: [
'if gh release view ${{ github.ref_name }} --repo=${{ github.repository }} &>/dev/null',
'then',
'echo "result=true" >> $GITHUB_OUTPUT',
'else',
'echo "result=false" >> $GITHUB_OUTPUT',
'fi',
].join('\n'),
env: {
GH_TOKEN: '${{ github.token }}',
},
},
{
name: 'Create PreRelease',
if: `!fromJSON(steps.release-exists.outputs.result) && fromJSON(needs.build.outputs.${PublishTargetOutput.IS_PRERELEASE})`,
run: [
'gh release create ${{ github.ref_name }}',
'--repo=${{ github.repository }}',
'--generate-notes',
'--title=${{ github.ref_name }}',
'--verify-tag',
'--prerelease',
`--latest=\${{ needs.build.outputs.${PublishTargetOutput.IS_LATEST} }}`,
].join(' '),
env: {
GH_TOKEN: '${{ github.token }}',
},
},
{
name: 'Create Release',
if: `!fromJSON(steps.release-exists.outputs.result) && !fromJSON(needs.build.outputs.${PublishTargetOutput.IS_PRERELEASE})`,
run: [
'gh release create ${{ github.ref_name }}',
'--repo=${{ github.repository }}',
'--generate-notes',
'--title=${{ github.ref_name }}',
'--verify-tag',
`--latest=\${{ needs.build.outputs.${PublishTargetOutput.IS_LATEST} }}`,
].join(' '),
env: {
GH_TOKEN: '${{ github.token }}',
},
},
{
name: 'Attach assets',
run: [
'gh release upload ${{ github.ref_name }}',
'--repo=${{ github.repository }}',
'--clobber',
'${{ github.workspace }}/**/*',
].join(' '),
env: {
GH_TOKEN: '${{ github.token }}',
},
},
],
});
release.addJob('release-npm-package', {
name: `Release to registry.npmjs.org`,
env: {
CI: 'true',
},
needs: ['build'],
permissions: {
idToken: github.workflows.JobPermission.WRITE,
contents: github.workflows.JobPermission.READ,
},
runsOn: ['ubuntu-latest'],
steps: [
downloadArtifactStep,
{
...ACTIONS_SETUP_NODE(),
with: {
'always-auth': true,
'node-version': nodeVersion,
'registry-url': `https://registry.npmjs.org/`,
},
},
federateToAwsStep,
{
name: 'Set NODE_AUTH_TOKEN',
run: [
'secret=$(aws secretsmanager get-secret-value --secret-id=${{ secrets.NPM_TOKEN_ARN }} --query=SecretString --output=text)',
'token=$(node -p "(${secret}).token")',
'unset secret',
'echo "::add-mask::${token}"', // !!! IMPORTANT !!! (Ensures the value does not leak into public logs)
'echo "NODE_AUTH_TOKEN=${token}" >> $GITHUB_ENV',
'unset token',
].join('\n'),
},
{
name: 'Publish',
run: [
'npm publish ${{ github.workspace }}/js/jsii-*.tgz',
'--access=public',
`--tag=\${{ needs.build.outputs.${PublishTargetOutput.DIST_TAG} }}`,
].join(' '),
},
{
name: 'Tag "latest"',
if: `fromJSON(needs.build.outputs.${PublishTargetOutput.IS_LATEST})`,
run: 'npm dist-tag add jsii@${{ github.ref_name }} latest',
},
],
});
}