Skip to content

Commit 4ed20e8

Browse files
authored
Merge pull request #61 from exploreriii/merge-conflict-test
feat: merge conflict test
2 parents 0caa232 + 1b33f92 commit 4ed20e8

File tree

4 files changed

+151
-71
lines changed

4 files changed

+151
-71
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// .github/scripts/merge_conflict_helpers.js
2+
3+
const BOT_SIGNATURE = '[MergeConflictBotSignature-v1]';
4+
5+
module.exports = async ({ github, context, core }) => {
6+
const { owner, repo } = context.repo;
7+
8+
// fetch PR with retry logic for unknown state
9+
async function getPrWithRetry(prNumber) {
10+
for (let i = 0; i < 10; i++) {
11+
const { data: pr } = await github.rest.pulls.get({
12+
owner, repo, pull_number: prNumber
13+
});
14+
15+
if (pr.mergeable_state !== 'unknown') return pr;
16+
17+
console.log(`PR #${prNumber} state is 'unknown'. Retrying (${i+1}/10)...`);
18+
await new Promise(r => setTimeout(r, 2000));
19+
}
20+
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber });
21+
if (pr.mergeable_state === 'unknown') {
22+
console.warn(`PR #${prNumber} state still 'unknown' after 10 retries.`);
23+
}
24+
return pr;
25+
}
26+
27+
// post comment
28+
async function notifyUser(prNumber) {
29+
const { data: comments } = await github.rest.issues.listComments({
30+
owner, repo, issue_number: prNumber,
31+
});
32+
33+
if (comments.some(c => c.body.includes(BOT_SIGNATURE))) {
34+
console.log(`Already commented on PR #${prNumber}. Skipping.`);
35+
return;
36+
}
37+
38+
const body = `Hi, this is MergeConflictBot.\nYour pull request cannot be merged because it contains **merge conflicts**.\n\nPlease resolve these conflicts locally and push the changes.\n\nTo assist you, please read:\n- [Resolving Merge Conflicts](https://github.com/${owner}/${repo}/blob/main/docs/sdk_developers/merge_conflicts.md)\n- [Rebasing Guide](https://github.com/${owner}/${repo}/blob/main/docs/sdk_developers/rebasing.md)\n\nThank you for contributing!\nFrom the Hiero Python SDK Team\n\n${BOT_SIGNATURE}`;
39+
40+
await github.rest.issues.createComment({
41+
owner, repo, issue_number: prNumber, body: body
42+
});
43+
}
44+
45+
//set commit status
46+
async function setCommitStatus(sha, state, description) {
47+
await github.rest.repos.createCommitStatus({
48+
owner, repo, sha: sha, state: state,
49+
context: 'Merge Conflict Detector',
50+
description: description,
51+
target_url: `${process.env.GITHUB_SERVER_URL}/${owner}/${repo}/actions/runs/${context.runId}`
52+
});
53+
}
54+
55+
//main
56+
let prsToCheck = [];
57+
58+
//push to main
59+
if (context.eventName === 'push') {
60+
console.log("Triggered by Push to Main. Fetching all open PRs...");
61+
const openPrs = await github.paginate(github.rest.pulls.list, {
62+
owner, repo, state: 'open', base: 'main', per_page: 100
63+
});
64+
prsToCheck = openPrs.map(pr => pr.number);
65+
}
66+
//PR update
67+
else {
68+
console.log("Triggered by PR update.");
69+
prsToCheck.push(context.payload.pull_request.number);
70+
}
71+
72+
for (const prNumber of prsToCheck) {
73+
try {
74+
console.log(`Checking PR #${prNumber}...`);
75+
const pr = await getPrWithRetry(prNumber);
76+
77+
if (pr.mergeable_state === 'unknown') {
78+
console.log(`PR #${prNumber} state is still 'unknown'. Skipping conflict check.`);
79+
continue;
80+
}
81+
82+
if (pr.mergeable_state === 'dirty') {
83+
console.log(`Conflict detected in PR #${prNumber}`);
84+
await notifyUser(prNumber);
85+
86+
// Push events: set commit status on PR head SHA
87+
// PR events: fail the workflow run (creates a check on the PR)
88+
if (context.eventName === 'push') {
89+
await setCommitStatus(pr.head.sha, 'failure', 'Conflicts detected with main');
90+
} else {
91+
core.setFailed(`Merge conflicts detected in PR #${prNumber}.`);
92+
}
93+
} else {
94+
console.log(`PR #${prNumber} is clean.`);
95+
// For push events, set success status; PR events rely on workflow run success
96+
if (context.eventName === 'push') {
97+
await setCommitStatus(pr.head.sha, 'success', 'No conflicts detected');
98+
}
99+
}
100+
} catch (error) {
101+
console.error(`Error checking PR #${prNumber}: ${error.message}`);
102+
if (context.eventName !== 'push') {
103+
throw error; // Re-throw for PR events to fail the workflow
104+
}
105+
// For push events, log and continue to check remaining PRs
106+
}
107+
}
108+
};

.github/workflows/bot-merge-conflict.yml

Lines changed: 0 additions & 71 deletions
This file was deleted.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Merge Conflict Bot
2+
3+
on:
4+
pull_request_target:
5+
types: [opened, synchronize, reopened]
6+
push:
7+
branches:
8+
- main
9+
10+
permissions:
11+
contents: read
12+
pull-requests: write
13+
issues: write
14+
statuses: write
15+
16+
concurrency:
17+
group: "check-conflicts-${{ github.event.pull_request.number || github.sha }}"
18+
cancel-in-progress: true
19+
20+
jobs:
21+
check-conflicts:
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Checkout code
25+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
26+
with:
27+
ref: ${{ github.event.repository.default_branch }}
28+
29+
- name: Harden the runner
30+
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
31+
with:
32+
egress-policy: audit
33+
34+
- name: Check for merge conflicts
35+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
36+
with:
37+
script: |
38+
const path = require('path')
39+
const scriptPath = path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/merge_conflict_helpers.js')
40+
console.log(`Loading script from: ${scriptPath}`)
41+
const script = require(scriptPath)
42+
await script({github, context, core})

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
8585
- Move `account_allowance_delete_transaction_hbar.py` from `examples/` to `examples/account/` for better organization (#1003)
8686
- Improved consistency of transaction examples (#1120)
8787
- Refactored `account_create_transaction_with_fallback_alias.py` by splitting the monolithic `create_account_with_fallback_alias` function into modular functions: `generate_fallback_key`, `fetch_account_info`, and `print_account_summary`. The existing `setup_client()` function was reused for improved readability and structure (#1018)
88+
- not creating a merge conflict
8889
- Allow `PublicKey` for batch_key in `Transaction`, enabling both `PrivateKey` and `PublicKey` for batched transactions
8990
- Allow `PublicKey` for `TokenUpdateKeys` in `TokenUpdateTransaction`, enabling non-custodial workflows where operators can build transactions using only public keys (#934).
9091
- Bump protobuf toml to protobuf==6.33.2

0 commit comments

Comments
 (0)