Skip to content

Commit

Permalink
fix(cli,serverless): correctly bundle and deploy assets (#160)
Browse files Browse the repository at this point in the history
* refactor: use cursor instead of converting Vec<u8> -> string -> Vec<u8>

* fix: bundle all assets

* fix: correctly assign domains and assets

* chore: format

* fix(website): remove console.log

* chore: add changeset
  • Loading branch information
QuiiBz authored Oct 1, 2022
1 parent dcfdf5d commit 94c14ac
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 55 deletions.
6 changes: 6 additions & 0 deletions .changeset/spotty-experts-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@lagon/cli': patch
'@lagon/serverless': patch
---

Correctly bundle assets
5 changes: 5 additions & 0 deletions .changeset/thin-vans-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lagon/serverless': patch
---

Fix deployments not working with multiple assets
8 changes: 8 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion examples/preact-ssr/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const html = `<!DOCTYPE html>
</head>
<body>
<div id="root">${render(<App />)}</div>
<script type="module" src="/app.js"></script>
<script type="module" src="/App.js"></script>
</body>
</html>`;

Expand Down
2 changes: 1 addition & 1 deletion examples/preact/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const html = `<!DOCTYPE html>
</head>
<body>
<div id="root" />
<script type="module" src="/app.js"></script>
<script type="module" src="/App.js"></script>
</body>
</html>`;

Expand Down
2 changes: 1 addition & 1 deletion examples/react-ssr/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const html = `<!DOCTYPE html>
</head>
<body>
<div id="root">${renderToString(<App />)}</div>
<script type="module" src="/app.js"></script>
<script type="module" src="/App.js"></script>
</body>
</html>`;

Expand Down
2 changes: 1 addition & 1 deletion examples/react/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const html = `<!DOCTYPE html>
</head>
<body>
<div id="root" />
<script type="module" src="/app.js"></script>
<script type="module" src="/App.js"></script>
</body>
</html>`;

Expand Down
2 changes: 2 additions & 0 deletions packages/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ multipart = "0.18.0"
mime = "0.3.16"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
walkdir = "2.3.2"
pathdiff = "0.2.1"
12 changes: 10 additions & 2 deletions packages/cli/src/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,22 @@ pub fn build(
let (index, assets) = bundle_function(file, client, public_dir)?;

let end_progress = print_progress("Writting index.js...");

fs::create_dir_all(".lagon")?;
fs::write(".lagon/index.js", index)?;
fs::write(".lagon/index.js", index.get_ref())?;

end_progress();

for (path, content) in assets {
let message = format!("Writting {}...", path);
let end_progress = print_progress(&message);
fs::write(format!(".lagon/{}", path), content)?;

let dir = PathBuf::from(".lagon")
.join("public")
.join(PathBuf::from(&path).parent().unwrap());
fs::create_dir_all(dir)?;
fs::write(format!(".lagon/public/{}", path), content.get_ref())?;

end_progress();
}

Expand Down
83 changes: 60 additions & 23 deletions packages/cli/src/utils/deployments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ use colored::Colorize;
use std::{
collections::HashMap,
fs,
io::{self, Error, ErrorKind, Read},
io::{self, Cursor, Error, ErrorKind, Read},
path::{Path, PathBuf},
process::Command,
};
use walkdir::WalkDir;

use multipart::{
client::lazy::Multipart,
server::nickel::nickel::hyper::{header::Headers, Client},
};
use pathdiff::diff_paths;
use serde::{Deserialize, Serialize};

use crate::utils::{get_api_url, print_progress, success};
use crate::utils::{debug, get_api_url, print_progress, success};

#[derive(Serialize, Deserialize, Debug)]
pub struct DeploymentConfig {
Expand Down Expand Up @@ -58,7 +60,7 @@ pub fn delete_function_config() -> io::Result<()> {
fs::remove_file(path)
}

fn esbuild(file: &PathBuf) -> io::Result<String> {
fn esbuild(file: &PathBuf) -> io::Result<Cursor<Vec<u8>>> {
let result = Command::new("esbuild")
.arg(file)
.arg("--bundle")
Expand All @@ -71,13 +73,7 @@ fn esbuild(file: &PathBuf) -> io::Result<String> {
if result.status.success() {
let output = result.stdout;

return match String::from_utf8(output) {
Ok(s) => Ok(s),
Err(_) => Err(Error::new(
ErrorKind::Other,
"Failed to convert output to string",
)),
};
return Ok(Cursor::new(output));
}

Err(Error::new(
Expand All @@ -89,8 +85,8 @@ fn esbuild(file: &PathBuf) -> io::Result<String> {
pub fn bundle_function(
index: PathBuf,
client: Option<PathBuf>,
_public_dir: PathBuf,
) -> io::Result<(String, HashMap<String, String>)> {
public_dir: PathBuf,
) -> io::Result<(Cursor<Vec<u8>>, HashMap<String, Cursor<Vec<u8>>>)> {
if let Err(_) = Command::new("esbuild").arg("--version").output() {
return Err(Error::new(
ErrorKind::Other,
Expand All @@ -102,20 +98,53 @@ pub fn bundle_function(
let index_output = esbuild(&index)?;
end_progress();

let mut assets = HashMap::<String, String>::new();
let mut assets = HashMap::<String, Cursor<Vec<u8>>>::new();

if let Some(client) = client {
let end_progress = print_progress("Bundling client file...");
let client_output = esbuild(&client)?;
end_progress();

assets.insert(
client.file_name().unwrap().to_str().unwrap().to_string(),
client
.as_path()
.file_stem()
.unwrap()
.to_str()
.unwrap()
.to_string()
+ ".js",
client_output,
);
}

// TODO: assets
if public_dir.exists() && public_dir.is_dir() {
let msg = format!(
"Found public directory ({}), bundling assets...",
public_dir.display()
);
let end_progress = print_progress(&msg);

for file in WalkDir::new(&public_dir) {
let file = file?;
let path = file.path();

if path.is_file() {
let diff = diff_paths(path, &public_dir)
.unwrap()
.to_str()
.unwrap()
.to_string();
let file_content = fs::read(path)?;

assets.insert(diff, Cursor::new(file_content));
}
}

end_progress();
} else {
println!("{}", debug("No public directory found, skipping..."));
}

Ok((index_output, assets))
}
Expand All @@ -138,15 +167,23 @@ pub fn create_deployment(

let mut multipart = Multipart::new();
multipart.add_text("functionId", function_id);
multipart.add_stream(
"code",
index.as_bytes(),
Some("index.js"),
Some(mime::TEXT_JAVASCRIPT),
);
multipart.add_stream("code", index, Some("index.js"), Some(mime::TEXT_JAVASCRIPT));

for (path, content) in assets {
let extension = Path::new(&path).extension().unwrap().to_str().unwrap();
let content_type = match extension {
"js" => mime::APPLICATION_JAVASCRIPT,
"css" => mime::TEXT_CSS,
"html" => mime::TEXT_HTML,
"png" => mime::IMAGE_PNG,
"jpg" | "jpeg" => mime::IMAGE_JPEG,
"svg" => mime::IMAGE_SVG,
"json" => mime::APPLICATION_JSON,
"txt" => mime::TEXT_PLAIN,
_ => mime::APPLICATION_OCTET_STREAM,
};

for (path, content) in &assets {
multipart.add_stream("assets", content.as_bytes(), Some(path), None);
multipart.add_stream("assets", content, Some(path), Some(content_type));
}

let client = Client::new();
Expand Down
10 changes: 9 additions & 1 deletion packages/serverless/src/deployments/filesystem.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::io::Write;
use std::path::PathBuf;
use std::{env, fs, io, path::Path};

use super::Deployment;
Expand Down Expand Up @@ -37,7 +38,14 @@ pub fn write_deployment(deployment_id: String, buf: &[u8]) -> io::Result<()> {
}

pub fn write_deployment_asset(deployment_id: String, asset: &str, buf: &[u8]) -> io::Result<()> {
fs::create_dir(Path::new("deployments").join(&deployment_id))?;
let asset = asset.replace("public/", "");
let asset = asset.as_str();

let dir = PathBuf::from("deployments")
.join(&deployment_id)
.join(PathBuf::from(asset).parent().unwrap());
fs::create_dir_all(dir)?;

let mut file = fs::File::create(Path::new("deployments").join(deployment_id + "/" + asset))?;

file.write_all(buf)?;
Expand Down
81 changes: 57 additions & 24 deletions packages/serverless/src/deployments/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use std::{collections::HashMap, fs, io, path::Path, sync::Arc};
use std::{
collections::{HashMap, HashSet},
fs, io,
path::Path,
sync::Arc,
};

use mysql::{prelude::Queryable, PooledConn};
use s3::Bucket;
Expand All @@ -18,8 +23,8 @@ pub mod pubsub;
pub struct Deployment {
pub id: String,
pub function_id: String,
pub domains: Vec<String>,
pub assets: Vec<String>,
pub domains: HashSet<String>,
pub assets: HashSet<String>,
pub environment_variables: HashMap<String, String>,
pub memory: usize, // in MB (MegaBytes)
pub timeout: usize, // in ms (MilliSeconds)
Expand All @@ -31,9 +36,10 @@ pub async fn get_deployments(
) -> Arc<RwLock<HashMap<String, Deployment>>> {
let deployments = Arc::new(RwLock::new(HashMap::new()));

let deployments_list = conn
.query_map(
r"
let mut deployments_list: HashMap<String, Deployment> = HashMap::new();

conn.query_map(
r"
SELECT
Deployment.id,
Function.id,
Expand All @@ -50,24 +56,51 @@ pub async fn get_deployments(
LEFT JOIN Asset
ON Deployment.id = Asset.deploymentId
",
|(id, function_id, memory, timeout, domain, asset): (
String,
String,
usize,
usize,
Option<String>,
Option<String>,
)| Deployment {
id,
function_id,
domains: domain.map(|d| vec![d]).unwrap_or(vec![]),
assets: asset.map(|a| vec![a]).unwrap_or(vec![]),
environment_variables: HashMap::new(),
memory,
timeout,
},
)
.unwrap();
|(id, function_id, memory, timeout, domain, asset): (
String,
String,
usize,
usize,
Option<String>,
Option<String>,
)| {
deployments_list
.entry(id.clone())
.and_modify(|deployment| {
if let Some(domain) = domain.clone() {
deployment.domains.insert(domain);
}

if let Some(asset) = asset.clone() {
deployment.assets.insert(asset);
}
})
.or_insert(Deployment {
id,
function_id,
domains: domain
.map(|domain| {
let mut domains = HashSet::new();
domains.insert(domain);
domains
})
.unwrap_or_default(),
assets: asset
.map(|asset| {
let mut assets = HashSet::new();
assets.insert(asset);
assets
})
.unwrap_or_default(),
environment_variables: HashMap::new(),
memory,
timeout,
});
},
)
.unwrap();

let deployments_list = deployments_list.values().cloned().collect();

if let Err(error) = create_deployments_folder() {
println!("Could not create deployments folder: {}", error);
Expand Down
2 changes: 1 addition & 1 deletion packages/serverless/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use hyper::{Body, Request as HyperRequest, Response as HyperResponse, Server};
use lagon_runtime::http::RunResult;
use lagon_runtime::isolate::{Isolate, IsolateOptions};
use lagon_runtime::runtime::{Runtime, RuntimeOptions};
use metrics::{histogram, increment_counter, counter};
use metrics::{counter, histogram, increment_counter};
use metrics_exporter_prometheus::PrometheusBuilder;
use mysql::Pool;
use s3::creds::Credentials;
Expand Down

0 comments on commit 94c14ac

Please sign in to comment.