Skip to content

Commit 8123fdd

Browse files
committed
chore(ci): add weekly bdk-ffi release watch issue automation
1 parent b968453 commit 8123fdd

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
name: bdk-ffi Release Watch
2+
3+
on:
4+
schedule:
5+
# Run every Monday at 09:30 UTC.
6+
- cron: "30 9 * * 1"
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
issues: write
12+
13+
concurrency:
14+
group: bdk-ffi-release-watch
15+
cancel-in-progress: false
16+
17+
jobs:
18+
open-update-issue:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v4
23+
24+
- name: Check latest bdk-ffi release and open issue
25+
uses: actions/github-script@v7
26+
env:
27+
UPSTREAM_REPO: bitcoindevkit/bdk-ffi
28+
DEPENDENCY_FILE: native/Cargo.toml
29+
with:
30+
script: |
31+
const fs = require('node:fs');
32+
33+
const dependencyFile = process.env.DEPENDENCY_FILE;
34+
const upstreamRepo = process.env.UPSTREAM_REPO;
35+
const [upstreamOwner, upstreamName] = upstreamRepo.split('/');
36+
37+
if (!upstreamOwner || !upstreamName) {
38+
core.setFailed(`Invalid UPSTREAM_REPO: ${upstreamRepo}`);
39+
return;
40+
}
41+
42+
const cargoToml = fs.readFileSync(dependencyFile, 'utf8');
43+
const pinMatch = cargoToml.match(/^\s*bdk-ffi\s*=\s*\{[^}]*\btag\s*=\s*"([^"]+)"/ms);
44+
if (!pinMatch) {
45+
core.setFailed(`Could not find a tag-pinned bdk-ffi dependency in ${dependencyFile}`);
46+
return;
47+
}
48+
49+
const currentTag = pinMatch[1];
50+
51+
function parseSemver(tag) {
52+
const match = String(tag).trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+.*)?$/);
53+
if (!match) return null;
54+
return {
55+
major: Number(match[1]),
56+
minor: Number(match[2]),
57+
patch: Number(match[3]),
58+
prerelease: match[4] ?? null,
59+
};
60+
}
61+
62+
function compareSemver(aTag, bTag) {
63+
const a = parseSemver(aTag);
64+
const b = parseSemver(bTag);
65+
if (!a || !b) return null;
66+
67+
for (const key of ['major', 'minor', 'patch']) {
68+
if (a[key] > b[key]) return 1;
69+
if (a[key] < b[key]) return -1;
70+
}
71+
72+
if (a.prerelease && !b.prerelease) return -1;
73+
if (!a.prerelease && b.prerelease) return 1;
74+
if (!a.prerelease && !b.prerelease) return 0;
75+
76+
if (a.prerelease > b.prerelease) return 1;
77+
if (a.prerelease < b.prerelease) return -1;
78+
return 0;
79+
}
80+
81+
let latestRelease;
82+
try {
83+
latestRelease = await github.rest.repos.getLatestRelease({
84+
owner: upstreamOwner,
85+
repo: upstreamName,
86+
});
87+
} catch (error) {
88+
if (error.status === 404) {
89+
core.info(`No stable GitHub release found for ${upstreamRepo}; skipping.`);
90+
return;
91+
}
92+
throw error;
93+
}
94+
95+
const latestTag = latestRelease.data.tag_name;
96+
const cmp = compareSemver(latestTag, currentTag);
97+
98+
core.info(`Current pinned bdk-ffi tag: ${currentTag}`);
99+
core.info(`Latest upstream bdk-ffi release: ${latestTag}`);
100+
101+
if (currentTag === latestTag) {
102+
core.info('Pinned tag is already at the latest release.');
103+
return;
104+
}
105+
106+
if (cmp !== null && cmp <= 0) {
107+
core.info('Pinned tag is not older than the latest stable release; skipping.');
108+
return;
109+
}
110+
111+
const issueTitle = `chore: update bdk-ffi to ${latestTag}`;
112+
const searchQuery = `repo:${context.repo.owner}/${context.repo.repo} is:open "${issueTitle}"`;
113+
const existing = await github.rest.search.issuesAndPullRequests({
114+
q: searchQuery,
115+
per_page: 10,
116+
});
117+
118+
const existingMatch = existing.data.items.find((item) => item.title === issueTitle);
119+
if (existingMatch) {
120+
core.info(`Open issue or PR already exists: #${existingMatch.number}`);
121+
return;
122+
}
123+
124+
const issueBody = [
125+
'<!-- bdk-ffi-release-watch -->',
126+
'',
127+
'A new upstream `bdk-ffi` release is available.',
128+
'',
129+
`- Current pinned tag in \`${dependencyFile}\`: \`${currentTag}\``,
130+
`- Latest upstream release: \`${latestTag}\``,
131+
`- Release notes: ${latestRelease.data.html_url}`,
132+
'',
133+
'Suggested follow-up:',
134+
'1. Update the `bdk-ffi` tag in `native/Cargo.toml`.',
135+
'2. Regenerate bindings with `./scripts/generate_bindings.sh`.',
136+
'3. Run `dart test` (and CI) before merging.',
137+
].join('\n');
138+
139+
const created = await github.rest.issues.create({
140+
owner: context.repo.owner,
141+
repo: context.repo.repo,
142+
title: issueTitle,
143+
body: issueBody,
144+
});
145+
146+
core.info(`Created issue #${created.data.number}: ${created.data.html_url}`);

0 commit comments

Comments
 (0)