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