Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
122 linesCodeBlameRaw
1//! The program inside a g1t sandbox: it does one piece of work and
2//! reports back to g1t.
3//!
4//! `MODE` selects the work: `actions` runs one job of a GitHub Actions
5//! workflow, `queue` builds and checks a state of the merge queue,
6//! `mergecheck` finds out whether a pull request merges cleanly, `deploy`
7//! builds a commit for g1t.page, `bump` makes a dependency update (one
8//! package raised in its lockfiles, pushed as g1t), `backup` cuts a
9//! repository's nightly backup bundle, and `checks` runs an issue's
10//! commands, from before checks were workflows. See the modules of those
11//! names. It talks to g1t only through the public API and git.
12//!
13//! Every mode runs with the mining watch in `abuse`: a sandbox that looks
14//! like it is mining stops itself and exits with `abuse::EXIT_CODE`.
15//!
16//! Given a command instead (`register`, `run`, `service`, `remove`,
17//! `update`, `version`), it is a self-hosted runner on someone's own
18//! machine, which runs work in these modes: see `selfhosted`.
19//!
20//! `deploy` and `detect` are for a workflow step: a g1t.page build of the
21//! job's own checkout, and what that build would find (see `deploy`).
22
23mod abuse;
24mod actions;
25mod backup;
26mod bump;
27mod checks;
28mod clone;
29mod deploy;
30mod docker;
31mod guard;
32mod mergecheck;
33mod queue;
34mod selfhosted;
35
36use std::path::Path;
37use std::process::Command;
38
39use anyhow::{Context, Result, bail};
40use base64::Engine;
41use base64::engine::general_purpose::STANDARD;
42
43pub(crate) const WORKDIR: &str = "/work/repo";
44/// The name and address on every commit g1t makes here:
45/// `g1t_contracts::system::{USERNAME, EMAIL}`.
46pub(crate) const AUTHOR_NAME: &str = "g1t";
47pub(crate) const AUTHOR_EMAIL: &str = "g1t@users.noreply.g1t.sh";
48
49pub(crate) fn env(name: &str) -> Result<String> {
50 std::env::var(name).with_context(|| format!("{name} is not set"))
51}
52
53/// Runs git and returns its trimmed output, failing on a non-zero exit.
54pub(crate) fn git(dir: &Path, args: &[&str]) -> Result<String> {
55 let output = Command::new("git")
56 .current_dir(dir)
57 .args(args)
58 .output()
59 .context("could not run git")?;
60 if !output.status.success() {
61 bail!(
62 "git {} failed: {}",
63 args.first().unwrap_or(&""),
64 String::from_utf8_lossy(&output.stderr).trim()
65 );
66 }
67 Ok(String::from_utf8_lossy(&output.stdout).trim().to_owned())
68}
69
70/// A git option that authenticates one command. The credential is passed
71/// per command and never written to the clone's config or its remote URL,
72/// where the work's own commands would find it.
73pub(crate) fn auth_option(user: &str, token: &str) -> String {
74 let credentials = STANDARD.encode(format!("{user}:{token}"));
75 format!("http.extraHeader=Authorization: Basic {credentials}")
76}
77
78fn main() {
79 // The runc the job's Docker Engine starts containers with (docker/oci.rs).
80 if docker::oci::invoked_as_runc() {
81 docker::oci::main();
82 }
83 // A self-hosted runner's commands; the modes below are what it, and
84 // g1t's sandboxes, run work with.
85 let args: Vec<String> = std::env::args().skip(1).collect();
86 // From a workflow step: a g1t.page build of the job's own checkout,
87 // and what it would find (deploy.rs).
88 match args.first().map(String::as_str) {
89 Some("deploy") => std::process::exit(deploy::cli(&args[1..])),
90 Some("detect") => std::process::exit(deploy::detect_cli(&args[1..])),
91 _ => {}
92 }
93 if selfhosted::is_command(&args) {
94 std::process::exit(selfhosted::main(args));
95 }
96 // A sandbox whose HTTPS passes the Worker has it re-signed: trust that
97 // before anything is fetched.
98 guard::trust_egress_ca();
99 // Watches for mining for as long as the sandbox runs (abuse.rs).
100 abuse::watch();
101 // The work a sandbox is started for.
102 let code = match std::env::var("MODE").as_deref() {
103 Ok("actions") => actions::main(),
104 Ok("backup") => backup::main(),
105 Ok("bump") => bump::main(),
106 Ok("checks") => checks::main(),
107 Ok("deploy") => deploy::main(),
108 Ok("queue") => queue::main(),
109 Ok("mergecheck") => mergecheck::main(),
110 // Work g1t no longer does (an agent's run, a review, a plan, a
111 // catch-up merge), handed over by an older g1t: refused, saying so.
112 Ok(mode) => {
113 eprintln!("g1t-runner: this runner does not do work of mode {mode}");
114 2
115 }
116 Err(_) => {
117 eprintln!("g1t-runner: MODE is not set: nothing to do");
118 2
119 }
120 };
121 std::process::exit(code);
122}