Merge pull request #2 from LucasErcolano/codex/github-management-infra
Add GitHub management infrastructure
This commit is contained in:
commit
5c824b66a6
|
|
@ -0,0 +1,53 @@
|
||||||
|
name: Bug
|
||||||
|
description: Report a defect that needs investigation or a fix
|
||||||
|
title: "[Bug] "
|
||||||
|
labels:
|
||||||
|
- "type:bug"
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Use this template for broken or incorrect behavior. Include enough detail to reproduce the issue.
|
||||||
|
- type: textarea
|
||||||
|
id: current_behavior
|
||||||
|
attributes:
|
||||||
|
label: Current behavior
|
||||||
|
description: What is happening now?
|
||||||
|
placeholder: Describe the observed behavior.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected_behavior
|
||||||
|
attributes:
|
||||||
|
label: Expected behavior
|
||||||
|
description: What should happen instead?
|
||||||
|
placeholder: Describe the correct or intended behavior.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: steps
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: How can someone reproduce the issue?
|
||||||
|
placeholder: |
|
||||||
|
1. Go to ...
|
||||||
|
2. Run ...
|
||||||
|
3. Observe ...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: impact
|
||||||
|
attributes:
|
||||||
|
label: Impact
|
||||||
|
description: Who or what is affected, and how severe is it?
|
||||||
|
placeholder: Explain user impact, system impact, or urgency.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: done_criteria
|
||||||
|
attributes:
|
||||||
|
label: Done criteria
|
||||||
|
description: What confirms the bug is fixed?
|
||||||
|
placeholder: Define the validation or acceptance conditions.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Questions about setup or usage
|
||||||
|
url: https://github.com/LucasErcolano/MiroFish/blob/main/README.md
|
||||||
|
about: Check the project README before opening a workflow issue for general usage questions.
|
||||||
|
- name: Community chat on Discord
|
||||||
|
url: http://discord.gg/ePf5aPaHnA
|
||||||
|
about: Use Discord for broad questions, discussions, or community support.
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
name: Epic
|
||||||
|
description: Coordinating issue for a larger body of work
|
||||||
|
title: "[Epic] "
|
||||||
|
labels:
|
||||||
|
- "type:epic"
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Use this template for a larger initiative that will be split into smaller issues.
|
||||||
|
- type: textarea
|
||||||
|
id: objective
|
||||||
|
attributes:
|
||||||
|
label: Objective
|
||||||
|
description: What outcome should this epic deliver?
|
||||||
|
placeholder: Describe the main goal and the value this epic should create.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: scope
|
||||||
|
attributes:
|
||||||
|
label: Scope
|
||||||
|
description: What is included in this epic?
|
||||||
|
placeholder: List the work this epic should cover.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: out_of_scope
|
||||||
|
attributes:
|
||||||
|
label: Out of scope
|
||||||
|
description: What is explicitly not part of this epic?
|
||||||
|
placeholder: Capture boundaries to keep the work focused.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: dependencies
|
||||||
|
attributes:
|
||||||
|
label: Dependencies
|
||||||
|
description: What work, systems, or decisions does this depend on?
|
||||||
|
placeholder: Reference related issues, external dependencies, or blockers.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: done_criteria
|
||||||
|
attributes:
|
||||||
|
label: Done criteria
|
||||||
|
description: How will we know this epic is complete?
|
||||||
|
placeholder: Define the conditions required to close this epic.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: subissues
|
||||||
|
attributes:
|
||||||
|
label: Sub-issues checklist
|
||||||
|
description: List the issues that should roll up into this epic.
|
||||||
|
placeholder: |
|
||||||
|
- [ ] #123
|
||||||
|
- [ ] Create issue for ...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
name: Spike
|
||||||
|
description: Time-boxed investigation or research task
|
||||||
|
title: "[Spike] "
|
||||||
|
labels:
|
||||||
|
- "type:spike"
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Use this template for research or technical exploration that should end with a recommendation.
|
||||||
|
- type: textarea
|
||||||
|
id: question
|
||||||
|
attributes:
|
||||||
|
label: Question to answer
|
||||||
|
description: What decision or uncertainty is this spike meant to resolve?
|
||||||
|
placeholder: State the main question as clearly as possible.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: options
|
||||||
|
attributes:
|
||||||
|
label: Options to evaluate
|
||||||
|
description: Which alternatives should be compared?
|
||||||
|
placeholder: List the candidate approaches or tools to investigate.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: deliverable
|
||||||
|
attributes:
|
||||||
|
label: Expected deliverable
|
||||||
|
description: What output should this spike produce?
|
||||||
|
placeholder: "Example: recommendation, benchmark, decision memo, or migration notes."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: done_criteria
|
||||||
|
attributes:
|
||||||
|
label: Done criteria
|
||||||
|
description: What needs to happen before this spike can be closed?
|
||||||
|
placeholder: Define the minimum evidence or conclusion expected.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: deadline
|
||||||
|
attributes:
|
||||||
|
label: Target date
|
||||||
|
description: Optional deadline for the spike
|
||||||
|
placeholder: YYYY-MM-DD
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
name: Task
|
||||||
|
description: Actionable implementation task
|
||||||
|
title: "[Task] "
|
||||||
|
labels:
|
||||||
|
- "type:task"
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Use this template for a focused piece of work that should usually land in a single pull request.
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Context
|
||||||
|
description: What problem or opportunity is this task addressing?
|
||||||
|
placeholder: Summarize the relevant context and why this task matters.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected_result
|
||||||
|
attributes:
|
||||||
|
label: Expected result
|
||||||
|
description: What should be true once this task is complete?
|
||||||
|
placeholder: Describe the intended outcome in concrete terms.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: acceptance_criteria
|
||||||
|
attributes:
|
||||||
|
label: Acceptance criteria
|
||||||
|
description: What conditions must be met before this can be closed?
|
||||||
|
placeholder: |
|
||||||
|
- [ ] ...
|
||||||
|
- [ ] ...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: dependencies
|
||||||
|
attributes:
|
||||||
|
label: Dependencies
|
||||||
|
description: What related issues, decisions, or blockers affect this task?
|
||||||
|
placeholder: Reference anything this task needs before it can start or finish.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: technical_notes
|
||||||
|
attributes:
|
||||||
|
label: Technical notes
|
||||||
|
description: Optional implementation notes, caveats, or pointers
|
||||||
|
placeholder: Include technical details that will help the implementer.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
labels:
|
||||||
|
- name: "type:epic"
|
||||||
|
color: "5319e7"
|
||||||
|
description: "Large coordinating issue that groups related work"
|
||||||
|
- name: "type:task"
|
||||||
|
color: "0e8a16"
|
||||||
|
description: "Actionable implementation task"
|
||||||
|
- name: "type:spike"
|
||||||
|
color: "fbca04"
|
||||||
|
description: "Time-boxed research or technical investigation"
|
||||||
|
- name: "type:bug"
|
||||||
|
color: "d73a4a"
|
||||||
|
description: "Defect or incorrect behavior that requires a fix"
|
||||||
|
- name: "area:llm"
|
||||||
|
color: "1d76db"
|
||||||
|
description: "Work related to LLM providers, routing, or inference"
|
||||||
|
- name: "area:memory"
|
||||||
|
color: "0052cc"
|
||||||
|
description: "Work related to conversation memory or persistence"
|
||||||
|
- name: "area:rag"
|
||||||
|
color: "0e4e8a"
|
||||||
|
description: "Work related to retrieval, indexing, or knowledge access"
|
||||||
|
- name: "area:planning"
|
||||||
|
color: "5319e7"
|
||||||
|
description: "Work related to planning, orchestration, or agent coordination"
|
||||||
|
- name: "area:infra"
|
||||||
|
color: "c2e0c6"
|
||||||
|
description: "Repository, tooling, CI, or operational infrastructure work"
|
||||||
|
- name: "area:eval"
|
||||||
|
color: "bfdadc"
|
||||||
|
description: "Benchmarks, experiments, or evaluation work"
|
||||||
|
- name: "blocked"
|
||||||
|
color: "b60205"
|
||||||
|
description: "Cannot progress until a dependency or blocker is resolved"
|
||||||
|
- name: "good first issue"
|
||||||
|
color: "7057ff"
|
||||||
|
description: "Good candidate for a newcomer to the project"
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
## Linked issue
|
||||||
|
|
||||||
|
Closes #
|
||||||
|
|
||||||
|
## What changed
|
||||||
|
|
||||||
|
Describe the changes in this pull request.
|
||||||
|
|
||||||
|
## How to test
|
||||||
|
|
||||||
|
Describe the verification steps. If no runtime test applies, explain why.
|
||||||
|
|
||||||
|
## Risks
|
||||||
|
|
||||||
|
List known risks, follow-up work, or rollout considerations.
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
name: PR Hygiene
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- edited
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Validate pull request body
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const body = context.payload.pull_request.body || "";
|
||||||
|
|
||||||
|
function getSection(title) {
|
||||||
|
const escaped = title.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
const regex = new RegExp(`##\\s+${escaped}\\s*\\n([\\s\\S]*?)(?=\\n##\\s+|$)`, "i");
|
||||||
|
const match = body.match(regex);
|
||||||
|
return match ? match[1].trim() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkedIssue = getSection("Linked issue");
|
||||||
|
const howToTest = getSection("How to test");
|
||||||
|
|
||||||
|
const hasIssueReference =
|
||||||
|
/#\d+/.test(linkedIssue) ||
|
||||||
|
/https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/issues\/\d+/i.test(linkedIssue);
|
||||||
|
|
||||||
|
const sanitizedHowToTest = howToTest
|
||||||
|
.replace(/Describe the verification steps\. If no runtime test applies, explain why\./i, "")
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
const failures = [];
|
||||||
|
|
||||||
|
if (!hasIssueReference) {
|
||||||
|
failures.push("Add an issue reference under `## Linked issue` (for example `Closes #123`).");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sanitizedHowToTest) {
|
||||||
|
failures.push("Fill in `## How to test` with verification steps or explain why no runtime test applies.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failures.length > 0) {
|
||||||
|
core.setFailed(failures.join("\n"));
|
||||||
|
} else {
|
||||||
|
core.info("Pull request body passed hygiene checks.");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
name: Sync Labels
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- ".github/labels.yml"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.11"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: python -m pip install --disable-pip-version-check pyyaml
|
||||||
|
|
||||||
|
- name: Sync labels
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||||
|
run: |
|
||||||
|
python - <<'PY'
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import urllib.error
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
token = os.environ["GITHUB_TOKEN"]
|
||||||
|
owner_repo = os.environ["GITHUB_REPOSITORY"]
|
||||||
|
base_url = f"https://api.github.com/repos/{owner_repo}"
|
||||||
|
|
||||||
|
with open(".github/labels.yml", "r", encoding="utf-8") as fh:
|
||||||
|
config = yaml.safe_load(fh) or {}
|
||||||
|
|
||||||
|
desired_labels = config.get("labels", [])
|
||||||
|
desired_by_name = {label["name"]: label for label in desired_labels}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Accept": "application/vnd.github+json",
|
||||||
|
"Authorization": f"Bearer {token}",
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28",
|
||||||
|
"User-Agent": "mirofish-label-sync",
|
||||||
|
}
|
||||||
|
|
||||||
|
def request(method, url, payload=None):
|
||||||
|
data = None
|
||||||
|
if payload is not None:
|
||||||
|
data = json.dumps(payload).encode("utf-8")
|
||||||
|
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||||
|
with urllib.request.urlopen(req) as response:
|
||||||
|
raw = response.read()
|
||||||
|
return json.loads(raw.decode("utf-8")) if raw else None
|
||||||
|
|
||||||
|
existing = []
|
||||||
|
page = 1
|
||||||
|
while True:
|
||||||
|
url = f"{base_url}/labels?per_page=100&page={page}"
|
||||||
|
batch = request("GET", url)
|
||||||
|
if not batch:
|
||||||
|
break
|
||||||
|
existing.extend(batch)
|
||||||
|
if len(batch) < 100:
|
||||||
|
break
|
||||||
|
page += 1
|
||||||
|
|
||||||
|
existing_by_name = {label["name"]: label for label in existing}
|
||||||
|
|
||||||
|
for name, desired in desired_by_name.items():
|
||||||
|
payload = {
|
||||||
|
"new_name": desired["name"],
|
||||||
|
"color": desired["color"],
|
||||||
|
"description": desired["description"],
|
||||||
|
}
|
||||||
|
if name in existing_by_name:
|
||||||
|
url = f"{base_url}/labels/{urllib.parse.quote(name, safe='')}"
|
||||||
|
request("PATCH", url, payload)
|
||||||
|
print(f"updated {name}")
|
||||||
|
else:
|
||||||
|
url = f"{base_url}/labels"
|
||||||
|
request("POST", url, {
|
||||||
|
"name": desired["name"],
|
||||||
|
"color": desired["color"],
|
||||||
|
"description": desired["description"],
|
||||||
|
})
|
||||||
|
print(f"created {name}")
|
||||||
|
|
||||||
|
for name in sorted(existing_by_name):
|
||||||
|
if name not in desired_by_name:
|
||||||
|
url = f"{base_url}/labels/{urllib.parse.quote(name, safe='')}"
|
||||||
|
try:
|
||||||
|
request("DELETE", url)
|
||||||
|
print(f"deleted {name}")
|
||||||
|
except urllib.error.HTTPError as exc:
|
||||||
|
raise RuntimeError(f"Failed to delete label '{name}': {exc.read().decode('utf-8')}") from exc
|
||||||
|
PY
|
||||||
Loading…
Reference in New Issue