name: Label Issue Metadata on: issues: types: [opened, edited] permissions: contents: read issues: write jobs: label-issue-metadata: runs-on: ubuntu-latest steps: - name: Apply compositor and distribution labels from issue form uses: actions/github-script@v7 with: script: | const issue = context.payload.issue; const body = issue.body || ""; const issueNumber = issue.number; const { owner, repo } = context.repo; const extractValue = (heading) => { const escapedHeading = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const match = body.match(new RegExp(`^\\s*(?:###\\s*)?${escapedHeading}\\s*\\r?\\n+([^\\n\\r]+)`, "im")); return match ? match[1].trim() : null; }; const compositorValue = extractValue("Compositor"); const distributionValue = extractValue("Distribution"); const compositorLabelMap = { "Niri": "compositor:niri", "Hyprland": "compositor:hyprland", "Sway": "compositor:sway", "Scroll": "compositor:scroll", "Labwc": "compositor:labwc", "Mango": "compositor:mango", "MangoWC": "compositor:mango", "Other": "compositor:other" }; const distributionLabelMap = { "Arch-based": "distro:arch-based", "Fedora-based": "distro:fedora-based", "Debian-based": "distro:debian-based", "NixOS": "distro:nixos", "openSUSE-based": "distro:opensuse", "Gentoo-based": "distro:gentoo", "Void": "distro:void", "Void-based": "distro:void", "Other": "distro:other" }; const mappings = [ { kind: "compositor", prefix: "compositor:", selectedValue: compositorValue, labelMap: compositorLabelMap, description: "Issue reported for this compositor stack" }, { kind: "distribution", prefix: "distro:", selectedValue: distributionValue, labelMap: distributionLabelMap, description: "Issue reported for this Linux distribution family" } ]; async function ensureLabelExists(name, description) { try { await github.rest.issues.getLabel({ owner, repo, name }); } catch (error) { if (error.status !== 404) throw error; await github.rest.issues.createLabel({ owner, repo, name, color: "5319e7", description }); core.info(`Created missing label "${name}".`); } } const existingLabels = issue.labels.map((label) => label.name); for (const mapping of mappings) { if (!mapping.selectedValue) { core.info(`${mapping.kind} field not found in issue body; skipping.`); continue; } const targetLabel = mapping.labelMap[mapping.selectedValue]; if (!targetLabel) { core.info(`No ${mapping.kind} label mapping found for value: "${mapping.selectedValue}"`); continue; } await ensureLabelExists(targetLabel, mapping.description); const currentKindLabels = existingLabels.filter((name) => name.startsWith(mapping.prefix)); for (const label of currentKindLabels) { if (label === targetLabel) continue; try { await github.rest.issues.removeLabel({ owner, repo, issue_number: issueNumber, name: label }); } catch (error) { core.info(`Could not remove label "${label}": ${error.message}`); } } await github.rest.issues.addLabels({ owner, repo, issue_number: issueNumber, labels: [targetLabel] }); core.info(`Applied ${mapping.kind} label "${targetLabel}" from value "${mapping.selectedValue}".`); }