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