summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@noreply.codeberg.org>2023-09-02 12:30:32 +0000
committerLars Wirzenius <liw@noreply.codeberg.org>2023-09-02 12:30:32 +0000
commitfc56a38d96372011731ee875500b2f15d6918077 (patch)
treeee9470ec1ba697cd3294427500f0fa9e2b312e79
parentaec463dfe8b842d7831cdcba5954ac0689168ae0 (diff)
parent07b75366442bedbd7d5931f864c3289fa5258c7a (diff)
downloadambient-run-fc56a38d96372011731ee875500b2f15d6918077.tar.gz
Merge pull request 'feat: persist a build artifact cache between builds' (#7) from liw/cache into main
Reviewed-on: https://codeberg.org/ambient/ambient-run/pulls/7
-rw-r--r--ambient-run.md42
-rw-r--r--src/bin/ambient-run.rs3
-rw-r--r--src/project.rs8
-rw-r--r--src/qemu.rs34
4 files changed, 84 insertions, 3 deletions
diff --git a/ambient-run.md b/ambient-run.md
index 76fca3f..db16dfb 100644
--- a/ambient-run.md
+++ b/ambient-run.md
@@ -168,6 +168,7 @@ image: /my/image.qcow2
artifact: null
artifact_max_size: null
dependencies: null
+cache: null
~~~
### Show per-build configuration
@@ -346,6 +347,47 @@ dependencies: foo-deps
~~~
### Cache is persistent between builds
+
+_Requirement:_ The build can persist files between builds in a cache
+device.
+
+_Justification:_ This allows incremental builds and caching of build
+artifacts.
+
+_Stakeholder:_ Lars.
+
+~~~scenario
+given an installed ambient-run
+given file cache-project.yaml
+given file foo/README.md from cache-project.yaml
+given a directory foo-cache
+given image file image.qcow2 specified for test suite
+then file foo-cache/cached.file does not exist
+when I run ambient-run build cache-project.yaml --log foo.log
+then file foo.log contains "creating cached file"
+then file foo-cache/cached.file exists
+when I run ambient-run build cache-project.yaml --log foo2.log
+then file foo2.log contains "cache has file"
+~~~
+
+
+~~~{#cache-project.yaml .file .yaml}
+source: foo
+shell: |
+ #!/bin/bash
+ set -xeuo pipefail
+ find /workspace -ls
+ file="/workspace/cache/cached.file"
+ if [ -e "$file" ]; then
+ echo "cache has file"
+ else
+ echo "creating cached file"
+ touch "$file"
+ fi
+image: image.qcow2
+cache: foo-cache
+~~~
+
### Build gets the resources is demands
## Utility functionality
diff --git a/src/bin/ambient-run.rs b/src/bin/ambient-run.rs
index dbe7bfa..8157de7 100644
--- a/src/bin/ambient-run.rs
+++ b/src/bin/ambient-run.rs
@@ -218,7 +218,8 @@ impl BuildCommand {
.with_source(&project.source())
.with_artifact(project.artifact())
.with_artifact_max_size(project.artifact_max_size())
- .with_dependencies(project.dependencies());
+ .with_dependencies(project.dependencies())
+ .with_cache(project.cache());
if let Some(log) = &self.log {
qemu = qemu.with_log(log);
}
diff --git a/src/project.rs b/src/project.rs
index c9b2c38..19ca947 100644
--- a/src/project.rs
+++ b/src/project.rs
@@ -15,6 +15,7 @@ pub struct Project {
artifact: Option<PathBuf>,
artifact_max_size: Option<u64>,
dependencies: Option<PathBuf>,
+ cache: Option<PathBuf>,
}
impl Project {
@@ -74,6 +75,7 @@ impl Project {
self.artifact = snippet.artifact;
self.artifact_max_size = snippet.artifact_max_size;
self.dependencies = snippet.dependencies;
+ self.cache = snippet.cache;
Ok(())
}
@@ -111,6 +113,11 @@ impl Project {
pub fn dependencies(&self) -> &Option<PathBuf> {
&self.dependencies
}
+
+ /// Cache directory.
+ pub fn cache(&self) -> &Option<PathBuf> {
+ &self.cache
+ }
}
#[derive(Debug, Deserialize)]
@@ -122,6 +129,7 @@ struct ProjectSnippet {
artifact: Option<PathBuf>,
artifact_max_size: Option<u64>,
dependencies: Option<PathBuf>,
+ cache: Option<PathBuf>,
}
/// Possible errors from configuration file handling.
diff --git a/src/qemu.rs b/src/qemu.rs
index 2853e52..2a7a6b6 100644
--- a/src/qemu.rs
+++ b/src/qemu.rs
@@ -21,6 +21,7 @@ pub struct Qemu {
artifact: Option<PathBuf>,
artifact_max_xize: u64,
dependencies: Option<PathBuf>,
+ cache: Option<PathBuf>,
}
impl Qemu {
@@ -70,6 +71,12 @@ impl Qemu {
self
}
+ /// Set directory where project build cache is.
+ pub fn with_cache(mut self, dirname: &Option<PathBuf>) -> Self {
+ self.cache = dirname.clone();
+ self
+ }
+
/// Run QEMU in the specified way.
pub fn run(&self) -> Result<(), QemuError> {
eprintln!("qemu run");
@@ -97,8 +104,13 @@ impl Qemu {
};
eprintln!("cache drive");
- let cache_drive =
- Self::create_tar_with_size(tmp.path().join("cache"), empty.path(), MAX_OUTPUT_SIZE)?;
+ let cache_drive = if let Some(dirname) = &self.cache {
+ eprintln!("cache {}", dirname.display());
+ Self::create_tar_with_size(tmp.path().join("cache"), dirname, MAX_OUTPUT_SIZE)?
+ } else {
+ eprintln!("no cache");
+ Self::create_tar(tmp.path().join("cache"), empty.path())?
+ };
eprintln!("deps drive");
let deps_drive = if let Some(dirname) = &self.dependencies {
@@ -154,6 +166,18 @@ impl Qemu {
let output = child.wait_with_output().map_err(QemuError::Run)?;
if output.status.success() {
eprintln!("kvm OK");
+ if let Some(dirname) = &self.cache {
+ eprintln!("persisting cache");
+ if dirname.exists() {
+ eprintln!("removing old cache");
+ std::fs::remove_dir_all(dirname)
+ .map_err(|e| QemuError::RemoveCache(dirname.into(), e))?
+ }
+ eprintln!("extracting cache");
+ cache_drive
+ .extract_to(dirname)
+ .map_err(|e| QemuError::ExtractCache(dirname.into(), e))?;
+ }
Ok(())
} else {
let out = String::from_utf8_lossy(&output.stdout);
@@ -285,6 +309,12 @@ pub enum QemuError {
#[error("failed to create a tar archive from {0}")]
Tar(PathBuf, #[source] VirtualDriveError),
+
+ #[error("failed to remove cache (so it can be replaced): {0}")]
+ RemoveCache(PathBuf, #[source] std::io::Error),
+
+ #[error("failed to extract cache drive to {0}")]
+ ExtractCache(PathBuf, #[source] VirtualDriveError),
}
#[cfg(test)]