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
118 linesCodeBlameRaw
1//! What g1t keeps and allows for a workspace, from wrangler vars, so an
2//! installation of its own can change them:
3//!
4//! - `LOG_RETENTION_DAYS` (90): a finished job's log is deleted this many
5//! days after it finished (retention.rs). `0` keeps logs for good.
6//! - `FREE_ACTIONS_STORAGE_BYTES` (2 GB): what a workspace without the
7//! plan may keep in artifacts and the cache together; past it, new
8//! uploads are refused (run_artifacts.rs, cache.rs). `0` is no cap.
9//! - `FREE_SELF_HOSTED_JOBS` (4): jobs a workspace without the plan may
10//! have running on its self-hosted runners at once (plan.rs). `0` is no
11//! cap.
12
13use worker::Env;
14
15pub const DEFAULT_LOG_RETENTION_DAYS: u64 = 90;
16pub const DEFAULT_FREE_ACTIONS_STORAGE_BYTES: u64 = 2 * 1024 * 1024 * 1024;
17pub const DEFAULT_FREE_SELF_HOSTED_JOBS: u64 = 4;
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub struct Limits {
21 pub log_retention_days: u64,
22 pub free_actions_storage_bytes: u64,
23 pub free_self_hosted_jobs: u64,
24}
25
26impl Default for Limits {
27 fn default() -> Self {
28 Limits {
29 log_retention_days: DEFAULT_LOG_RETENTION_DAYS,
30 free_actions_storage_bytes: DEFAULT_FREE_ACTIONS_STORAGE_BYTES,
31 free_self_hosted_jobs: DEFAULT_FREE_SELF_HOSTED_JOBS,
32 }
33 }
34}
35
36/// A var as a whole number, or `default` when it is unset or not one.
37pub fn number(raw: Option<String>, default: u64) -> u64 {
38 raw.and_then(|raw| raw.trim().parse().ok()).unwrap_or(default)
39}
40
41impl Limits {
42 pub fn from_env(env: &Env) -> Limits {
43 let var = |name: &str| env.var(name).ok().map(|value| value.to_string());
44 Limits {
45 log_retention_days: number(var("LOG_RETENTION_DAYS"), DEFAULT_LOG_RETENTION_DAYS),
46 free_actions_storage_bytes: number(var("FREE_ACTIONS_STORAGE_BYTES"), DEFAULT_FREE_ACTIONS_STORAGE_BYTES),
47 free_self_hosted_jobs: number(var("FREE_SELF_HOSTED_JOBS"), DEFAULT_FREE_SELF_HOSTED_JOBS),
48 }
49 }
50}
51
52/// `2 GB`, `1.5 GB`, `500 MB`: a cap as people read it.
53pub fn shown_bytes(bytes: u64) -> String {
54 const GB: u64 = 1024 * 1024 * 1024;
55 const MB: u64 = 1024 * 1024;
56 if bytes >= GB {
57 let tenths = (bytes * 10).div_ceil(GB);
58 if tenths % 10 == 0 { format!("{} GB", tenths / 10) } else { format!("{}.{} GB", tenths / 10, tenths % 10) }
59 } else {
60 format!("{} MB", bytes.div_ceil(MB))
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn vars_fall_back_to_their_defaults() {
70 assert_eq!(number(None, 90), 90);
71 assert_eq!(number(Some(" 30 ".into()), 90), 30);
72 assert_eq!(number(Some("0".into()), 90), 0);
73 assert_eq!(number(Some("ninety".into()), 90), 90);
74 assert_eq!(Limits::default().free_actions_storage_bytes, 2 * 1024 * 1024 * 1024);
75 }
76
77 #[test]
78 fn caps_read_in_gigabytes_or_megabytes() {
79 assert_eq!(shown_bytes(2 * 1024 * 1024 * 1024), "2 GB");
80 assert_eq!(shown_bytes(1536 * 1024 * 1024), "1.5 GB");
81 assert_eq!(shown_bytes(500 * 1024 * 1024), "500 MB");
82 }
83}
84
85/// Whether a workspace without the plan may start one more job on its
86/// self-hosted runners, with `running` going already. `cap` 0 is no cap.
87pub fn self_hosted_room(running: u64, cap: u64) -> bool {
88 cap == 0 || running < cap
89}
90
91impl crate::Actions {
92 /// The workspace's standing with billing: its plan, and any hold staff
93 /// put on it. None when billing does not answer: callers let things
94 /// through rather than stop everyone's work on billing's account.
95 pub(crate) async fn standing(&self, workspace: &str) -> Option<g1t_contracts::billing::Entitlements> {
96 let args = g1t_contracts::billing::EntitlementsArgs { workspace: workspace.to_lowercase() };
97 match g1t_kit::call(&self.billing, "entitlements", &args).await {
98 Ok(entitlements) => Some(entitlements),
99 Err(error) => {
100 worker::console_error!("actions: billing's entitlements for {workspace} failed: {error}");
101 None
102 }
103 }
104 }
105}
106
107#[cfg(test)]
108mod room_tests {
109 use super::self_hosted_room;
110
111 #[test]
112 fn free_workspaces_run_a_few_self_hosted_jobs_at_once() {
113 assert!(self_hosted_room(0, 4));
114 assert!(self_hosted_room(3, 4));
115 assert!(!self_hosted_room(4, 4));
116 assert!(self_hosted_room(400, 0));
117 }
118}