+
Skip to content

nf-core/pacsomatic: comprehensive somatic analysis using Pacbio HiFi read data #549

nf-core/pacsomatic: comprehensive somatic analysis using Pacbio HiFi read data

nf-core/pacsomatic: comprehensive somatic analysis using Pacbio HiFi read data #549

Workflow file for this run

name: RFC approval automation
on:
issues:
types: [opened, closed]
issue_comment:
types: [created, edited]
jobs:
rfc_approval:
# Only run for RFC proposal issues
if: startsWith(github.event.issue.title, 'New RFC')
runs-on: ubuntu-latest
steps:
- name: Handle RFC approval logic
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
with:
github-token: ${{ secrets.nf_core_bot_auth_token }}
script: |
const issueNumber = context.issue.number;
const org = context.repo.owner;
const repo = context.repo.repo;
// Ignore comments on closed issues
if (context.eventName === 'issue_comment' && context.payload.issue.state === 'closed') {
console.log('Comment event on closed issue, ignoring.');
return;
}
// ---------------------------------------------
// Fetch members of the core team
// ---------------------------------------------
async function getTeamMembers() {
try {
const res = await github.request('GET /orgs/{org}/teams/{team_slug}/members', {
org,
team_slug: 'core',
per_page: 100
});
console.log(`Fetched ${res.data.length} core team members.`);
return res.data.map(m => m.login);
} catch (err) {
console.error('Failed to fetch core team members:', err);
throw err;
}
}
const teamMembers = await getTeamMembers();
console.log('Core team members:', teamMembers);
const quorum = Math.ceil(teamMembers.length / 2);
console.log(`Quorum set to ${quorum}.`);
// Helper for list formatting and complete status body
function formatUserList(users) {
return users.length ? users.map(u => `[@${u}](https://github.com/${u})`).join(', ') : '-';
}
function generateStatusBody(status, approvalsSet, rejectionsSet, awaitingArr) {
const approvers = [...approvalsSet];
const rejecters = [...rejectionsSet];
const awaiting = awaitingArr;
let body = `## RFC approval status: ${status}\n\nRFC has approvals from ${approvalsSet.size}/${quorum} required @core-team quorum.\n\n`;
if (approvers.length > 0 || rejecters.length > 0 || awaiting.length > 0) {
body += `|Review Status|Core Team members|\n|--|--|\n`;
if (approvers.length > 0) {
body += `| ✅ Approved | ${formatUserList(approvers)} |\n`;
}
if (rejecters.length > 0) {
body += `| ❌ Rejected | ${formatUserList(rejecters)} |\n`;
}
if (awaiting.length > 0) {
body += `| 🕐 Pending | ${formatUserList(awaiting)} |\n`;
}
}
return body;
}
// -------------------------------------------------
// If this workflow was triggered by issue creation
// -------------------------------------------------
if (context.eventName === 'issues' && context.payload.action === 'opened') {
const body = generateStatusBody('🕐 Pending', new Set(), new Set(), teamMembers);
console.log('Creating initial comment for review status');
await github.rest.issues.createComment({
owner: org,
repo,
issue_number: issueNumber,
body
});
return; // Nothing more to do for newly-opened issues
}
// ---------------------------------------------------------------------
// Collect comments and compute votes (shared for comment & closed events)
// ---------------------------------------------------------------------
// Collect all comments on the issue
const comments = await github.paginate(github.rest.issues.listComments, {
owner: org,
repo,
issue_number: issueNumber,
per_page: 100
});
const approvals = new Set();
const rejections = new Set();
for (const comment of comments) {
const commenter = comment.user.login;
if (!teamMembers.includes(commenter)) continue; // Only core team members count
// Count approvals / rejections based on line starting with /approve or /reject
const lines = comment.body.split(/\r?\n/);
for (const rawLine of lines) {
const line = rawLine.trim();
if (/^\/approve\b/i.test(line)) {
approvals.add(commenter);
} else if (/^\/reject\b/i.test(line)) {
rejections.add(commenter);
}
}
}
console.log(`Approvals (${approvals.size}):`, [...approvals]);
console.log(`Rejections (${rejections.size}):`, [...rejections]);
const awaiting = teamMembers.filter(u => !approvals.has(u) && !rejections.has(u));
// Determine status
let status = '🕐 Pending';
if (context.eventName === 'issues' && context.payload.action === 'closed' && context.payload.issue.state_reason === 'not_planned' && rejections.size > 0) {
status = '❌ Rejected';
} else if (approvals.size >= quorum && rejections.size === 0) {
status = '✅ Approved';
}
const statusBody = generateStatusBody(status, approvals, rejections, awaiting);
console.log('New status body to post:\n', statusBody);
// Try to locate the existing status comment (starts with our header)
let statusComment = comments.find(c => c.body.startsWith('## RFC approval status:'));
if (statusComment) {
if (statusComment.body.trim() === statusBody.trim()) {
console.log('Status comment already up to date - no update required.');
} else {
console.log('Updating existing status comment.');
await github.rest.issues.updateComment({
owner: org,
repo,
comment_id: statusComment.id,
body: statusBody
});
}
} else {
// Fallback: create a new status comment if missing (shouldn't normally happen)
await github.rest.issues.createComment({
owner: org,
repo,
issue_number: issueNumber,
body: statusBody
});
}
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载