Fetch flake outputs and display them
This commit is contained in:
36
figure-out-flake-outputs.nix
Normal file
36
figure-out-flake-outputs.nix
Normal file
@@ -0,0 +1,36 @@
|
||||
outputAttrs: let
|
||||
|
||||
attrNamePathToString = attrNamePath: builtins.concatStringsSep "." attrNamePath;
|
||||
|
||||
outputInfo = drv: builtins.listToAttrs (builtins.map (outputName: {
|
||||
name = outputName;
|
||||
value = {
|
||||
store_path = drv."${outputName}".outPath;
|
||||
};
|
||||
}) drv.outputs);
|
||||
|
||||
recurseAttrs = attrs: attrNamePath: builtins.concatLists (builtins.attrValues (
|
||||
builtins.mapAttrs (attrName: attrValue:
|
||||
if builtins.typeOf attrValue == "set" then
|
||||
if builtins.hasAttr "type" attrValue then
|
||||
if attrValue.type == "derivation" then
|
||||
[ {
|
||||
name = attrNamePathToString (attrNamePath ++ [ attrName ]);
|
||||
value = {
|
||||
name = attrValue.name;
|
||||
derivation_path = attrValue.drvPath;
|
||||
system = attrValue.system;
|
||||
outputs = outputInfo attrValue;
|
||||
};
|
||||
} ]
|
||||
else
|
||||
[ "unknown type" ]
|
||||
else
|
||||
recurseAttrs attrValue (attrNamePath ++ [ attrName ])
|
||||
else
|
||||
[]
|
||||
) attrs
|
||||
));
|
||||
|
||||
in
|
||||
builtins.listToAttrs (recurseAttrs outputAttrs [])
|
@@ -1,20 +1,20 @@
|
||||
CREATE TABLE revisions (
|
||||
revision_uri TEXT NOT NULL PRIMARY KEY,
|
||||
flake_uri TEXT NOT NULL,
|
||||
store_path TEXT,
|
||||
last_modified INT
|
||||
store_path TEXT NOT NULL,
|
||||
last_modified INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE output_attributes (
|
||||
revision_uri TEXT NOT NULL,
|
||||
output_attribute_name TEXT NOT NULL,
|
||||
derivation_path TEXT,
|
||||
derivation_path TEXT NOT NULL,
|
||||
PRIMARY KEY (revision_uri, output_attribute_name)
|
||||
);
|
||||
|
||||
CREATE TABLE build_outputs (
|
||||
derivation_path TEXT NOT NULL,
|
||||
build_output_name TEXT NOT NULL,
|
||||
store_path TEXT,
|
||||
store_path TEXT NOT NULL,
|
||||
PRIMARY KEY (derivation_path, build_output_name)
|
||||
);
|
||||
|
54
src/scan.rs
54
src/scan.rs
@@ -13,12 +13,29 @@ use crate::{
|
||||
},
|
||||
storage::{
|
||||
FlakeRow,
|
||||
OutputAttributeRow,
|
||||
RevisionRow,
|
||||
Storage,
|
||||
},
|
||||
};
|
||||
use serde::{
|
||||
Deserialize,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::process::Command;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct OutputAttribute {
|
||||
pub derivation_path: String,
|
||||
pub name: String,
|
||||
pub outputs: HashMap<String, BuildOutput>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct BuildOutput {
|
||||
pub store_path: String,
|
||||
}
|
||||
|
||||
pub async fn scan_flake(storage: Storage, flake_uri: &str) -> Result<()> {
|
||||
let scan_time = Utc::now().timestamp();
|
||||
|
||||
@@ -30,6 +47,20 @@ pub async fn scan_flake(storage: Storage, flake_uri: &str) -> Result<()> {
|
||||
storage.set_revision(revision_row)
|
||||
.await?;
|
||||
|
||||
let flake_outputs = fetch_outputs(flake_uri)
|
||||
.await?;
|
||||
|
||||
for (output_attribute_name, derivation_info) in &flake_outputs {
|
||||
let output_attribute_row = OutputAttributeRow {
|
||||
revision_uri: flake_metadata.locked.flake_uri()?.clone(),
|
||||
output_attribute_name: output_attribute_name.clone(),
|
||||
derivation_path: derivation_info.derivation_path.clone(),
|
||||
};
|
||||
|
||||
storage.set_output_attribute(output_attribute_row)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -52,9 +83,28 @@ pub fn get_revision_from_metadata(flake_metadata: &FlakeMetadata) -> Result<Revi
|
||||
let revision_row = RevisionRow {
|
||||
revision_uri: flake_metadata.locked.flake_uri()?.clone(),
|
||||
flake_uri: flake_metadata.resolved.flake_uri()?.clone(),
|
||||
store_path: Some(flake_metadata.path.clone()),
|
||||
last_modified: Some(flake_metadata.locked.last_modified.clone()),
|
||||
store_path: flake_metadata.path.clone(),
|
||||
last_modified: flake_metadata.locked.last_modified.clone(),
|
||||
};
|
||||
|
||||
Ok(revision_row)
|
||||
}
|
||||
|
||||
pub async fn fetch_outputs(flake_uri: &str) -> Result<HashMap<String, OutputAttribute>> {
|
||||
let figure_out_flake_outputs = std::include_str!("../figure-out-flake-outputs.nix");
|
||||
|
||||
let flake_outputs_raw = Command::new("nix")
|
||||
.arg("eval")
|
||||
.arg("--json")
|
||||
.arg(format!("{}#hydraJobs", flake_uri))
|
||||
.arg("--apply")
|
||||
.arg(figure_out_flake_outputs)
|
||||
.output()
|
||||
.context("Failed to fetch flake outputs")?;
|
||||
|
||||
println!("{}", str::from_utf8(&flake_outputs_raw.stdout)?);
|
||||
let flake_outputs: HashMap<String, OutputAttribute> = serde_json::from_slice(&flake_outputs_raw.stdout)
|
||||
.context("Failed to parse flake outputs")?;
|
||||
|
||||
Ok(flake_outputs)
|
||||
}
|
||||
|
@@ -97,14 +97,44 @@ impl Storage {
|
||||
.await
|
||||
.context("Failed to fetch data from database")
|
||||
}
|
||||
|
||||
pub async fn set_output_attribute(&self, output_attribute_row: OutputAttributeRow) -> Result<SqliteQueryResult> {
|
||||
sqlx::query("INSERT INTO output_attributes (revision_uri, output_attribute_name, derivation_path)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT(revision_uri, output_attribute_name) DO UPDATE SET
|
||||
derivation_path=excluded.derivation_path
|
||||
")
|
||||
.bind(&output_attribute_row.revision_uri)
|
||||
.bind(&output_attribute_row.output_attribute_name)
|
||||
.bind(&output_attribute_row.derivation_path)
|
||||
.execute(&self.db)
|
||||
.await
|
||||
.context("Failed to execute database query")
|
||||
}
|
||||
|
||||
pub async fn output_attributes_for_revision(&self, revision_uri: &str) -> Result<Vec<OutputAttributeRow>> {
|
||||
sqlx::query_as("
|
||||
SELECT
|
||||
revision_uri,
|
||||
output_attribute_name,
|
||||
derivation_path
|
||||
FROM output_attributes
|
||||
WHERE revision_uri = ?
|
||||
ORDER BY output_attribute_name DESC
|
||||
")
|
||||
.bind(&revision_uri)
|
||||
.fetch_all(&self.db)
|
||||
.await
|
||||
.context("Failed to fetch data from database")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromRow)]
|
||||
pub struct RevisionRow {
|
||||
pub revision_uri: String,
|
||||
pub flake_uri: String,
|
||||
pub store_path: Option<String>,
|
||||
pub last_modified: Option<i64>,
|
||||
pub store_path: String,
|
||||
pub last_modified: i64,
|
||||
}
|
||||
|
||||
impl RevisionRow {
|
||||
@@ -117,10 +147,7 @@ impl RevisionRow {
|
||||
}
|
||||
|
||||
pub fn last_modified_time(&self) -> Option<DateTime<Utc>> {
|
||||
match &self.last_modified {
|
||||
Some(last_modified) => DateTime::from_timestamp(last_modified.clone(), 0),
|
||||
None => None,
|
||||
}
|
||||
DateTime::from_timestamp(self.last_modified.clone(), 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,3 +162,33 @@ impl FlakeRow {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromRow)]
|
||||
pub struct OutputAttributeRow {
|
||||
pub revision_uri: String,
|
||||
pub output_attribute_name: String,
|
||||
pub derivation_path: String,
|
||||
}
|
||||
|
||||
impl OutputAttributeRow {
|
||||
pub fn revision_link(&self) -> String {
|
||||
format!("/revisions/{}", urlencode(&self.revision_uri))
|
||||
}
|
||||
|
||||
pub fn derivation_link(&self) -> String {
|
||||
format!("/derivations/{}", urlencode(&self.derivation_path))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromRow)]
|
||||
pub struct BuildOutputRow {
|
||||
pub derivation_path: String,
|
||||
pub build_output_name: String,
|
||||
pub store_path: String,
|
||||
}
|
||||
|
||||
impl BuildOutputRow {
|
||||
pub fn derivation_link(&self ) -> String {
|
||||
format!("/derivations/{}", urlencode(&self.derivation_path))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,6 +4,7 @@ use askama::{
|
||||
use crate::{
|
||||
storage::{
|
||||
FlakeRow,
|
||||
OutputAttributeRow,
|
||||
RevisionRow,
|
||||
},
|
||||
};
|
||||
@@ -26,4 +27,5 @@ pub struct FlakeTemplate {
|
||||
pub struct RevisionTemplate {
|
||||
pub revision_uri: String,
|
||||
pub revision: RevisionRow,
|
||||
pub output_attributes: Vec<OutputAttributeRow>,
|
||||
}
|
||||
|
@@ -118,5 +118,6 @@ async fn route_revision(
|
||||
Ok(render_template(&RevisionTemplate {
|
||||
revision_uri: revision_uri.clone(),
|
||||
revision: state.storage.revision(&revision_uri).await?,
|
||||
output_attributes: state.storage.output_attributes_for_revision(&revision_uri).await?,
|
||||
})?)
|
||||
}
|
||||
|
@@ -8,5 +8,12 @@
|
||||
<li>Revision of: <a href="{{ revision.flake_link() }}">{{ revision.flake_uri }}</a></a>
|
||||
</ul>
|
||||
|
||||
<h2>Outputs</h2>
|
||||
|
||||
<ul>
|
||||
{% for output in output_attributes %}
|
||||
<li>{{ output.output_attribute_name }}: <a href="{{ output.derivation_link() }}">{{ output.derivation_path }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
|
Reference in New Issue
Block a user