Simulators
Issues, events and the timeline
Issues are a REST surface of their own: list and create them, get and edit one by number (state closes and reopens it, with a state_reason), and lock and unlock its conversation. Its conversation comments list per subject and repository-wide, are created on a subject and are edited or deleted by comment id. Load the issue tracker scenario, open an issue, comment, close it and read its events:
H='Authorization: Bearer ghp_anything'
curl -s -X POST "$RYSTIC_URL/_rystic/scenario" -d '{"name":"github-pulls_issue_tracker"}' -o /dev/null
curl -s "$RYSTIC_URL/repos/tb-owner/tb-repo/issues?state=all" -H "$H" | jq -c '[.[] | {number, pr: has("pull_request"), state}]'
# -> [{"number":6,"pr":false,"state":"open"},{"number":3,"pr":true,"state":"open"},{"number":5,"pr":false,"state":"closed"},{"number":4,"pr":false,"state":"closed"},{"number":2,"pr":false,"state":"open"},{"number":1,"pr":false,"state":"open"}]
curl -s -X POST "$RYSTIC_URL/repos/tb-owner/tb-repo/issues" -H "$H" -d '{"title":"Retries drop the last error","labels":["bug"]}' | jq -c '{number, state}'
# -> {"number":7,"state":"open"}
curl -s -X POST "$RYSTIC_URL/repos/tb-owner/tb-repo/issues/7/comments" -H "$H" -d '{"body":"Reproduced **twice**"}' | jq -c '{id, body}'
# -> {"id":12100006,"body":"Reproduced **twice**"}
curl -s "$RYSTIC_URL/repos/tb-owner/tb-repo/issues/7/comments" -H "$H" -H 'Accept: application/vnd.github.html+json' | jq -c '.[0] | {body_html}'
# -> {"body_html":"<p>Reproduced <strong>twice</strong></p>\n"}
curl -s -X PATCH "$RYSTIC_URL/repos/tb-owner/tb-repo/issues/7" -H "$H" -d '{"state":"closed","state_reason":"completed"}' | jq -c '{state, state_reason}'
# -> {"state":"closed","state_reason":"completed"}
curl -s "$RYSTIC_URL/repos/tb-owner/tb-repo/issues/7/events" -H "$H" | jq -c '[.[].event]'
# -> ["labeled","closed"]
curl -s "$RYSTIC_URL/repos/tb-owner/tb-repo/issues/7/timeline" -H "$H" | jq -c '[.[].event]'
# -> ["commented","labeled","closed"]
One number sequence
A repository numbers its issues and its pull requests from the same counter, as GitHub does. In github-pulls_open_queue, which holds pull requests #41 to #44, the next thing opened, issue or pull request, is #45. A number addresses one or the other and never both, and GET .../issues/{n} on a pull request’s number returns that pull request rendered as an issue.
github-pulls_issue_tracker is the scenario to boot for issues: five issues around one pull request (#3), interleaved so the shared sequence is visible from the first read. Its conversation comments hang off both: two on #1, one on #2, one on #6 and one on the pull request. A pull request’s conversation comments live on the issues route that gh pr comment writes to, not on the diff-anchored .../pulls/{n}/comments.
Events and the timeline
GET .../issues/{n}/events lists a subject’s issue events oldest first: labeled, assigned, review requested, head ref force-pushed, merged, closed and the rest. A scenario’s events agree with its records: a closed issue logs its close, a merged pull request its merge and close, a locked subject its lock.
The writes below append the event GitHub logs, with its keys. Closing, reopening, renaming and merging count over REST or GraphQL. Locking and unlocking count over REST. Label, assignee and milestone changes count through the label and assignee routes, PATCH .../issues/{n}, or at creation.
| Write | Event | Keys |
|---|---|---|
| closing an issue or pull request | closed | state_reason, and intent (null) |
| reopening one | reopened | on an issue, state_reason: "reopened" |
| renaming one | renamed | rename |
| locking one | locked | lock_reason |
| unlocking one | unlocked | |
| merging a pull request | merged, then closed | |
| readying a pull request | ready_for_review | |
| drafting a pull request | convert_to_draft | |
| adding or removing a label | labeled or unlabeled | label {name, color} |
| adding or removing an assignee | assigned or unassigned | assignee, assigner |
| adding or removing a milestone | milestoned or demilestoned | milestone {title} |
Differs from GitHub: requesting a review or removing a request logs no event (review_requested, review_request_removed).
GET /repos/{owner}/{repo}/issues/events reads the repository’s whole feed, newest first. GET .../issues/events/{event_id} reads one of them.
Differs from GitHub: an event in the repository’s feed, or read by its id, carries no issue.
GET .../issues/{n}/timeline merges a subject’s events with these entries, ordered by time:
event | Entry |
|---|---|
commented | a conversation comment |
reviewed | a submitted review |
line_commented | the review comments of one review, grouped |
committed | a commit of a pull request |
The committed entries include every commit reachable from the pull request’s head, through every parent, and exclude each commit reachable from its base. The events list and the timeline page with per_page and page.
Rendered bodies
Every route that returns a body honors GitHub’s body media types:
Accept | Body fields |
|---|---|
application/vnd.github.html+json | body_html in place of body |
application/vnd.github.text+json | body_text in place of body |
application/vnd.github.full+json | body, body_text and body_html |
application/vnd.github.raw+json, or no media type | body, the Markdown as stored |
Reviews and review comments take the same suffixes under application/vnd.github-commitcomment.*.
Differs from GitHub: the HTML is CommonMark with GitHub’s tables, strikethrough, autolinks and task lists, not GitHub’s renderer. Mentions and issue references stay text.
Avatars
Every avatar_url names avatars.githubusercontent.com, which the simulator re-bases onto its own origin like api.github.com. /u/{id} (/in/{id} for an app) returns a PNG identicon drawn from the id: the same image for the same user every time, sized by ?s= up to 460. Nothing is fetched from GitHub.