Skip to content

Commit a15c7c4

Browse files
committed
ci(ci): add infra for maintenance branches
1 parent 3c02043 commit a15c7c4

10 files changed

Lines changed: 205 additions & 9 deletions

File tree

.github/workflows/dev-cleanup.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ on:
55
types: [closed]
66
branches:
77
- main
8+
- 'release/**'
89

910
env:
1011
GH_NPM_REGISTRY_TOKEN: ${{ secrets.GH_NPM_REGISTRY_TOKEN }}
@@ -25,7 +26,7 @@ jobs:
2526
- name: Checkout code
2627
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
2728
with:
28-
ref: main
29+
ref: ${{ github.event.pull_request.base.ref }}
2930

3031
- name: Setup pnpm
3132
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4

.github/workflows/dev-publish.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ on:
99
types: [labeled, synchronize]
1010
branches:
1111
- main
12+
- 'release/**'
1213

1314
env:
1415
GH_NPM_REGISTRY_TOKEN: ${{ secrets.GH_NPM_REGISTRY_TOKEN }}
1516
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
1617
TURBO_TELEMETRY_DISABLED: 1
1718
DO_NOT_TRACK: 1
19+
BASE_REF: ${{ github.event.pull_request.base.ref }}
1820

1921
concurrency:
2022
group: dev-publish-pr-${{ github.event.pull_request.number }}
@@ -96,7 +98,7 @@ jobs:
9698
- name: Get changed packages
9799
id: changed
98100
run: |
99-
CHANGED_FILES=$(git diff --name-only origin/main...HEAD)
101+
CHANGED_FILES=$(git diff --name-only origin/${BASE_REF}...HEAD)
100102
PACKAGES=""
101103
102104
for pkg_dir in packages/* web-packages/*; do

.github/workflows/pr-checks.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ on:
44
pull_request:
55
branches:
66
- main
7+
- 'release/**'
78

89
env:
910
GH_NPM_REGISTRY_TOKEN: ${{ secrets.GH_NPM_REGISTRY_TOKEN }}
1011
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
12+
BASE_REF: ${{ github.event.pull_request.base.ref }}
1113

1214
concurrency:
1315
group: ${{ github.workflow }}-${{ github.ref }}
@@ -99,7 +101,7 @@ jobs:
99101
- name: Run Build
100102
if: matrix.check == 'Build'
101103
run: |
102-
if git diff --name-only origin/main...HEAD | grep -qE '^(pnpm-lock\.yaml|package\.json)$'; then
104+
if git diff --name-only origin/${BASE_REF}...HEAD | grep -qE '^(pnpm-lock\.yaml|package\.json)$'; then
103105
echo "📦 Root dependency files changed - building all packages"
104106
pnpm build
105107
else
@@ -133,7 +135,7 @@ jobs:
133135
- name: Check for lockfile changes
134136
id: lockfile_check
135137
run: |
136-
if git diff --name-only origin/main...HEAD | grep -q '^pnpm-lock\.yaml$'; then
138+
if git diff --name-only origin/${BASE_REF}...HEAD | grep -q '^pnpm-lock\.yaml$'; then
137139
echo "changed=true" >> $GITHUB_OUTPUT
138140
else
139141
echo "changed=false" >> $GITHUB_OUTPUT

.github/workflows/release.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- main
7+
- 'release/**'
78

89
env:
910
GH_NPM_REGISTRY_TOKEN: ${{ secrets.GH_NPM_REGISTRY_TOKEN }}
@@ -12,11 +13,12 @@ env:
1213
DO_NOT_TRACK: 1
1314

1415
concurrency:
15-
group: ${{ github.workflow }}
16+
group: ${{ github.workflow }}-${{ github.ref }}
1617
cancel-in-progress: false
1718

1819
jobs:
1920
release:
21+
if: "!contains(github.event.head_commit.message, '[skip ci]')"
2022
name: Release and Publish
2123
runs-on: ubuntu-latest
2224
permissions:

CONTRIBUTING.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,75 @@ feat(apollo-react): add ApButton variant
344344
feat(apollo-core): add new color token for button variant
345345
```
346346

347+
## Maintenance Releases
348+
349+
Maintenance branches allow backporting fixes to older major versions after a new major version has been released. For example, releasing `@uipath/apollo-react@3.70.4` after `4.0.0` ships.
350+
351+
### Branch Naming
352+
353+
Maintenance branches are package-scoped: `release/<package-name>@<major>.x`
354+
355+
Examples: `release/apollo-react@3.x`, `release/apollo-core@5.x`
356+
357+
### When to Create a Maintenance Branch
358+
359+
Create one when you ship a new major version and need to continue supporting the previous major for existing consumers.
360+
361+
### Creating a Maintenance Branch
362+
363+
Use the helper script:
364+
365+
```bash
366+
scripts/create-maintenance-branch.sh apollo-react 3
367+
```
368+
369+
This will:
370+
1. Find the latest `@uipath/apollo-react@3.*` tag
371+
2. Create `release/apollo-react@3.x` from that tag
372+
3. Configure semantic-release on the new branch
373+
4. Print next steps
374+
375+
After running the script:
376+
1. Push the maintenance branch: `git push -u origin 'release/apollo-react@3.x'`
377+
2. On `main`, update `packages/apollo-react/.releaserc.json` to add the maintenance branch entry **before** `"main"` in the `branches` array:
378+
```json
379+
"branches": [
380+
{ "name": "release/apollo-react@3.x", "range": "3.x", "channel": "release-3.x" },
381+
"main"
382+
]
383+
```
384+
385+
### Backporting Fixes
386+
387+
Cherry-pick from `main` or create a PR targeting the maintenance branch directly:
388+
389+
```bash
390+
git checkout release/apollo-react@3.x
391+
git cherry-pick <sha>
392+
git push
393+
# → CI releases apollo-react@3.70.4 with dist-tag `release-3.x`
394+
```
395+
396+
### Installing Maintenance Releases
397+
398+
Maintenance releases publish under a dedicated npm dist-tag (not `latest`):
399+
400+
```bash
401+
# Install latest maintenance release for 3.x
402+
npm install @uipath/apollo-react@release-3.x
403+
404+
# Install a specific version
405+
npm install @uipath/apollo-react@3.70.4
406+
```
407+
408+
### Cross-Package Fixes
409+
410+
If a fix touches multiple packages (e.g., `apollo-core` and `apollo-react`), add the maintenance branch entry to each package's `.releaserc.json`. Both packages will be released from the same push.
411+
412+
### How Other Packages Behave
413+
414+
When CI runs `pnpm release` on a maintenance branch, semantic-release executes for all packages. Packages whose `.releaserc.json` doesn't list the branch simply skip with exit code 0 — no special handling needed.
415+
347416
## Package Structure
348417

349418
```

packages/apollo-core/.releaserc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
[
3131
"@semantic-release/exec",
3232
{
33-
"publishCmd": "bash ../../scripts/publish-to-registries.sh --no-git-checks --access public"
33+
"publishCmd": "bash ../../scripts/publish-to-registries.sh --no-git-checks --access public --tag ${nextRelease.channel || 'latest'}"
3434
}
3535
],
3636
[

packages/apollo-react/.releaserc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
[
3131
"@semantic-release/exec",
3232
{
33-
"publishCmd": "bash ../../scripts/publish-to-registries.sh --no-git-checks --access public"
33+
"publishCmd": "bash ../../scripts/publish-to-registries.sh --no-git-checks --access public --tag ${nextRelease.channel || 'latest'}"
3434
}
3535
],
3636
[

packages/apollo-wind/.releaserc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
[
3131
"@semantic-release/exec",
3232
{
33-
"publishCmd": "bash ../../scripts/publish-to-registries.sh --no-git-checks --access public"
33+
"publishCmd": "bash ../../scripts/publish-to-registries.sh --no-git-checks --access public --tag ${nextRelease.channel || 'latest'}"
3434
}
3535
],
3636
[
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#!/bin/bash
2+
# Create a maintenance branch for an older major version of a package.
3+
#
4+
# Usage: scripts/create-maintenance-branch.sh <package-name> <major-version>
5+
# Example: scripts/create-maintenance-branch.sh apollo-react 3
6+
#
7+
# This script:
8+
# 1. Finds the latest git tag for @uipath/<package>@<major>.*
9+
# 2. Creates release/<package>@<major>.x branch from that tag
10+
# 3. Updates the package's .releaserc.json with maintenance branch config
11+
# 4. Commits the config change
12+
# 5. Prints next steps (push, update main's .releaserc.json)
13+
14+
set -euo pipefail
15+
16+
PACKAGE="${1:?Usage: $0 <package-name> <major-version>}"
17+
MAJOR="${2:?Usage: $0 <package-name> <major-version>}"
18+
19+
if ! [[ "$MAJOR" =~ ^[0-9]+$ ]]; then
20+
echo "Error: major version must be a number, got '$MAJOR'"
21+
exit 1
22+
fi
23+
24+
TAG_PATTERN="@uipath/${PACKAGE}@${MAJOR}.*"
25+
BRANCH="release/${PACKAGE}@${MAJOR}.x"
26+
CHANNEL="release-${MAJOR}.x"
27+
28+
# Find package directory
29+
if [ -d "packages/${PACKAGE}" ]; then
30+
RELEASERC="packages/${PACKAGE}/.releaserc.json"
31+
elif [ -d "web-packages/${PACKAGE}" ]; then
32+
RELEASERC="web-packages/${PACKAGE}/.releaserc.json"
33+
else
34+
echo "Error: package '${PACKAGE}' not found in packages/ or web-packages/"
35+
exit 1
36+
fi
37+
38+
if [ ! -f "${RELEASERC}" ]; then
39+
echo "Error: release config '${RELEASERC}' not found for package '${PACKAGE}'"
40+
exit 1
41+
fi
42+
43+
# Find latest tag for this major version
44+
LATEST_TAG=$(git tag --list "${TAG_PATTERN}" --sort=-version:refname | head -1)
45+
46+
if [ -z "$LATEST_TAG" ]; then
47+
echo "Error: no tags found matching '${TAG_PATTERN}'"
48+
echo ""
49+
echo "Available tags for @uipath/${PACKAGE}:"
50+
git tag --list "@uipath/${PACKAGE}@*" --sort=-version:refname | head -5
51+
exit 1
52+
fi
53+
54+
echo "Latest tag: ${LATEST_TAG}"
55+
echo "Branch: ${BRANCH}"
56+
echo "Channel: ${CHANNEL}"
57+
echo ""
58+
59+
# Check if branch already exists
60+
if git show-ref --verify --quiet "refs/heads/${BRANCH}" 2>/dev/null || \
61+
git show-ref --verify --quiet "refs/remotes/origin/${BRANCH}" 2>/dev/null; then
62+
echo "Error: branch '${BRANCH}' already exists"
63+
exit 1
64+
fi
65+
66+
if ! command -v jq &> /dev/null; then
67+
echo "Error: jq is required. Please install jq using your package manager (e.g. 'brew install jq' on macOS) and try again."
68+
exit 1
69+
fi
70+
71+
# Create branch from tag
72+
git checkout -b "${BRANCH}" "${LATEST_TAG}"
73+
74+
# Update .releaserc.json — insert the maintenance branch entry immediately before
75+
# "main", preserving order of all existing branches (other maintenance branches,
76+
# prereleases that come after main, etc.). If an entry for this branch already
77+
# exists, it's replaced in place.
78+
jq --arg name "${BRANCH}" \
79+
--arg range "${MAJOR}.x" \
80+
--arg channel "${CHANNEL}" \
81+
'
82+
.branches as $existing
83+
| { name: $name, range: $range, channel: $channel } as $entry
84+
| ($existing | map((type == "object" and .name == $name) or . == $name) | index(true)) as $existing_idx
85+
| ($existing | map((type == "object" and .name == "main") or . == "main") | index(true)) as $main_idx
86+
| if $existing_idx != null then
87+
.branches = ($existing | .[0:$existing_idx] + [$entry] + .[$existing_idx+1:])
88+
elif $main_idx != null then
89+
.branches = ($existing | .[0:$main_idx] + [$entry] + .[$main_idx:])
90+
else
91+
.branches = ($existing + [$entry])
92+
end
93+
' \
94+
"${RELEASERC}" > "${RELEASERC}.tmp" && mv "${RELEASERC}.tmp" "${RELEASERC}"
95+
96+
echo "✓ Updated ${RELEASERC}"
97+
echo ""
98+
99+
# Commit the change
100+
git add "${RELEASERC}"
101+
git commit -m "ci(${PACKAGE}): configure maintenance branch for ${MAJOR}.x [skip ci]"
102+
103+
echo ""
104+
echo "✓ Branch '${BRANCH}' created and configured"
105+
echo ""
106+
echo "Next steps:"
107+
echo ""
108+
echo " 1. Push the branch:"
109+
echo " git push -u origin '${BRANCH}'"
110+
echo ""
111+
echo " 2. On main, update ${RELEASERC} to add this entry"
112+
echo " BEFORE \"main\" in the branches array:"
113+
echo ""
114+
echo " {\"name\": \"${BRANCH}\", \"range\": \"${MAJOR}.x\", \"channel\": \"${CHANNEL}\"}"
115+
echo ""
116+
echo " Example .releaserc.json branches on main:"
117+
echo " \"branches\": ["
118+
echo " {\"name\": \"${BRANCH}\", \"range\": \"${MAJOR}.x\", \"channel\": \"${CHANNEL}\"},"
119+
echo " \"main\""
120+
echo " ]"

web-packages/ap-chat/.releaserc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
[
3131
"@semantic-release/exec",
3232
{
33-
"publishCmd": "bash ../../scripts/publish-to-registries.sh --no-git-checks --access public"
33+
"publishCmd": "bash ../../scripts/publish-to-registries.sh --no-git-checks --access public --tag ${nextRelease.channel || 'latest'}"
3434
}
3535
],
3636
[

0 commit comments

Comments
 (0)