use crate::build_targets;
use crate::common::load_metadata_repo;
use crate::datetime::parse_datetime;
use crate::error::{self, Result};
use crate::source::parse_key_source;
use chrono::{DateTime, Utc};
use clap::Parser;
use snafu::ResultExt;
use std::num::NonZeroU64;
use std::num::NonZeroUsize;
use std::path::PathBuf;
use tough::editor::signed::PathExists;
use tough::editor::targets::TargetsEditor;
use tough::key_source::KeySource;
use url::Url;
#[derive(Debug, Parser)]
pub(crate) struct UpdateTargetsArgs {
#[clap(short = 'k', long = "key", required = true, parse(try_from_str = parse_key_source))]
keys: Vec<Box<dyn KeySource>>,
#[clap(short = 'e', long = "expires", parse(try_from_str = parse_datetime))]
expires: DateTime<Utc>,
#[clap(short = 'v', long = "version")]
version: NonZeroU64,
#[clap(short = 'r', long = "root")]
root: PathBuf,
#[clap(short = 'm', long = "metadata-url")]
metadata_base_url: Url,
#[clap(short = 't', long = "add-targets")]
targets_indir: Option<PathBuf>,
#[clap(short = 'o', long = "outdir")]
outdir: PathBuf,
#[clap(short = 'f', long = "follow")]
follow: bool,
#[clap(short = 'j', long = "jobs")]
jobs: Option<NonZeroUsize>,
#[clap(long = "target-path-exists", default_value = "skip")]
target_path_exists: PathExists,
}
impl UpdateTargetsArgs {
pub(crate) fn run(&self, role: &str) -> Result<()> {
let repository = load_metadata_repo(&self.root, self.metadata_base_url.clone())?;
self.update_targets(
TargetsEditor::from_repo(repository, role)
.context(error::EditorFromRepoSnafu { path: &self.root })?,
)
}
fn update_targets(&self, mut editor: TargetsEditor) -> Result<()> {
editor.version(self.version).expires(self.expires);
if let Some(ref targets_indir) = self.targets_indir {
if let Some(jobs) = self.jobs {
rayon::ThreadPoolBuilder::new()
.num_threads(usize::from(jobs))
.build_global()
.context(error::InitializeThreadPoolSnafu)?;
}
let new_targets = build_targets(&targets_indir, self.follow)?;
for (target_name, target) in new_targets {
editor
.add_target(target_name, target)
.context(error::InvalidTargetNameSnafu)?;
}
};
let signed_role = editor.sign(&self.keys).context(error::SignRepoSnafu)?;
if let Some(ref targets_indir) = self.targets_indir {
let targets_outdir = &self.outdir.join("targets");
signed_role
.copy_targets(&targets_indir, &targets_outdir, self.target_path_exists)
.context(error::LinkTargetsSnafu {
indir: &targets_indir,
outdir: targets_outdir,
})?;
};
let metadata_dir = &self.outdir.join("metadata");
signed_role
.write(metadata_dir, false)
.context(error::WriteRepoSnafu {
directory: metadata_dir,
})?;
Ok(())
}
}