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

Support evaluating with submodules enabled #182

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
11 changes: 11 additions & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ pub(crate) struct FlakeHubPushCli {
)]
pub(crate) error_on_conflict: bool,

/// Include submodules when evaluating the flake.
///
/// Adds `?submodules=1` to the flake while evaluating.
#[clap(
long,
env = "FLAKEHUB_PUSH_INCLUDE_SUBMODULES",
value_parser = EmptyBoolParser,
default_value_t = false
)]
pub(crate) include_submodules: bool,

/// Do less work on extremely large flakes.
///
/// This flag is intended to limit the scope of evaluations which are too large to complete on one machine.
Expand Down
125 changes: 96 additions & 29 deletions src/flake_info.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::{
io::Write,
path::{Path, PathBuf},
};
use std::ffi::{OsStr, OsString};
use std::io::Write;
use std::path::{Path, PathBuf};

use color_eyre::eyre::{eyre, Result, WrapErr};
use serde::Deserialize;
Expand All @@ -14,9 +13,79 @@ use crate::flakehub_client::Tarball;
const FLAKE_URL_PLACEHOLDER_UUID: &str = "c9026fc0-ced9-48e0-aa3c-fc86c4c86df1";
const README_FILENAME_LOWERCASE: &str = "readme.md";

#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum SubmoduleParameter {
Include,
Exclude,
}

#[derive(Debug, Clone)]
struct LocalFlakeRef {
source_dir: std::path::PathBuf,

parent_dir: std::path::PathBuf,
directory_name: OsString,

submodules: SubmoduleParameter,
}

impl LocalFlakeRef {
pub fn try_new(source_dir: &std::path::Path, submodules: SubmoduleParameter) -> Result<Self> {
let parent_dir = source_dir
.parent()
.ok_or_else(|| eyre!("Getting parent directory"))?;

let directory_name = source_dir
.file_name()
.ok_or_else(|| eyre!("No file name of directory"))?;

Ok(Self {
source_dir: source_dir.to_path_buf(),
parent_dir: parent_dir.to_path_buf(),
directory_name: directory_name.into(),
submodules,
})
}

pub fn directory(&self) -> &Path {
&self.source_dir
}

pub fn directory_name(&self) -> &OsStr {
&self.directory_name
}

pub fn parent(&self) -> &Path {
&self.parent_dir
}

/// Dirty: actually clones
pub fn as_os_string(&self) -> OsString {
let x = self.clone();
x.into_os_string()
}

pub fn into_os_string(self) -> OsString {
let mut flakeref = self.source_dir.into_os_string();

if self.submodules == SubmoduleParameter::Include {
flakeref.push("?submodules=1");
}

flakeref
}

pub fn display(&self) -> String {
let x = self.clone();
let y = x.into_os_string();
let z = y.to_string_lossy();
z.into_owned()
}
}

#[derive(Debug)]
pub struct FlakeMetadata {
pub(crate) source_dir: std::path::PathBuf,
local_flake_ref: LocalFlakeRef,
pub(crate) flake_locked_url: String,
pub(crate) metadata_json: serde_json::Value,
my_flake_is_too_big: bool,
Expand All @@ -26,27 +95,33 @@ pub struct FlakeMetadata {
pub struct FlakeOutputs(pub serde_json::Value);

impl FlakeMetadata {
pub async fn from_dir(directory: &Path, my_flake_is_too_big: bool) -> Result<Self> {
pub async fn from_dir(
source_dir: &Path,
submodules: SubmoduleParameter,
my_flake_is_too_big: bool,
) -> Result<Self> {
let local_flake_ref = LocalFlakeRef::try_new(source_dir, submodules)?;

let output = tokio::process::Command::new("nix")
.arg("flake")
.arg("metadata")
.arg("--json")
.arg("--no-write-lock-file")
.arg(directory)
.arg(local_flake_ref.as_os_string())
.output()
.await
.wrap_err_with(|| {
eyre!(
"Failed to execute `nix flake metadata --json {}`",
directory.display()
local_flake_ref.display()
)
})?;

let metadata_json: serde_json::Value = serde_json::from_slice(&output.stdout)
.wrap_err_with(|| {
eyre!(
"Parsing `nix flake metadata --json {}` as JSON",
directory.display()
local_flake_ref.display()
)
})?;

Expand Down Expand Up @@ -75,7 +150,7 @@ impl FlakeMetadata {
};

Ok(FlakeMetadata {
source_dir: source,
local_flake_ref: LocalFlakeRef::try_new(&source, submodules)?,
flake_locked_url: flake_locked_url.to_string(),
metadata_json,
my_flake_is_too_big,
Expand All @@ -95,19 +170,19 @@ impl FlakeMetadata {

command.arg("--json");
command.arg("--no-write-lock-file");
command.arg(&self.source_dir);
command.arg(&self.local_flake_ref.as_os_string());

let output = command.output().await.wrap_err_with(|| {
eyre!(
"Failed to execute `nix flake show --all-systems --json --no-write-lock-file {}`",
self.source_dir.display()
self.local_flake_ref.display()
)
})?;

if !output.status.success() {
let command = format!(
"nix flake show --all-systems --json --no-write-lock-file {}",
self.source_dir.display(),
self.local_flake_ref.display(),
);
let msg = format!(
"\
Expand All @@ -134,26 +209,26 @@ impl FlakeMetadata {
/// and committed/pushed that without the corresponding update to the flake.lock. Importantly,
/// this does not ensure anything about the recentness of the locked revs.
pub async fn check_lock_if_exists(&self) -> Result<()> {
if self.source_dir.join("flake.lock").exists() {
if self.local_flake_ref.directory().join("flake.lock").exists() {
let output = tokio::process::Command::new("nix")
.arg("flake")
.arg("metadata")
.arg("--json")
.arg("--no-update-lock-file")
.arg(&self.source_dir)
.arg(&self.local_flake_ref.as_os_string())
.output()
.await
.wrap_err_with(|| {
eyre!(
"Failed to execute `nix flake metadata --json --no-update-lock-file {}`",
self.source_dir.display()
self.local_flake_ref.display()
)
})?;

if !output.status.success() {
let command = format!(
"nix flake metadata --json --no-update-lock-file {}",
self.source_dir.display(),
self.local_flake_ref.display(),
);
let msg = format!(
"\
Expand Down Expand Up @@ -195,20 +270,12 @@ impl FlakeMetadata {
// `tar` works according to the current directory (yay)
// So we change dir and restory it after
// TODO: Fix this
let source = &self.source_dir; // refactor to be known when we create struct with from_dir
let current_dir = std::env::current_dir().wrap_err("Could not get current directory")?;
std::env::set_current_dir(
source
.parent()
.ok_or_else(|| eyre!("Getting parent directory"))?,
)?;
let dirname = self
.source_dir
.file_name()
.ok_or_else(|| eyre!("No file name of directory"))?;
std::env::set_current_dir(self.local_flake_ref.parent())?;
let dirname = self.local_flake_ref.directory_name();
tarball_builder
.append_dir_all(dirname, dirname)
.wrap_err_with(|| eyre!("Adding `{}` to tarball", self.source_dir.display()))?;
.wrap_err_with(|| eyre!("Adding `{}` to tarball", self.local_flake_ref.display()))?;
std::env::set_current_dir(current_dir).wrap_err("Could not set current directory")?;

let tarball = tarball_builder.into_inner().wrap_err("Creating tarball")?;
Expand Down Expand Up @@ -307,7 +374,7 @@ impl FlakeMetadata {

#[tracing::instrument(skip_all, fields(readme_dir))]
pub(crate) async fn get_readme_contents(&self) -> Result<Option<String>> {
let mut read_dir = tokio::fs::read_dir(&self.source_dir).await?;
let mut read_dir = tokio::fs::read_dir(self.local_flake_ref.directory()).await?;

let readme_path: Option<PathBuf> = {
let mut readme_path = None;
Expand Down
15 changes: 12 additions & 3 deletions src/release_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use color_eyre::eyre::{eyre, Context as _, Result};

use crate::cli::FlakeHubPushCli;
use crate::flake_info::FlakeMetadata;
use crate::flake_info::SubmoduleParameter;
use crate::flakehub_client::Tarball;
use crate::git_context::GitContext;
use crate::github::graphql::{MAX_LABEL_LENGTH, MAX_NUM_TOTAL_LABELS};
Expand Down Expand Up @@ -46,9 +47,17 @@ impl ReleaseMetadata {
// flake_dir is an absolute path of flake_root(aka git_root)/subdir
let flake_dir = local_git_root.join(&subdir);

let flake_metadata = FlakeMetadata::from_dir(&flake_dir, cli.my_flake_is_too_big)
.await
.wrap_err("Getting flake metadata")?;
let flake_metadata = FlakeMetadata::from_dir(
&flake_dir,
if cli.include_submodules {
SubmoduleParameter::Include
} else {
SubmoduleParameter::Exclude
},
cli.my_flake_is_too_big,
)
.await
.wrap_err("Getting flake metadata")?;
tracing::debug!("Got flake metadata: {:?}", flake_metadata);

// sanity checks
Expand Down