Skip to main content

torii_lib/platforms/
runner.rs

1//! CI runners surface — list / show / remove / reset-token / pause / resume.
2//!
3//! GitLab exposes all six operations on shared/group/project runners.
4//! GitHub Actions exposes only list/show/remove on self-hosted runners
5//! (gating is done via labels, and self-hosted tokens are rotated
6//! manually on the runner host). Other platforms aren't covered here
7//! yet — calling `get_runner_client` on them returns `Unsupported`.
8
9use super::github::GitHubRunnerClient;
10use super::gitlab::GitLabRunnerClient;
11use crate::error::{Result, ToriiError};
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct Runner {
16    /// Platform-native ID (GitLab numeric; GitHub numeric).
17    pub id: String,
18    pub description: String,
19    /// Normalized: online | offline | paused | active | stale | other
20    pub status: String,
21    pub paused: bool,
22    pub ip_address: String,
23    pub os: String,
24    pub tags: Vec<String>,
25    pub version: String,
26    /// GitLab: instance_type | group_type | project_type.
27    /// GitHub: "self-hosted".
28    pub runner_type: String,
29    pub web_url: String,
30}
31
32#[allow(dead_code)]
33pub trait RunnerClient: Send {
34    fn list(&self, owner: &str, repo: &str) -> Result<Vec<Runner>>;
35    fn show(&self, owner: &str, repo: &str, id: &str) -> Result<Runner>;
36    fn remove(&self, owner: &str, repo: &str, id: &str) -> Result<()>;
37    /// Reset the runner's authentication token; returns the new token
38    /// the operator must paste into the runner's config.
39    fn reset_token(&self, owner: &str, repo: &str, id: &str) -> Result<String>;
40    fn pause(&self, owner: &str, repo: &str, id: &str) -> Result<()>;
41    fn resume(&self, owner: &str, repo: &str, id: &str) -> Result<()>;
42    /// Obtain a short-lived registration token. `torii runner register`
43    /// uses it to wrap the platform's CLI (gitlab-runner register,
44    /// ./config.sh on GitHub Actions). Returns (token, register_url) —
45    /// the URL is the value the CLI wants for its `--url` arg.
46    fn registration_token(&self, owner: &str, repo: &str) -> Result<RegistrationToken>;
47}
48
49#[derive(Debug, Clone)]
50pub struct RegistrationToken {
51    pub token: String,
52    /// URL the runner CLI expects (e.g. https://gitlab.com or
53    /// https://github.com/<owner>/<repo>).
54    pub register_url: String,
55    /// Expiry hint in seconds, when the platform reports one. GitHub
56    /// gives ~1h; GitLab tokens don't expire until you regenerate.
57    pub expires_in_seconds: Option<u64>,
58}
59
60// ============================================================================
61// GitLab
62// ============================================================================
63
64pub(crate) fn set_paused(
65    c: &GitLabRunnerClient,
66    _owner: &str,
67    _repo: &str,
68    id: &str,
69    paused: bool,
70) -> Result<()> {
71    let url = format!("{}/runners/{}", c.base_url, id);
72    let req = c
73        .client()
74        .put(&url)
75        .header("Authorization", c.auth())
76        .json(&serde_json::json!({ "paused": paused }));
77    crate::http::send_empty(req, &format!("GitLab set runner.paused={}", paused))
78}
79
80pub fn get_runner_client(platform: &str) -> Result<Box<dyn RunnerClient>> {
81    match platform {
82        "gitlab" => Ok(Box::new(GitLabRunnerClient::new()?)),
83        "github" => Ok(Box::new(GitHubRunnerClient::new()?)),
84        other => Err(ToriiError::Unsupported(format!(
85            "Runners surface not implemented for `{}` yet. \
86             Supported: github, gitlab.",
87            other
88        ))),
89    }
90}