From e13e961cbbbadb589fdd017f5af79320ad54bf84 Mon Sep 17 00:00:00 2001 From: WhiteMind Date: Thu, 25 Jan 2024 11:30:34 +0800 Subject: [PATCH] tmp --- .github/workflows/update_report_snapshot.yaml | 17 +- packages/scripts/snapshots/current.json | 36 ++++ packages/scripts/snapshots/prev.json | 36 ++++ .../scripts/src/compare_report_snapshot.ts | 140 +-------------- .../scripts/src/update_report_snapshot.ts | 28 +-- packages/scripts/src/utils/file.ts | 8 + packages/scripts/src/utils/github.ts | 4 +- packages/scripts/src/utils/report.ts | 170 ++++++++++++++++++ 8 files changed, 273 insertions(+), 166 deletions(-) create mode 100644 packages/scripts/snapshots/current.json create mode 100644 packages/scripts/snapshots/prev.json create mode 100644 packages/scripts/src/utils/file.ts create mode 100644 packages/scripts/src/utils/report.ts diff --git a/.github/workflows/update_report_snapshot.yaml b/.github/workflows/update_report_snapshot.yaml index e27c7175..825483e7 100644 --- a/.github/workflows/update_report_snapshot.yaml +++ b/.github/workflows/update_report_snapshot.yaml @@ -1,6 +1,8 @@ name: Update Report Snapshot on: + push: + branches: [feat/338_WeeklyReportScript] workflow_dispatch: schedule: - cron: '0 0 * * 1' @@ -19,12 +21,17 @@ jobs: ref: ${{ github.ref }} - name: report:update + id: report_update env: # The script requires public_repo, read:org, read:project permissions, and needs a personal access token (classic) to obtain these permissions. GITHUB_TOKEN: ${{ secrets.REPORT_GITHUB_TOKEN }} run: | yarn install - yarn workspace @magickbase-website/scripts report:update + { + echo 'DEVLOG<> $GITHUB_OUTPUT git add packages/scripts/snapshots - name: Set GPG @@ -40,6 +47,12 @@ jobs: with: title: Update Report Snapshot commit-message: 'feat: update report snapshot' - body: 'After this PR is merged, a corresponding discussion will be automatically created' + body: ' + After this PR is merged, a corresponding discussion will be automatically created + + --- + + ${{ steps.report_update.outputs.DEVLOG }} + ' committer: ${{ vars.COMMITTER }} branch: update-report-snapshot diff --git a/packages/scripts/snapshots/current.json b/packages/scripts/snapshots/current.json new file mode 100644 index 00000000..c161fd3a --- /dev/null +++ b/packages/scripts/snapshots/current.json @@ -0,0 +1,36 @@ +{ + "CKB Explorer": [ + { + "id": "PVTI_lADOBjLfC84AMiKwzgFHFUU", + "content": { + "title": "Make workflow of m-nft synchronization clear and maybe provide a standalone service", + "number": 49, + "url": "https://github.com/Magickbase/ckb-explorer-public-issues/issues/49" + }, + "fieldValues": { + "nodes": [ + { "field": { "name": "Assignees" } }, + { + "repository": { + "name": "ckb-explorer-public-issues", + "url": "https://github.com/Magickbase/ckb-explorer-public-issues" + }, + "field": { "name": "Repository" } + }, + { + "labels": { + "nodes": [{ "id": "LA_kwDOHFQx5M7tEEBJ", "name": "enhancement", "description": "New feature or request" }] + }, + "field": { "name": "Labels" } + }, + { + "text": "Make workflow of m-nft synchronization clear and maybe provide a standalone service", + "field": { "name": "Title" } + }, + { "name": "πŸ†• New", "field": { "name": "Status" } }, + { "name": "🏝 Low", "field": { "name": "Priority" } } + ] + } + } + ] +} diff --git a/packages/scripts/snapshots/prev.json b/packages/scripts/snapshots/prev.json new file mode 100644 index 00000000..c161fd3a --- /dev/null +++ b/packages/scripts/snapshots/prev.json @@ -0,0 +1,36 @@ +{ + "CKB Explorer": [ + { + "id": "PVTI_lADOBjLfC84AMiKwzgFHFUU", + "content": { + "title": "Make workflow of m-nft synchronization clear and maybe provide a standalone service", + "number": 49, + "url": "https://github.com/Magickbase/ckb-explorer-public-issues/issues/49" + }, + "fieldValues": { + "nodes": [ + { "field": { "name": "Assignees" } }, + { + "repository": { + "name": "ckb-explorer-public-issues", + "url": "https://github.com/Magickbase/ckb-explorer-public-issues" + }, + "field": { "name": "Repository" } + }, + { + "labels": { + "nodes": [{ "id": "LA_kwDOHFQx5M7tEEBJ", "name": "enhancement", "description": "New feature or request" }] + }, + "field": { "name": "Labels" } + }, + { + "text": "Make workflow of m-nft synchronization clear and maybe provide a standalone service", + "field": { "name": "Title" } + }, + { "name": "πŸ†• New", "field": { "name": "Status" } }, + { "name": "🏝 Low", "field": { "name": "Priority" } } + ] + } + } + ] +} diff --git a/packages/scripts/src/compare_report_snapshot.ts b/packages/scripts/src/compare_report_snapshot.ts index 6f12925f..f9471d2a 100644 --- a/packages/scripts/src/compare_report_snapshot.ts +++ b/packages/scripts/src/compare_report_snapshot.ts @@ -1,140 +1,4 @@ import './prepare' -import { join } from 'path' -import { mkdirSync, readFileSync } from 'fs' -import { ProjectItem, createDiscussion } from './utils/github' -import { assert } from './utils/error' +import { createDevlogDiscussion } from './utils/report' -const sortedStatusValues = [ - 'πŸ†• New', - 'πŸ“«Hold On', - 'πŸ“‹ Backlog', - 'πŸ“ŒPlanning', - '🎨 Designing', - 'πŸ— In Progress', - 'πŸ”Ž Code Review', - 'πŸ‘€ Testing', - '🚩Pre Release', - 'βœ… Done', -] - -const folder = join(process.cwd(), 'snapshots') -mkdirSync(folder, { recursive: true }) - -const currentFilepath = join(folder, 'current.json') -const prevFilepath = join(folder, 'prev.json') - -const currentProjectItemsMap = JSON.parse(readFileSync(currentFilepath).toString()) as Record -const prevProjectItemsMap = JSON.parse(readFileSync(prevFilepath).toString()) as Record - -function itemsToItemMap(items: ProjectItem[]): Record { - const map: Record = {} - for (const item of items) { - map[item.id] = item - } - return map -} - -function getField(item: ProjectItem, filedName: string) { - return item.fieldValues.nodes.find(n => n.field.name === filedName) -} - -let devLog = '' - -for (const [title, currentItems] of Object.entries(currentProjectItemsMap)) { - const prevItems = prevProjectItemsMap[title] - if (!prevItems) continue - - const currentMap = itemsToItemMap(currentItems) - const prevMap = itemsToItemMap(prevItems) - - const newItems: ProjectItem[] = [] - const update: ProjectItem[] = [] - const done: ProjectItem[] = [] - - for (const id of Object.keys(currentMap)) { - const currentItem = currentMap[id] - const prevItem = prevMap[id] - assert(currentItem) - - const hasAnyChange = !prevItem || JSON.stringify(currentItem) !== JSON.stringify(prevItem) - const hasStatusChange = !prevItem || getField(currentItem, 'Status')?.name !== getField(prevItem, 'Status')?.name - if (!hasAnyChange) continue - - const currentStatus = getField(currentItem, 'Status')?.name ?? '' - switch (currentStatus) { - case 'πŸ†• New': - newItems.push(currentItem) - break - case '🚩Pre Release': - case 'βœ… Done': - const prevStatus = prevItem == null ? '' : getField(prevItem, 'Status')?.name ?? '' - const prevIsDone = ['🚩Pre Release', 'βœ… Done'].includes(prevStatus) - if (hasStatusChange && !prevIsDone) { - done.push(currentItem) - } - break - default: - update.push(currentItem) - break - } - } - - ;[newItems, update, done].forEach(items => - items.sort((a, b) => { - const aStatus = getField(a, 'Status')?.name ?? '' - const bStatus = getField(b, 'Status')?.name ?? '' - return sortedStatusValues.indexOf(aStatus) - sortedStatusValues.indexOf(bStatus) - }), - ) - - const getStatusChangeLog = (item: ProjectItem) => { - const currentStatus = getField(item, 'Status')?.name - const prevItem = prevMap[item.id] - const prevStatus = prevItem == null ? undefined : getField(prevItem, 'Status')?.name - if (prevStatus == null || currentStatus === prevStatus) return ` [${currentStatus}]` - return ` [${prevStatus} -> ${currentStatus}]` - } - - const getContentURL = (item: ProjectItem) => - item.content.url != null ? ` [#${item.content.number}](${item.content.url})` : '' - - const getDescription = (item: ProjectItem) => { - const desc = getField(item, 'Description')?.text - return desc != null ? ` [${desc}]` : '' - } - - devLog += `# ${title}\n\n` - - if (newItems.length > 0) { - devLog += '## [NEW]\n\n' - newItems.forEach((item, idx) => { - devLog += `${idx + 1}. ${item.content.title}${getContentURL(item)}${getDescription(item)}\n` - }) - devLog += '\n' - } - - if (update.length > 0) { - devLog += '## [UPDATE]\n\n' - update.forEach((item, idx) => { - devLog += `${idx + 1}.${getStatusChangeLog(item)} ${item.content.title}${getContentURL(item)}${getDescription( - item, - )}\n` - }) - devLog += '\n' - } - - if (done.length > 0) { - devLog += '## [DONE]\n\n' - done.forEach((item, idx) => { - devLog += `${idx + 1}. [${getField(item, 'Status')?.name}] ${item.content.title}${getContentURL( - item, - )}${getDescription(item)}\n` - }) - } - - devLog += '\n' -} - -createDiscussion('Magickbase', 'shaping', 'Dev Log', `Dev Log ${new Date().toISOString().slice(0, 10)}`, devLog) - .then(res => console.log('Dev log created', res.id)) - .catch(console.error) +await createDevlogDiscussion() diff --git a/packages/scripts/src/update_report_snapshot.ts b/packages/scripts/src/update_report_snapshot.ts index 3317bd74..284cc8e9 100644 --- a/packages/scripts/src/update_report_snapshot.ts +++ b/packages/scripts/src/update_report_snapshot.ts @@ -1,26 +1,6 @@ import './prepare' -import { join } from 'path' -import { existsSync, mkdirSync, renameSync, writeFileSync } from 'fs' -import { ProjectItem, getOrganizationProjects, getProjectItems } from './utils/github' -import { assert } from './utils/error' +import { generateDevlogFromSnapshotsDiff, updateSnapshots } from './utils/report' -const projectNames = ['Neuron', 'CKB Explorer'] -const projects = await getOrganizationProjects('Magickbase') -const filteredProjects = projects.filter(p => projectNames.includes(p.title)) -assert(filteredProjects.length === projectNames.length) - -const projectItemsMap: Record = {} -for (const project of filteredProjects) { - projectItemsMap[project.title] = await getProjectItems(project.id) -} - -const folder = join(process.cwd(), 'snapshots') -mkdirSync(folder, { recursive: true }) - -const currentFilepath = join(folder, 'current.json') -const prevFilepath = join(folder, 'prev.json') - -if (existsSync(currentFilepath)) { - renameSync(currentFilepath, prevFilepath) -} -writeFileSync(currentFilepath, JSON.stringify(projectItemsMap)) +await updateSnapshots() +console.log('generateDevlogFromSnapshotsDiff():') +console.log(generateDevlogFromSnapshotsDiff()) diff --git a/packages/scripts/src/utils/file.ts b/packages/scripts/src/utils/file.ts new file mode 100644 index 00000000..a4b8fc6c --- /dev/null +++ b/packages/scripts/src/utils/file.ts @@ -0,0 +1,8 @@ +import { existsSync, mkdirSync } from 'fs' +import { dirname } from 'path' + +export function ensureFileFolderExists(filePath: string) { + const folder = dirname(filePath) + if (existsSync(folder)) return + mkdirSync(folder, { recursive: true }) +} diff --git a/packages/scripts/src/utils/github.ts b/packages/scripts/src/utils/github.ts index a2d94df8..19b7f91e 100644 --- a/packages/scripts/src/utils/github.ts +++ b/packages/scripts/src/utils/github.ts @@ -134,7 +134,7 @@ export interface ProjectItem { } export async function getProjectItems(id: string) { - const res = await octokit.graphql.paginate<{ + const res = await octokit.graphql<{ node: { items: { nodes: ProjectItem[] @@ -145,7 +145,7 @@ export async function getProjectItems(id: string) { query($cursor: String, $id: ID!) { node(id: $id) { ... on ProjectV2 { - items(first: 20, after: $cursor) { + items(first: 5, after: $cursor) { nodes { id content { diff --git a/packages/scripts/src/utils/report.ts b/packages/scripts/src/utils/report.ts new file mode 100644 index 00000000..b85ea39c --- /dev/null +++ b/packages/scripts/src/utils/report.ts @@ -0,0 +1,170 @@ +import { join } from 'path' +import { existsSync, readFileSync, renameSync, writeFileSync } from 'fs' +import { ProjectItem, createDiscussion, getOrganizationProjects, getProjectItems } from './github' +import { assert } from './error' +import { ensureFileFolderExists } from './file' + +const projectNames = ['CKB Explorer'] +const sortedStatusValues = [ + 'πŸ†• New', + 'πŸ“«Hold On', + 'πŸ“‹ Backlog', + 'πŸ“ŒPlanning', + '🎨 Designing', + 'πŸ— In Progress', + 'πŸ”Ž Code Review', + 'πŸ‘€ Testing', + '🚩Pre Release', + 'βœ… Done', +] + +const folder = join(process.cwd(), 'snapshots') +const currentFilepath = join(folder, 'current.json') +const prevFilepath = join(folder, 'prev.json') + +export async function updateSnapshots() { + const projects = await getOrganizationProjects('Magickbase') + const filteredProjects = projects.filter(p => projectNames.includes(p.title)) + assert(filteredProjects.length === projectNames.length) + + const projectItemsMap: Record = {} + for (const project of filteredProjects) { + projectItemsMap[project.title] = await getProjectItems(project.id) + } + + if (existsSync(currentFilepath)) { + renameSync(currentFilepath, prevFilepath) + } + ensureFileFolderExists(currentFilepath) + writeFileSync(currentFilepath, JSON.stringify(projectItemsMap)) +} + +export function generateDevlogFromSnapshotsDiff() { + if (!existsSync(currentFilepath) || !existsSync(prevFilepath)) return null + const currentProjectItemsMap = JSON.parse(readFileSync(currentFilepath).toString()) as Record + const prevProjectItemsMap = JSON.parse(readFileSync(prevFilepath).toString()) as Record + + let devLog = '' + + for (const [title, currentItems] of Object.entries(currentProjectItemsMap)) { + const prevItems = prevProjectItemsMap[title] + if (!prevItems) continue + + const currentMap = itemsToItemMap(currentItems) + const prevMap = itemsToItemMap(prevItems) + + const newItems: ProjectItem[] = [] + const update: ProjectItem[] = [] + const done: ProjectItem[] = [] + + for (const id of Object.keys(currentMap)) { + const currentItem = currentMap[id] + const prevItem = prevMap[id] + assert(currentItem) + + const hasAnyChange = !prevItem || JSON.stringify(currentItem) !== JSON.stringify(prevItem) + const hasStatusChange = !prevItem || getField(currentItem, 'Status')?.name !== getField(prevItem, 'Status')?.name + if (!hasAnyChange) continue + + const currentStatus = getField(currentItem, 'Status')?.name ?? '' + switch (currentStatus) { + case 'πŸ†• New': + newItems.push(currentItem) + break + case '🚩Pre Release': + case 'βœ… Done': + const prevStatus = prevItem == null ? '' : getField(prevItem, 'Status')?.name ?? '' + const prevIsDone = ['🚩Pre Release', 'βœ… Done'].includes(prevStatus) + if (hasStatusChange && !prevIsDone) { + done.push(currentItem) + } + break + default: + update.push(currentItem) + break + } + } + + ;[newItems, update, done].forEach(items => + items.sort((a, b) => { + const aStatus = getField(a, 'Status')?.name ?? '' + const bStatus = getField(b, 'Status')?.name ?? '' + return sortedStatusValues.indexOf(aStatus) - sortedStatusValues.indexOf(bStatus) + }), + ) + + const getStatusChangeLog = (item: ProjectItem) => { + const currentStatus = getField(item, 'Status')?.name + const prevItem = prevMap[item.id] + const prevStatus = prevItem == null ? undefined : getField(prevItem, 'Status')?.name + if (prevStatus == null || currentStatus === prevStatus) return ` [${currentStatus}]` + return ` [${prevStatus} -> ${currentStatus}]` + } + + const getContentURL = (item: ProjectItem) => + item.content.url != null ? ` [#${item.content.number}](${item.content.url})` : '' + + const getDescription = (item: ProjectItem) => { + const desc = getField(item, 'Description')?.text + return desc != null ? ` [${desc}]` : '' + } + + devLog += `# ${title}\n\n` + + if (newItems.length > 0) { + devLog += '## [NEW]\n\n' + newItems.forEach((item, idx) => { + devLog += `${idx + 1}. ${item.content.title}${getContentURL(item)}${getDescription(item)}\n` + }) + devLog += '\n' + } + + if (update.length > 0) { + devLog += '## [UPDATE]\n\n' + update.forEach((item, idx) => { + devLog += `${idx + 1}.${getStatusChangeLog(item)} ${item.content.title}${getContentURL(item)}${getDescription( + item, + )}\n` + }) + devLog += '\n' + } + + if (done.length > 0) { + devLog += '## [DONE]\n\n' + done.forEach((item, idx) => { + devLog += `${idx + 1}. [${getField(item, 'Status')?.name}] ${item.content.title}${getContentURL( + item, + )}${getDescription(item)}\n` + }) + } + + devLog += '\n' + } + + return devLog +} + +export async function createDevlogDiscussion() { + const devLog = generateDevlogFromSnapshotsDiff() + if (devLog == null) return null + + return createDiscussion( + 'Magickbase', + 'shaping', + 'Dev Log', + `Dev Log ${new Date().toISOString().slice(0, 10)}`, + devLog, + ) +} + +function itemsToItemMap(items: ProjectItem[]): Record { + const map: Record = {} + for (const item of items) { + map[item.id] = item + } + return map +} + +function getField(item: ProjectItem, filedName: string) { + return item.fieldValues.nodes.find(n => n.field.name === filedName) +}