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

Commit

An agent can turn on a project's g1t.page deployments, and have them from the moment it makes the repository: an agent (or Claude Code through the g1t MCP) could create a repository but not make it deploy, so flagon-io/session-lab needed a person to click Turn on deployments under Settings → Deployments, because only the site (that page, and Ask's new-repository card) called the deployments service's update_settings, and update_project's runs does not turn them on. Two operations now read and change a project's deployment settings as that page does, through the same service call: get_deployment_settings (GET /workspaces/{workspace}/projects/{project}/deployments/settings, deployments:read, the Read role as the service checks) and update_deployment_settings (PATCH there, repo:admin, the Admin role the page and the service require), in snake_case: enabled, production, previews, build_as, build_command, output_dir, dockerfile, instance_type, idle_days, via_workflow, with production_url, primary_domain, detected and framework read back. enabled true builds production at once; without the g1t plan it is refused with 402 payment_required saying where an owner starts it, without Admin with 403, and for a library, tool or other with 409, as the site says; bad values are refused with 422 before the service is asked. They are actions of the MCP workflow tool next to the deployment actions, and the CLI has g1t deploy settings (flags such as --previews off and --build-command change them), g1t deploy enable and g1t deploy disable, which find the current repository's project as g1t logs does (logs' resolve_project). create_repo takes deploy (REST, the repository tool's create, g1t repo create --deploy): once the repository is made its project's deployments are turned on as Ask's card does it, as its creator, and the response adds deployments (the settings) and deploy_error (why not); the repository is made either way, and deploy needs no scope beyond repo:create since the creator is its admin. Run credentials never change deployment settings (NEVER), and get_deployment_settings is a read for the audit log. The site keeps its own calls (ask.ts and the Settings page call the same service method the API now does). The deployments guide (Without the site, Settings), the deployments API guide, projects, the CLI guide, the MCP reference, reference.json and the regenerated openapi.json, CLI ops table and CLI reference say how. Deploy order: api, then the docs; the CLI commands ship with the next CLI release; no service or migration changes. Checked: cargo test -p g1t-api (211; openapi regenerated with G1T_WRITE_OPENAPI), -p g1t-contracts (316), -p g1t (110 + 51; ops_table and cli_reference regenerated with G1T_WRITE_OPS and G1T_WRITE_DOCS); cargo check -p g1t-api --target wasm32-unknown-unknown; node --test apps/web/app/lib/token-scopes.test.ts.

G1t-Session:
ses_01m4z48a8be8mbhv0qt30jyg0j
syntaqxcommitted Parent047ae5fBrowse files
22 files+530−126 files not counted0/22 viewed
+252−3
77 //! them (Read to see, Write to report) and keeps them; deployments travel
88 //! in `snake_case` between services too, so a request's fields reach it
99 //! as they are, and a deployment's `payload` comes back as it was given.
10+//!
11+//! A project's g1t.page settings (whether it deploys at all, and how it
12+//! builds) are here too, named by workspace and project as the project
13+//! routes are: the deployments service keeps them in `camelCase`, as the
14+//! site reads them, so they are shaped here.
1015
1116 use g1t_contracts::{FailureCode, Outcome, Viewer};
1217 use serde_json::{Map, Value, json};
2530 ListEnvironments,
2631 GetEnvironment,
2732 StartDeploymentBuild,
33+ GetDeploymentSettings,
34+ UpdateDeploymentSettings,
2835 }
2936
3037 /// The states a deployment's status can have.
3138 const STATES: [&str; 6] = ["queued", "in_progress", "success", "failure", "error", "inactive"];
3239
40+/// What a project may be built as (`BUILD_AS` in @g1t/contracts).
41+const BUILD_AS: [&str; 4] = ["auto", "static", "workers", "docker"];
42+
43+/// The sizes a container app runs at (`APP_INSTANCES` in @g1t/contracts).
44+const INSTANCE_TYPES: [&str; 6] = ["lite", "basic", "standard-1", "standard-2", "standard-3", "standard-4"];
45+
3346 impl DeploymentsOp {
3447 /// Every one: `Op::ALL` lists each as `Op::Deployments(…)`, which a
3548 /// test checks against this.
3649 #[cfg(test)]
37− pub const ALL: [DeploymentsOp; 8] = [
50+ pub const ALL: [DeploymentsOp; 10] = [
3851 DeploymentsOp::ListDeployments,
3952 DeploymentsOp::GetDeployment,
4053 DeploymentsOp::CreateDeployment,
4356 DeploymentsOp::ListEnvironments,
4457 DeploymentsOp::GetEnvironment,
4558 DeploymentsOp::StartDeploymentBuild,
59+ DeploymentsOp::GetDeploymentSettings,
60+ DeploymentsOp::UpdateDeploymentSettings,
4661 ];
4762
4863 pub fn name(self) -> &'static str {
5570 DeploymentsOp::ListEnvironments => "list_environments",
5671 DeploymentsOp::GetEnvironment => "get_environment",
5772 DeploymentsOp::StartDeploymentBuild => "start_deployment_build",
73+ DeploymentsOp::GetDeploymentSettings => "get_deployment_settings",
74+ DeploymentsOp::UpdateDeploymentSettings => "update_deployment_settings",
5875 }
5976 }
6077
6986 DeploymentsOp::ListEnvironments => "List environments",
7087 DeploymentsOp::GetEnvironment => "Get an environment",
7188 DeploymentsOp::StartDeploymentBuild => "Start a g1t.page build from a workflow",
89+ DeploymentsOp::GetDeploymentSettings => "Get a project's deployment settings",
90+ DeploymentsOp::UpdateDeploymentSettings => "Update a project's deployment settings",
7291 }
7392 }
7493
82101 DeploymentsOp::ListEnvironments => "List the environments a repository's deployments went to, those people use directly first (production by name before others), then the most recently deployed, and after them those with protection rules but no deployment yet. Each has its name, url (where its current deployment is served), production_environment, transient_environment, deployments_count, latest (its newest deployment, whatever its state), current (its newest successful deployment that is still active) and updated_at, and, when it has rules, protection_rules (required_reviewers, wait_timer, branch_policy), deployment_branch_policy, branch_policies and can_admins_bypass. total_count counts deployments across every environment. Needs the Read role.",
83102 DeploymentsOp::GetEnvironment => "Get one environment by name, matched without regard to case, with its current and latest deployments and its protection rules (see update_environment). An environment with rules but no deployment yet is found too. A name with slashes is URL-encoded in the path. Needs the Read role.",
84103 DeploymentsOp::StartDeploymentBuild => "From a running g1t Actions job, open a g1t.page build of one of the repository's projects, which the job then builds in its own checkout with `g1t-runner deploy` (the g1t/deploy action does both). Only the job's own token (G1T_TOKEN) may call it, with `deployments: write` (or `pages: write`) in its `permissions:`. project is the project's slug; it may be left out when the repository has one project with Deployments on. The run decides what is built: a run on the project's production branch (a push, a dispatch or a schedule there) builds production, a pull request's run builds that pull request's preview, and a run on another branch builds that branch's preview; a tag's run is refused. sha is the commit built, the run's unless you say. A pull request from a fork or from someone without the Write role, and a run that had to be approved, get no build secrets. The workspace's plan must include Deployments and its spend limit must not be reached; the build's time is the job's Actions time and is not charged again. Returns deploy_id and token (DEPLOY_ID and DEPLOY_TOKEN for `g1t-runner deploy`, the token good for this build alone until it finishes), kind (production or preview), project, branch, number, commit, url (the app's address), trusted, the project's root_dir, build_command and output_dir, build_as (auto, static, workers or docker) and dockerfile (its Dockerfile's path, or null to find `Dockerfile` or `Containerfile`), and build_env and build_secrets (the project's variables and secrets for builds of that kind). The commit's `g1t / deploy` check is pending until the build reports.",
104+ DeploymentsOp::GetDeploymentSettings => "A project's g1t.page deployment settings, as its Settings → Deployments page shows them: whether Deployments are on (`enabled`; off for every project until someone turns them on), whether the default branch deploys to production on every push (`production`) and every pull request gets a preview (`previews`), production's address (`production_url`, and `primary_domain` once a custom domain serves it), how it builds (`build_as`: auto, static, workers or docker; `build_command`, `output_dir`, `dockerfile` and `instance_type` for a container; `via_workflow` to build through the deploy workflow g1t manages), how many days an unvisited preview stays up (`idle_days`), and what its last finished build found it to be (`detected`: workers, static, html or container, with `framework`), null before one has. A repository's own project has its name. Needs the Read role on the project's repository.",
105+ DeploymentsOp::UpdateDeploymentSettings => "Change a project's g1t.page deployment settings; only what you give changes. `enabled: true` turns Deployments on, as Turn on deployments under Settings → Deployments does: production starts building from the default branch at once, and every pull request gets a preview. That needs the g1t plan on the workspace (refused with payment_required otherwise; an owner starts it under Billing), and is refused for a project set to be a library, a tool or other until update_project makes it an app. `enabled: false` takes production and every preview down now and stops building; turning `production` or `previews` off takes those apps down. build_command, output_dir and dockerfile take null or \"\" to go back to what is found by itself; idle_days is 1 to 90; build_as is auto, static, workers or docker; instance_type is a container size (lite, basic, standard-1 to standard-4). Needs the Admin role on the project's repository. Returns the settings as get_deployment_settings does.",
85106 }
86107 }
87108
89110 pub fn writes(self) -> bool {
90111 matches!(
91112 self,
92− DeploymentsOp::CreateDeployment | DeploymentsOp::CreateDeploymentStatus | DeploymentsOp::StartDeploymentBuild
113+ DeploymentsOp::CreateDeployment
114+ | DeploymentsOp::CreateDeploymentStatus
115+ | DeploymentsOp::StartDeploymentBuild
116+ | DeploymentsOp::UpdateDeploymentSettings
93117 )
94118 }
95119
120+ /// Whether it is about one repository, named by `repo`: a project's
121+ /// settings are named by its workspace and slug instead.
122+ pub fn needs_repo(self) -> bool {
123+ !matches!(self, DeploymentsOp::GetDeploymentSettings | DeploymentsOp::UpdateDeploymentSettings)
124+ }
125+
96126 pub fn input(self) -> Value {
97127 let repo = json!({ "type": "string", "description": "Repository as \"owner/name\", e.g. \"flagon-io/hello\"." });
98128 let id = json!({ "type": "string", "description": "The deployment's id: dep_… for a reported one, dpl_… for a g1t.page build." });
99129 let state = |what: &str| json!({ "type": "string", "enum": STATES, "description": what });
100130 let address = |what: &str| json!({ "type": "string", "description": what });
131+ let workspace = json!({ "type": "string", "description": "The workspace's slug, e.g. \"flagon-io\"." });
132+ let project = json!({ "type": "string", "description": "The project's slug, as in g1t.sh/{workspace}/{project}; a repository's own project has its name." });
101133 let (properties, required): (Value, &[&str]) = match self {
102134 DeploymentsOp::ListDeployments => (
103135 json!({
160192 }),
161193 &["repo", "environment"],
162194 ),
195+ DeploymentsOp::GetDeploymentSettings => (json!({ "workspace": workspace, "project": project }), &["workspace", "project"]),
196+ DeploymentsOp::UpdateDeploymentSettings => (
197+ json!({
198+ "workspace": workspace,
199+ "project": project,
200+ "enabled": { "type": "boolean", "description": "Turn Deployments on (production builds at once; needs the g1t plan) or off (every app comes down)." },
201+ "production": { "type": "boolean", "description": "Deploy the default branch to production on every push." },
202+ "previews": { "type": "boolean", "description": "A preview for every branch with an open pull request." },
203+ "build_as": { "type": "string", "enum": BUILD_AS, "description": "What it is built as; auto finds it from its files." },
204+ "build_command": { "type": ["string", "null"], "description": "Runs instead of the project's own build script; null or \"\" for its own." },
205+ "output_dir": { "type": ["string", "null"], "description": "What a static site serves; null or \"\" to find it by itself." },
206+ "dockerfile": { "type": ["string", "null"], "description": "For a container: its Dockerfile's path inside the project; null or \"\" for Dockerfile or Containerfile." },
207+ "instance_type": { "type": "string", "enum": INSTANCE_TYPES, "description": "For a container: the size it runs at." },
208+ "idle_days": { "type": "integer", "minimum": 1, "maximum": 90, "description": "A preview no one visits for this many days comes down." },
209+ "via_workflow": { "type": "boolean", "description": "Build through the deploy workflow g1t manages for the project, listed under Actions." },
210+ }),
211+ &["workspace", "project"],
212+ ),
163213 };
164214 json!({ "type": "object", "properties": properties, "required": required })
165215 }
267317 out.insert("name".into(), name.into());
268318 "get_environment"
269319 }
320+ // Named by workspace and project: see `settings_args`.
321+ DeploymentsOp::GetDeploymentSettings | DeploymentsOp::UpdateDeploymentSettings => {
322+ return Err("Name the workspace and the project.".to_owned());
323+ }
270324 };
271325 Ok((method, out))
272326 }
273327
328+/// The project a settings operation names, as the deployments service
329+/// takes it.
330+fn project_ref(input: &Value) -> std::result::Result<Value, String> {
331+ let slug = |key: &str| input[key].as_str().map(str::trim).filter(|value| !value.is_empty()).map(str::to_lowercase);
332+ let workspace = slug("workspace").ok_or("Give the workspace's slug.")?;
333+ let project = slug("project").ok_or("Give the project's slug.")?;
334+ Ok(json!({ "workspace": workspace, "slug": project }))
335+}
336+
337+/// The change an update asks for, in the deployments service's spelling:
338+/// only what it gives. `Err` says what is wrong.
339+pub(crate) fn settings_changes(input: &Value) -> std::result::Result<Map<String, Value>, String> {
340+ let mut out = Map::new();
341+ for (key, field) in [("enabled", "enabled"), ("production", "production"), ("previews", "previews"), ("via_workflow", "viaWorkflow")] {
342+ match input.get(key) {
343+ None | Some(Value::Null) => {}
344+ Some(_) => {
345+ let value = flag(input, key).ok_or(format!("{key} must be true or false."))?;
346+ out.insert(field.into(), value.into());
347+ }
348+ }
349+ }
350+ // Text that "" or null puts back to what is found by itself.
351+ for (key, field) in [("build_command", "buildCommand"), ("output_dir", "outputDir"), ("dockerfile", "dockerfile")] {
352+ match input.get(key) {
353+ None => {}
354+ Some(Value::Null) => {
355+ out.insert(field.into(), Value::Null);
356+ }
357+ Some(Value::String(text)) if text.trim().chars().count() > 500 => {
358+ return Err(format!("{key} is at most 500 characters."));
359+ }
360+ Some(Value::String(text)) if text.trim().is_empty() => {
361+ out.insert(field.into(), Value::Null);
362+ }
363+ Some(Value::String(text)) => {
364+ out.insert(field.into(), text.trim().into());
365+ }
366+ Some(_) => return Err(format!("{key} must be a string or null.")),
367+ }
368+ }
369+ let word = |key: &str, allowed: &[&str]| -> std::result::Result<Option<String>, String> {
370+ match input.get(key) {
371+ None | Some(Value::Null) => Ok(None),
372+ Some(Value::String(text)) if allowed.contains(&text.trim()) => Ok(Some(text.trim().to_owned())),
373+ Some(_) => Err(format!("{key} must be one of {}.", allowed.join(", "))),
374+ }
375+ };
376+ if let Some(build_as) = word("build_as", &BUILD_AS)? {
377+ out.insert("buildAs".into(), build_as.into());
378+ }
379+ if let Some(instance_type) = word("instance_type", &INSTANCE_TYPES)? {
380+ out.insert("instanceType".into(), instance_type.into());
381+ }
382+ match input.get("idle_days") {
383+ None | Some(Value::Null) => {}
384+ Some(_) => match number(input, "idle_days") {
385+ Some(days @ 1..=90) => {
386+ out.insert("idleDays".into(), days.into());
387+ }
388+ _ => return Err("idle_days must be a whole number from 1 to 90.".to_owned()),
389+ },
390+ }
391+ Ok(out)
392+}
393+
394+/// A project's deployment settings as the deployments service answers
395+/// (`camelCase`), as the API shows them.
396+pub(crate) fn settings_json(settings: &Value, project: &Value) -> Value {
397+ json!({
398+ "workspace": project["workspace"],
399+ "project": project["slug"],
400+ "enabled": settings["enabled"].as_bool().unwrap_or(false),
401+ "production": settings["production"].as_bool().unwrap_or(true),
402+ "previews": settings["previews"].as_bool().unwrap_or(true),
403+ "production_url": settings["productionUrl"],
404+ "primary_domain": settings["primaryDomain"],
405+ "build_as": settings["buildAs"].as_str().unwrap_or("auto"),
406+ "build_command": settings["buildCommand"],
407+ "output_dir": settings["outputDir"],
408+ "dockerfile": settings["dockerfile"],
409+ "instance_type": settings["instanceType"].as_str().unwrap_or(INSTANCE_TYPES[0]),
410+ "idle_days": settings["idleDays"],
411+ "via_workflow": settings["viaWorkflow"].as_bool().unwrap_or(false),
412+ "detected": settings["detected"],
413+ "framework": settings["framework"],
414+ })
415+}
416+
417+/// Reads or changes a project's deployment settings.
418+async fn run_settings(op: DeploymentsOp, services: &Services, viewer: &Viewer, input: &Value) -> Result<Outcome<Value>> {
419+ let project = match project_ref(input) {
420+ Ok(project) => project,
421+ Err(message) => return Ok(Outcome::fail(FailureCode::Invalid, message)),
422+ };
423+ let answered: Outcome<Value> = if op == DeploymentsOp::GetDeploymentSettings {
424+ g1t_kit::call(&services.deployments, "settings", &json!({ "project": project, "viewer": viewer })).await?
425+ } else {
426+ let Some(actor) = viewer else {
427+ return Ok(Outcome::fail(FailureCode::Unauthenticated, "Changing deployment settings needs a g1t access token."));
428+ };
429+ let changes = match settings_changes(input) {
430+ Ok(changes) => changes,
431+ Err(message) => return Ok(Outcome::fail(FailureCode::Invalid, message)),
432+ };
433+ g1t_kit::call(&services.deployments, "update_settings", &json!({ "actor": actor, "project": project, "changes": changes })).await?
434+ };
435+ Ok(match answered {
436+ Outcome::Ok(settings) => Outcome::Ok(settings_json(&settings, &project)),
437+ Outcome::Fail(failure) => Outcome::Fail(failure),
438+ })
439+}
440+
441+/// For create_repo with `deploy`: turns Deployments on for the new
442+/// repository's own project (named as the repository is), as the site's
443+/// new-repository card does, as the person who made it. Never fails the
444+/// creation: what it answers is `deployments` (the settings) or
445+/// `deploy_error` (why they are not on: no g1t plan, a role too low).
446+pub(crate) async fn turn_on_for_new_repo(services: &Services, actor: &Value, namespace: &str, name: &str) -> (Value, Value) {
447+ let project = json!({ "workspace": namespace.to_lowercase(), "slug": name.to_lowercase() });
448+ let asked = json!({ "actor": actor, "project": project, "changes": { "enabled": true } });
449+ let answered: Result<Outcome<Value>> = g1t_kit::call(&services.deployments, "update_settings", &asked).await;
450+ match answered {
451+ Ok(Outcome::Ok(settings)) => (settings_json(&settings, &project), Value::Null),
452+ Ok(Outcome::Fail(failure)) => (Value::Null, failure.message.into()),
453+ Err(error) => {
454+ worker::console_error!("created {namespace}/{name} without deployments: {error}");
455+ (
456+ Value::Null,
457+ "Deployments could not be turned on. Try again with update_deployment_settings, or from the project's Settings → Deployments.".into(),
458+ )
459+ }
460+ }
461+}
462+
274463 pub async fn run(op: DeploymentsOp, services: &Services, viewer: &Viewer, input: &Value) -> Result<Outcome<Value>> {
464+ if !op.needs_repo() {
465+ return run_settings(op, services, viewer, input).await;
466+ }
275467 let (method, mut args) = match args(op, input) {
276468 Ok(found) => found,
277469 Err(message) => return Ok(Outcome::fail(FailureCode::Invalid, message)),
331523 fn each_operation_is_described_with_a_schema() {
332524 for op in DeploymentsOp::ALL {
333525 assert!(!op.title().is_empty() && op.description().len() > 40, "{}", op.name());
334− assert!(op.input()["required"].as_array().unwrap().contains(&json!("repo")), "{}", op.name());
526+ let required = op.input()["required"].as_array().unwrap().clone();
527+ if op.needs_repo() {
528+ assert!(required.contains(&json!("repo")), "{}", op.name());
529+ } else {
530+ assert_eq!(required, vec![json!("workspace"), json!("project")], "{}", op.name());
531+ }
335532 }
533+ assert!(DeploymentsOp::UpdateDeploymentSettings.writes());
534+ assert!(!DeploymentsOp::GetDeploymentSettings.writes());
535+ }
536+
537+ #[test]
538+ fn a_settings_change_is_only_what_is_given_in_the_service_s_spelling() {
539+ let sent = settings_changes(&json!({
540+ "workspace": "flagon-io", "project": "web", "enabled": true, "previews": "false",
541+ "build_command": " npm run build ", "output_dir": "", "dockerfile": null,
542+ "build_as": "static", "instance_type": "basic", "idle_days": "14", "ignored": 1,
543+ }))
544+ .unwrap();
545+ assert_eq!(
546+ Value::Object(sent),
547+ json!({
548+ "enabled": true, "previews": false, "buildCommand": "npm run build", "outputDir": null,
549+ "dockerfile": null, "buildAs": "static", "instanceType": "basic", "idleDays": 14,
550+ })
551+ );
552+ assert!(settings_changes(&json!({})).unwrap().is_empty());
553+ assert_eq!(Value::Object(settings_changes(&json!({ "enabled": false })).unwrap()), json!({ "enabled": false }));
554+ for bad in [
555+ json!({ "enabled": "yes" }),
556+ json!({ "build_as": "container" }),
557+ json!({ "instance_type": "huge" }),
558+ json!({ "idle_days": 0 }),
559+ json!({ "idle_days": 91 }),
560+ json!({ "build_command": 3 }),
561+ json!({ "output_dir": "x".repeat(501) }),
562+ ] {
563+ assert!(settings_changes(&bad).is_err(), "{bad}");
564+ }
565+ assert!(project_ref(&json!({ "workspace": "acme" })).is_err());
566+ assert_eq!(project_ref(&json!({ "workspace": " Acme ", "project": "Web" })).unwrap(), json!({ "workspace": "acme", "slug": "web" }));
567+ }
568+
569+ #[test]
570+ fn settings_are_snake_case() {
571+ let shown = settings_json(
572+ &json!({
573+ "enabled": true, "previews": true, "production": true, "buildCommand": null, "outputDir": "dist",
574+ "idleDays": 7, "viaWorkflow": false, "productionUrl": "https://web-acme.g1t.page", "primaryDomain": null,
575+ "detected": "static", "framework": "astro", "buildAs": "auto", "dockerfile": null, "instanceType": "lite",
576+ "containers": { "available": true, "sleepMinutes": 5, "prices": [] },
577+ }),
578+ &json!({ "workspace": "acme", "slug": "web" }),
579+ );
580+ assert!(g1t_kit::wire::camel_case_keys(&shown).is_empty(), "{:?}", g1t_kit::wire::camel_case_keys(&shown));
581+ assert_eq!(shown["production_url"], "https://web-acme.g1t.page");
582+ assert_eq!(shown["output_dir"], "dist");
583+ assert_eq!((shown["workspace"].clone(), shown["project"].clone()), (json!("acme"), json!("web")));
584+ assert!(shown.get("containers").is_none());
336585 }
337586 }
+2−0
505505 Op::Deployments(DeploymentsOp::ListEnvironments),
506506 Op::Deployments(DeploymentsOp::GetEnvironment),
507507 Op::Deployments(DeploymentsOp::StartDeploymentBuild),
508+ Op::Deployments(DeploymentsOp::GetDeploymentSettings),
509+ Op::Deployments(DeploymentsOp::UpdateDeploymentSettings),
508510 ],
509511 ),
510512 (
+28−3
812812 }
813813
814814 impl Op {
815− pub const ALL: [Op; 406] = [
815+ pub const ALL: [Op; 408] = [
816816 Op::Whoami,
817817 Op::GetWorkspace,
818818 Op::CreateWorkspace,
11081108 Op::Deployments(DeploymentsOp::ListEnvironments),
11091109 Op::Deployments(DeploymentsOp::GetEnvironment),
11101110 Op::Deployments(DeploymentsOp::StartDeploymentBuild),
1111+ Op::Deployments(DeploymentsOp::GetDeploymentSettings),
1112+ Op::Deployments(DeploymentsOp::UpdateDeploymentSettings),
11111113 Op::Telemetry(TelemetryOp::GetOverview),
11121114 Op::Telemetry(TelemetryOp::ListRequests),
11131115 Op::Telemetry(TelemetryOp::GetRequest),
15991601 "A repository's merge queue: the pull requests waiting to land, in order, each with the state it is being tested in (the default branch with the pull requests ahead of it landed in turn, each by its merge method) and how that went; then those that recently landed or left. With the queue on, merging a pull request adds it here."
16001602 }
16011603 Op::CreateRepo => {
1602− "Create a repository in one of your workspaces, empty or as a copy of a public git repository elsewhere. It gets a project of its own name; description is that project's."
1604+ "Create a repository in one of your workspaces, empty or as a copy of a public git repository elsewhere. It gets a project of its own name; description is that project's. With deploy true, its project's g1t.page Deployments are turned on as soon as it is made, as update_deployment_settings with enabled true does: its first push to the default branch deploys production. The repository is made either way: the response has `deployments` (the settings, with production_url) when they are on, or `deploy_error` saying why not (the workspace has no g1t plan, or your role is too low)."
16031605 }
16041606 Op::ListIssues => {
16051607 "Issues on a repository, newest first. An issue is something that should change: a bug, a feature, a question. Pull requests are made against it. Filter by state, by a label's name, or by a milestone's number."
26072609 "type": "string",
26082610 "description": "Copy the default branch of a public git repository at this https address, e.g. https://github.com/owner/repo.",
26092611 },
2612+ "deploy": {
2613+ "type": "boolean",
2614+ "description": "Turn on its project's g1t.page Deployments once it is made, so its first push deploys production. Needs the g1t plan on the workspace; when they cannot be turned on, it is still made and deploy_error says why. False unless you say.",
2615+ },
26102616 }),
26112617 &["name"],
26122618 ),
38263832 if let Op::Telemetry(_) = self {
38273833 return false;
38283834 }
3835+ // So do its deployment settings.
3836+ if let Op::Deployments(op) = self {
3837+ return op.needs_repo();
3838+ }
38293839 if let Op::About(op) = self {
38303840 return op.needs_repo();
38313841 }
49224932 Err(error) => worker::console_error!("created {} without its description: {error}", repo["id"]),
49234933 }
49244934 }
4925− Ok(created)
4935+ // Its deployments on from the start, when asked; the
4936+ // repository stays made whether or not they can be.
4937+ match created {
4938+ Outcome::Ok(mut repo) if yes(input, "deploy") == Some(true) => {
4939+ let namespace = repo["namespace"].as_str().unwrap_or_default().to_owned();
4940+ let name = repo["name"].as_str().unwrap_or_default().to_owned();
4941+ let (deployments, deploy_error) =
4942+ crate::deployments::turn_on_for_new_repo(services, &serde_json::to_value(&owner)?, &namespace, &name).await;
4943+ if let Some(fields) = repo.as_object_mut() {
4944+ fields.insert("deployments".to_owned(), deployments);
4945+ fields.insert("deploy_error".to_owned(), deploy_error);
4946+ }
4947+ Ok(Outcome::Ok(repo))
4948+ }
4949+ created => Ok(created),
4950+ }
49264951 }
49274952 Op::ListIssues => {
49284953 pass(
+54−1
662662 "website": "https://hello.g1t.page",
663663 "archived_at": null
664664 },
665− "notes": "`owner_id` is the id of the person who created the repository. With `import_url`, `default_branch` is the default branch of the repository copied."
665+ "notes": "`owner_id` is the id of the person who created the repository. With `import_url`, `default_branch` is the default branch of the repository copied. With `deploy: true`, its project's [Deployments](/guides/deployments/) are turned on once it is made, as [`update_deployment_settings`](/reference/api/deployments/update-deployment-settings/) with `enabled: true` does, and the response adds `deployments` (those settings, with `production_url`) and `deploy_error` (null). When they cannot be turned on (the workspace has no g1t plan, or your role there is below Admin) the repository is still made: `deployments` is null and `deploy_error` says why, and they can be turned on later. Turning them on here needs no scope beyond `repo:create`: you are the new repository's admin."
666666 },
667667 "get_repo": {
668668 "response": {
80768076 },
80778077 "notes": "Only the fields given change. `kind` and `runs` take `auto` to go back to detection. Setting `runs` makes the project an app unless it is docs. Making it a `library`, `tool` or `other` while [Deployments](/guides/deployments/) are on is refused with `409 conflict`: turn them off first. `description` and `homepage` given as null or `\"\"` follow the repository's again; `production_url` and `docs_url` given as null or `\"\"` are cleared. `links` replaces the project's other links: at most 10, each with a `label` of up to 40 characters and an http or https `url` (`https://` is added when you leave the scheme out); anything else is refused with `422 invalid`. Needs the Maintain role or higher on the project's repository. Recorded in the [audit log](/guides/audit-log/). See [Settings](/guides/projects/#settings)."
80788078 },
8079+ "get_deployment_settings": {
8080+ "params": {
8081+ "workspace": "flagon-io",
8082+ "project": "session-lab"
8083+ },
8084+ "response": {
8085+ "workspace": "flagon-io",
8086+ "project": "session-lab",
8087+ "enabled": false,
8088+ "production": true,
8089+ "previews": true,
8090+ "production_url": "https://session-lab-flagonio.g1t.page",
8091+ "primary_domain": null,
8092+ "build_as": "auto",
8093+ "build_command": null,
8094+ "output_dir": null,
8095+ "dockerfile": null,
8096+ "instance_type": "lite",
8097+ "idle_days": 7,
8098+ "via_workflow": false,
8099+ "detected": null,
8100+ "framework": null
8101+ },
8102+ "notes": "A repository's own project has the repository's name, so `flagon-io/session-lab`'s settings are at `/workspaces/flagon-io/projects/session-lab/deployments/settings`. `production_url` is where production is served once Deployments are on; `primary_domain` is the first active custom domain that serves it. `detected` and `framework` are what the last finished build found, and null before one has. Needs the Read role on the project's repository (`404` to anyone who cannot see it). See [Deployments](/guides/deployments/#settings)."
8103+ },
8104+ "update_deployment_settings": {
8105+ "params": {
8106+ "workspace": "flagon-io",
8107+ "project": "session-lab"
8108+ },
8109+ "request": {
8110+ "enabled": true
8111+ },
8112+ "response": {
8113+ "workspace": "flagon-io",
8114+ "project": "session-lab",
8115+ "enabled": true,
8116+ "production": true,
8117+ "previews": true,
8118+ "production_url": "https://session-lab-flagonio.g1t.page",
8119+ "primary_domain": null,
8120+ "build_as": "auto",
8121+ "build_command": null,
8122+ "output_dir": null,
8123+ "dockerfile": null,
8124+ "instance_type": "lite",
8125+ "idle_days": 7,
8126+ "via_workflow": false,
8127+ "detected": null,
8128+ "framework": null
8129+ },
8130+ "notes": "Only the fields given change. `enabled: true` turns Deployments on: production starts building from the default branch at once (an empty repository's first push to it deploys), and every pull request gets a preview. It is refused with `402 payment_required` when the workspace does not have the [g1t plan](/guides/usage-and-billing/#the-g1t-plan), saying where an owner starts it, and with `409 conflict` for a project set to be a library, a tool or other (change `kind` with [`update_project`](/reference/api/projects/update-project/) first). `enabled: false` takes production and every preview down at once. `build_command`, `output_dir` and `dockerfile` given as null or `\"\"` go back to what is found by itself; `idle_days` is 1 to 90; anything else is refused with `422 invalid`. Needs the Admin role on the project's repository (`403` otherwise, saying so) and a token with `repo:admin`. To turn them on as a repository is made, give `deploy: true` to [`create_repo`](/reference/api/repositories/create-repo/). See [Turn on deployments](/guides/deployments/#turn-on-deployments)."
8131+ },
80798132 "list_secret_scanning_alerts": {
80808133 "params": {
80818134 "owner": "flagon-io",
+13−0
161161 ),
162162 route("GET", "/workspaces/:workspace/projects/:project", Op::GetProject, &[]),
163163 route("PATCH", "/workspaces/:workspace/projects/:project", Op::UpdateProject, &[]),
164+ // Its g1t.page deployments: whether it deploys, and how it builds.
165+ route(
166+ "GET",
167+ "/workspaces/:workspace/projects/:project/deployments/settings",
168+ Op::Deployments(DeploymentsOp::GetDeploymentSettings),
169+ &[],
170+ ),
171+ route(
172+ "PATCH",
173+ "/workspaces/:workspace/projects/:project/deployments/settings",
174+ Op::Deployments(DeploymentsOp::UpdateDeploymentSettings),
175+ &[],
176+ ),
164177 // A project's observability: what its software did, and how it is collected.
165178 route(
166179 "GET",
+3−1
8585 a("contents", Op::GetContents, "A file's text, or a directory's entries and README, at a branch, tag or commit"),
8686 a("tarball", Op::Archives(ArchiveOp::DownloadTarball), "A 10-minute link to a branch, tag or commit as a .tar.gz"),
8787 a("zipball", Op::Archives(ArchiveOp::DownloadZipball), "A 10-minute link to a branch, tag or commit as a .zip"),
88− a("create", Op::CreateRepo, "Create one, empty or copied from a public git URL; private unless private is false"),
88+ a("create", Op::CreateRepo, "Create one, empty or copied from a public git URL; private unless private is false; deploy true turns its g1t.page deployments on"),
8989 a("update", Op::UpdateRepo, "Change description (its project's), website, topics, default branch, protection, forking"),
9090 a("get_settings", Op::GetRepoSettings, "How pull requests merge, and the default branch's protection as its rules stack"),
9191 a("update_settings", Op::UpdateRepoSettings, "Change how pull requests merge and the default branch protection ruleset"),
267267 a("list_environments", Op::Deployments(DeploymentsOp::ListEnvironments), "Environments with their current and latest deployments"),
268268 a("get_environment", Op::Deployments(DeploymentsOp::GetEnvironment), "One environment by name, with its protection rules"),
269269 a("start_deployment_build", Op::Deployments(DeploymentsOp::StartDeploymentBuild), "From a workflow job: open a g1t.page build it runs itself"),
270+ a("get_deployment_settings", Op::Deployments(DeploymentsOp::GetDeploymentSettings), "A project's g1t.page settings: whether it deploys, production's address, how it builds"),
271+ a("update_deployment_settings", Op::Deployments(DeploymentsOp::UpdateDeploymentSettings), "Turn a project's g1t.page deployments on (enabled true) or off, or change how it builds"),
270272 a("update_environment", Op::Protection(ProtectionOp::UpdateEnvironment), "Set an environment's reviewers, wait timer and branches"),
271273 a("delete_environment", Op::Protection(ProtectionOp::DeleteEnvironment), "Remove an environment's protection rules"),
272274 a("pending_deployments", Op::Protection(ProtectionOp::GetPendingDeployments), "The environments holding a run's jobs"),
+3−0
204204 ```sh
205205 g1t repo create my-project --source . --remote g1t # make it, add it as a remote, push
206206 g1t repo create acme/web --public --clone
207+g1t repo create acme/site --deploy # its g1t.page deployments on from the start
207208 g1t repo list acme
208209 g1t repo view acme/web --web
209210 g1t repo clone acme/web
341342 ```sh
342343 g1t deploy list --env production
343344 g1t deploy view dep_01kq8m3t5v7x9z1b3d5f7h9k2m
345+g1t deploy enable # turn on this project's g1t.page deployments
346+g1t deploy settings --project acme/web --previews off --build-command "npm run build:site"
344347
345348 g1t logs --since 1h --level error,fatal
346349 g1t logs -q '"payment failed"' --follow
+5−1
422422 | `get_environment` | `GET /repos/{owner}/{name}/environments/{environment}` |
423423 | `update_environment` | `PUT /repos/{owner}/{name}/environments/{environment}` |
424424 | `delete_environment` | `DELETE /repos/{owner}/{name}/environments/{environment}` |
425+| `get_deployment_settings` | `GET /workspaces/{workspace}/projects/{project}/deployments/settings` |
426+| `update_deployment_settings` | `PATCH /workspaces/{workspace}/projects/{project}/deployments/settings` |
425427
426428 They take the repository as `repo`, written `owner/name`, and the same
427−fields as the routes.
429+fields as the routes; a project's g1t.page settings take `workspace` and
430+`project` instead, and `update_deployment_settings` with `enabled: true`
431+[turns its deployments on](/guides/deployments/#without-the-site).
428432
429433 ## On the site
430434
+43−0
7878 Production starts building at once from the default branch. Every pull
7979 request opened or pushed to from then on gets a preview.
8080
81+### Without the site
82+
83+An agent, a script or the CLI turns them on the same way, as someone with
84+the Admin role on the repository; the workspace still needs the g1t plan.
85+
86+- **As you make the repository.** Give `deploy` when you create it:
87+ `g1t repo create acme/web --deploy`, `deploy: true` on the
88+ [MCP](/reference/mcp/) `repository` tool's `create`, or on
89+ [`POST /repos`](/reference/api/repositories/create-repo/). Its first
90+ push to the default branch then deploys production. The repository is
91+ made even when deployments can't be turned on: the response has
92+ `deployments` (the settings, with `production_url`) when they are on, or
93+ `deploy_error` saying why not, such as the workspace having no g1t plan.
94+- **For a project that exists.** `g1t deploy enable` in its checkout
95+ (`--project acme/web` from anywhere), the MCP `workflow` tool's
96+ `update_deployment_settings` with `enabled: true`, or:
97+
98+```sh
99+curl -X PATCH https://api.g1t.sh/workspaces/acme/projects/web/deployments/settings \
100+ -H "Authorization: Bearer $G1T_TOKEN" \
101+ -H "Content-Type: application/json" \
102+ -d '{"enabled": true}'
103+```
104+
105+It answers with the project's settings, `production_url` among them.
106+Without the g1t plan it is refused with `402 payment_required`, saying
107+where an owner starts it; without the Admin role, with `403`. Turning them
108+on for an existing project needs a token with `repo:admin`; turning them
109+on as you make a repository needs only `repo:create`, which the
110+[Agent preset](/guides/authentication/#presets) gives, since you are the
111+new repository's admin. `g1t deploy disable` (or `enabled: false`) turns
112+them off. The other [settings](#settings) change the same way:
113+`g1t deploy settings` shows them, and takes flags such as
114+`--build-command` and `--previews off`.
115+
81116 Deployments are for projects g1t.page hosts. Not every project is one: a
82117 library, a tool or documentation is published rather than deployed, and
83118 an app may deploy itself, with [a workflow](#deploying-from-a-workflow)
725760 | Idle days | 7 | 1 to 90. A preview no one visits this long comes down. |
726761 | Build with a workflow | Off | Build through the deploy workflow g1t manages for the project; see [build with a workflow](#build-with-a-workflow). |
727762
763+The same settings are read and changed with
764+[`get_deployment_settings`](/reference/api/deployments/get-deployment-settings/) and
765+[`update_deployment_settings`](/reference/api/deployments/update-deployment-settings/)
766+(`GET` and `PATCH /workspaces/{workspace}/projects/{project}/deployments/settings`,
767+the MCP `workflow` tool's actions of those names, and `g1t deploy settings`), as
768+`production`, `previews`, `build_as`, `dockerfile`, `instance_type`,
769+`build_command`, `output_dir`, `idle_days` and `via_workflow`.
770+
728771 ## Logs, traces and errors
729772
730773 Every request an app answers shows under the project's
+5−0
301301 | `GET /workspaces/{workspace}/projects/{project}` | `get_project` | One project, with `kind`, `runs`, `production_url`, `setting`, `detected` and `links`. |
302302 | `PATCH /workspaces/{workspace}/projects/{project}` | `update_project` | Changes `kind`, `runs`, `production_url`, `homepage`, `docs_url`, `links`, or its name, description or root directory. Needs `repo:write`. |
303303
304+Its g1t.page deployment settings, on the `workflow` tool, are at
305+`GET` and `PATCH /workspaces/{workspace}/projects/{project}/deployments/settings`
306+(`get_deployment_settings`, `update_deployment_settings`): `enabled: true`
307+turns its deployments on. See [Turn on deployments](/guides/deployments/#without-the-site).
308+
304309 To say an app is deployed by your own pipeline, with its docs:
305310
306311 ```sh
+53−1
160160 | `-c, --clone` | Clone it once it is made. |
161161 | `--source <DIR>` | Add it as a remote of this directory's repository and push it there. |
162162 | `--remote <REMOTE>` | The remote's name, with --source. Default: `origin`. |
163+| `--deploy` | Turn on its g1t.page deployments once it is made, so its first push deploys production. |
163164
164165 Calls [`create_repo`](/reference/api/repositories/create-repo/).
165166
905906
906907 ## g1t deploy
907908
908−Deployments: list and view.
909+Deployments: list and view, and turn a project's g1t.page deployments on or off.
909910
910911 ### g1t deploy list
911912
939940
940941 Calls [`get_deployment`](/reference/api/deployments/get-deployment/).
941942
943+### g1t deploy settings
944+
945+Show a project's g1t.page deployment settings, or change them with flags.
946+
947+```text
948+g1t deploy settings [OPTIONS]
949+```
950+
951+| Argument or flag | |
952+| --- | --- |
953+| `-p, --project <PROJECT>` | The project, as SLUG or WORKSPACE/SLUG; the current repository's when left out. |
954+| `--production <SWITCH>` | Deploy the default branch on every push. One of `on`, `off`. |
955+| `--previews <SWITCH>` | A preview for every branch with an open pull request. One of `on`, `off`. |
956+| `--build-as <KIND>` | auto, static, workers or docker. |
957+| `--build-command <COMMAND>` | Runs instead of the project's own build script; "" for its own. |
958+| `--output-dir <DIR>` | What a static site serves; "" to find it by itself. |
959+| `--dockerfile <PATH>` | A container's Dockerfile, inside the project; "" for Dockerfile or Containerfile. |
960+| `--instance-type <SIZE>` | A container's size: lite, basic, standard-1 to standard-4. |
961+| `--idle-days <DAYS>` | A preview no one visits this many days comes down: 1 to 90. |
962+| `--via-workflow <SWITCH>` | Build through the deploy workflow g1t manages for the project. One of `on`, `off`. |
963+
964+Calls [`get_deployment_settings`](/reference/api/deployments/get-deployment-settings/), [`update_deployment_settings`](/reference/api/deployments/update-deployment-settings/).
965+
966+### g1t deploy enable
967+
968+Turn on a project's g1t.page deployments: production builds from the default branch at once.
969+
970+```text
971+g1t deploy enable [OPTIONS]
972+```
973+
974+| Argument or flag | |
975+| --- | --- |
976+| `-p, --project <PROJECT>` | The project, as SLUG or WORKSPACE/SLUG; the current repository's when left out. |
977+
978+Calls [`update_deployment_settings`](/reference/api/deployments/update-deployment-settings/).
979+
980+### g1t deploy disable
981+
982+Turn off a project's g1t.page deployments: production and every preview come down.
983+
984+```text
985+g1t deploy disable [OPTIONS]
986+```
987+
988+| Argument or flag | |
989+| --- | --- |
990+| `-p, --project <PROJECT>` | The project, as SLUG or WORKSPACE/SLUG; the current repository's when left out. |
991+
992+Calls [`update_deployment_settings`](/reference/api/deployments/update-deployment-settings/).
993+
942994 ## g1t logs
943995
944996 A project's logs and errors.
+3−1
259259 | [`contents`](/reference/api/repositories/get-contents/) | What it holds at `path` (the root when left out) at `ref` (the default branch when left out): a file's `text` (null for a binary one; at most 200,000 characters), or a directory's `entries` and its README. | `repo` | `code:read` |
260260 | [`tarball`](/reference/api/repositories/download-tarball-archive/) | A link to a branch, tag or commit (`ref`, the default branch when left out) as a `.tar.gz` that needs no token, good for 10 minutes, as `url`, with the `commit` it is of. | `repo` | `code:read` |
261261 | [`zipball`](/reference/api/repositories/download-zipball-archive/) | The same as a `.zip`. | `repo` | `code:read` |
262−| [`create`](/reference/api/repositories/create-repo/) | Create a repository in one of your workspaces, empty or as a copy of a public git repository (`import_url`). `description` becomes its [project's](/guides/projects/), as a repository has none of its own. `workspace` may be left out if you belong to exactly one. It is private unless `private` is false (or `visibility` is `public`); the [REST API](/reference/api/repositories/create-repo/) makes one public unless `private` is true. | `name` | `repo:create`, which `repo:write` includes and the [Agent preset](/guides/authentication/#presets) gives |
262+| [`create`](/reference/api/repositories/create-repo/) | Create a repository in one of your workspaces, empty or as a copy of a public git repository (`import_url`). `description` becomes its [project's](/guides/projects/), as a repository has none of its own. `workspace` may be left out if you belong to exactly one. It is private unless `private` is false (or `visibility` is `public`); the [REST API](/reference/api/repositories/create-repo/) makes one public unless `private` is true. `deploy: true` turns its [deployments](/guides/deployments/#without-the-site) on once it is made: the answer has `deployments` with `production_url`, or `deploy_error` saying why not (the repository is made either way). | `name` | `repo:create`, which `repo:write` includes and the [Agent preset](/guides/authentication/#presets) gives |
263263 | [`update`](/reference/api/repositories/update-repo/) | Change its `website`, `topics` and `default_branch`, whether its default branch is `protected`, whether it is `private`, and whether a private repository may be forked (`allow_forking`). Maintain role; `protected`, `private`, `allow_forking` and `default_branch` need Admin, and `private` the [member privileges](/guides/workspaces/#member-privileges) unless you are an owner. | `repo` | `repo:write` |
264264 | [`get_settings`](/reference/api/repositories/get-repo-settings/) | How it handles pull requests: how they merge, and the default branch's required checks, approvals, bypassing checks, being up to date and merge queue as its [rulesets](/guides/rules/) stack. | `repo` | `repo:read` |
265265 | [`update_settings`](/reference/api/repositories/update-repo-settings/) | Change those settings. Only the fields given change; `required_checks` replaces the whole list. Maintain role. | `repo` | `repo:write` |
478478 | [`list_environments`](/reference/api/deployments/list-environments/) | Environments with their address, current and latest deployments, production first. | `repo` | `deployments:read` |
479479 | [`get_environment`](/reference/api/deployments/get-environment/) | One environment by name, with its protection rules. | `repo`, `environment` | `deployments:read` |
480480 | [`start_deployment_build`](/reference/api/deployments/start-deployment-build/) | From a running workflow job, with the job's own token only: open a g1t.page build of a `project` (optional when the repository has one with Deployments on) that the job builds itself with `g1t-runner deploy`, optionally at `sha`. Production for a run on the production branch, a preview otherwise. Returns `deploy_id`, `token`, `kind`, `url`, `build_env` and `build_secrets`. See [deploying from a workflow](/guides/deployments/#deploying-from-a-workflow). | `repo` | `deployments:write` |
481+| [`get_deployment_settings`](/reference/api/deployments/get-deployment-settings/) | A project's g1t.page settings: `enabled`, `production`, `previews`, `production_url`, `primary_domain`, `build_as`, `build_command`, `output_dir`, `dockerfile`, `instance_type`, `idle_days`, `via_workflow`, and what its last build `detected`. A repository's own project has its name. Read role. | `workspace`, `project` | `deployments:read` |
482+| [`update_deployment_settings`](/reference/api/deployments/update-deployment-settings/) | Change them; only what you give changes. `enabled: true` [turns deployments on](/guides/deployments/#turn-on-deployments) (production builds at once; refused without the g1t plan), `false` takes every app down. Admin role. | `workspace`, `project` | `repo:admin` |
481483 | [`update_environment`](/reference/api/run-protection/update-environment/) | Set an environment's [protection rules](/guides/actions/#environments): `reviewers` (up to 6, `{"type": "User" or "Team", "name"}`), `prevent_self_review`, `wait_timer` (minutes), `deployment_branch_policy`, `branch_policies` and `can_admins_bypass`. Admin role. | `repo`, `environment` | `repo:admin` |
482484 | [`delete_environment`](/reference/api/run-protection/delete-environment/) | Remove an environment's protection rules; its secrets and deployments stay. Admin role. | `repo`, `environment` | `repo:admin` |
483485 | [`pending_deployments`](/reference/api/run-protection/get-pending-deployments/) | The environments holding a run's jobs: their state, reviewers, wait timer, jobs, and whether you may approve. | `repo`, `id` | `workflows:read` |
+0−0

Binary or large file; its contents are not shown.

+3−0
271271 "list_commit_comments",
272272 "list_projects",
273273 "get_project",
274+ "get_deployment_settings",
274275 "list_deleted_repos",
275276 "list_collaborators",
276277 "get_collaborator_permission",
377378 "detach_fork",
378379 "update_repo",
379380 "update_project",
381+ // Turning a project's deployments on starts paid work; off, takes it down.
382+ "update_deployment_settings",
380383 "delete_repo",
381384 "list_deleted_repos",
382385 "restore_repo",
+5−0
12421242 ("create_deployment", Scope::DeploymentsWrite),
12431243 ("create_deployment_status", Scope::DeploymentsWrite),
12441244 ("start_deployment_build", Scope::DeploymentsWrite),
1245+ // A project's g1t.page settings: reading them is reading its
1246+ // deployments; turning them on or off, or changing how it builds,
1247+ // takes the Admin role on the site and here.
1248+ ("get_deployment_settings", Scope::DeploymentsRead),
1249+ ("update_deployment_settings", Scope::RepoAdmin),
12451250 // Observability: what a project's software did, and how it is collected.
12461251 ("get_observability_overview", Scope::ObservabilityRead),
12471252 ("list_requests", Scope::ObservabilityRead),
+58−1
101101 /// The repository's actions/cache entries: list, usage, delete.
102102 #[command(subcommand)]
103103 Cache(CacheCommand),
104− /// Deployments: list and view.
104+ /// Deployments: list and view, and turn a project's g1t.page deployments on or off.
105105 #[command(subcommand)]
106106 Deploy(DeployCommand),
107107 /// A project's logs and errors.
318318 /// The remote's name, with --source.
319319 #[arg(long, default_value = "origin")]
320320 pub remote: String,
321+ /// Turn on its g1t.page deployments once it is made, so its first push deploys production.
322+ #[arg(long)]
323+ pub deploy: bool,
321324 }
322325
323326 #[derive(Debug, Args)]
978981 List(DeployListArgs),
979982 /// Show a deployment and its statuses.
980983 View(DeployViewArgs),
984+ /// Show a project's g1t.page deployment settings, or change them with flags.
985+ Settings(DeploySettingsArgs),
986+ /// Turn on a project's g1t.page deployments: production builds from the default branch at once.
987+ Enable(DeployProjectArgs),
988+ /// Turn off a project's g1t.page deployments: production and every preview come down.
989+ Disable(DeployProjectArgs),
990+}
991+
992+/// A setting turned on or off.
993+#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
994+pub enum Switch {
995+ On,
996+ Off,
997+}
998+
999+#[derive(Debug, Args)]
1000+pub struct DeployProjectArgs {
1001+ /// The project, as SLUG or WORKSPACE/SLUG; the current repository's when left out.
1002+ #[arg(short, long)]
1003+ pub project: Option<String>,
1004+}
1005+
1006+#[derive(Debug, Args)]
1007+pub struct DeploySettingsArgs {
1008+ /// The project, as SLUG or WORKSPACE/SLUG; the current repository's when left out.
1009+ #[arg(short, long)]
1010+ pub project: Option<String>,
1011+ /// Deploy the default branch on every push.
1012+ #[arg(long, value_name = "SWITCH")]
1013+ pub production: Option<Switch>,
1014+ /// A preview for every branch with an open pull request.
1015+ #[arg(long, value_name = "SWITCH")]
1016+ pub previews: Option<Switch>,
1017+ /// auto, static, workers or docker.
1018+ #[arg(long, value_name = "KIND")]
1019+ pub build_as: Option<String>,
1020+ /// Runs instead of the project's own build script; "" for its own.
1021+ #[arg(long, value_name = "COMMAND")]
1022+ pub build_command: Option<String>,
1023+ /// What a static site serves; "" to find it by itself.
1024+ #[arg(long, value_name = "DIR")]
1025+ pub output_dir: Option<String>,
1026+ /// A container's Dockerfile, inside the project; "" for Dockerfile or Containerfile.
1027+ #[arg(long, value_name = "PATH")]
1028+ pub dockerfile: Option<String>,
1029+ /// A container's size: lite, basic, standard-1 to standard-4.
1030+ #[arg(long, value_name = "SIZE")]
1031+ pub instance_type: Option<String>,
1032+ /// A preview no one visits this many days comes down: 1 to 90.
1033+ #[arg(long, value_name = "DAYS")]
1034+ pub idle_days: Option<u32>,
1035+ /// Build through the deploy workflow g1t manages for the project.
1036+ #[arg(long, value_name = "SWITCH")]
1037+ pub via_workflow: Option<Switch>,
9811038 }
9821039
9831040 #[derive(Debug, Args)]
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…

Comments

Sign in to comment.