debian_analyzer/
publish.rs1use crate::salsa::guess_repository_url;
3use crate::vcs::determine_browser_url;
4use crate::{get_committer, parseaddr};
5use debian_control::control::Source;
6
7use breezyshim::error::Error as BrzError;
8use breezyshim::forge::create_project;
9use breezyshim::tree::WorkingTree;
10use breezyshim::workspace::check_clean_tree;
11use debian_control::vcs::ParsedVcs;
12use std::path::Path;
13use url::Url;
14
15pub fn update_control_for_vcs_url(
17 source: &mut Source,
18 vcs_type: breezyshim::foreign::VcsType,
19 vcs_url: &str,
20) {
21 source.as_mut_deb822().insert(
22 match vcs_type {
23 breezyshim::foreign::VcsType::Git => "Vcs-Git",
24 breezyshim::foreign::VcsType::Bazaar => "Vcs-Bzr",
25 breezyshim::foreign::VcsType::Svn => "Vcs-Svn",
26 breezyshim::foreign::VcsType::Hg => "Vcs-Hg",
27 breezyshim::foreign::VcsType::Cvs => "Vcs-Cvs",
28 breezyshim::foreign::VcsType::Darcs => "Vcs-Darcs",
29 breezyshim::foreign::VcsType::Fossil => "Vcs-Fossil",
30 breezyshim::foreign::VcsType::Arch => "Vcs-Arch",
31 breezyshim::foreign::VcsType::Svk => "Vcs-Svk",
32 },
33 vcs_url,
34 );
35 if let Some(url) = determine_browser_url("git", vcs_url, None) {
36 source.as_mut_deb822().insert("Vcs-Browser", url.as_ref());
37 } else {
38 source.as_mut_deb822().remove("Vcs-Browser");
39 }
40}
41
42pub fn create_vcs_url(repo_url: &Url, summary: Option<&str>) -> Result<(), BrzError> {
44 match create_project(repo_url.as_str(), summary) {
45 Ok(()) => {
46 log::info!("Created {}", repo_url);
47 Ok(())
48 }
49 Err(BrzError::ForgeProjectExists(..)) | Err(BrzError::AlreadyControlDir(..)) => {
50 log::debug!("{} already exists", repo_url);
51 Ok(())
52 }
53 Err(e) => Err(e),
54 }
55}
56
57#[derive(Debug, Clone)]
59pub enum Error {
60 NoVcsLocation,
62 FileNotFound(std::path::PathBuf),
64 ConflictingVcsAlreadySpecified(String, String, String),
66}
67
68impl std::fmt::Display for Error {
69 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
70 use Error::*;
71 match self {
72 NoVcsLocation => write!(f, "No Vcs-* location specified"),
73 FileNotFound(path) => write!(f, "File not found: {}", path.display()),
74 ConflictingVcsAlreadySpecified(_vcs_type, existing_url, new_url) => write!(
75 f,
76 "Conflicting Vcs-* location already specified: {} vs {}",
77 existing_url, new_url
78 ),
79 }
80 }
81}
82
83pub fn update_official_vcs(
85 wt: &WorkingTree,
86 subpath: &Path,
87 repo_url: Option<&Url>,
88 branch: Option<&str>,
89 committer: Option<&str>,
90 force: Option<bool>,
91) -> Result<ParsedVcs, Error> {
92 let force = force.unwrap_or(false);
93 check_clean_tree(wt, &wt.basis_tree().unwrap(), subpath).unwrap();
95
96 let debian_path = subpath.join("debian");
97 let subpath = match subpath.to_string_lossy().as_ref() {
98 "" | "." => None,
99 _ => Some(subpath.to_path_buf()),
100 };
101 let control_path = debian_path.join("control");
102
103 let editor = match crate::control::TemplatedControlEditor::open(&control_path) {
104 Ok(e) => e,
105 Err(crate::editor::EditorError::IoError(e)) if e.kind() == std::io::ErrorKind::NotFound => {
106 return Err(Error::FileNotFound(control_path));
107 }
108 Err(e) => panic!("Failed to open control file: {:?}", e),
109 };
110 let mut source = editor.source().unwrap();
111
112 if let Some(package_vcs) = crate::vcs::source_package_vcs(&source) {
113 let vcs_type = package_vcs.type_str();
114 let existing: ParsedVcs = package_vcs.clone().into();
115 let actual = ParsedVcs {
116 repo_url: repo_url.unwrap().to_string(),
117 branch: branch.map(|s| s.to_string()),
118 subpath: subpath.map(|p| p.to_string_lossy().to_string()),
119 };
120 if existing != actual && !force {
121 return Err(Error::ConflictingVcsAlreadySpecified(
122 vcs_type.to_owned(),
123 existing.to_string(),
124 actual.to_string(),
125 ));
126 }
127 log::debug!("Using existing URL {}", existing);
128 return Ok(existing);
129 }
130 let maintainer_email = parseaddr(source.maintainer().unwrap().as_str())
131 .unwrap()
132 .1
133 .unwrap();
134 let source_name = source.name().unwrap();
135 let mut repo_url = repo_url.map(|u| u.to_owned());
136 if repo_url.is_none() {
137 repo_url = guess_repository_url(source_name.as_str(), maintainer_email.as_str());
138 }
139 let repo_url = match repo_url {
140 Some(url) => url,
141 None => {
142 return Err(Error::NoVcsLocation);
143 }
144 };
145 log::info!("Using repository URL: {}", repo_url);
146 let branch = wt.branch();
147
148 let branch_name = match branch.vcs_type() {
149 breezyshim::foreign::VcsType::Git => Some("debian/main"),
150 _ => None,
151 };
152
153 let vcs_url = ParsedVcs {
154 repo_url: repo_url.to_string(),
155 branch: branch_name.map(|s| s.to_string()),
156 subpath: subpath.map(|p| p.to_string_lossy().to_string()),
157 };
158 update_control_for_vcs_url(&mut source, branch.vcs_type(), &vcs_url.to_string());
159 let parsed_vcs = vcs_url.clone();
160
161 let committer = committer.map_or_else(|| get_committer(wt), |s| s.to_string());
162
163 match wt
164 .build_commit()
165 .message("Set Vcs headers.")
166 .allow_pointless(false)
167 .committer(committer.as_str())
168 .commit()
169 {
170 Ok(_) | Err(BrzError::PointlessCommit) => {}
171 Err(e) => {
172 panic!("Failed to commit: {:?}", e);
173 }
174 }
175
176 Ok(parsed_vcs)
177}