snix_build/buildservice/
bwrap.rs1use std::path::{Path, PathBuf};
2
3use futures::stream::BoxStream;
4use snix_castore::{
5 blobservice::BlobService, directoryservice::DirectoryService, fs::fuse::FuseDaemon,
6};
7use tracing::{Span, info, instrument};
8use uuid::Uuid;
9
10use super::BuildService;
11use crate::{
12 buildservice::{BuildConstraints, BuildRequest, BuildUpdate, streaming::run_build_streaming},
13 bwrap::Bwrap,
14 sandbox::SandboxSpec,
15};
16const SANDBOX_SHELL: &str = env!("SNIX_BUILD_SANDBOX_SHELL");
17
18pub struct BubblewrapBuildService<BS, DS> {
19 workdir: PathBuf,
21
22 blob_service: BS,
24 directory_service: DS,
26
27 concurrent_builds: tokio::sync::Semaphore,
30}
31impl<BS, DS> BubblewrapBuildService<BS, DS> {
32 pub fn new(workdir: PathBuf, blob_service: BS, directory_service: DS) -> Self {
33 Self {
38 workdir,
39 blob_service,
40 directory_service,
41 concurrent_builds: tokio::sync::Semaphore::new(2),
42 }
43 }
44}
45
46impl<BS, DS> BubblewrapBuildService<BS, DS>
47where
48 BS: BlobService + Clone + 'static,
49 DS: DirectoryService + Clone + 'static,
50{
51 fn make_spec(&self, request: BuildRequest, sandbox_path: &Path) -> SandboxSpec {
52 let blob_service = self.blob_service.clone();
53 let directory_service = self.directory_service.clone();
54
55 SandboxSpec::builder()
56 .host_workdir(sandbox_path.to_path_buf())
57 .sandbox_workdir(request.working_dir)
58 .scratches(request.scratch_paths)
59 .command(request.command_args)
60 .env_vars(request.environment_vars)
61 .additional_files(request.additional_files)
62 .with_inputs(request.inputs_dir, move |path| {
63 let root_nodes = Box::new(request.inputs.clone());
64 let fs = snix_castore::fs::SnixStoreFs::new(
65 blob_service.clone(),
66 directory_service.clone(),
67 root_nodes,
68 snix_castore::fs::FSSettings {
69 list_root: true,
70 uid_gid_override: None,
71 show_xattr: false,
72 },
73 tokio::runtime::Handle::current(),
74 );
75 FuseDaemon::new(fs, path, 4, false)
77 })
78 .allow_network(
79 request
80 .constraints
81 .contains(&BuildConstraints::NetworkAccess),
82 )
83 .provide_shell(
84 request
85 .constraints
86 .contains(&BuildConstraints::ProvideBinSh)
87 .then_some(SANDBOX_SHELL.into()),
88 )
89 .build()
90 }
91}
92
93impl<BS, DS> BuildService for BubblewrapBuildService<BS, DS>
94where
95 BS: BlobService + Clone + 'static,
96 DS: DirectoryService + Clone + 'static,
97{
98 #[instrument(skip_all, fields(build.name=tracing::field::Empty))]
99 fn do_build(&self, request: BuildRequest) -> BoxStream<'_, BuildUpdate> {
100 let span = Span::current();
101 let build_name = Uuid::new_v4();
102 let sandbox_path = self.workdir.join(build_name.to_string());
103 span.record("build.name", build_name.to_string());
104
105 let outputs = request.outputs.clone();
106 let needles = request.refscan_needles.clone();
107
108 let spec = self.make_spec(request, &sandbox_path);
109
110 run_build_streaming(
111 &self.concurrent_builds,
112 self.blob_service.clone(),
113 self.directory_service.clone(),
114 outputs,
115 needles,
116 move || async move {
117 info!("Starting bwrap build");
118
119 let (stream, finder) = Bwrap::initialize(spec)?.run()?;
120 Ok((stream, finder))
121 },
122 )
123 }
124}