[Character Bug]: 野营晴摸头后会闪(顿)一下 #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Auto Replace Issue Category Labels | |
| on: | |
| issues: | |
| types: [opened, edited] | |
| permissions: | |
| issues: write | |
| contents: read | |
| jobs: | |
| replace-category-label: | |
| if: ${{ github.event.action == 'opened' || github.event.changes.body != null }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Replace category labels based on issue form | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const issue = context.payload.issue; | |
| const body = issue.body || ""; | |
| const issueNumber = issue.number; | |
| const currentLabels = (issue.labels || []).map(label => | |
| typeof label === "string" ? label : label.name | |
| ); | |
| const characterCategoryLabels = [ | |
| "character:bug:metadata", | |
| "character:bug:opening-animation", | |
| "character:bug:interaction-animation", | |
| "character:bug:animation-blend", | |
| "character:bug:subtitle", | |
| "character:bug:bgm", | |
| "character:bug:voice", | |
| "character:bug:vfx", | |
| "character:bug:other" | |
| ]; | |
| const softwareCategoryLabels = [ | |
| "software:bug:ui-interaction", | |
| "software:bug:ui-animation", | |
| "software:bug:logic", | |
| "software:bug:function", | |
| "software:bug:performance", | |
| "software:bug:other", | |
| "software:bug:ui-interaction", | |
| "software:bug:ui-animation", | |
| "software:bug:logic", | |
| "software:bug:function", | |
| "software:bug:performance", | |
| "software:bug:other", | |
| "software:feat:logic-function", | |
| "software:feat:ui-function", | |
| "software:feat:logic-optimization", | |
| "software:feat:setting" | |
| ]; | |
| const characterMap = { | |
| "元数据异常": "character:bug:metadata", | |
| "开场动画异常": "character:bug:opening-animation", | |
| "交互动画异常": "character:bug:interaction-animation", | |
| "动画混合异常": "character:bug:animation-blend", | |
| "字幕异常": "character:bug:subtitle", | |
| "背景音乐异常": "character:bug:bgm", | |
| "语音异常": "character:bug:voice", | |
| "视效异常": "character:bug:vfx", | |
| "其他": "character:bug:other" | |
| }; | |
| const softwareMap = { | |
| "UI 交互异常": "software:bug:ui-interaction", | |
| "UI 动画异常": "software:bug:ui-animation", | |
| "操作逻辑异常": "software:bug:logic", | |
| "功能异常": "software:bug:function", | |
| "性能异常": "software:bug:performance", | |
| "其他": "software:bug:other" | |
| }; | |
| const softwareFeatureMap = { | |
| "逻辑功能": "software:feat:logic-function", | |
| "UI 功能": "software:feat:ui-function", | |
| "逻辑优化": "software:feat:logic-optimization", | |
| "设置条目": "software:feat:setting" | |
| }; | |
| const labelMetadata = { | |
| "software:bug:ui-interaction": { | |
| color: "d73a4a", | |
| description: "软件缺陷:UI 交互异常。" | |
| }, | |
| "software:bug:ui-animation": { | |
| color: "d73a4a", | |
| description: "软件缺陷:UI 动画异常。" | |
| }, | |
| "software:bug:logic": { | |
| color: "d73a4a", | |
| description: "软件缺陷:操作逻辑异常。" | |
| }, | |
| "software:bug:function": { | |
| color: "d73a4a", | |
| description: "软件缺陷:功能异常。" | |
| }, | |
| "software:bug:performance": { | |
| color: "d73a4a", | |
| description: "软件缺陷:性能异常。" | |
| }, | |
| "software:bug:other": { | |
| color: "d73a4a", | |
| description: "软件缺陷:其他软件异常。" | |
| }, | |
| "software:feat:logic-function": { | |
| color: "a2eeef", | |
| description: "软件建议:逻辑功能。" | |
| }, | |
| "software:feat:ui-function": { | |
| color: "a2eeef", | |
| description: "软件建议:UI 功能。" | |
| }, | |
| "software:feat:logic-optimization": { | |
| color: "a2eeef", | |
| description: "软件建议:逻辑优化。" | |
| }, | |
| "software:feat:setting": { | |
| color: "a2eeef", | |
| description: "软件建议:设置条目。" | |
| } | |
| }; | |
| function extractField(fieldName) { | |
| const escaped = fieldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | |
| const regex = new RegExp( | |
| `###\\s*${escaped}\\s*\\n+([\\s\\S]*?)(?=\\n###\\s|$)`, | |
| "m" | |
| ); | |
| const match = body.match(regex); | |
| return match ? match[1].trim() : ""; | |
| } | |
| async function removeLabelIfExists(labelName) { | |
| if (!currentLabels.includes(labelName)) return; | |
| try { | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issueNumber, | |
| name: labelName | |
| }); | |
| core.info(`Removed label: ${labelName}`); | |
| } catch (error) { | |
| core.warning(`Failed to remove label "${labelName}": ${error.message}`); | |
| } | |
| } | |
| async function addLabelIfMissing(labelName) { | |
| if (!labelName) return; | |
| if (currentLabels.includes(labelName)) { | |
| core.info(`Label already exists: ${labelName}`); | |
| return; | |
| } | |
| await ensureLabelExists(labelName); | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issueNumber, | |
| labels: [labelName] | |
| }); | |
| core.info(`Added label: ${labelName}`); | |
| } | |
| async function ensureLabelExists(labelName) { | |
| const metadata = labelMetadata[labelName]; | |
| if (!metadata) return; | |
| try { | |
| await github.rest.issues.getLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| name: labelName | |
| }); | |
| } catch (error) { | |
| if (error.status !== 404) { | |
| throw error; | |
| } | |
| await github.rest.issues.createLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| name: labelName, | |
| color: metadata.color, | |
| description: metadata.description | |
| }); | |
| core.info(`Created label: ${labelName}`); | |
| } | |
| } | |
| const issueType = extractField("问题类型"); | |
| core.info(`Extracted 问题类型: ${issueType || "(empty)"}`); | |
| const suggestionType = extractField("建议类型"); | |
| core.info(`Extracted 建议类型: ${suggestionType || "(empty)"}`); | |
| const isCharacter = currentLabels.includes("character"); | |
| const isSoftware = currentLabels.includes("software"); | |
| const isFeature = currentLabels.includes("enhancement"); | |
| if (!isCharacter && !isSoftware) { | |
| core.info('Skip: neither "character" nor "software" label found.'); | |
| return; | |
| } | |
| if (isCharacter && isSoftware) { | |
| core.warning('Issue has both "character" and "software" labels. Skip to avoid ambiguity.'); | |
| return; | |
| } | |
| if (isCharacter) { | |
| if (!issueType) { | |
| core.info('Skip: character issue form does not contain a bug category field.'); | |
| return; | |
| } | |
| const newLabel = characterMap[issueType]; | |
| if (!newLabel) { | |
| core.warning(`No mapped character label for issue type: ${issueType}`); | |
| return; | |
| } | |
| for (const label of characterCategoryLabels) { | |
| if (label !== newLabel) { | |
| await removeLabelIfExists(label); | |
| } | |
| } | |
| await addLabelIfMissing(newLabel); | |
| return; | |
| } | |
| if (isSoftware) { | |
| const newLabel = isFeature ? softwareFeatureMap[suggestionType] : softwareMap[issueType]; | |
| if (!newLabel) { | |
| core.warning(`No mapped software label for category: ${isFeature ? suggestionType : issueType}`); | |
| return; | |
| } | |
| for (const label of softwareCategoryLabels) { | |
| if (label !== newLabel) { | |
| await removeLabelIfExists(label); | |
| } | |
| } | |
| await addLabelIfMissing(newLabel); | |
| return; | |
| } |