Skip to content

Commit f139569

Browse files
authored
Configure auto-creation tags workflow
- Implemented GitHub Actions workflow for automated tag creation (`create-tags.yaml`) - Implemented GitHub Actions workflow for automated package publishing (`publish-packages.yaml`) - Fixed validate-versioning.yaml to check all actual `/packages` namespace
1 parent af7648b commit f139569

5 files changed

Lines changed: 367 additions & 210 deletions

File tree

.github/workflows/ci.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ jobs:
1616
uses: actions/checkout@v5
1717

1818
- name: Setup pnpm
19-
uses: pnpm/action-setup@v4
19+
uses: pnpm/action-setup@v5
2020
with:
21-
version: '10'
21+
version: "10"
2222

2323
- name: Setup Node.js
2424
uses: actions/setup-node@v5
2525
with:
26-
node-version: '22'
27-
cache: 'pnpm'
26+
node-version: "22"
27+
cache: "pnpm"
2828

2929
- name: Install dependencies
3030
run: pnpm install --frozen-lockfile
31-
31+
3232
- name: Run tests
33-
run: pnpm test
33+
run: pnpm test

.github/workflows/create-tags.yaml

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
name: Create Git Tags
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
create-tags:
10+
runs-on: ubuntu-latest
11+
12+
permissions:
13+
contents: write
14+
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v5
18+
with:
19+
fetch-depth: 0
20+
21+
- name: Setup Node.js
22+
uses: actions/setup-node@v5
23+
with:
24+
node-version: "22"
25+
26+
- name: Detect changed packages dynamically
27+
id: changes
28+
run: |
29+
echo "Detecting changed packages..."
30+
31+
# get all packages in the packages/ directory
32+
ALL_PACKAGES=$(ls packages | grep -v '^$')
33+
echo "All packages: $ALL_PACKAGES"
34+
35+
# detect which packages changed in the last commit
36+
CHANGED_PACKAGES=$(git diff --name-only HEAD~1 HEAD | grep "^packages/" | cut -d/ -f2 | sort -u)
37+
echo "Changed packages: $CHANGED_PACKAGES"
38+
39+
# use only changed packages that actually exist
40+
PACKAGES=""
41+
for pkg in $CHANGED_PACKAGES; do
42+
if echo "$ALL_PACKAGES" | grep -qw "$pkg"; then
43+
PACKAGES="$PACKAGES $pkg"
44+
fi
45+
done
46+
47+
echo "PACKAGES=$PACKAGES" >> $GITHUB_ENV
48+
echo "Packages to process: $PACKAGES"
49+
50+
- name: Create tags
51+
if: env.PACKAGES != ''
52+
run: |
53+
set -e
54+
read -r -a PACKAGE_ARRAY <<< "$PACKAGES"
55+
56+
for PACKAGE in "${PACKAGE_ARRAY[@]}"; do
57+
PACKAGE_DIR="packages/$PACKAGE"
58+
59+
echo "-----------------------------"
60+
echo "Processing $PACKAGE"
61+
62+
CURRENT_VERSION=$(node -p "require('./$PACKAGE_DIR/package.json').version")
63+
64+
BASE_VERSION=$(git show HEAD~1:$PACKAGE_DIR/package.json 2>/dev/null | node -e "
65+
const fs = require('fs');
66+
const data = fs.readFileSync(0, 'utf-8');
67+
console.log(JSON.parse(data).version);
68+
" || echo "")
69+
70+
echo "Current version: $CURRENT_VERSION"
71+
echo "Base version: $BASE_VERSION"
72+
73+
# check that the version has changed
74+
if [ "$CURRENT_VERSION" = "$BASE_VERSION" ]; then
75+
echo "::error file=$PACKAGE_DIR/package.json::Version was not updated for $PACKAGE"
76+
echo "You modified the package but did not bump the version."
77+
exit 1
78+
fi
79+
80+
TAG_NAME="packages/$PACKAGE/v$CURRENT_VERSION"
81+
82+
# create git tag if it does not exist
83+
if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then
84+
echo "⏭ Tag already exists: $TAG_NAME"
85+
else
86+
echo "🏷 Creating tag: $TAG_NAME"
87+
git tag $TAG_NAME
88+
git push origin $TAG_NAME
89+
fi
90+
done
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Publish Packages
2+
3+
# Starts after successful workflow Create Git Tags
4+
on:
5+
workflow_run:
6+
workflows: ["Create Git Tags"]
7+
types:
8+
- completed
9+
10+
jobs:
11+
publish:
12+
runs-on: ubuntu-latest
13+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
14+
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v5
18+
with:
19+
fetch-depth: 0
20+
21+
- name: Setup pnpm
22+
uses: pnpm/action-setup@v5
23+
with:
24+
version: "10"
25+
26+
- name: Setup Node.js
27+
uses: actions/setup-node@v5
28+
with:
29+
node-version: "22"
30+
31+
- name: Configure npm auth
32+
run: |
33+
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
34+
35+
- name: Detect changed packages dynamically
36+
id: changes
37+
run: |
38+
echo "Detecting changed packages..."
39+
ALL_PACKAGES=$(ls packages | grep -v '^$')
40+
CHANGED_PACKAGES=$(git diff --name-only HEAD~1 HEAD | grep "^packages/" | cut -d/ -f2 | sort -u)
41+
PACKAGES=""
42+
for pkg in $CHANGED_PACKAGES; do
43+
if echo "$ALL_PACKAGES" | grep -qw "$pkg"; then
44+
PACKAGES="$PACKAGES $pkg"
45+
fi
46+
done
47+
echo "PACKAGES=$PACKAGES" >> $GITHUB_ENV
48+
echo "Packages to publish: $PACKAGES"
49+
50+
- name: Publish packages
51+
if: env.PACKAGES != ''
52+
run: |
53+
set -e
54+
read -r -a PACKAGE_ARRAY <<< "$PACKAGES"
55+
56+
for PACKAGE in "${PACKAGE_ARRAY[@]}"; do
57+
PACKAGE_DIR="packages/$PACKAGE"
58+
echo "-----------------------------"
59+
echo "Publishing $PACKAGE"
60+
61+
pnpm publish "$PACKAGE_DIR" --access public
62+
done
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
name: Validate Versioning and Changelog
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
branches:
7+
- main
8+
9+
jobs:
10+
validate:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout code
14+
uses: actions/checkout@v5
15+
with:
16+
fetch-depth: 0
17+
18+
- name: Setup pnpm
19+
uses: pnpm/action-setup@v5
20+
with:
21+
version: "10"
22+
23+
- name: Setup Node.js
24+
uses: actions/setup-node@v5
25+
with:
26+
node-version: "22"
27+
cache: "pnpm"
28+
29+
- name: Install semver package
30+
run: pnpm add semver
31+
32+
- name: Check for changes in package
33+
id: check-changes
34+
run: |
35+
# Get actual list of packages
36+
PACKAGES=$(find packages -maxdepth 1 -mindepth 1 -type d -printf "%f\n")
37+
echo "All packages found: $PACKAGES"
38+
39+
# Check for changes in package files (excluding dist, node_modules)
40+
CHANGED_PACKAGES=""
41+
for pkg in $PACKAGES; do
42+
CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD -- packages/$pkg | grep -v -E "dist|node_modules" || true)
43+
if [ -n "$CHANGED" ]; then
44+
CHANGED_PACKAGES="$CHANGED_PACKAGES $pkg"
45+
fi
46+
done
47+
48+
if [ -z "$CHANGED_PACKAGES" ]; then
49+
echo "No packages changed"
50+
echo "has_changes=false" >> $GITHUB_OUTPUT
51+
echo "No relevant package files modified (excluding dist, node_modules)"
52+
else
53+
echo "Changed packages: $CHANGED_PACKAGES"
54+
echo "has_changes=true" >> $GITHUB_OUTPUT
55+
echo "changed_packages=$CHANGED_PACKAGES" >> $GITHUB_OUTPUT
56+
fi
57+
58+
- name: Skip if no packages changed
59+
if: steps.check-changes.outputs.has_changes == 'false'
60+
run: |
61+
echo "No package changes detected. Skipping version and changelog validation."
62+
exit 0
63+
64+
- name: Validate versioning and changelog
65+
if: steps.check-changes.outputs.has_changes == 'true'
66+
run: |
67+
set -e
68+
69+
# Validate each changed package
70+
for PACKAGE in ${{ steps.check-changes.outputs.changed_packages }}; do
71+
PACKAGE_DIR="packages/$PACKAGE"
72+
73+
echo "::group::Version Validation"
74+
echo "Starting validation for package: $PACKAGE_DIR"
75+
76+
# Get current version from package.json
77+
CURRENT_VERSION=$(node -e "console.log(require('./$PACKAGE_DIR/package.json').version)")
78+
echo "Current version in package.json: $CURRENT_VERSION"
79+
80+
# Get base version from base branch
81+
git fetch origin ${{ github.base_ref }} --depth=1 || true
82+
BASE_VERSION=$(git show origin/${{ github.base_ref }}:$PACKAGE_DIR/package.json 2>/dev/null | node -e "const fs=require('fs'); const data=fs.readFileSync(0,'utf-8'); console.log(JSON.parse(data).version)" || echo "0.0.0")
83+
echo "Base version in package.json: $BASE_VERSION"
84+
85+
# Validate version format and check if version was increased (semantic versioning)
86+
echo "Validating version format and increment..."
87+
set +e
88+
VERSION_CHECK=$(node -e "
89+
const semver = require('semver');
90+
const current = '$CURRENT_VERSION';
91+
const base = '$BASE_VERSION';
92+
93+
// Validate current version format
94+
if (!semver.valid(current)) { console.error('INVALID_CURRENT'); process.exit(1); }
95+
96+
// Validate base version format (if not 0.0.0)
97+
if (base !== '0.0.0' && !semver.valid(base)) { console.error('INVALID_BASE'); process.exit(1); }
98+
99+
// Check if versions are the same
100+
if (current === base) { console.error('SAME_VERSION'); process.exit(1); }
101+
102+
// Check if current version is greater than base version
103+
if (!semver.gt(current, base)) { console.error('VERSION_NOT_INCREASED'); process.exit(1); }
104+
105+
// Calculate suggested versions
106+
const patch = semver.inc(base, 'patch');
107+
const minor = semver.inc(base, 'minor');
108+
const major = semver.inc(base, 'major');
109+
110+
console.log('OK');
111+
console.log('PATCH=' + patch);
112+
console.log('MINOR=' + minor);
113+
console.log('MAJOR=' + major);
114+
" 2>&1)
115+
VERSION_CHECK_EXIT=$?
116+
set -e
117+
118+
echo "Full version check output:"
119+
echo "$VERSION_CHECK"
120+
121+
VERSION_CHECK_OUTPUT=$(echo "$VERSION_CHECK" | head -1 | tr -d '\r\n')
122+
echo "First line of output: '$VERSION_CHECK_OUTPUT'"
123+
echo "Exit code: $VERSION_CHECK_EXIT"
124+
125+
if [ $VERSION_CHECK_EXIT -ne 0 ]; then
126+
echo "::error file=$PACKAGE_DIR/package.json::Version validation failed"
127+
128+
if echo "$VERSION_CHECK" | grep -q "INVALID_CURRENT"; then
129+
echo "::error::Current version '$CURRENT_VERSION' is not a valid semantic version!"
130+
echo "Please use semantic versioning format (e.g., 1.0.0, 1.0.1, 1.1.0, 2.0.0)"
131+
exit 1
132+
elif echo "$VERSION_CHECK" | grep -q "INVALID_BASE"; then
133+
echo "⚠️ WARNING: Base version '$BASE_VERSION' is not a valid semantic version"
134+
echo "Assuming version bump is valid (first release or manual version)"
135+
elif echo "$VERSION_CHECK" | grep -q "SAME_VERSION"; then
136+
echo "::error file=$PACKAGE_DIR/package.json::Version was not bumped!"
137+
echo "Current version: $CURRENT_VERSION"
138+
echo "Base version: $BASE_VERSION"
139+
echo "Please bump the version in package.json"
140+
exit 1
141+
elif echo "$VERSION_CHECK" | grep -q "VERSION_NOT_INCREASED"; then
142+
PATCH_VER=$(echo "$VERSION_CHECK" | grep "PATCH=" | cut -d= -f2)
143+
MINOR_VER=$(echo "$VERSION_CHECK" | grep "MINOR=" | cut -d= -f2)
144+
MAJOR_VER=$(echo "$VERSION_CHECK" | grep "MAJOR=" | cut -d= -f2)
145+
echo "::error file=$PACKAGE_DIR/package.json::Version must be greater than base version!"
146+
echo "Current version: $CURRENT_VERSION"
147+
echo "Base version: $BASE_VERSION"
148+
echo "The new version must be higher according to semantic versioning rules."
149+
echo "Suggested versions:"
150+
echo " - Patch: $BASE_VERSION -> $PATCH_VER"
151+
echo " - Minor: $BASE_VERSION -> $MINOR_VER"
152+
echo " - Major: $BASE_VERSION -> $MAJOR_VER"
153+
exit 1
154+
else
155+
echo "::error::Unknown version validation error"
156+
echo "Exit code: $VERSION_CHECK_EXIT"
157+
echo "Full output was:"
158+
echo "$VERSION_CHECK"
159+
exit 1
160+
fi
161+
else
162+
echo "✅ Version was bumped from $BASE_VERSION to $CURRENT_VERSION (valid semver increment)"
163+
fi
164+
echo "::endgroup::"
165+
166+
echo "::group::Changelog Validation"
167+
# Check if changelog has entry for this version
168+
CHANGELOG_FILE="$PACKAGE_DIR/CHANGELOG.md"
169+
echo "Checking changelog file: $CHANGELOG_FILE"
170+
echo "Looking for version: $CURRENT_VERSION"
171+
172+
if ! grep -q "^## \\[$CURRENT_VERSION\\]" "$CHANGELOG_FILE"; then
173+
echo "::error file=$CHANGELOG_FILE::Changelog entry for version $CURRENT_VERSION not found!"
174+
echo "Please add a changelog entry in $CHANGELOG_FILE with the format:"
175+
echo "## [$CURRENT_VERSION] - YYYY-MM-DD"
176+
exit 1
177+
fi
178+
echo "✅ Changelog entry found for version $CURRENT_VERSION"
179+
180+
# Verify changelog entry is not empty (has content after the version header)
181+
CHANGELOG_LINE=$(grep -n "^## \\[$CURRENT_VERSION\\]" "$CHANGELOG_FILE" | cut -d: -f1)
182+
echo "Changelog entry found at line: $CHANGELOG_LINE"
183+
184+
# Check if there's content after the version header (skip empty entries)
185+
NEXT_VERSION_LINE=$(tail -n +$((CHANGELOG_LINE + 1)) "$CHANGELOG_FILE" | grep -n "^## \\[" | head -1 | cut -d: -f1)
186+
187+
if [ -z "$NEXT_VERSION_LINE" ]; then
188+
# This is the latest version, check if there's any content after the header
189+
LINES_AFTER_HEADER=$(tail -n +$((CHANGELOG_LINE + 1)) "$CHANGELOG_FILE" | grep -v '^$' | head -1)
190+
if [ -z "$LINES_AFTER_HEADER" ]; then
191+
echo "::error file=$CHANGELOG_FILE::Changelog entry for version $CURRENT_VERSION is empty!"
192+
echo "Please add at least one change entry (Added, Changed, Fixed, etc.)"
193+
exit 1
194+
fi
195+
else
196+
# Check if there's content between this version and the next
197+
LINES_BETWEEN=$(sed -n "$((CHANGELOG_LINE + 1)),$((CHANGELOG_LINE + NEXT_VERSION_LINE - 1))p" "$CHANGELOG_FILE" | grep -v '^$' | head -1)
198+
if [ -z "$LINES_BETWEEN" ]; then
199+
echo "::error file=$CHANGELOG_FILE::Changelog entry for version $CURRENT_VERSION is empty!"
200+
echo "Please add at least one change entry (Added, Changed, Fixed, etc.)"
201+
exit 1
202+
fi
203+
fi
204+
205+
echo "✅ Changelog entry for version $CURRENT_VERSION has content"
206+
echo "::endgroup::"
207+
208+
echo "::notice::All validations passed successfully for package $PACKAGE!"
209+
done

0 commit comments

Comments
 (0)