Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/setup-project/action.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: Set up and build project
description: Set up Node.js and Yarn, install dependencies, and optionally build packages.
inputs:
skip-build:
description: Skip the build step
Expand Down
33 changes: 33 additions & 0 deletions .github/release-comment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/bash
set -euo pipefail

if [[ ! -f .release/release.json ]]; then
exit 0
fi

GH_REPO=${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is required}
RELEASE_COMMIT=$(node -p "require('./.release/release.json').sourceCommit")
PR_NUM=$(git log -1 --format="%s" "${RELEASE_COMMIT}^" | grep -Poe '#\d+' | tr -d '#') || true

if [[ -z "${PR_NUM}" ]]; then
echo "No pull request number found in the merged commit; skipping release comment."
exit 0
fi

if [[ -z "${GH_PR_TOKEN:-}" ]]; then
echo "GH_PR_TOKEN is required to comment on the pull request."
exit 1
fi

PAYLOAD=$(node --input-type=module -e '
import { readFileSync } from "node:fs";
const release = JSON.parse(readFileSync(".release/release.json", "utf8"));
const versions = release.projects.map((name) => `${name}@${release.versionData[name].newVersion}`).join(", ");
const url = `https://github.com/${process.env.GITHUB_REPOSITORY}/commit/${release.sourceCommit}`;
process.stdout.write(JSON.stringify({ body: `Your changes have been released in: ${url}\n\n${versions}\n\nThanks for your contribution! :tada:` }));
')

COMMENTS_URL="https://api.github.com/repos/${GH_REPO}/issues/${PR_NUM}/comments"
echo "Adding release comment to PR #${PR_NUM}"
curl --fail-with-body -H "Authorization: token ${GH_PR_TOKEN}" -H "Accept: application/vnd.github+json" \
--request POST "${COMMENTS_URL}" --data "${PAYLOAD}"
52 changes: 13 additions & 39 deletions .github/release.sh
Original file line number Diff line number Diff line change
@@ -1,48 +1,22 @@
#!/bin/bash
set -euo pipefail

GIT_USERNAME="patternfly-build"
GH_REPO=${GITHUB_REPOSITORY}
REPO="github.com/${GH_REPO}"
echo "Preparing release environment..."
git config user.email "patternfly-build@redhat.com"
git config user.name ${GIT_USERNAME}

echo "Doing a release..."
# Lerna is complicated. Commands: https://github.com/lerna/lerna/tree/master/commands
# Identify packages that have been updated since the previous tagged release
# Update their versions and changelogs according to angular commit guidelines
# https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit
git config user.name "${GIT_USERNAME}"

# Grab the log before lerna makes a commit
LOG=$(git log --format="%s" -1 | grep -Poe "#\d+")
PR_NUM=${LOG:1}
echo "Preparing the Nx release..."
yarn release:nx --ci-prepare

if ! (
set -o pipefail
yarn run lerna publish prerelease --preid=prerelease --dist-tag=prerelease --yes 2>&1 | tee lerna-output.txt
); then
echo "Failed lerna publish"
exit 1
if [[ ! -f .release/release.json ]]; then
echo "No changed Felt packages to publish."
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
echo "prepared=false" >> "${GITHUB_OUTPUT}"
fi
exit 0
fi

# use lerna command below for dry run
# yarn run lerna version prerelease --preid=prerelease --dist-tag=prerelease --yes --no-git-tag-version --no-push | tee lerna-output.txt

if grep -i "Successfully published" lerna-output.txt; # Leave a Github comment
then
if [ -n "${PR_NUM}" ]
then
# Use Issues api instead of PR api because
# PR api requires comments be made on specific files of specific commits
GH_PR_COMMENTS="https://api.github.com/repos/${GH_REPO}/issues/${PR_NUM}/comments"
COMMENT=$(git log --author="patternfly-build" -1 --pretty=%B | tail -n +2 | python -c 'import json,sys; print(json.dumps(sys.stdin.read()))' | cut -d '"' -f 2)
JSON="{\"body\":\"Your changes have been released in: ${COMMENT}Thanks for your contribution! :tada:\"}"
echo "Adding github PR comment ${GH_PR_COMMENTS} ${JSON}"
curl -H "Authorization: token ${GH_PR_TOKEN}" --request POST "${GH_PR_COMMENTS}" --data "${JSON}"
fi
elif grep -i "No changed packages to publish" lerna-output.txt;
then
echo "No changed packages to publish"
else
echo "Failed lerna publish"
exit 1
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
echo "prepared=true" >> "${GITHUB_OUTPUT}"
fi
3 changes: 3 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ jobs:
- name: Set up and build project
uses: ./.github/actions/setup-project

- name: Run release tests
run: yarn test:release

- name: Run tests
run: yarn test --maxWorkers=2

Expand Down
100 changes: 96 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,36 @@ on:
push:
branches:
- felt-v1
workflow_dispatch:
inputs:
publish-only-run-id:
description: Failed release workflow run ID to recover from
required: true
type: string
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
group: ${{ github.workflow }}-felt-v1
cancel-in-progress: false
jobs:
ci:
name: CI
if: github.event_name == 'push'
uses: ./.github/workflows/main.yml

docs:
name: Documentation
if: github.event_name == 'push'
uses: ./.github/workflows/documentation.yml
with:
is-release: true
secrets: inherit

deploy:
name: Deploy release
if: github.event_name == 'push'
runs-on: ubuntu-latest
needs: [ci, docs]
permissions:
contents: read
contents: write
id-token: write
steps:
- name: Check out project
Expand All @@ -45,7 +54,90 @@ jobs:
node --version
npm --version

- name: Deploy to NPM and Github
- name: Prepare release commit, tags, and packages
id: prepare
run: .github/release.sh

- name: Preserve prepared release packages for recovery
if: steps.prepare.outputs.prepared == 'true'
uses: actions/upload-artifact@v4
with:
name: nx-release-packages
path: .release/
include-hidden-files: true
if-no-files-found: error
retention-days: 30

- name: Publish prepared release packages
if: steps.prepare.outputs.prepared == 'true'
run: yarn release:nx --publish-only

- name: Comment on the merged pull request
id: comment
if: steps.prepare.outputs.prepared == 'true'
continue-on-error: true
run: .github/release-comment.sh
env:
GH_PR_TOKEN: ${{ secrets.GH_PR_TOKEN }}

- name: Report notification failure
if: steps.comment.outcome == 'failure'
run: echo "::warning::Packages were published successfully, but the release PR comment failed. Do not rerun publication to retry the comment."

recover-publish:
name: Recover release publication
if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/felt-v1'
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Check out project
uses: actions/checkout@v4
with:
ref: felt-v1
fetch-depth: 0
token: ${{ secrets.GH_TOKEN_REDALLEN }}

- name: Download prepared release packages
uses: actions/download-artifact@v4
with:
name: nx-release-packages
run-id: ${{ inputs.publish-only-run-id }}
github-token: ${{ secrets.GH_TOKEN_REDALLEN }}
path: .release

- name: Check out the original release commit
shell: bash
run: |
SOURCE_COMMIT=$(node -p "require('./.release/release.json').sourceCommit")
if [[ ! "${SOURCE_COMMIT}" =~ ^[0-9a-f]{40}$ ]]; then
echo "Invalid source commit in release artifact: ${SOURCE_COMMIT}"
exit 1
fi
git checkout "${SOURCE_COMMIT}"

- name: Install dependencies without rebuilding release packages
uses: ./.github/actions/setup-project
with:
skip-build: true

- name: Set up npm for trusted publishing
run: |
npm install --global npm@11.21.1
node --version
npm --version

- name: Publish the prepared packages
run: yarn release:nx --publish-only

- name: Comment on the merged pull request
id: comment
continue-on-error: true
run: .github/release-comment.sh
env:
GH_PR_TOKEN: ${{ secrets.GH_PR_TOKEN }}

- name: Report notification failure
if: steps.comment.outcome == 'failure'
run: echo "::warning::Packages were published successfully, but the release PR comment failed. Do not rerun publication to retry the comment."
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,9 @@ lerna-debug.log
# For vim
*.swp
.yarn

# Nx local state and cache
/.nx/

# Prepared Nx Release packages and archives
/.release/
104 changes: 104 additions & 0 deletions nx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"defaultBase": "felt-v1",
"parallel": 12,
"pluginsConfig": {
"@nx/js": {
"analyzeSourceFiles": false
}
},
"release": {
"projects": [
"@rh-felt/react-charts",
"@rh-felt/react-code-editor",
"@rh-felt/react-core",
"@rh-felt/react-docs",
"@rh-felt/react-drag-drop",
"@rh-felt/react-icons",
"@rh-felt/react-styles",
"@rh-felt/react-table",
"@rh-felt/react-templates",
"@rh-felt/react-tokens"
],
"projectsRelationship": "independent",
"releaseTag": {
"pattern": "{projectName}@{version}",
"strictPreid": false,
"checkAllBranchesWhen": false
},
"version": {
"specifierSource": "conventional-commits",
"currentVersionResolver": "git-tag",
"preserveLocalDependencyProtocols": true,
"updateDependents": "always",
"applyPreidToDependents": true
},
"conventionalCommits": {
"useCommitScope": false,
"types": {
"docs": { "semverBump": "patch" },
"chore": { "semverBump": "patch" },
"refactor": { "semverBump": "patch" },
"perf": { "semverBump": "patch" },
"test": { "semverBump": "patch" },
"style": { "semverBump": "patch" },
"build": { "semverBump": "patch" },
"ci": { "semverBump": "patch" }
}
},
"changelog": {
"workspaceChangelog": false,
"projectChangelogs": true
},
"git": {
"commitMessage": "chore(release): releasing packages [ci skip]",
"commit": false,
"stageChanges": false,
"tag": false,
"push": false
}
},
"targetDefaults": {
"nx-release-publish": {
"cache": false,
"options": {
"packageRoot": ".release/packages/{projectName}",
"access": "public",
"tag": "prerelease"
}
},
"clean": {
"cache": false
},
"generate": {
"cache": false
},
"subpaths": {
"cache": false
},
"build:umd": {
"cache": false
},
"build:single:packages": {
"cache": false,
"dependsOn": ["^build:single:packages"]
},
"build": {
"cache": false,
"dependsOn": ["^build"]
},
"preview": {
"cache": false
},
"dev": {
"cache": false
},
"cypress:open": {
"cache": false
},
"test:a11y": {
"cache": false
}
},
"analytics": false
}
Loading
Loading