Add dependency update and automerge workflows

This commit is contained in:
xixu-me committed 2026-03-11 14:49:38 +08:00
1 parent aa47344a9d
commit 20f1abc2e8
4 files changed
+1047

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "09:00"
timezone: "Asia/Shanghai"
open-pull-requests-limit: 5
labels:
- "dependencies"
commit-message:
prefix: "deps"
rebase-strategy: "auto"
groups:
production-dependencies:
dependency-type: "direct:production"
patterns:
- "*"
development-dependencies:
dependency-type: "direct:development"
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "09:15"
timezone: "Asia/Shanghai"
open-pull-requests-limit: 3
labels:
- "dependencies"
- "github-actions"
commit-message:
prefix: "deps"
rebase-strategy: "auto"
@@ -0,0 +1,116 @@
name: Auto-merge dependency updates
on:
workflow_run:
workflows:
- CI
types:
- completed
permissions:
contents: write
pull-requests: write
jobs:
automerge:
if: >
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Auto-merge safe Dependabot PRs
uses: actions/github-script@v7
with:
script: |
const pullRequests = context.payload.workflow_run.pull_requests || [];
if (pullRequests.length === 0) {
core.info('No pull requests were attached to this workflow run.');
return;
}
const parseVersion = (raw) => {
const cleaned = raw.trim().replace(/^v/i, '');
if (!/^\d+(\.\d+){0,3}$/.test(cleaned)) {
return null;
}
const parts = cleaned.split('.').map((value) => Number.parseInt(value, 10));
while (parts.length < 3) {
parts.push(0);
}
return parts.slice(0, 3);
};
const getUpdateType = (title) => {
const match = title.match(/\sfrom\s+([^\s]+)\s+to\s+([^\s]+)/i);
if (!match) {
return null;
}
const from = parseVersion(match[1]);
const to = parseVersion(match[2]);
if (!from || !to) {
return null;
}
if (to[0] !== from[0]) {
return 'major';
}
if (to[1] !== from[1]) {
return 'minor';
}
if (to[2] !== from[2]) {
return 'patch';
}
return 'none';
};
for (const prRef of pullRequests) {
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prRef.number,
});
if (pr.user?.login !== 'dependabot[bot]') {
core.info(`#${pr.number} skipped: author is ${pr.user?.login}.`);
continue;
}
if (pr.draft) {
core.info(`#${pr.number} skipped: PR is a draft.`);
continue;
}
const labels = pr.labels.map((label) => label.name);
if (labels.includes('automerge:disabled')) {
core.info(`#${pr.number} skipped: automerge disabled by label.`);
continue;
}
const updateType = getUpdateType(pr.title);
if (!updateType || !['minor', 'patch'].includes(updateType)) {
core.info(`#${pr.number} skipped: unsupported update type (${updateType ?? 'unknown'}).`);
continue;
}
const reviewDecision = pr.requested_reviewers?.length || pr.requested_teams?.length;
if (reviewDecision) {
core.info(`#${pr.number} skipped: PR is still waiting for requested review.`);
continue;
}
try {
await github.rest.pulls.merge({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
merge_method: 'squash',
});
core.info(`#${pr.number} merged successfully.`);
} catch (error) {
core.warning(`#${pr.number} was not merged automatically: ${error.message}`);
}
}
+37
View File
@@ -0,0 +1,37 @@
name: CI
on:
push:
branches:
- main
pull_request:
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Run tests
run: npm run test:ci