Skip to content
Open
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
7 changes: 3 additions & 4 deletions .github/promote.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#!/bin/bash
GIT_USERNAME="patternfly-build"
GIT_USERNAME="Patternfly CI/CD"
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}
git config user.email "patternfly-ci@redhat.com"
git config user.name "${GIT_USERNAME}"
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc

# Lerna is complicated. Commands: https://github.com/lerna/lerna/tree/master/commands
Expand All @@ -26,4 +26,3 @@ yarn run lerna version preminor --force-publish --conventional-commits --no-priv

# dry run
# yarn run lerna version --conventional-commits --conventional-graduate --no-private --yes --no-git-tag-version --no-push

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}"
56 changes: 15 additions & 41 deletions .github/release.sh
Original file line number Diff line number Diff line change
@@ -1,48 +1,22 @@
#!/bin/bash
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}
set -euo pipefail

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_USERNAME="Patternfly CI/CD"
echo "Preparing release environment..."
git config user.email "patternfly-ci@redhat.com"
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
98 changes: 90 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,40 @@ 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
prepare:
name: Prepare release
if: github.event_name == 'push'
runs-on: ubuntu-latest
needs: [ci, docs]
permissions:
contents: read
id-token: write
# Push the release commit and tags. This job never receives the OIDC token
# used for npm trusted publishing.
contents: write
outputs:
prepared: ${{ steps.prepare.outputs.prepared }}
steps:
- name: Check out project
uses: actions/checkout@v4
Expand All @@ -39,13 +51,83 @@ jobs:
with:
skip-build-cache: true

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

- name: Preserve prepared release packages for publishing and 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

publish:
name: Publish release
needs: prepare
if: >
!cancelled() &&
((github.event_name == 'push' && needs.prepare.outputs.prepared == 'true') ||
(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/felt-v1'))
runs-on: ubuntu-latest
permissions:
# This job installs dependencies and runs repository code, so it only
# receives the npm trusted-publishing OIDC token and read access. It never
# pushes and is reused unchanged for manual publication recovery.
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
# Normal releases publish the artifact of this run; manual recovery
# reuses the artifact of the failed run passed as an input.
run-id: ${{ inputs.publish-only-run-id || github.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: Deploy to NPM and Github
run: .github/release.sh
- 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", "changelog": false },
"chore": { "semverBump": "none", "changelog": false },
"refactor": { "semverBump": "patch" },
"perf": { "semverBump": "patch" },
"test": { "semverBump": "patch" },
"style": { "semverBump": "patch" },
"build": { "semverBump": "patch" },
"ci": { "semverBump": "patch", "changelog": false }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming these are ok @kmcfaul @wise-king-sullyman?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

}
},
"changelog": {
"workspaceChangelog": false,
"projectChangelogs": true
},
"git": {
"commitMessage": "chore(release): releasing packages [ci skip]",
Comment thread
rebeccaalpert marked this conversation as resolved.
Resolved
"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