difftreelog
feat(jrb) compute hash
in: master
5 files changed
cmds/jrb/src/main.rsdiffbeforeafterboth18 /// The directory used to cache packages in.18 /// The directory used to cache packages in.19 #[clap(long, default_value = "vendor")]19 #[clap(long, default_value = "vendor")]20 jsonnetpkg_home: PathBuf,20 jsonnetpkg_home: PathBuf,21 /// Accept checksums of trees jsonnet-bundler hashes incorrectly. Correct22 /// checksums are written back, so this is only needed once per lockfile.23 #[clap(long)]24 compat: bool,21 #[clap(subcommand)]25 #[clap(subcommand)]22 command: Command,26 command: Command,23}27}103 manifest: &JsonnetFile,107 manifest: &JsonnetFile,104 lock: Option<&JsonnetFile>,108 lock: Option<&JsonnetFile>,105 vendor_dir: &Path,109 vendor_dir: &Path,106 dry_run: bool,110 opts: install::Options,107) {111) {112 let dry_run = opts.dry_run;108 let new_lock = install::install(manifest, lock, vendor_dir, dry_run).unwrap_or_else(|e| {113 let new_lock = install::install(manifest, lock, vendor_dir, opts).unwrap_or_else(|e| {109 error!("install failed: {e}");114 error!("install failed: {e}");110 exit(1);115 exit(1);111 });116 });169 &manifest,170 lock.as_ref(),171 &opts.jsonnetpkg_home,172 install::Options {173 dry_run,174 compat: opts.compat,175 },176 );164 }177 }165 Command::Update { uris, dry_run } => {178 Command::Update { uris, dry_run } => {201 &manifest,202 None,203 &opts.jsonnetpkg_home,204 install::Options {205 dry_run,206 compat: opts.compat,207 },208 );188 }209 }189 Command::Remove { names, dry_run } => {210 Command::Remove { names, dry_run } => {crates/jrsonnet-pkg/Cargo.tomldiffbeforeafterboth29# Global cache dir29# Global cache dir30directories.workspace = true30directories.workspace = true3132# Lockfile checksums33sha2.workspace = true34base64.workspace = true3135crates/jrsonnet-pkg/src/install/hash.rsdiffbeforeafterbothno changes
crates/jrsonnet-pkg/src/install/mod.rsdiffbeforeafterboth3pub mod accessor;3pub mod accessor;4mod git;4mod git;5mod github;5mod github;6mod hash;677use std::{8use std::{8 collections::{BTreeMap, HashSet},9 collections::{BTreeMap, HashSet},11 result,12 result,12};13};131414use camino::Utf8PathBuf;15use camino::{Utf8Path, Utf8PathBuf};15use tracing::info;16use tracing::{info, warn};161717use crate::jsonnet_bundler::{Dependency, GitScheme, GitSource, JsonnetFile, Source, SubDir};18use crate::{19 install::hash::{Exclusions, Mode, hash_dir},20 jsonnet_bundler::{Dependency, GitScheme, GitSource, JsonnetFile, Source, SubDir},21};182219pub const PKG_USER_AGENT: &str = "jrsonnet-pkg (https://delta.rocks/jrsonnet)";23pub const PKG_USER_AGENT: &str = "jrsonnet-pkg (https://delta.rocks/jrsonnet)";202487 pub entries: BTreeMap<Utf8PathBuf, VendorSource>,91 pub entries: BTreeMap<Utf8PathBuf, VendorSource>,88}92}9394#[derive(Debug, Clone, Copy, Default)]95pub struct Options {96 pub dry_run: bool,97 /// Accept checksums of trees `jsonnet-bundler` hashes incorrectly. Correct98 /// checksums are written back, so this is only needed once per lockfile.99 pub compat: bool,100}8910190pub fn install(102pub fn install(91 manifest: &JsonnetFile,103 manifest: &JsonnetFile,92 lock: Option<&JsonnetFile>,104 lock: Option<&JsonnetFile>,93 vendor_dir: &Path,105 vendor_dir: &Path,94 dry_run: bool,106 opts: Options,95) -> Result<JsonnetFile, Error> {107) -> Result<JsonnetFile, Error> {96 let plan = resolve(manifest, lock)?;108 let mut plan = resolve(manifest, lock)?;97 execute(&plan, vendor_dir, dry_run)?;109 execute(&plan, vendor_dir, opts)?;110 if !opts.dry_run {111 update_sums(&mut plan, vendor_dir, opts.compat)?;112 }98 Ok(plan.lock)113 Ok(plan.lock)99}114}100115138 ))153 ))139}154}140155141fn is_up_to_date(dest: &Path, version: &str) -> bool {156fn is_up_to_date(157 plan: &InstallPlan,158 vendor_dir: &Path,159 path: &Utf8PathBuf,160 version: &str,161 compat: bool,162) -> bool {163 let dest = vendor_dir.join(path);142 fs::read_to_string(dest.join(VERSION_FILE)).is_ok_and(|v| v.trim() == version)164 if !fs::read_to_string(dest.join(VERSION_FILE)).is_ok_and(|v| v.trim() == version) {165 return false;166 }167 // The vendored tree may have been modified since it was extracted.168 let Some(expected) = locked_sum(plan, path) else {169 return true;170 };171 let exclude = exclusions(&plan.entries, vendor_dir, path);172 let matches =173 |mode| hash_dir(&dest, &exclude, Some(version), mode).is_ok_and(|sum| sum == expected);174 matches(Mode::Correct) || (compat && matches(Mode::JsonnetBundler))143}175}144176145fn write_version(dest: &Path, version: &str) -> Result<(), Error> {177fn write_version(dest: &Path, version: &str) -> Result<(), Error> {178 let marker = dest.join(VERSION_FILE);179 if marker.exists() {180 warn!(181 "{}: dependency ships its own {VERSION_FILE}, keeping it and re-extracting on every install",182 dest.display()183 );184 return Ok(());185 }146 fs::write(dest.join(VERSION_FILE), format!("{version}\n"))186 fs::write(&marker, format!("{version}\n")).map_err(|e| Error::Io(marker, e))147 .map_err(|e| Error::Io(dest.join(VERSION_FILE), e))148}187}149188150pub fn execute(plan: &InstallPlan, vendor_dir: &Path, dry_run: bool) -> Result<(), Error> {189pub fn execute(plan: &InstallPlan, vendor_dir: &Path, opts: Options) -> Result<(), Error> {190 let dry_run = opts.dry_run;151 if !dry_run {191 if !dry_run {152 for (path, source) in &plan.entries {192 for (path, source) in &plan.entries {153 let dest = vendor_dir.join(path);193 let dest = vendor_dir.join(path);157 commit_sha,197 commit_sha,158 subdir,198 subdir,159 } => {199 } => {160 if is_up_to_date(&dest, commit_sha) {200 if is_up_to_date(plan, vendor_dir, path, commit_sha, opts.compat) {161 continue;201 continue;162 }202 }163 info!("extract {path}");203 info!("extract {path}");173 commit_sha,213 commit_sha,174 subdir,214 subdir,175 } => {215 } => {176 if is_up_to_date(&dest, commit_sha) {216 if is_up_to_date(plan, vendor_dir, path, commit_sha, opts.compat) {177 continue;217 continue;178 }218 }179 info!("extract {path}");219 info!("extract {path}");208 Ok(())248 Ok(())209}249}250251/// Nested dependencies are not part of the checksum of the dependency they are252/// nested in, as `jsonnet-bundler` wipes them when extracting the parent.253fn exclusions(254 entries: &BTreeMap<Utf8PathBuf, VendorSource>,255 vendor_dir: &Path,256 path: &Utf8Path,257) -> Exclusions {258 let prefix = format!("{path}/");259 Exclusions(260 entries261 .keys()262 .filter(|k| k.as_str().starts_with(&prefix))263 .map(|k| vendor_dir.join(k))264 .collect(),265 )266}267268fn locked_sum<'a>(plan: &'a InstallPlan, path: &Utf8Path) -> Option<&'a str> {269 plan.lock270 .dependencies271 .iter()272 .find(|d| d.canonical_name() == path)?273 .sum274 .as_deref()275}276277/// Verify checksums of the vendored trees against the ones carried over from278/// the lockfile, and record the missing ones.279fn update_sums(plan: &mut InstallPlan, vendor_dir: &Path, compat: bool) -> Result<(), Error> {280 let InstallPlan { lock, entries } = plan;281 for dep in &mut lock.dependencies {282 if !matches!(dep.source, Source::Git(_)) {283 continue;284 }285 let path = dep.canonical_name();286 if !entries.contains_key(&path) {287 continue;288 }289 let dir = vendor_dir.join(&path);290 let exclude = exclusions(entries, vendor_dir, &path);291 if !exclude.0.is_empty() {292 warn!(293 "{path}: nested dependencies are excluded from its checksum, jsonnet-bundler's value depends on install order here"294 );295 }296 let version = dep.version.as_deref();297 let sum = hash_dir(&dir, &exclude, version, Mode::Correct)?;298 if let Some(expected) = &dep.sum299 && *expected != sum300 {301 // The tree itself is intact, the lockfile just carries a checksum302 // jsonnet-bundler computed incorrectly. Replace it with a correct one.303 let bundler_sum = compat304 .then(|| hash_dir(&dir, &exclude, version, Mode::JsonnetBundler))305 .transpose()?;306 if bundler_sum.as_deref() != Some(expected.as_str()) {307 return Err(Error::ChecksumMismatch {308 dep: path,309 expected: expected.clone(),310 got: sum,311 });312 }313 warn!("{path}: replacing the checksum written by jsonnet-bundler");314 }315 dep.sum = Some(sum);316 }317 Ok(())318}210319211fn prune(plan: &InstallPlan, vendor_dir: &Path, dry_run: bool) -> Result<(), Error> {320fn prune(plan: &InstallPlan, vendor_dir: &Path, dry_run: bool) -> Result<(), Error> {212 if !vendor_dir.is_dir() {321 if !vendor_dir.is_dir() {289 git::resolve(git_source, version)398 git::resolve(git_source, version)290}399}291400292fn locked_version<'a>(dep: &Dependency, lock: Option<&'a JsonnetFile>) -> Option<&'a str> {401fn locked_dep<'a>(dep: &Dependency, lock: Option<&'a JsonnetFile>) -> Option<&'a Dependency> {293 let lock = lock?;294 let key = dep.canonical_name();402 let key = dep.canonical_name();295 lock.dependencies403 lock?296 .iter()404 .dependencies297 .find(|d| d.canonical_name() == key)405 .iter()298 .and_then(|d| d.version.as_deref())406 .find(|d| d.canonical_name() == key)299}407}300408301fn resolve_deps(409fn resolve_deps(315 continue;423 continue;316 }424 }317425426 let locked = locked_dep(dep, lock);318 let version = locked_version(dep, lock).or(dep.version.as_deref());427 let version = locked428 .and_then(|d| d.version.as_deref())429 .or(dep.version.as_deref());319430320 info!(431 info!(324435325 let result = resolve_one(git_source, version)?;436 let result = resolve_one(git_source, version)?;437438 // Only the lockfile checksum is authoritative, and only for the exact439 // version it was recorded for.440 let expected_sum = locked441 .filter(|d| d.version.as_deref() == Some(result.version.as_str()))442 .and_then(|d| d.sum.clone());326443327 plan.lock.dependencies.push(Dependency {444 plan.lock.dependencies.push(Dependency {328 source: dep.source.clone(),445 source: dep.source.clone(),329 version: Some(result.version),446 version: Some(result.version),330 sum: dep.sum.clone(),447 sum: expected_sum,331 name: dep.name.clone(),448 name: dep.name.clone(),332 single: dep.single,449 single: dep.single,333 });450 });417 Zip(Box<zip::result::ZipError>),534 Zip(Box<zip::result::ZipError>),418 #[error(transparent)]535 #[error(transparent)]419 Accessor(#[from] accessor::Error),536 Accessor(#[from] accessor::Error),537 #[error(538 "checksum mismatch for {dep}: expected {expected}, got {got}. If the lockfile was written by jsonnet-bundler, pass --compat to accept and rewrite its checksums"539 )]540 ChecksumMismatch {541 dep: Utf8PathBuf,542 expected: String,543 got: String,544 },420 #[error("unknown subdir: {0}")]545 #[error("unknown subdir: {0}")]421 SubdirNotFound(String),546 SubdirNotFound(String),422 #[error("invalid path in tree: {0}")]547 #[error("invalid path in tree: {0}")]tests/realworld/jsonnetfile.lock.jsondiffbeforeafterboth7 "remote": "https://github.com/prometheus-operator/kube-prometheus"7 "remote": "https://github.com/prometheus-operator/kube-prometheus"8 }8 }9 },9 },10 "version": "392c6c5e8e83c9a0f76355d4b0837dccf76ad7ea"10 "version": "392c6c5e8e83c9a0f76355d4b0837dccf76ad7ea",11 "sum": "wGFFNc+cprdcsemJ+Fx1738tiRWXPaV9uqFie7UoBlE="11 },12 },12 {13 {13 "source": {14 "source": {16 "subdir": "mixin-utils"17 "subdir": "mixin-utils"17 }18 }18 },19 },19 "version": "3b8056670048416f5fb660dc039e473eb20a40b2"20 "version": "3b8056670048416f5fb660dc039e473eb20a40b2",21 "sum": "zVkvwae4zHP6xeIoDvde/wldI6ClprFjnk50mMD9/z4="20 },22 },21 {23 {22 "source": {24 "source": {25 "subdir": "grafana"27 "subdir": "grafana"26 }28 }27 },29 },28 "version": "5698c8940b6dadca3f42107b7839557bc041761f"30 "version": "5698c8940b6dadca3f42107b7839557bc041761f",31 "sum": "l6fPvh3tW6fWot308w71QY/amrYsFPeitvz1IgJxqQA="29 },32 },30 {33 {31 "source": {34 "source": {34 "subdir": "grafonnet"37 "subdir": "grafonnet"35 }38 }36 },39 },37 "version": "a1d61cce1da59c71409b99b5c7568511fec661ea"40 "version": "a1d61cce1da59c71409b99b5c7568511fec661ea",41 "sum": "342u++/7rViR/zj2jeJOjshzglkZ1SY+hFNuyCBFMdc="38 },42 },39 {43 {40 "source": {44 "source": {44 }48 }45 },49 },46 "version": "278a9c4cb758a546229e2b408def7e3dcb5f4ec0",50 "version": "278a9c4cb758a546229e2b408def7e3dcb5f4ec0",51 "sum": "sRjOJwJhhkCDmMQls7clo/csVix/b47W+Wc3YCzn64Y=",47 "name": "grafana-mixin"52 "name": "grafana-mixin"48 },53 },49 {54 {53 "subdir": "contrib/mixin"58 "subdir": "contrib/mixin"54 }59 }55 },60 },56 "version": "0cbc031e7f224102bd1ec42a81f659a2b8c3350c"61 "version": "0cbc031e7f224102bd1ec42a81f659a2b8c3350c",62 "sum": "XmXkOCriQIZmXwlIIFhqlJMa0e6qGWdxZD+ZDYaN0Po="57 },63 },58 {64 {59 "source": {65 "source": {62 "subdir": "gen/grafonnet-v10.0.0"68 "subdir": "gen/grafonnet-v10.0.0"63 }69 }64 },70 },65 "version": "7380c9c64fb973f34c3ec46265621a2b0dee0058"71 "version": "7380c9c64fb973f34c3ec46265621a2b0dee0058",72 "sum": "xdcrJPJlpkq4+5LpGwN4tPAuheNNLXZjE6tDcyvFjr0="66 },73 },67 {74 {68 "source": {75 "source": {71 "subdir": "doc-util"78 "subdir": "doc-util"72 }79 }73 },80 },74 "version": "bf6f08ae02a51c48bdcec4629b1c1a5a62c6f803"81 "version": "bf6f08ae02a51c48bdcec4629b1c1a5a62c6f803",82 "sum": "BrAL/k23jq+xy9oA7TWIhUx07dsA/QLm3g7ktCwe//U="75 },83 },76 {84 {77 "source": {85 "source": {78 "git": {86 "git": {79 "remote": "https://github.com/jsonnet-libs/xtd.git"87 "remote": "https://github.com/jsonnet-libs/xtd.git"80 }88 }81 },89 },82 "version": "4d7f8cb24d613430799f9d56809cc6964f35cea9"90 "version": "4d7f8cb24d613430799f9d56809cc6964f35cea9",91 "sum": "hOrwkOx34tOXqoDVnwuI/Uf/dr9HFFSPWpDPOvnEGrk="83 },92 },84 {93 {85 "source": {94 "source": {88 "subdir": "jsonnet/prometheus-operator"97 "subdir": "jsonnet/prometheus-operator"89 }98 }90 },99 },91 "version": "288f0ae3f4ef14e3c75078cbd7dfe1857c3fb066"100 "version": "288f0ae3f4ef14e3c75078cbd7dfe1857c3fb066",101 "sum": "fftlgLNJpgRQQjT32E3w/pGqnHK32NIbYX19IiWSTls="92 },102 },93 {103 {94 "source": {104 "source": {98 }108 }99 },109 },100 "version": "288f0ae3f4ef14e3c75078cbd7dfe1857c3fb066",110 "version": "288f0ae3f4ef14e3c75078cbd7dfe1857c3fb066",111 "sum": "gi+knjdxs2T715iIQIntrimbHRgHnpM8IFBJDD1gYfs=",101 "name": "prometheus-operator-mixin"112 "name": "prometheus-operator-mixin"102 },113 },103 {114 {106 "remote": "https://github.com/kubernetes-monitoring/kubernetes-mixin"117 "remote": "https://github.com/kubernetes-monitoring/kubernetes-mixin"107 }118 }108 },119 },109 "version": "acd544dbd02ebce1e000abe006df6d2e4ac1438e"120 "version": "acd544dbd02ebce1e000abe006df6d2e4ac1438e",121 "sum": "aRBESvZpnMG7iDNIR0CP+8yuvZyF4rQn8FLlXpBxGW8="110 },122 },111 {123 {112 "source": {124 "source": {115 "subdir": "gen/grafonnet-latest"127 "subdir": "gen/grafonnet-latest"116 }128 }117 },129 },118 "version": "7380c9c64fb973f34c3ec46265621a2b0dee0058"130 "version": "7380c9c64fb973f34c3ec46265621a2b0dee0058",131 "sum": "V9vAj21qJOc2DlMPDgB1eEjSQU4A+sAA4AXuJ6bd4xc="119 },132 },120 {133 {121 "source": {134 "source": {124 "subdir": "gen/grafonnet-v11.4.0"137 "subdir": "gen/grafonnet-v11.4.0"125 }138 }126 },139 },127 "version": "7380c9c64fb973f34c3ec46265621a2b0dee0058"140 "version": "7380c9c64fb973f34c3ec46265621a2b0dee0058",141 "sum": "aVAX09paQYNOoCSKVpuk1exVIyBoMt/C50QJI+Q/3nA="128 },142 },129 {143 {130 "source": {144 "source": {133 "subdir": "jsonnet/kube-state-metrics"147 "subdir": "jsonnet/kube-state-metrics"134 }148 }135 },149 },136 "version": "cd5430fd1834f43ffd093e3e22c2af1a625dbe2e"150 "version": "cd5430fd1834f43ffd093e3e22c2af1a625dbe2e",151 "sum": "7FPi/0YmErTgBmFQWavjoI/9WpWe5eL9G+OHnyc9d5A="137 },152 },138 {153 {139 "source": {154 "source": {142 "subdir": "jsonnet/kube-state-metrics-mixin"157 "subdir": "jsonnet/kube-state-metrics-mixin"143 }158 }144 },159 },145 "version": "cd5430fd1834f43ffd093e3e22c2af1a625dbe2e"160 "version": "cd5430fd1834f43ffd093e3e22c2af1a625dbe2e",161 "sum": "qclI7LwucTjBef3PkGBkKxF0mfZPbHnn4rlNWKGtR4c="146 },162 },147 {163 {148 "source": {164 "source": {151 "subdir": "docs/node-mixin"167 "subdir": "docs/node-mixin"152 }168 }153 },169 },154 "version": "d6d0e710bb7daf07a2743fde060f0d5f32c565f3"170 "version": "d6d0e710bb7daf07a2743fde060f0d5f32c565f3",171 "sum": "PGx1okd1Ny4U+Ma3AV0haZWvXnX8eJBN4+BsQInWUo4="155 },172 },156 {173 {157 "source": {174 "source": {161 }178 }162 },179 },163 "version": "91c184a899b8e8237cdd08876fba54aa5f9feb6c",180 "version": "91c184a899b8e8237cdd08876fba54aa5f9feb6c",181 "sum": "UuADJwhNploRwfJYGb0sr2mi+MvswKpumoOMXnGs0Dc=",164 "name": "prometheus"182 "name": "prometheus"165 },183 },166 {184 {171 }189 }172 },190 },173 "version": "c85e06d3cd4ba24d003454ee11834404224d7763",191 "version": "c85e06d3cd4ba24d003454ee11834404224d7763",192 "sum": "diZM7b/jQKqaIjuVSiseRaxgpgdQyVCtCgh0XhyPnXQ=",174 "name": "alertmanager"193 "name": "alertmanager"175 },194 },176 {195 {181 }200 }182 },201 },183 "version": "25bac00a6c478211326eed4aa46583d1b9466ca8",202 "version": "25bac00a6c478211326eed4aa46583d1b9466ca8",203 "sum": "vY3+ZELH6GFEpNJ0tKeU8ybCMLOQePKnnqFP4CSLSWo=",184 "name": "pyrra"204 "name": "pyrra"185 },205 },186 {206 {191 }211 }192 },212 },193 "version": "cdca5483752e1635d249fbb1271978262775dc1b",213 "version": "cdca5483752e1635d249fbb1271978262775dc1b",214 "sum": "ieCD4eMgGbOlrI8GmckGPHBGQDcLasE1rULYq56W/bs=",194 "name": "thanos-mixin"215 "name": "thanos-mixin"195 },216 },196 {217 {200 "subdir": "grafana-builder"221 "subdir": "grafana-builder"201 }222 }202 },223 },203 "version": "3b8056670048416f5fb660dc039e473eb20a40b2"224 "version": "3b8056670048416f5fb660dc039e473eb20a40b2",225 "sum": "TWpxm5vPO23V3hoF3pdwGs1NvR+PQce9baN716mJq+4="204 },226 },205 {227 {206 "source": {228 "source": {207 "git": {229 "git": {208 "remote": "https://github.com/oracle/graal"230 "remote": "https://github.com/oracle/graal"209 }231 }210 },232 },211 "version": "d24e4efdac60ee798d7a795cf015f084556c912e"233 "version": "d24e4efdac60ee798d7a795cf015f084556c912e",234 "sum": "6Ht2xkgFs2xIjRBF2Mb1AiRWDoEUCXsmICmsa9qw6iU="212 },235 },213 {236 {214 "source": {237 "source": {217 "subdir": "production/ksonnet/loki"240 "subdir": "production/ksonnet/loki"218 }241 }219 },242 },220 "version": "fe7f43b7f36d8f90931a3bfd35fadc83e1c23d5f"243 "version": "fe7f43b7f36d8f90931a3bfd35fadc83e1c23d5f",244 "sum": "2yQh4LZE2q1vqF6ReW1FXY3Hu43WL+vrJnm3Dw6w3sM="221 },245 },222 {246 {223 "source": {247 "source": {226 "subdir": "consul"250 "subdir": "consul"227 }251 }228 },252 },229 "version": "3b8056670048416f5fb660dc039e473eb20a40b2"253 "version": "3b8056670048416f5fb660dc039e473eb20a40b2",254 "sum": "Po3c1Ic96ngrJCtOazic/7OsLkoILOKZWXWyZWl+od8="230 },255 },231 {256 {232 "source": {257 "source": {235 "subdir": "jaeger-agent-mixin"260 "subdir": "jaeger-agent-mixin"236 }261 }237 },262 },238 "version": "3b8056670048416f5fb660dc039e473eb20a40b2"263 "version": "3b8056670048416f5fb660dc039e473eb20a40b2",264 "sum": "h/nMdmRK8Ep4bnRXKOuVL+RlUm85hhtD0LGIsf2THAY="239 },265 },240 {266 {241 "source": {267 "source": {244 "subdir": "ksonnet-util"270 "subdir": "ksonnet-util"245 }271 }246 },272 },247 "version": "3b8056670048416f5fb660dc039e473eb20a40b2"273 "version": "3b8056670048416f5fb660dc039e473eb20a40b2",274 "sum": "0y3AFX9LQSpfWTxWKSwoLgbt0Wc9nnCwhMH2szKzHv0="248 },275 },249 {276 {250 "source": {277 "source": {253 "subdir": "memcached"280 "subdir": "memcached"254 }281 }255 },282 },256 "version": "3b8056670048416f5fb660dc039e473eb20a40b2"283 "version": "3b8056670048416f5fb660dc039e473eb20a40b2",284 "sum": "zNTCDRaCqJUHjQSQRsxGR3jLrMP9tU7YNQb13dbm1bU="257 },285 },258 {286 {259 "source": {287 "source": {262 "subdir": "operations/mimir"290 "subdir": "operations/mimir"263 }291 }264 },292 },265 "version": "39dd4ffb859d0abb3951d406495bc4bd422600dd"293 "version": "39dd4ffb859d0abb3951d406495bc4bd422600dd",294 "sum": "9YmzQyhMEVRj6OcLgkk02Uk2FOw3vykuvRXA3CA6E5k="266 },295 },267 {296 {268 "source": {297 "source": {271 "subdir": "operations/rollout-operator"300 "subdir": "operations/rollout-operator"272 }301 }273 },302 },274 "version": "2afe062893496f0554b4764bbe1638f8019832fb"303 "version": "2afe062893496f0554b4764bbe1638f8019832fb",304 "sum": "t3nMMxw7cEOOmpdtKfadQRhvu2XQVvPigDq4CipmPIg="275 },305 },276 {306 {277 "source": {307 "source": {281 }311 }282 },312 },283 "version": "dbc8cf1a9847f123d8325378111155fb135983ab",313 "version": "dbc8cf1a9847f123d8325378111155fb135983ab",314 "sum": "EVCNg9VpU9ghIYHZtbQffHbZs+EqKOD0cw+ZPilWR48=",284 "name": "keda-libsonnet"315 "name": "keda-libsonnet"285 },316 },286 {317 {290 "subdir": "operations/jsonnet/microservices"321 "subdir": "operations/jsonnet/microservices"291 }322 }292 },323 },293 "version": "f6124c0dea954763dbaa461dcc7b8a89bc8d2635"324 "version": "f6124c0dea954763dbaa461dcc7b8a89bc8d2635",325 "sum": "uKkEEUq2qR69lmrDDDQAR2nVVYdQewz7MR0iJnyn3Kw="294 },326 },295 {327 {296 "source": {328 "source": {299 "subdir": "1.32"331 "subdir": "1.32"300 }332 }301 },333 },302 "version": "55380470fb7979e6ce0c4316cb9c27a266caf298"334 "version": "55380470fb7979e6ce0c4316cb9c27a266caf298",335 "sum": "L70C3SiMFOsNCuIGOxRKAzd6wUBuabAoEGrgvCyeCBc="303 },336 },304 {337 {305 "source": {338 "source": {308 "subdir": "1.0.0"341 "subdir": "1.0.0"309 }342 }310 },343 },311 "version": "5d16cd466d12489e6ffba5b17813a0203016e68d"344 "version": "5d16cd466d12489e6ffba5b17813a0203016e68d",345 "sum": "ksOv5A0fQkz4fRz4KMCHloLUiD7BPwGKvUn2sHIAB7s="312 },346 },313 {347 {314 "source": {348 "source": {315 "git": {349 "git": {316 "remote": "https://gitlab.com/gitlab-com/runbooks"350 "remote": "https://gitlab.com/gitlab-com/runbooks"317 }351 }318 },352 },319 "version": "c4082c94065b1b6a521eed59ed624478131cd9c0"353 "version": "c4082c94065b1b6a521eed59ed624478131cd9c0",354 "sum": "GPAWtUsS3aCFXOHWbk6qdwMH0jA2kFpHknagnRYd4sw="320 },355 },321 {356 {322 "source": {357 "source": {325 "subdir": "memcached-mixin"360 "subdir": "memcached-mixin"326 }361 }327 },362 },328 "version": "3b8056670048416f5fb660dc039e473eb20a40b2"363 "version": "3b8056670048416f5fb660dc039e473eb20a40b2",364 "sum": "qv5EBh5EVpWSpzXRtrgn5CBb1W60kLqVEGkItfVAgd4="329 },365 },330 {366 {331 "source": {367 "source": {334 "subdir": "jsonnetunit"370 "subdir": "jsonnetunit"335 }371 }336 },372 },337 "version": "6927c58cae7624a00f368b977ccc477d4f74071f"373 "version": "6927c58cae7624a00f368b977ccc477d4f74071f",374 "sum": "9FFqqln65hooRF0l6rjICDtnTxUlmDj34+sKMh4sjPI="338 }375 }339 ],376 ],340 "legacyImports": true377 "legacyImports": true