A focused Docker Compose management web application.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: project build/update

Brooke c47a7787 af9b482c

+77 -11
+1 -1
justfile
··· 21 21 preview: 22 22 ./luminary-node 23 23 24 - export DATABASE_URL := "sqlite://" + justfile_dir() + "/luminary.db" 24 + export DATABASE_URL := "sqlite://" + justfile_dir() + "/data/luminary.db" 25 25 26 26 [working-directory('packages/node')] 27 27 prepare:
+1
packages/node/.gitignore
··· 1 + openapi.json
+11
packages/node/src/api/action.rs
··· 15 15 .push(Router::with_path("recreate").post(recreate_project)) 16 16 .push(Router::with_path("pull").post(pull_project)) 17 17 .push(Router::with_path("build").post(build_project)) 18 + .push(Router::with_path("update").post(update_project)) 18 19 .push( 19 20 Router::with_path("service/{service}") 20 21 .push(Router::with_path("restart").post(restart_service)) ··· 141 142 pub async fn build_project(project: PathParam<String>, depot: &mut Depot) -> LuminaryResponse<()> { 142 143 let engine = obtain!(depot, LuminaryEngine); 143 144 145 + engine.build(&project, None).await?; 146 + return Ok(().into()); 147 + } 148 + 149 + /// Pulls and builds the images for all the services in the given project. 150 + #[endpoint] 151 + pub async fn update_project(project: PathParam<String>, depot: &mut Depot) -> LuminaryResponse<()> { 152 + let engine = obtain!(depot, LuminaryEngine); 153 + 154 + engine.pull(&project, None).await?; 144 155 engine.build(&project, None).await?; 145 156 return Ok(().into()); 146 157 }
+1 -1
packages/node/src/api/mod.rs
··· 52 52 crate::core::LuminaryProjectList::to_schema(&mut openapi.components); 53 53 crate::logging::LogMessage::to_schema(&mut openapi.components); 54 54 55 - let location = concat!(env!("CARGO_MANIFEST_DIR"), "/../panel/static/openapi.json"); 55 + let location = concat!(env!("CARGO_MANIFEST_DIR"), "/openapi.json"); 56 56 std::fs::write(location, openapi.to_pretty_json()?)?; 57 57 info!("OpenAPI documentation written to {}", location); 58 58 }
+11 -7
packages/node/src/core/action.rs
··· 97 97 Ok(()) 98 98 } 99 99 100 + /// Recreates the given project and optionally, a specific service within that project. 100 101 #[wrap_err("Failed to recreate project/service")] 101 102 pub async fn recreate(&self, project: &str, service: Option<&str>) -> Result<()> { 102 103 self.stop(project, service).await?; ··· 104 105 Ok(()) 105 106 } 106 107 107 - /// Stops the given project and optionally, a specific service within that project. 108 + /// Pulls the latest images for the given project and optionally, a specific service within that project. 108 109 #[wrap_err("Failed to pull project/service images")] 109 110 pub async fn pull(&self, project: &str, service: Option<&str>) -> Result<()> { 110 - self.run(LuminaryAction::Pulling, project, service, vec!["pull"]) 111 - .await?; 112 - self.recreate(project, service).await?; 111 + self.run( 112 + LuminaryAction::Pulling, 113 + project, 114 + service, 115 + vec!["up", "--pull", "always", "-d"], 116 + ) 117 + .await?; 113 118 Ok(()) 114 119 } 115 120 116 - /// Stops the given project and optionally, a specific service within that project. 121 + /// Builds the images for the given project and optionally, a specific service within that project. 117 122 #[wrap_err("Failed to build project/service images")] 118 123 pub async fn build(&self, project: &str, service: Option<&str>) -> Result<()> { 119 124 self.run( 120 125 LuminaryAction::Building, 121 126 project, 122 127 service, 123 - vec!["build", "--no-cache"], 128 + vec!["up", "--build", "-d"], 124 129 ) 125 130 .await?; 126 131 127 - self.recreate(project, service).await?; 128 132 Ok(()) 129 133 } 130 134 }
-1
packages/panel/.gitignore
··· 9 9 /build 10 10 11 11 # Generated 12 - /static/openapi.json 13 12 /src/lib/api/openapi.ts 14 13 15 14 # OS
+1 -1
packages/panel/plugin.ts
··· 3 3 import { existsSync } from "node:fs"; 4 4 import type { Plugin } from "vite"; 5 5 6 - const OPENAPI_PATH = new URL("static/openapi.json", import.meta.url); 6 + const OPENAPI_PATH = new URL("../node/openapi.json", import.meta.url); 7 7 8 8 async function generateTypes() { 9 9 if (!existsSync(OPENAPI_PATH)) return;
+51
packages/panel/src/routes/(authenticated)/projects/[project]/ProjectStatus.svelte
··· 9 9 faArrowsRotate, 10 10 faBan, 11 11 faCircleExclamation, 12 + faDownload, 13 + faHammer, 12 14 faPlay, 13 15 faRocket, 14 16 faStop, 17 + faTimeline, 15 18 } from "@fortawesome/free-solid-svg-icons"; 16 19 17 20 let { project }: { project: api.LuminaryProject } = $props(); ··· 146 149 </div> 147 150 {/snippet} 148 151 </PromiseButton> 152 + <PromiseButton 153 + fit 154 + style="outline" 155 + disabled={project.busy} 156 + onclick={() => 157 + api.client.POST("/api/project/{project}/update", { params: { path: { project: project.name } } })} 158 + > 159 + {#snippet children(loading)} 160 + <div class="flexr center gap-10"> 161 + {#if !loading}<Fa icon={faTimeline} />{/if} 162 + Update All 163 + </div> 164 + {/snippet} 165 + </PromiseButton> 149 166 <button class="outline" disabled={project.busy} onclick={clickDelete}> 150 167 <div class="flexr center gap-10"> 151 168 <Fa icon={faBan} /> ··· 244 261 > 245 262 {#snippet children(loading)} 246 263 {#if !loading}<Fa icon={faRocket} />{/if} 264 + {/snippet} 265 + </PromiseButton> 266 + </Tooltip> 267 + 268 + <Tooltip placement="left" content="Pull Service"> 269 + <PromiseButton 270 + style="a" 271 + aria-label="Pull Service" 272 + disabled={service.action !== "idle"} 273 + loading={service.action === "pulling"} 274 + onclick={() => 275 + api.client.POST("/api/project/{project}/service/{service}/pull", { 276 + params: { path: { project: project.name, service: service.serviceName } }, 277 + })} 278 + > 279 + {#snippet children(loading)} 280 + {#if !loading}<Fa icon={faDownload} />{/if} 281 + {/snippet} 282 + </PromiseButton> 283 + </Tooltip> 284 + 285 + <Tooltip placement="left" content="Rebuild Service"> 286 + <PromiseButton 287 + style="a" 288 + aria-label="Rebuild Service" 289 + disabled={service.action !== "idle"} 290 + loading={service.action === "building"} 291 + onclick={() => 292 + api.client.POST("/api/project/{project}/service/{service}/build", { 293 + params: { path: { project: project.name, service: service.serviceName } }, 294 + })} 295 + > 296 + {#snippet children(loading)} 297 + {#if !loading}<Fa icon={faHammer} />{/if} 247 298 {/snippet} 248 299 </PromiseButton> 249 300 </Tooltip>