Install an integration package from a local directory (a synced cache clone or a `--from` checkout).
(pkg_dir: &Path, opts: &InstallOptions)
| 94 | /// Install an integration package from a local directory (a synced cache |
| 95 | /// clone or a `--from` checkout). |
| 96 | pub fn install_from_dir(pkg_dir: &Path, opts: &InstallOptions) -> AgentResult<InstallOutcome> { |
| 97 | let manifest = IntegrationManifest::load(pkg_dir)?; |
| 98 | |
| 99 | if let Some(expect) = &opts.expect_agent { |
| 100 | if &manifest.agent != expect { |
| 101 | return Err(AgentError::Integration { |
| 102 | agent: expect.clone(), |
| 103 | reason: format!( |
| 104 | "package at {} is for agent '{}'", |
| 105 | pkg_dir.display(), |
| 106 | manifest.agent |
| 107 | ), |
| 108 | }); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | manifest.check_cli_version(&opts.cli_version)?; |
| 113 | |
| 114 | let previous = Receipt::load(&manifest.agent)?; |
| 115 | |
| 116 | let mut installed = Vec::new(); |
| 117 | let mut refreshed = Vec::new(); |
| 118 | let mut skipped = Vec::new(); |
| 119 | let mut receipt_files = Vec::new(); |
| 120 | |
| 121 | for entry in &manifest.files { |
| 122 | reject_parent_traversal(&manifest.agent, &entry.src)?; |
| 123 | let src = pkg_dir.join(&entry.src); |
| 124 | if !src.is_file() { |
| 125 | return Err(AgentError::Integration { |
| 126 | agent: manifest.agent.clone(), |
| 127 | reason: format!("manifest file source missing: {}", src.display()), |
| 128 | }); |
| 129 | } |
| 130 | let dst = expand_dst(&entry.dst)?; |
| 131 | |
| 132 | match install_decision(&dst, previous.as_ref(), opts.force)? { |
| 133 | Decision::Install => installed.push(dst.clone()), |
| 134 | Decision::Refresh => refreshed.push(dst.clone()), |
| 135 | Decision::Skip(reason) => { |
| 136 | skipped.push(SkippedFile { |
| 137 | dst: dst.clone(), |
| 138 | reason, |
| 139 | }); |
| 140 | // Keep protecting this file: preserve any prior receipt entry |
| 141 | // so a later --force still knows whether it was ours. |
| 142 | if let Some(old) = previous |
| 143 | .as_ref() |
| 144 | .and_then(|r| r.files.iter().find(|f| f.dst == dst)) |
| 145 | { |
| 146 | receipt_files.push(old.clone()); |
| 147 | } |
| 148 | continue; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | if let Some(parent) = dst.parent() { |
| 153 | std::fs::create_dir_all(parent)?; |
no test coverage detected