debian_workbench/
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::tree::WorkingTree;
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 maintainer_email = parseaddr(source.maintainer().unwrap().as_str())
133 .unwrap()
134 .1
135 .unwrap();
136 let source_name = source.name().unwrap();
137 let mut repo_url = repo_url.map(|u| u.to_owned());
138 if repo_url.is_none() {
139 repo_url = guess_repository_url(source_name.as_str(), maintainer_email.as_str());
140 }
141 let repo_url = match repo_url {
142 Some(url) => url,
143 None => {
144 return Err(Error::NoVcsLocation);
145 }
146 };
147 log::info!("Using repository URL: {}", repo_url);
148 let branch = wt.branch();
149
150 let branch_name = match branch.vcs_type() {
151 breezyshim::foreign::VcsType::Git => Some("debian/main"),
152 _ => None,
153 };
154
155 let vcs_url = ParsedVcs {
156 repo_url: repo_url.to_string(),
157 branch: branch_name.map(|s| s.to_string()),
158 subpath: subpath.map(|p| p.to_string_lossy().to_string()),
159 };
160 update_control_for_vcs_url(&mut source, branch.vcs_type(), &vcs_url.to_string());
161 let parsed_vcs = vcs_url;
162
163 let committer = committer.map_or_else(|| get_committer(wt), |s| s.to_string());
164
165 match wt
166 .build_commit()
167 .message("Set Vcs headers.")
168 .allow_pointless(false)
169 .committer(committer.as_str())
170 .commit()
171 {
172 Ok(_) | Err(BrzError::PointlessCommit) => {}
173 Err(e) => {
174 panic!("Failed to commit: {:?}", e);
175 }
176 }
177
178 Ok(parsed_vcs)
179}