Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(source-scan): logging, refactor, image with same cargo-near embedded, remove --no-docker flag #144

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
run: sudo apt-get update && sudo apt-get install --assume-yes libudev-dev

- name: Run tests
run: cargo test --workspace --verbose
run: cargo test --workspace

lint:
runs-on: ubuntu-latest
Expand Down
4 changes: 3 additions & 1 deletion cargo-near/src/commands/build_command/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use crate::util;

const COMPILATION_TARGET: &str = "wasm32-unknown-unknown";

pub fn run(args: super::BuildCommand) -> color_eyre::eyre::Result<util::CompilationArtifact> {
pub(super) fn run(
args: super::BuildCommand,
) -> color_eyre::eyre::Result<util::CompilationArtifact> {
let color = args.color.unwrap_or(ColorPreference::Auto);
color.apply();

Expand Down
226 changes: 226 additions & 0 deletions cargo-near/src/commands/build_command/docker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
use std::{
process::{id, Command, ExitStatus},
time::{SystemTime, UNIX_EPOCH},
};

use crate::types::manifest::CargoManifestPath;
use crate::{types::metadata::CrateMetadata, util};

use color_eyre::{
eyre::{ContextCompat, WrapErr},
owo_colors::OwoColorize,
};

#[cfg(unix)]
use nix::unistd::{getgid, getuid};

use super::BuildContext;

mod cloned_repo;
mod git_checks;
mod metadata;

impl super::BuildCommand {
pub(super) fn docker_run(
self,
context: BuildContext,
) -> color_eyre::eyre::Result<util::CompilationArtifact> {
util::handle_step("Performing git checks...", || {
match context {
BuildContext::Deploy => {
let contract_path: camino::Utf8PathBuf = self.contract_path()?;
// TODO: clone to tmp folder and checkout specific revision must be separate steps
eprintln!(
"\n The URL of the remote repository:\n {}\n",
git_checks::remote_repo_url(&contract_path)?
);
Ok(())
}
BuildContext::Build => Ok(()),
}
})?;
let mut cloned_repo = util::handle_step(
"Cloning project repo to a temporary build site, removing uncommitted changes...",
|| cloned_repo::ClonedRepo::clone(&self),
)?;

let crate_metadata = util::handle_step(
"Collecting cargo project metadata from temporary build site...",
|| {
let cargo_toml_path: camino::Utf8PathBuf = {
let mut cloned_path: std::path::PathBuf = cloned_repo.tmp_contract_path.clone();
cloned_path.push("Cargo.toml");
cloned_path.try_into()?
};
CrateMetadata::collect(CargoManifestPath::try_from(cargo_toml_path)?)
},
)?;

let docker_build_meta =
util::handle_step("Parsing and validating `Cargo.toml` metadata...", || {
metadata::ReproducibleBuild::parse(&crate_metadata)
})?;

util::print_step("Running docker command step...");
let (status, docker_cmd) = self.docker_subprocess_step(docker_build_meta, &cloned_repo)?;

if status.success() {
util::print_success("Running docker command step (finished)");
// TODO: make this a `ClonedRepo` `copy_artifact` method
dj8yfo marked this conversation as resolved.
Show resolved Hide resolved
cloned_repo.tmp_contract_path.push("target");
cloned_repo.tmp_contract_path.push("near");

let dir = cloned_repo.tmp_contract_path.read_dir().wrap_err_with(|| {
format!(
"No artifacts directory found: `{:?}`.",
cloned_repo.tmp_contract_path
)
})?;

for entry in dir.flatten() {
if entry.path().extension().unwrap().to_str().unwrap() == "wasm" {
let wasm_path = {
let mut contract_path = cloned_repo.contract_path.clone();
contract_path.push("contract.wasm");
contract_path
};
std::fs::copy::<std::path::PathBuf, camino::Utf8PathBuf>(
entry.path(),
wasm_path.clone(),
)?;

// TODO: ensure fresh
dj8yfo marked this conversation as resolved.
Show resolved Hide resolved
return Ok(util::CompilationArtifact {
path: wasm_path,
fresh: true,
from_docker: true,
});
}
}

Err(color_eyre::eyre::eyre!(
"Wasm file not found in directory: `{:?}`.",
cloned_repo.tmp_contract_path
))
} else {
println!();
println!(
"{}",
format!(
"See output above ↑↑↑.\nSourceScan command `{:?}` failed with exit status: {status}.",
docker_cmd
).yellow()
);
println!(
"{}",
"You can choose to opt out into non-docker build behaviour by using `--no-docker` flag.".cyan()
);

Err(color_eyre::eyre::eyre!(
"Reproducible build in docker container failed"
))
}
}

fn docker_subprocess_step(
self,
docker_build_meta: metadata::ReproducibleBuild,
cloned_repo: &cloned_repo::ClonedRepo,
) -> color_eyre::eyre::Result<(ExitStatus, Command)> {
let mut cargo_args = vec![];

if self.no_abi {
cargo_args.push("--no-abi")
}
if self.no_embed_abi {
cargo_args.push("--no-embed-abi")
}
if self.no_doc {
cargo_args.push("--no-doc")
}

let color = self
.color
.clone()
.unwrap_or(crate::common::ColorPreference::Auto)
.to_string();
cargo_args.extend(&["--color", &color]);
// Cross-platform process ID and timestamp
let pid = id().to_string();
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
.to_string();

let volume = format!(
"{}:/host",
cloned_repo
.tmp_repo
.workdir()
.wrap_err("Could not get the working directory for the repository")?
.to_string_lossy()
);
let docker_container_name = format!("cargo-near-{}-{}", timestamp, pid);
let docker_image = docker_build_meta.concat_image();
let near_build_env_ref = format!("NEAR_BUILD_ENVIRONMENT_REF={}", docker_image);

// Platform-specific UID/GID retrieval
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty pointless comment. More useful comment would be to explain that we only really need to match uid/gid on Linux since Docker is running natively and mounted FS needs to have proper permissions.

#[cfg(unix)]
let uid_gid = format!("{}:{}", getuid(), getgid());
#[cfg(not(unix))]
let uid_gid = "1000:1000".to_string();

let mut docker_args = vec![
"-u",
&uid_gid,
"-it",
"--name",
&docker_container_name,
"--volume",
&volume,
"--rm",
"--workdir",
"/host",
"--env",
&near_build_env_ref,
"--env",
"RUST_LOG=cargo_near=info",
&docker_image,
"/bin/bash",
"-c",
];

let mut cargo_cmd_list = vec!["cargo", "near", "build"];
cargo_cmd_list.extend(&cargo_args);

let cargo_cmd = cargo_cmd_list.join(" ");

docker_args.push(&cargo_cmd);
log::debug!("docker command : {:?}", docker_args);

let mut docker_cmd = Command::new("docker");
docker_cmd.arg("run");
docker_cmd.args(docker_args);

let status = match docker_cmd.status() {
Ok(exit_status) => exit_status,
Err(io_err) => {
println!();
println!(
"{}",
format!(
"Error obtaining status from executing SourceScan command `{:?}`",
docker_cmd
)
.yellow()
);
println!("{}", format!("Error `{:?}`", io_err).yellow());
return Err(color_eyre::eyre::eyre!(
"Reproducible build in docker container failed"
));
}
};
Ok((status, docker_cmd))
}
}
27 changes: 27 additions & 0 deletions cargo-near/src/commands/build_command/docker/cloned_repo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use crate::commands::build_command::BuildCommand;

pub(super) struct ClonedRepo {
pub tmp_repo: git2::Repository,
pub tmp_contract_path: std::path::PathBuf,
pub contract_path: camino::Utf8PathBuf,
#[allow(unused)]
tmp_contract_dir: tempfile::TempDir,
}

impl ClonedRepo {
pub(super) fn clone(args: &BuildCommand) -> color_eyre::eyre::Result<Self> {
let contract_path: camino::Utf8PathBuf = args.contract_path()?;
log::info!("ClonedRepo.contract_path: {:?}", contract_path,);

let tmp_contract_dir = tempfile::tempdir()?;
let tmp_contract_path = tmp_contract_dir.path().to_path_buf();
log::info!("ClonedRepo.tmp_contract_path: {:?}", tmp_contract_path);
let tmp_repo = git2::Repository::clone(contract_path.as_str(), &tmp_contract_path)?;
Ok(ClonedRepo {
tmp_repo,
tmp_contract_path,
tmp_contract_dir,
contract_path,
})
}
}
Loading
Loading