Queued issues are never dropped on the way to an agent, and a start that fails says why
ready_issues took an issue off the queue, then looked its repository up as whoever queued it; when that lookup failed the issue was lost: no pull request, no comment, no retry. Planned issues opened with 'assign g1t' sat untouched. The address now comes from path_by_id (the runner checks permissions when it starts), and an issue that cannot be handed over goes back on the queue. The runner logs every start it could not make, and says on the issue when one is refused for good.
2 files+24−240/2 viewed
| 1896 | 1896 | if (started.ok) continue; | |
| 1897 | 1897 | // Waiting for a slot: `run` put it back in the queue itself. | |
| 1898 | 1898 | if (isWaiting(started.error.message)) continue; | |
| 1899 | − | // The workspace's plan refused it: said on the issue, once, rather | |
| 1900 | − | // than tried again every few minutes. | |
| 1901 | − | if (started.error.code === "payment_required") { | |
| 1899 | + | const where = `${issue.repo.namespace}/${issue.repo.name}#${issue.number}`; | |
| 1900 | + | console.error(`startReady: ${where} not started (${started.error.code}): ${started.error.message}`); | |
| 1901 | + | // Refused for good (the plan, or who queued it may not run agents | |
| 1902 | + | // here): said on the issue, once, rather than tried again every few | |
| 1903 | + | // minutes with nothing to show for it. | |
| 1904 | + | if (started.error.code === "payment_required" || started.error.code === "forbidden") { | |
| 1902 | 1905 | await agentsClient(this.env.WORK) | |
| 1903 | 1906 | .agentComment(issue.repo, issue.number, `I could not start on this: ${started.error.message}`) | |
| 1904 | 1907 | .catch(() => false); |
| 12 | 12 | ||
| 13 | 13 | use g1t_contracts::access::Capability; | |
| 14 | 14 | use g1t_contracts::events::IssueEvent; | |
| 15 | − | use g1t_contracts::repos::{GetByIdArgs, Repo, RepoPath}; | |
| 15 | + | use g1t_contracts::repos::{PathByIdArgs, Repo, RepoPath}; | |
| 16 | 16 | use g1t_contracts::time::rfc3339; | |
| 17 | 17 | use g1t_contracts::work::*; | |
| 18 | 18 | use g1t_contracts::{FailureCode, Outcome, User, new_id}; | |
| 620 | 620 | continue; | |
| 621 | 621 | } | |
| 622 | 622 | *left -= 1; | |
| 623 | − | // Whoever queued it could see the repository then; where it is | |
| 624 | − | // now is asked as them. | |
| 625 | − | let repo: Outcome<Repo> = g1t_kit::call( | |
| 626 | − | &self.repos, | |
| 627 | − | "get_by_id", | |
| 628 | − | &GetByIdArgs { | |
| 629 | − | id: row.repo_id.clone(), | |
| 630 | − | viewer: Some(actor.clone()), | |
| 631 | − | }, | |
| 632 | − | ) | |
| 633 | − | .await?; | |
| 634 | − | if let Outcome::Ok(repo) = repo { | |
| 635 | − | ready.push(ReadyIssue { | |
| 636 | − | repo: RepoPath { | |
| 637 | − | namespace: repo.namespace, | |
| 638 | − | name: repo.name, | |
| 639 | − | }, | |
| 640 | − | number: row.number, | |
| 641 | − | actor, | |
| 642 | − | }); | |
| 623 | + | // Where the repository is now. Who may run agents there is the | |
| 624 | + | // runner's question when it starts the issue, asked as whoever | |
| 625 | + | // queued it; this only needs the address. | |
| 626 | + | let path: Option<RepoPath> = | |
| 627 | + | g1t_kit::call(&self.repos, "path_by_id", &PathByIdArgs { id: row.repo_id.clone() }).await?; | |
| 628 | + | match path { | |
| 629 | + | Some(repo) => ready.push(ReadyIssue { repo, number: row.number, actor }), | |
| 630 | + | None => { | |
| 631 | + | // Taken but not handed over: put it back, so it is not | |
| 632 | + | // lost with nothing said. | |
| 633 | + | worker::console_error!("ready_issues: no path for {}; issue #{} stays queued", row.repo_id, row.number); | |
| 634 | + | self.db | |
| 635 | + | .prepare("UPDATE issues SET queued_by = ? WHERE repo_id = ? AND number = ?") | |
| 636 | + | .bind(&[row.queued_by.as_str().into(), row.repo_id.as_str().into(), row.number.into()])? | |
| 637 | + | .run() | |
| 638 | + | .await?; | |
| 639 | + | } | |
| 643 | 640 | } | |
| 644 | 641 | } | |
| 645 | 642 | Ok(ready) |