From c441f9665d9c91d86884978601a3316446ef39e8 Mon Sep 17 00:00:00 2001 From: jyotsnak1603 Date: Sat, 6 Jun 2026 02:50:14 +0530 Subject: [PATCH 1/2] feat(ci): validate issue template label references --- .github/workflows/ci.yml | 10 +++++ CONTRIBUTING.md | 18 +++++++++ scripts/validate_issue_template_labels.py | 49 +++++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 scripts/validate_issue_template_labels.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 124bde53..e1bcc991 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,16 @@ jobs: git fetch origin "${{ github.base_ref }}" --depth=1 git diff --check "origin/${{ github.base_ref }}"...HEAD + issue-template-label-validation: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Validate issue template labels + run: python scripts/validate_issue_template_labels.py + backend-lint: needs: detect-changes if: needs.detect-changes.outputs.run_backend == 'true' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2c3526b9..073eaa74 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,6 +22,24 @@ SecuScan is built for learning, defensive security workflows, and ethical testin When issue labels are available, look for tags such as `good first issue`, `documentation`, `frontend`, `backend`, `plugin`, `help wanted`, or `gssoc`. +## Issue Template Label Maintenance + +Issue templates in `.github/ISSUE_TEMPLATE/` must only reference labels from the active repository taxonomy. + +When adding or updating issue template labels: + +- Use active label groups such as `type:*`, `area:*`, `priority:*`, and `level:*`. +- Avoid deprecated labels such as `bug`, `feature`, `documentation`, and `help wanted`. +- Keep template labels aligned with the labels used by maintainers and CI. + +Before opening a pull request that changes issue templates, run: + +```bash +python scripts/validate_issue_template_labels.py +``` + +The CI workflow also runs this validation and will fail if an issue template references a label that is not included in the approved label taxonomy. + ## Local Setup ### Prerequisites diff --git a/scripts/validate_issue_template_labels.py b/scripts/validate_issue_template_labels.py new file mode 100644 index 00000000..db2d4627 --- /dev/null +++ b/scripts/validate_issue_template_labels.py @@ -0,0 +1,49 @@ +from pathlib import Path +import re +import sys + +VALID_LABELS = { + "type:bug", + "type:feature", + "type:docs", + "type:devops", + "type:security", + "type:testing", + "type:performance", + "type:refactor", + "area:ci", + "area:docs", + "area:backend", + "area:frontend", + "priority:low", + "priority:medium", + "priority:high", + "level:beginner", + "level:intermediate", + "level:advanced", +} + +TEMPLATE_DIR = Path(".github/ISSUE_TEMPLATE") + +errors = [] + +for template in TEMPLATE_DIR.glob("*.md"): + content = template.read_text(encoding="utf-8") + match = re.search(r"^labels:\s*(.+)$", content, re.MULTILINE) + + if not match: + continue + + labels = [label.strip() for label in match.group(1).split(",") if label.strip()] + + for label in labels: + if label not in VALID_LABELS: + errors.append(f"{template}: invalid label '{label}'") + +if errors: + print("Invalid issue template labels found:") + for error in errors: + print(f"- {error}") + sys.exit(1) + +print("All issue template labels are valid.") \ No newline at end of file From a6c6ff2ed8f6495b6cb9cbd298658966e8ac5bc3 Mon Sep 17 00:00:00 2001 From: jyotsnak1603 Date: Sat, 6 Jun 2026 03:03:34 +0530 Subject: [PATCH 2/2] fix(ci): improve issue template label validation --- .github/workflows/ci.yml | 16 +++--- scripts/validate_issue_template_labels.py | 70 ++++++++++++++++++++--- 2 files changed, 69 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1bcc991..bfa1de66 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,14 +44,14 @@ jobs: git diff --check "origin/${{ github.base_ref }}"...HEAD issue-template-label-validation: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - name: Validate issue template labels - run: python scripts/validate_issue_template_labels.py + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Validate issue template labels + run: python scripts/validate_issue_template_labels.py backend-lint: needs: detect-changes diff --git a/scripts/validate_issue_template_labels.py b/scripts/validate_issue_template_labels.py index db2d4627..39e8a85f 100644 --- a/scripts/validate_issue_template_labels.py +++ b/scripts/validate_issue_template_labels.py @@ -23,27 +23,79 @@ "level:advanced", } -TEMPLATE_DIR = Path(".github/ISSUE_TEMPLATE") +REPO_ROOT = Path(__file__).resolve().parents[1] +TEMPLATE_DIR = REPO_ROOT / ".github" / "ISSUE_TEMPLATE" errors = [] -for template in TEMPLATE_DIR.glob("*.md"): - content = template.read_text(encoding="utf-8") - match = re.search(r"^labels:\s*(.+)$", content, re.MULTILINE) - if not match: - continue +def extract_front_matter(content): + if not content.startswith("---"): + return "" + + parts = content.split("---", 2) + if len(parts) < 3: + return "" + + return parts[1] + + +def parse_labels(raw_value): + raw_value = raw_value.strip().strip("\"'") + + if raw_value.startswith("[") and raw_value.endswith("]"): + raw_value = raw_value[1:-1] + + return [ + label.strip().strip("\"'") + for label in raw_value.split(",") + if label.strip() + ] + + +def extract_labels_from_front_matter(front_matter): + labels = [] + lines = front_matter.splitlines() + + for index, line in enumerate(lines): + match = re.match(r"^labels:\s*(.*)$", line) - labels = [label.strip() for label in match.group(1).split(",") if label.strip()] + if not match: + continue + + value = match.group(1).strip() + + if value: + labels.extend(parse_labels(value)) + continue + + for next_line in lines[index + 1:]: + stripped = next_line.strip() + + if not stripped: + continue + + if not stripped.startswith("-"): + break + + labels.append(stripped[1:].strip().strip("\"'")) + + return labels + + +for template in list(TEMPLATE_DIR.glob("*.md")) + list(TEMPLATE_DIR.glob("*.yml")) + list(TEMPLATE_DIR.glob("*.yaml")): + content = template.read_text(encoding="utf-8") + front_matter = extract_front_matter(content) + labels = extract_labels_from_front_matter(front_matter) for label in labels: if label not in VALID_LABELS: - errors.append(f"{template}: invalid label '{label}'") + errors.append("{}: invalid label '{}'".format(template.relative_to(REPO_ROOT), label)) if errors: print("Invalid issue template labels found:") for error in errors: - print(f"- {error}") + print("- {}".format(error)) sys.exit(1) print("All issue template labels are valid.") \ No newline at end of file