Skip to content

Commit

Permalink
fix(stctl): remove stale build artifacts for build and serve (#149)
Browse files Browse the repository at this point in the history
* feat(stctl): move more paths to the paths type

* feat(stctl): migrate watch builds

* feat(stctl): remove old code

* chore(stctl): remove third party pinning in favour of std

* feat(stctl): remove old serve builds

* fix(stctl): update description

* doc(stctl): update documentation

* doc(stctl): update documentation

* fix(stctl): rename variables

* feat(stctl): use build id for log names
  • Loading branch information
futursolo authored Aug 27, 2023
1 parent 66b305d commit 44f4fc7
Show file tree
Hide file tree
Showing 7 changed files with 663 additions and 360 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/stctl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ webbrowser = "0.8.11"

# Stellation Components
stellation-core = { version = "0.2.0", path = "../stellation-core" }
fs_extra = "1.3.0"

[package.metadata.docs.rs]
all-features = true
Expand Down
309 changes: 309 additions & 0 deletions crates/stctl/src/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
use std::path::{Path, PathBuf};
use std::pin::pin;
use std::process::Stdio;
use std::sync::Arc;

use anyhow::{bail, Context, Result};
use cargo_metadata::Metadata;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWriteExt};
use tokio::sync::OnceCell;
use tokio::{fs, spawn};

use crate::env_file::EnvFile;
use crate::manifest::Manifest;
use crate::paths::Paths;
use crate::profile::Profile;
use crate::utils::random_str;
use crate::Stctl;

#[derive(Debug)]
pub(crate) struct Builder {
build_id: String,
paths: Arc<Paths>,
profile: Profile,
env_file: EnvFile,
manifest: Arc<Manifest>,

is_watch_build: bool,

frontend_build_dir: OnceCell<PathBuf>,
backend_build_dir: OnceCell<PathBuf>,
}

impl Builder {
pub async fn new(stctl: &Stctl) -> Result<Self> {
Ok(Builder {
build_id: random_str()?,
paths: stctl.paths.clone(),
profile: stctl.profile.clone(),

env_file: stctl.env_file.clone(),
manifest: stctl.manifest.clone(),

is_watch_build: false,

frontend_build_dir: OnceCell::new(),
backend_build_dir: OnceCell::new(),
})
}

/// Sets to true for watch build.
pub fn watch_build(mut self, is_watch_build: bool) -> Self {
self.is_watch_build = is_watch_build;

self
}

/// Returns the frontend build directory of current build.
pub async fn frontend_build_dir(&self) -> Result<&Path> {
self.frontend_build_dir
.get_or_try_init(|| async {
let frontend_build_dir =
self.paths.frontend_builds_dir().await?.join(&self.build_id);

fs::create_dir_all(&frontend_build_dir)
.await
.context("failed to create build directory for frontend build.")?;

Ok(frontend_build_dir)
})
.await
.map(|m| m.as_ref())
}

/// Returns the backend build directory of current build.
pub async fn backend_build_dir(&self) -> Result<&Path> {
self.backend_build_dir
.get_or_try_init(|| async {
let backend_build_dir = self.paths.backend_builds_dir().await?.join(&self.build_id);

fs::create_dir_all(&backend_build_dir)
.await
.context("failed to create build directory for backend build.")?;

Ok(backend_build_dir)
})
.await
.map(|m| m.as_ref())
}

async fn transfer_to_file<R, P>(source: R, target: P) -> Result<()>
where
R: 'static + AsyncRead + Send,
P: Into<PathBuf>,
{
let target_path = target.into();
let mut target = fs::File::create(&target_path)
.await
.with_context(|| format!("failed to create {}", target_path.display()))?;

let inner = async move {
let mut source = pin!(source);

loop {
let mut buf = [0_u8; 8192];
let buf_len = source.read(&mut buf[..]).await?;

if buf_len == 0 {
break;
}
target.write_all(&buf[..buf_len]).await?;
}

Ok::<(), anyhow::Error>(())
};

spawn(async move {
if let Err(e) = inner
.await
.with_context(|| format!("failed to transfer logs to: {}", target_path.display()))
{
tracing::error!("{:#?}", e);
}
});

Ok(())
}

pub async fn build_frontend(&self) -> Result<&Path> {
use tokio::process::Command;

let frontend_logs_dir = self.paths.frontend_logs_dir().await?;
let frontend_build_dir = self.frontend_build_dir().await?;
let workspace_dir = self.paths.workspace_dir().await?;

let create_proc = || {
let mut proc = Command::new("trunk");
proc.arg("build")
.arg("--dist")
.arg(frontend_build_dir)
.arg(workspace_dir.join("index.html"))
.current_dir(workspace_dir)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped());

if let Some(m) = self.profile.to_profile_argument() {
proc.arg(m);
}

let envs = self.env_file.load(workspace_dir);
proc.envs(envs);

if !self.is_watch_build {
proc.stdout(Stdio::inherit()).stderr(Stdio::inherit());
}
proc
};

let mut child = create_proc().spawn()?;

if let Some(m) = child.stdout.take() {
Self::transfer_to_file(
m,
frontend_logs_dir.join(format!("log-stdout-{}", self.build_id)),
)
.await?;
}

if let Some(m) = child.stderr.take() {
Self::transfer_to_file(
m,
frontend_logs_dir.join(format!("log-stderr-{}", self.build_id)),
)
.await?;
}

let status = child.wait().await?;

// We try again with logs printed to console.
if !status.success() {
if !self.is_watch_build {
bail!("trunk failed with status {}", status);
}

let mut proc = create_proc();
proc.stdout(Stdio::inherit()).stderr(Stdio::inherit());

let mut child = proc.spawn()?;
let status = child.wait().await?;

if !status.success() {
bail!("trunk failed with status {}", status);
}
}

Ok(frontend_build_dir)
}

pub async fn build_backend(&self) -> Result<PathBuf> {
use tokio::process::Command;

let frontend_build_dir = self.frontend_build_dir().await?;
let backend_logs_dir = self.paths.backend_logs_dir().await?;
let workspace_dir = self.paths.workspace_dir().await?;
let backend_build_dir = self.backend_build_dir().await?;

let create_proc = || {
let mut proc = Command::new("cargo");
proc.arg("build")
.arg("--bin")
.arg(&self.manifest.dev_server.bin_name)
.current_dir(workspace_dir)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.kill_on_drop(true);

if let Some(m) = self.profile.to_profile_argument() {
proc.arg(m);
}

let envs = self.env_file.load(workspace_dir);
proc.envs(envs);

if !self.is_watch_build {
proc.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.env("RUSTFLAGS", "--cfg stellation_embedded_frontend");
}

proc.env("STELLATION_FRONTEND_BUILD_DIR", frontend_build_dir);

proc
};

let mut child = create_proc().spawn()?;

if let Some(m) = child.stdout.take() {
Self::transfer_to_file(
m,
backend_logs_dir.join(format!("log-stdout-{}", self.build_id)),
)
.await?;
}

if let Some(m) = child.stderr.take() {
Self::transfer_to_file(
m,
backend_logs_dir.join(format!("log-stderr-{}", self.build_id)),
)
.await?;
}

let status = child.wait().await?;

// We try again with logs printed to console.
if !status.success() {
if !self.is_watch_build {
bail!("trunk failed with status {}", status);
}

let mut proc = create_proc();
proc.stdout(Stdio::inherit()).stderr(Stdio::inherit());

let mut child = proc.spawn()?;
let status = child.wait().await?;

if !status.success() {
bail!("trunk failed with status {}", status);
}
}

// Copy artifact from target directory.
let pkg_meta_output = Command::new("cargo")
.arg("metadata")
.arg("--format-version=1")
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.current_dir(workspace_dir)
.spawn()?
.wait_with_output()
.await
.context("failed to read package metadata")?;

if !pkg_meta_output.status.success() {
bail!(
"cargo metadata failed with status {}",
pkg_meta_output.status
);
}

let meta: Metadata = serde_json::from_slice(&pkg_meta_output.stdout)
.context("failed to parse package metadata")?;

let bin_path = meta
.target_directory
.join_os(self.profile.name())
.join(&self.manifest.dev_server.bin_name);

let backend_bin_path = backend_build_dir.join(&self.manifest.dev_server.bin_name);

fs::copy(bin_path, &backend_bin_path)
.await
.context("failed to copy binary")?;

Ok(backend_bin_path)
}
}
2 changes: 1 addition & 1 deletion crates/stctl/src/env_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::HashMap;
use std::env;
use std::path::Path;

#[derive(Debug)]
#[derive(Debug, Clone)]
pub(crate) struct EnvFile {
name: String,
}
Expand Down
Loading

0 comments on commit 44f4fc7

Please sign in to comment.