src/issues/ holds the issue tracker code.
All code that starts gh is in src/issues/github/. No file outside src/issues/ imports from it, and a command gets a provider from the registry (see The issue provider). A boundary test enforces both rules.
Each member of the provider maps to one GitHub function and adds no behavior.
- The caller reads
updateOnStartandupdateOnFinishto decide if it calls a write at all. The provider does not read them. parsetakes the project, because one provider object serves many repos. It is the key parser itself, not a wrapper. Issue keys gives the rules.
The registry is the only place that reads issueTracker.provider. One configuration object gives one provider for each account.
- Limit: a caller that changes a configuration object after the first call gets the old provider.
- Limit: the jira tracker is not available in this version.
gh gets no input stream. Its output goes to pipes, never to the terminal, and nothing is logged.
The environment gives the rule that each call that acts on a repo names its repo.
An abort stops the call. It also stops the token call under the rule in The token. The version read makes no token call, because the version needs no login.
A gh api call sends a string value with -f and only a number with -F. -F turns digits, true, false and null into JSON types. A string such as a name of only digits, or true, then has the wrong type, and GitHub refuses the variable. -f always sends a string.
Before the first call of an account, tasker reads its token with gh auth token --hostname github.com --user <account>. With --user, gh 2.101.0 ignores GH_TOKEN.
Never call gh auth token with an empty user. With --user "", gh 2.101.0 falls back to the active account and prints its token.
- tasker reads the token one time for each account in one process. Concurrent calls share one read. A failed read is not kept, so the next call reads the token again.
- A caller with no signal never stops the read.
- A caller whose signal aborts rejects at once. The other callers keep waiting.
- When the last waiter leaves by an abort, the read stops and leaves the cache. As a result, a later caller starts a new read and never joins a stopped one.
The environment of the token call is in The environment.
- tasker removes each token variable,
GH_HOSTandGH_REPOof the caller. As a result, only the token of the account counts. Each call that acts on a repo must name the repo, because tasker removesGH_REPO, and without a repoghreads the repo from the git folder of its cwd. - gh gets no prompt, no update notice and no color.
- Other names pass as they are, for example
GH_CONFIG_DIR. - tasker does not add
process.env. - An empty
PATHfails beforeghstarts, so Node never uses thePATHof tasker. - The token call uses the same environment, without the new token.
The token never appears in an error. The token call never puts its stdout into an error, and a main call replaces the token with ***. No error has a cause.
The project and the number come from the stored key, so the stored spelling is used. See Issue keys.
gh 2.101.0 gives a pull request with exit code 0, and in any state. So the read finds a pull request from the path of its url: /<owner>/<repo>/issues/<N> for an issue and /<owner>/<repo>/pull/<N> for a pull request.
A missing repo stays a gh error here, because gh issue view gives only stderr.
The hint reads only the issue, so there is no second gh call. The read does not check the hint as a branch name. The base branch resolve checks it (see workspaces.md).
The base: prefix matches without regard to case, because GitHub label names are unique without regard to case. Only the ASCII letters A to Z fold. The text after the prefix keeps its case, because branch names use case.
The read gives every issue that blocks one GitHub issue.
- Only
numberuses-F, because the query needs anInt(see runGh()). - A cursor that comes again is bad output, so a cycle stops.
- The blocker key gets the spelling of the configuration when its repo is the project of the issue. The compare uses the same ASCII fold as the issue key resolve.
ghexits 1 for a GraphQL error and prints the JSON answer on stdout. So the read uses stdout after a failure only when it is a JSON object with adatakey or anerrorskey.- GitHub answers
issue: nullfor a pull request number. - The GraphQL answer says which part is null. So a missing repo is an issue read error here, but a gh error in the issue read.
The writes read nothing before they write. They rely on these facts of the gh 2.101.0 source:
pkg/cmd/issue/close/close.go: for an issue that is already closed,gh issue closeprints! Issue <owner>/<repo>#<N> (<title>) is already closedto stderr and exits 0. It makes this check before any change.pkg/cmd/pr/shared/editable.go:gh issue editbuilds the set of the current assignees. It replaces@mewith the login of the token user, adds the new login and sends the whole set. An assignee that the issue already has gives the same set, soghexits 0.pkg/cmd/pr/shared/editable_http.go:gh issue editsends a label through theaddLabelsToLabelablemutation. It does not read the labels of the issue first. Soghhas no separate path for a label that the issue already has.api/queries_repo.go:ghfinds the label id in the labels of the repo withstrings.EqualFold, so case does not matter. A name with no match fails with'<label>' not found.pkg/cmd/issue/edit/edit.go: a failed update printsfailed to update <issue url>: <error>to stderr. Thenghprintsfailed to update 1 issueand exits 1.
As a result, exit 0 is success and the code has no special case. The label must exist on GitHub. The writes do not create it.
The remove of the in-progress label uses gh issue edit --remove-label. The facts below come from a measure of gh 2.101.0 against a fake local HTTPS server:
- The issue has no such label, and the repo has it.
ghexits 0, prints the issue URL and sends theIssueByNumberquery, theRepositoryLabelListquery and theremoveLabelsFromLabelablemutation. - The issue has the label.
ghexits 0 and sends the same calls. Soghdoes not read the labels of the issue before the remove, and it has no separate path for a label that the issue does not have. - The repo does not have the label.
ghsends no mutation, printsfailed to update <issue url>: '<label>' not foundandfailed to update 1 issue, and exits 1.
Limit: the fake server answered the mutation, so nobody measured how GitHub itself answers a remove of a label that the issue does not have.
The assignee @me passes to gh as it is. gh resolves it to the user of the token, which is the account of the tracker.
gh issue edit splits the value of --add-label, of --remove-label and of --add-assignee on commas (gh 2.101.0).
Limit: the in-progress marker is a label only. GitHub Projects (v2) columns are not supported.
The compare of the login with the account uses the ASCII-only fold of GitHub names.
needsLogin is true only when a new login is the fix. An HTTP 401 of gh api user is such a case. An HTTP 502 is not.
For the timeout and signal rules, see doctor.md. The signal also covers the token call of each check.
tasker doctor asks each tracker once for its doctor checks, with all the repos of that tracker. As a result, the gh version check runs once, and the gh auth check runs once for each account of the github repos.
The minimum is 2.40.0, because gh auth token has the --user flag from that version. The source file pkg/cmd/auth/token/token.go has the flag at the tag v2.40.0, and has no flag at v2.39.2. The release notes of 2.40.0 add multi-account support.
The check ignores a suffix right after the patch number, for example -rc.1, so 2.40.0-rc.1 counts as 2.40.0.
Auth status says when the login remediation applies.
Only a failure of gh repo view itself names issueTracker.project in the remediation. Other failures, for example of the token call, give no project remediation.
- A missing in-progress label or done label is a warning, because the write only warns. A missing in-progress label makes the in-progress write of each
tasker startfail. A missing done label makes the done write of eachtasker finishfail. - The name is URL-encoded, so
/and a space are safe, and{and}never hit a placeholder thatgh apifills, such as{owner}. - GitHub gives the same 404 for a repo that does not exist, so the project check of the same repo names the real cause.
The checks never change GitHub. They make only these gh calls: gh --version, gh auth token, gh api user, gh repo view and a GET of gh api repos/<project>/labels/<name>. The check never creates a label.
Each test file that uses the token resets its modules before each test, so each test starts with an empty token cache. The token cache of the gh code stays the one home of the token read.
The registry test needs no reset, because each test builds its own configuration object.