Repository navigation
Add GitHub Issues as a Software Factory tracker - #692
Conversation
6bdb498 to
374062c
Compare
czpython
left a comment
There was a problem hiding this comment.
Thank you for this! GitHub Issues as a tracker is a good addition. The PR description is very clear, and the label-group idea fits the funnel well. I have a few changes to ask for before we merge.
Must fix
-
GitHub errors are not caught.
known_exceptionslistshttpx.HTTPError, but the GitHub client raises githubkit'sRequestFailedandGitHubAppNotInstalledError. Neither one is an httpx error. If a label write fails, the error goes up to the webhook. On a merge, this can make thepr.closedwebhook fail, and Druks can lose the merge result. Please add both toknown_exceptions. -
Routing uses the bare repo name. The webhook sends
repository["name"]asproject_name.ProjectRepo.get_for_namematches a bare name withLIKE '%/name', so an issue onacme/apican route toother/api. Please sendrepository["full_name"].get_for_namealready matches a full name exactly. -
software_factory_startcannot take a GitHub key. The route is/{ticket}/start, andowner/repo#12contains a/. FastAPI returns 404 for it. Please make the route accept the key, for example with{ticket:path}, and add a test. -
The intake belongs in Software Factory.
core/webhooks/github.pyknows nothing about work items, and Linear and Jira publishticket.transitionedfromcontrib/software_factory/webhooks.py. Please make core publish a neutral fact, for exampleissue.labeled, and add a Software Factory subscriber that turns it intoticket.transitioned. -
Please check the sender. The label event has a
sender, the user who added the label. Please publish only whensender["type"] == "User", as the comment handlers in the same file already do. Only people with triage access or higher can add labels, which is a good start. We don't need to link the sender to a Druks account in this PR, so the default account is fine for now.
Smaller things
- The docs say GitHub rejects a label that does not exist. I think the add-labels API creates the label instead. Could you check and correct the sentence?
get_account_idnever runs, becauseassignee_idis alwaysNone. The base class covers it, so you can remove the override.aclosedoes nothing, and its comment says "only close our own". Please make it close the client the tracker created, or remove the comment.- The
client=argument exists only for tests. Please patchGithub.get_clientin the tests instead.
These things work well, and I checked them: the funnel only acts when the source is the selected tracker, a repeated delivery finds the same work item, and the label echo does nothing.
Thanks again. If anything here is unclear, ask me here in the PR.
374062c to
bbb0cce
Compare
|
Thanks so much for such a careful review, super helpful! I've rebased onto main and pushed a second commit (bbb0cce) that works through everything. Must fix
Smaller things
The docs also gained a short paragraph on who can trigger a build and how the repo routing works, plus a note about encoding the
Thanks again! |
Software Factory took work from Linear or Jira only. GitHub was wired in on the code side - branches, pull requests, reviews, pushes - but never as a source of work items, so a project whose backlog lives in GitHub Issues had no intake. GitHub has no status field, so the tracker reads the five status settings as label names and drives them as an exclusive group: setting one removes the others, including the trigger label, so a re-label opens a fresh build. Reaching the done label also closes the issue as completed, which is what a GitHub reader expects finished work to look like. Issue numbers repeat across repositories, so a ticket key is `owner/repo#number`. Intake reuses the existing GitHub webhook. `issues_labeled` publishes the same normalized `ticket.transitioned` fact Linear and Jira publish, so the funnel subscriber is untouched. The operator App is the tracker's identity; there is no second credential to connect, and no new permission either: Issues write was already on the App's list. A GitHub login is not an identity any grant issuer vouches for, so the tracker resolves no account from an assignee and every GitHub build uses the default account. The webhook publishes the login for display and leaves the id and address unset rather than guess at them. The App manifest now subscribes to the issues event. Without it an App created through Settings > Services would connect cleanly, report healthy, and never open a build. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Core now publishes a neutral issue.labeled fact, and a Software Factory subscriber turns it into ticket.transitioned, the way Linear and Jira intake already lives in the app. Core publishes a label only when a person adds it or the App adds it itself: the start route asks for a build by setting the trigger label through the App, so its own label has to reach the funnel. Any other App's label is ignored. The ticket routes by the issue's full repo name, which matches one repo exactly. A bare name, or a label, could match the same name under another owner, so neither routes a GitHub ticket. The start route takes a GitHub key: owner/repo#12 has a slash, so the ticket segment is a path. The tracker catches githubkit's RequestFailed and GitHubAppNotInstalledError, so a failed label write no longer fails the webhook that caused it. It closes the client it creates, takes no client argument, and leaves account lookup to the base class. The docs now say GitHub creates a missing label rather than rejecting it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main now resolves a build's account from the person who moved the ticket, and every ticket.transitioned payload carries actor_id. A GitHub label names a login, which no grant issuer vouches for, so the GitHub transition carries no actor and the build keeps the default account. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bbb0cce to
9410218
Compare
|
@czpython just resolved conflicts, only a minor markdown conflict this time. |
A GitHub sign-in links a GitHub user to a Druks account, so the label's sender now picks the build's account as on Linear and Jira, with the assignee next. The tracker takes its client from get_tracker, catches every githubkit error, and closes a done issue with close_issue. Tests and docs drop what restated the code.
What this adds
Software Factory took work from Linear or Jira only. GitHub was already wired in on the code side - branches, pull requests, reviews, pushes - but never as a source of work items, so a project whose backlog lives in GitHub Issues had no intake. This adds
githubas a tracker choice.An issue that gains the trigger label opens a build, and the build drives the issue through the same funnel a Linear ticket goes through.
How it works
GitHub has no status field, so the tracker reads the five existing status settings as label names. They behave as one exclusive group: setting any of them removes the others, including the trigger label, so an issue never reads as two states at once and re-applying the trigger label opens a fresh build. Reaching the done label also closes the issue as completed.
Intake reuses the existing
/_external/github/events/webhook. Core publishes a neutralissue.labeledfact when a person (or the App itself, via thestartroute) adds a label, and a Software Factory subscriber turns it into the same normalizedticket.transitionedfact Linear and Jira publish, so the funnel subscriber is untouched. Labels added by any other App are ignored. The operator App is the tracker's identity - there is no second credential to connect - and the App manifest now subscribes to theissuesevent, without which an App would connect cleanly, report healthy, and never open a build.Two GitHub-specific details are worth review attention:
owner/repo#number. Issue numbers are only unique within a repository, so the repo has to travel in the key.split_keyrefuses a bare number rather than guess, and the agentstartroute takes the key as a path (with the#encoded as%23). The ticket routes by the issue's full repo name only.assignee_idandassignee_emailunset rather than invent an address. Every GitHub build uses the default account.A note on the settings shape
This reuses the five generic status fields rather than adding GitHub-specific ones, so the funnel subscriber and
get_trackerstay as they are. The trade-off is that the defaults read as Linear status names, so an operator has to rename them (ready-for-agent,agent:in-progress, …) before selecting this tracker. The docs say so. If you would rather see dedicatedgithub_*label settings with GitHub-shaped defaults, that is a small change and I am happy to make it.Testing
ruff check backendandruff format --check backendclean.pytest backend/passes locally after the review changes (2190 passed, 73 skipped).Docs
docs/configuration.mdgains a GitHub Issues as the tracker section and three corrections to claims this change makes stale: the manual App registration event list, the "Linear and Jira share five status settings" sentence, and thedruks doctorparagraph.