|
| 1 | +// This Source Code Form is subject to the terms of the Mozilla Public |
| 2 | +// License, v. 2.0. If a copy of the MPL was not distributed with this |
| 3 | +// file, You can obtain one at https://mozilla.org/MPL/2.0/. |
| 4 | + |
| 5 | +use anyhow::Context; |
| 6 | +use anyhow::Result; |
| 7 | +use camino::Utf8Path; |
| 8 | +use camino::Utf8PathBuf; |
| 9 | +use fs_err::tokio as fs; |
| 10 | +use fs_err::tokio::File; |
| 11 | +use serde::Deserialize; |
| 12 | +use slog::Logger; |
| 13 | +use tokio::io::AsyncWriteExt; |
| 14 | +use tokio::io::BufWriter; |
| 15 | + |
| 16 | +use crate::HELIOS_REPO; |
| 17 | +use crate::Jobs; |
| 18 | +use crate::cmd::Command; |
| 19 | + |
| 20 | +pub const INCORP_NAME: &str = |
| 21 | + "consolidation/oxide/omicron-release-incorporation"; |
| 22 | +const MANIFEST_PATH: &str = "incorporation.p5m"; |
| 23 | +const REPO_PATH: &str = "incorporation"; |
| 24 | +pub const ARCHIVE_PATH: &str = "incorporation.p5p"; |
| 25 | + |
| 26 | +pub const PUBLISHER: &str = "helios-dev"; |
| 27 | + |
| 28 | +pub(crate) enum Action { |
| 29 | + Generate { version: String }, |
| 30 | + Passthru { version: String }, |
| 31 | +} |
| 32 | + |
| 33 | +pub(crate) async fn push_incorporation_jobs( |
| 34 | + jobs: &mut Jobs, |
| 35 | + logger: &Logger, |
| 36 | + output_dir: &Utf8Path, |
| 37 | + action: Action, |
| 38 | +) -> Result<()> { |
| 39 | + let manifest_path = output_dir.join(MANIFEST_PATH); |
| 40 | + let repo_path = output_dir.join(REPO_PATH); |
| 41 | + let archive_path = output_dir.join(ARCHIVE_PATH); |
| 42 | + |
| 43 | + fs::remove_dir_all(&repo_path).await.or_else(ignore_not_found)?; |
| 44 | + fs::remove_file(&archive_path).await.or_else(ignore_not_found)?; |
| 45 | + |
| 46 | + match action { |
| 47 | + Action::Generate { version } => { |
| 48 | + jobs.push( |
| 49 | + "incorp-manifest", |
| 50 | + generate_incorporation_manifest( |
| 51 | + logger.clone(), |
| 52 | + manifest_path.clone(), |
| 53 | + version, |
| 54 | + ), |
| 55 | + ); |
| 56 | + } |
| 57 | + Action::Passthru { version } => { |
| 58 | + jobs.push( |
| 59 | + "incorp-manifest", |
| 60 | + passthru_incorporation_manifest( |
| 61 | + logger.clone(), |
| 62 | + manifest_path.clone(), |
| 63 | + version, |
| 64 | + ), |
| 65 | + ); |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + jobs.push_command( |
| 70 | + "incorp-fmt", |
| 71 | + Command::new("pkgfmt").args(["-u", "-f", "v2", manifest_path.as_str()]), |
| 72 | + ) |
| 73 | + .after("incorp-manifest"); |
| 74 | + |
| 75 | + jobs.push_command( |
| 76 | + "incorp-create", |
| 77 | + Command::new("pkgrepo").args(["create", repo_path.as_str()]), |
| 78 | + ); |
| 79 | + |
| 80 | + let path_args = ["-s", repo_path.as_str()]; |
| 81 | + jobs.push_command( |
| 82 | + "incorp-publisher", |
| 83 | + Command::new("pkgrepo") |
| 84 | + .arg("add-publisher") |
| 85 | + .args(&path_args) |
| 86 | + .arg(PUBLISHER), |
| 87 | + ) |
| 88 | + .after("incorp-create"); |
| 89 | + |
| 90 | + jobs.push_command( |
| 91 | + "incorp-pkgsend", |
| 92 | + Command::new("pkgsend") |
| 93 | + .arg("publish") |
| 94 | + .args(&path_args) |
| 95 | + .arg(manifest_path), |
| 96 | + ) |
| 97 | + .after("incorp-fmt") |
| 98 | + .after("incorp-publisher"); |
| 99 | + |
| 100 | + jobs.push_command( |
| 101 | + "helios-incorp", |
| 102 | + Command::new("pkgrecv") |
| 103 | + .args(path_args) |
| 104 | + .args(["-a", "-d", archive_path.as_str()]) |
| 105 | + .args(["-m", "latest", "-v", "*"]), |
| 106 | + ) |
| 107 | + .after("incorp-pkgsend"); |
| 108 | + |
| 109 | + Ok(()) |
| 110 | +} |
| 111 | + |
| 112 | +async fn generate_incorporation_manifest( |
| 113 | + logger: Logger, |
| 114 | + path: Utf8PathBuf, |
| 115 | + version: String, |
| 116 | +) -> Result<()> { |
| 117 | + #[derive(Deserialize, PartialEq, Eq, PartialOrd, Ord)] |
| 118 | + struct Package { |
| 119 | + fmri: String, |
| 120 | + } |
| 121 | + |
| 122 | + let mut manifest = BufWriter::new(File::create(path).await?); |
| 123 | + let preamble = format!( |
| 124 | + r#"set name=pkg.fmri value=pkg://{PUBLISHER}/{INCORP_NAME}@{version},5.11 |
| 125 | +set name=pkg.summary value="Incorporation to constrain software delivered in Omicron Release V{version} images" |
| 126 | +set name=info.classification value="org.opensolaris.category.2008:Meta Packages/Incorporations" |
| 127 | +set name=variant.opensolaris.zone value=global value=nonglobal |
| 128 | +"# |
| 129 | + ); |
| 130 | + manifest.write_all(preamble.as_bytes()).await?; |
| 131 | + |
| 132 | + let stdout = Command::new("pkg") |
| 133 | + .args(["list", "-g", HELIOS_REPO, "-F", "json"]) |
| 134 | + .args(["-o", "fmri", "*@latest"]) |
| 135 | + .ensure_stdout(&logger) |
| 136 | + .await?; |
| 137 | + let packages: Vec<Package> = serde_json::from_str(&stdout) |
| 138 | + .context("failed to parse pkgrepo output")?; |
| 139 | + let prefix = format!("pkg://{PUBLISHER}/"); |
| 140 | + for package in packages { |
| 141 | + let Some(partial) = package.fmri.strip_prefix(&prefix) else { |
| 142 | + continue; |
| 143 | + }; |
| 144 | + let Some((package, _)) = partial.split_once('@') else { |
| 145 | + continue; |
| 146 | + }; |
| 147 | + if package == INCORP_NAME || package == "driver/network/opte" { |
| 148 | + continue; |
| 149 | + } |
| 150 | + let line = format!("depend type=incorporate fmri=pkg:/{partial}\n"); |
| 151 | + manifest.write_all(line.as_bytes()).await?; |
| 152 | + } |
| 153 | + |
| 154 | + manifest.shutdown().await?; |
| 155 | + Ok(()) |
| 156 | +} |
| 157 | + |
| 158 | +async fn passthru_incorporation_manifest( |
| 159 | + logger: Logger, |
| 160 | + path: Utf8PathBuf, |
| 161 | + version: String, |
| 162 | +) -> Result<()> { |
| 163 | + let stdout = Command::new("pkgrepo") |
| 164 | + .args(["contents", "-m", "-s", HELIOS_REPO]) |
| 165 | + .arg(format!("pkg://{PUBLISHER}/{INCORP_NAME}@{version},5.11")) |
| 166 | + .ensure_stdout(&logger) |
| 167 | + .await?; |
| 168 | + fs::write(&path, stdout).await?; |
| 169 | + Ok(()) |
| 170 | +} |
| 171 | + |
| 172 | +fn ignore_not_found(err: std::io::Error) -> Result<(), std::io::Error> { |
| 173 | + if err.kind() == std::io::ErrorKind::NotFound { Ok(()) } else { Err(err) } |
| 174 | +} |
0 commit comments