Skip to content

Commit db3677f

Browse files
authored
Update sync-issue-to-csv.yml
1 parent 2c28d79 commit db3677f

1 file changed

Lines changed: 89 additions & 116 deletions

File tree

Lines changed: 89 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,89 @@
1-
name: Sync Issue to CSV
2-
3-
on:
4-
issues:
5-
types: [opened, edited]
6-
issue_comment:
7-
types: [created]
8-
schedule:
9-
- cron: "*/10 * * * *"
10-
workflow_dispatch:
11-
12-
permissions:
13-
issues: read
14-
contents: read
15-
16-
jobs:
17-
sync:
18-
runs-on: ubuntu-latest
19-
20-
steps:
21-
- name: Checkout repo
22-
uses: actions/checkout@v4
23-
24-
- name: Sync Issue to CSV
25-
uses: actions/github-script@v7
26-
with:
27-
script: |
28-
const fs = require('fs');
29-
const owner = context.repo.owner;
30-
const repo = context.repo.repo;
31-
32-
let csv = fs.existsSync('tasks.csv') ? fs.readFileSync('tasks.csv', 'utf8') : "Project,Repo,Issue,Owner,PR,Status,Source\n";
33-
let lines = csv.split('\n').filter(line => line.trim() !== '');
34-
const header = lines[0];
35-
let rows = lines.slice(1);
36-
37-
let issues = [];
38-
39-
if (context.payload.issue) {
40-
issues = [context.payload.issue];
41-
} else {
42-
// 手动触发 / schedule → 拉全部 issue
43-
const { data } = await github.rest.issues.listForRepo({
44-
owner,
45-
repo,
46-
state: 'open',
47-
per_page: 100
48-
});
49-
issues = data;
50-
}
51-
52-
for (const issue of issues) {
53-
const title = issue.title;
54-
const body = issue.body || "";
55-
const issueNumber = issue.number;
56-
const issueUrl = `https://github.com/${owner}/${repo}/issues/${issueNumber}`;
57-
58-
const repoMatch = body.match(/https:\/\/github\.com\/[^\s]+/);
59-
const repoUrl = repoMatch ? repoMatch[0] : "";
60-
if (!repoUrl) continue;
61-
62-
const ownerMatch = body.match(/Owner:\s*@([^\s]+)/);
63-
const taskOwner = ownerMatch ? ownerMatch[1] : "";
64-
65-
const prMatch = body.match(/PR:\s*(https:\/\/[^\s]+)/);
66-
const prUrl = prMatch ? prMatch[1] : "";
67-
68-
let status = "";
69-
if (prUrl) {
70-
const mergedMatch = body.match(/Status:\s*(merged|closed|open)/i);
71-
status = mergedMatch ? mergedMatch[1].toLowerCase() : "open";
72-
}
73-
74-
let found = false;
75-
rows = rows.map(row => {
76-
let parts = row.split(',');
77-
while (parts.length < 7) parts.push('');
78-
if (parts[2] === issueUrl || (parts[2] === "" && parts[1] === repoUrl)) {
79-
parts[2] = issueUrl;
80-
parts[3] = taskOwner;
81-
parts[4] = prUrl;
82-
parts[5] = status;
83-
found = true;
84-
}
85-
return parts.join(',');
86-
});
87-
88-
if (!found) {
89-
const project = title.replace("[Task]", "").trim();
90-
const newRow = [
91-
project,
92-
repoUrl,
93-
issueUrl,
94-
taskOwner,
95-
prUrl,
96-
status,
97-
"community"
98-
].join(',');
99-
rows.push(newRow);
100-
}
101-
}
102-
103-
const newCsv = [header, ...rows].join('\n');
104-
fs.writeFileSync('tasks.csv', newCsv);
105-
106-
- name: Show CSV
107-
run: cat tasks.csv
108-
109-
- name: Upload CSV artifact
110-
uses: actions/upload-artifact@v4
111-
with:
112-
name: tasks-csv
113-
path: tasks.csv
114-
115-
- name: Skip push
116-
run: echo "No write permission, CSV changes are local to runner"
1+
- name: Sync Issue to CSV
2+
uses: actions/github-script@v7
3+
with:
4+
script: |
5+
const fs = require('fs');
6+
const owner = context.repo.owner;
7+
const repo = context.repo.repo;
8+
9+
let csv = fs.existsSync('tasks.csv')
10+
? fs.readFileSync('tasks.csv', 'utf8')
11+
: "Project,Repo,Issue,Owner,PR,Status,Source\n";
12+
13+
let lines = csv.split('\n').filter(line => line.trim() !== '');
14+
const header = lines[0];
15+
let rows = lines.slice(1);
16+
17+
let issues = [];
18+
if (context.payload.issue) {
19+
issues = [context.payload.issue];
20+
} else {
21+
console.log("Skip full sync");
22+
return;
23+
}
24+
25+
for (const issue of issues) {
26+
const title = issue.title;
27+
const body = issue.body || "";
28+
const issueNumber = issue.number;
29+
const issueUrl = `https://github.com/${owner}/${repo}/issues/${issueNumber}`;
30+
31+
// ===== Repo =====
32+
const repoMatch = body.match(/https:\/\/github\.com\/[^\s]+/);
33+
const repoUrl = repoMatch ? repoMatch[0] : "";
34+
35+
// ===== Owner =====
36+
const ownerMatch = body.match(/Owner:\s*@([^\s]+)/);
37+
const taskOwner = ownerMatch ? ownerMatch[1] : "";
38+
39+
// ===== PR =====
40+
const prMatch = body.match(/PR:\s*(https:\/\/[^\s]+)/);
41+
const prUrl = prMatch ? prMatch[1] : "";
42+
43+
// ===== Status =====
44+
let status = prUrl ? "open" : "";
45+
46+
// ===== Source(核心逻辑)=====
47+
const sourceMatch = body.match(/Source:\s*(\w+)/i);
48+
let source = sourceMatch ? sourceMatch[1].toLowerCase() : "official";
49+
50+
// 防御非法值
51+
if (!["official", "community"].includes(source)) {
52+
source = "official";
53+
}
54+
55+
let found = false;
56+
57+
rows = rows.map(row => {
58+
let parts = row.split(',');
59+
while (parts.length < 7) parts.push('');
60+
61+
if (parts[2] === issueUrl) {
62+
parts[3] = taskOwner;
63+
parts[4] = prUrl;
64+
parts[5] = status;
65+
parts[6] = source;
66+
found = true;
67+
}
68+
69+
return parts.join(',');
70+
});
71+
72+
if (!found) {
73+
const project = title.replace("[Task]", "").trim();
74+
const newRow = [
75+
project,
76+
repoUrl,
77+
issueUrl,
78+
taskOwner,
79+
prUrl,
80+
status,
81+
source
82+
].join(',');
83+
84+
rows.push(newRow);
85+
}
86+
}
87+
88+
const newCsv = [header, ...rows].join('\n');
89+
fs.writeFileSync('tasks.csv', newCsv);

0 commit comments

Comments
 (0)