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
87 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

The sidebar asks billing whether it is on, not for the account, so no page waits on the card processor1//! Where a job's files go. In g1t's sandbox (and in a container a
2//! self-hosted runner starts) that is GitHub's layout under `/home/runner`.
3//! A self-hosted runner running a job directly on its machine
4//! (`g1t-runner run --no-docker`) sets `G1T_RUNNER_ROOT` to a folder of its
5//! own, and every `/home/runner` path, the spec's included, moves there.
6
7use std::path::PathBuf;
8
9use serde_json::Value;
10
11/// GitHub's runner layout, as g1t's sandbox has it.
12pub(crate) const HOME_RUNNER: &str = "/home/runner";
13
14/// The folder standing in for `/home/runner`, if the job runs elsewhere.
15pub(crate) fn root() -> Option<PathBuf> {
16 std::env::var_os("G1T_RUNNER_ROOT").filter(|root| !root.is_empty()).map(PathBuf::from)
17}
18
19/// `path` (under `/home/runner`) where this job keeps it.
20pub(crate) fn under_home(path: &str) -> PathBuf {
21 match (root(), path.strip_prefix(HOME_RUNNER)) {
22 (Some(root), Some(rest)) => root.join(rest.trim_start_matches('/')),
23 _ => PathBuf::from(path),
24 }
25}
26
27/// A path as the job's processes see it: with forward slashes, which every
28/// shell takes, Windows' included.
29pub(crate) fn shown(path: &std::path::Path) -> String {
30 let text = path.display().to_string();
31 if cfg!(windows) { text.replace('\\', "/") } else { text }
32}
33
34/// `text` with `/home/runner` moved to `root`.
35fn moved(text: &str, root: &str) -> String {
36 match text.strip_prefix(HOME_RUNNER) {
37 Some(rest) if rest.is_empty() || rest.starts_with('/') => format!("{root}{rest}"),
38 _ => text.to_owned(),
39 }
40}
41
42/// Moves every `/home/runner` path in a spec's values (its variables,
43/// `github` and `runner` contexts) to where this job keeps its files.
44pub(crate) fn relocate(value: &mut Value) {
45 let Some(root) = root() else { return };
46 relocate_to(value, &shown(&root));
47}
48
49fn relocate_to(value: &mut Value, root: &str) {
50 match value {
51 Value::String(text) => *text = moved(text, root),
52 Value::Array(items) => items.iter_mut().for_each(|item| relocate_to(item, root)),
53 Value::Object(fields) => fields.values_mut().for_each(|item| relocate_to(item, root)),
54 _ => {}
55 }
56}
57
58/// How `PATH` separates its entries here.
59pub(crate) const PATH_SEPARATOR: &str = if cfg!(windows) { ";" } else { ":" };
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use serde_json::json;
65
66 #[test]
67 fn home_runner_paths_move_and_nothing_else_does() {
68 let mut spec = json!({
69 "GITHUB_WORKSPACE": "/home/runner/work/repo",
70 "RUNNER_TEMP": "/home/runner/_temp",
71 "HOME": "/home/runnerish",
72 "list": ["/home/runner", "/etc/hosts"],
73 "n": 3,
74 });
75 relocate_to(&mut spec, "/srv/jobs/job_1");
76 assert_eq!(
77 spec,
78 json!({
79 "GITHUB_WORKSPACE": "/srv/jobs/job_1/work/repo",
80 "RUNNER_TEMP": "/srv/jobs/job_1/_temp",
81 "HOME": "/home/runnerish",
82 "list": ["/srv/jobs/job_1", "/etc/hosts"],
83 "n": 3,
84 })
85 );
86 }
87}

This file's history is long; its oldest lines are credited to the oldest commit read.