Skip to content

Commit 12d9da6

Browse files
authored
Merge pull request #873 from Romanitho/feature/new-releases
New Full GitFlow process
2 parents fb80f01 + 3cb7275 commit 12d9da6

11 files changed

+704
-397
lines changed

.github/workflows/Check-pull-request-source-branch.yml

Lines changed: 0 additions & 20 deletions
This file was deleted.
File renamed without changes.
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# This workflow enforces GitFlow branch patterns by:
2+
# - Ensuring only hotfix/* branches can target main (release branches are handled automatically)
3+
# - Adding labels and comments to non-compliant PRs
4+
# - Automatically cleaning up when PRs are updated to comply
5+
6+
name: GitFlow | Check PR Branch Pattern
7+
8+
on:
9+
pull_request_target:
10+
types:
11+
- opened
12+
- reopened
13+
- synchronize
14+
- edited
15+
16+
# Add explicit permissions
17+
permissions:
18+
pull-requests: write
19+
issues: write
20+
contents: read
21+
22+
env:
23+
MAIN_BRANCH: "main"
24+
DEVELOP_BRANCH: "develop"
25+
VALID_PATTERNS: "^(release|hotfix)/"
26+
LABEL_NAME: "invalid-branch"
27+
ERROR_MESSAGE_IDENTIFIER: "Invalid Branch Pattern for main Branch"
28+
29+
jobs:
30+
check_branch:
31+
runs-on: ubuntu-latest
32+
steps:
33+
# Step 1: Use GitHub Script for branch pattern validation (safer than shell)
34+
- name: Check branch pattern
35+
id: branch_check
36+
uses: actions/github-script@v7
37+
with:
38+
github-token: ${{ secrets.GITHUB_TOKEN }}
39+
result-encoding: string
40+
script: |
41+
// Get branch information from context
42+
const headRef = context.payload.pull_request.head.ref;
43+
const baseRef = context.payload.pull_request.base.ref;
44+
const mainBranch = process.env.MAIN_BRANCH;
45+
const validPattern = new RegExp(process.env.VALID_PATTERNS);
46+
47+
console.log(`Checking PR from '${headRef}' to '${baseRef}'`);
48+
49+
// Perform the validation in JavaScript instead of shell
50+
if (baseRef === mainBranch) {
51+
if (!validPattern.test(headRef)) {
52+
console.log(`::error::❌ Invalid branch! PRs to main must come from hotfix/* or release/* branches. Please target the develop branch instead.`);
53+
return 'invalid';
54+
} else {
55+
console.log(`::notice::✅ Branch pattern is valid: '${headRef}' → '${mainBranch}'`);
56+
return 'valid';
57+
}
58+
} else {
59+
console.log(`::notice::✅ Not targeting main branch, no pattern restrictions apply.`);
60+
return 'not-main';
61+
}
62+
63+
# Step 2: If the branch pattern is invalid, add a label and comment to the PR
64+
- name: Handle invalid branch (label + comment)
65+
if: steps.branch_check.outputs.result == 'invalid'
66+
uses: actions/github-script@v7
67+
with:
68+
github-token: ${{ secrets.GITHUB_TOKEN }}
69+
script: |
70+
const { owner, repo } = context.repo;
71+
const issue_number = context.payload.pull_request.number;
72+
const label = process.env.LABEL_NAME;
73+
const messageIdentifier = process.env.ERROR_MESSAGE_IDENTIFIER;
74+
75+
// Escape special characters in the message identifier for safer comparisons
76+
const escapedMessageIdentifier = messageIdentifier.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
77+
78+
const message = `❌ **${messageIdentifier}**
79+
80+
According to our GitFlow workflow:
81+
- Pull requests to the \`main\` branch are only allowed from \`hotfix/*\` branches
82+
- Regular feature development and other changes should target the \`develop\` branch
83+
84+
📝 **Action required:** Please update your PR to target the \`develop\` branch instead.
85+
86+
For more details about our contribution workflow, please refer to our [CONTRIBUTING.md](https://github.com/${owner}/${repo}/blob/main/CONTRIBUTING.md) guide.`;
87+
88+
// First step: Always apply the label
89+
console.log("Adding invalid-branch label to PR");
90+
try {
91+
await github.rest.issues.addLabels({
92+
owner,
93+
repo,
94+
issue_number,
95+
labels: [label]
96+
});
97+
} catch (e) {
98+
// In case label already exists or other error
99+
console.log(`Note: Could not add label: ${e.message}`);
100+
}
101+
102+
// Second step: Add comment if it doesn't exist
103+
const { data: comments } = await github.rest.issues.listComments({
104+
owner,
105+
repo,
106+
issue_number
107+
});
108+
109+
// Use regex test instead of includes for safer comparison
110+
const commentExists = comments.some(comment =>
111+
comment.body && new RegExp(escapedMessageIdentifier).test(comment.body)
112+
);
113+
114+
if (!commentExists) {
115+
console.log("Adding comment to PR");
116+
await github.rest.issues.createComment({
117+
owner,
118+
repo,
119+
issue_number,
120+
body: message
121+
});
122+
} else {
123+
console.log("Comment already exists, skipping");
124+
}
125+
126+
# Step 3: If the branch pattern is corrected, remove label and comment
127+
- name: Clean up if branch is corrected
128+
if: steps.branch_check.outputs.result == 'valid' || steps.branch_check.outputs.result == 'not-main'
129+
uses: actions/github-script@v7
130+
with:
131+
github-token: ${{ secrets.GITHUB_TOKEN }}
132+
script: |
133+
const { owner, repo } = context.repo;
134+
const issue_number = context.payload.pull_request.number;
135+
const label = process.env.LABEL_NAME;
136+
const messageIdentifier = process.env.ERROR_MESSAGE_IDENTIFIER;
137+
138+
// Escape special characters in the message identifier
139+
const escapedMessageIdentifier = messageIdentifier.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
140+
const messageRegex = new RegExp(escapedMessageIdentifier);
141+
142+
try {
143+
// Check if the label is present and remove it
144+
const { data: labels } = await github.rest.issues.listLabelsOnIssue({
145+
owner,
146+
repo,
147+
issue_number
148+
});
149+
150+
if (labels.some(l => l.name === label)) {
151+
console.log("Removing invalid-branch label from PR");
152+
await github.rest.issues.removeLabel({
153+
owner,
154+
repo,
155+
issue_number,
156+
name: label
157+
});
158+
} else {
159+
console.log("No label to remove");
160+
}
161+
162+
// Check existing comments and remove any invalid branch comments
163+
const { data: comments } = await github.rest.issues.listComments({
164+
owner,
165+
repo,
166+
issue_number
167+
});
168+
169+
// Find and delete any invalid branch comment
170+
for (const comment of comments) {
171+
// Use regex test instead of includes for safer comparison
172+
if (comment.body && messageRegex.test(comment.body)) {
173+
console.log(`Deleting comment ID: ${comment.id}`);
174+
await github.rest.issues.deleteComment({
175+
owner,
176+
repo,
177+
comment_id: comment.id
178+
});
179+
}
180+
}
181+
} catch (error) {
182+
console.log(`Error in cleanup: ${error}`);
183+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# This workflow implements the GitFlow release creation process:
2+
# - Creates a new release branch from develop
3+
# - Increments version according to semantic versioning
4+
# - Creates a pull request targeting main branch
5+
# - This enables final testing before the actual release is created
6+
7+
name: GitFlow | Create Release Branch and PR
8+
9+
on:
10+
# Manual trigger with version selection
11+
workflow_dispatch:
12+
inputs:
13+
version:
14+
type: choice
15+
default: "Minor"
16+
description: Select next release type
17+
options:
18+
- Patch # For bug fixes (x.y.Z)
19+
- Minor # For new features (x.Y.z)
20+
- Major # For breaking changes (X.y.z)
21+
required: true
22+
23+
jobs:
24+
create-release:
25+
runs-on: ubuntu-latest
26+
steps:
27+
# Step 1: Calculate the next version number based on the selected increment type
28+
- name: Auto Increment Semver Action
29+
uses: MCKanpolat/auto-semver-action@5003b8d37f4b03d95f15303ea10242cbf7c13141 # 2
30+
id: versioning
31+
with:
32+
github_token: ${{ secrets.GITHUB_TOKEN }}
33+
incrementPerCommit: false
34+
releaseType: ${{ github.event.inputs.version }}
35+
36+
# Step 2: Checkout the develop branch which contains all approved features
37+
- name: Checkout code
38+
uses: actions/checkout@v4.2.2
39+
with:
40+
ref: develop # Always create releases from develop branch
41+
fetch-depth: 0
42+
token: ${{ secrets.GITHUB_TOKEN }}
43+
44+
# Step 3: Create a new release branch following GitFlow naming convention
45+
- name: Create release branch
46+
run: |
47+
# Configure Git with GitHub Actions identity
48+
git config --global user.name "GitHub Actions"
49+
git config --global user.email "actions@github.com"
50+
51+
# Create a new branch with the pattern release/X.Y.Z
52+
git checkout -b release/${{ steps.versioning.outputs.version }}
53+
54+
# Push the branch to remote repository
55+
git push origin release/${{ steps.versioning.outputs.version }}
56+
echo "Release Branch: release/${{ steps.versioning.outputs.version }}"
57+
58+
# Step 4: Create a pull request from release branch to main
59+
- name: Create Pull Request with GitHub CLI
60+
env:
61+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62+
run: |
63+
# Use GitHub CLI to create a properly formatted Pull Request
64+
gh pr create \
65+
--base main \
66+
--head release/${{ steps.versioning.outputs.version }} \
67+
--title "Release ${{ steps.versioning.outputs.version }}" \
68+
--label "release" \
69+
--body "# Release ${{ steps.versioning.outputs.version }}
70+
71+
This PR is automatically created to perform the final tests and validations before the release is created."

0 commit comments

Comments
 (0)