raw
1mod branch;
2mod commit;
3mod core;
4mod tag;
5mod tree;
6
7use std::path::{Path, PathBuf};
8
9use git2::{Object, Signature};
10
11use crate::{config::Config, error::Context as _, error::Result, http::extractor::RepoName};
12
13pub(crate) struct TagEntry {
14 pub link: String,
15 pub tag: String,
16 pub message: String,
17 pub signature: Signature<'static>,
18}
19
20impl TagEntry {
21 #[tracing::instrument(skip_all)]
22 fn try_from_commit(name: String, obj: &Object<'_>) -> Result<Self> {
23 Ok(Self {
24 link: format!("refs/{name}"),
25 tag: name,
26 message: String::new(),
27 signature: obj
28 .as_commit()
29 .context("git object is not a commit")?
30 .committer()
31 .to_owned(),
32 })
33 }
34
35 #[tracing::instrument(skip_all)]
36 fn try_from_tag(name: String, obj: &Object<'_>) -> Result<Self> {
37 let tag = obj.as_tag().context("git object is not a tag")?;
38
39 Ok(Self {
40 link: format!("refs/{name}"),
41 tag: name,
42 message: tag.message().unwrap_or("").to_string(),
43 signature: tag
44 .tagger()
45 .context("git tag does not have a tagger")
46 .or_else(|_| -> Result<Signature<'_>> {
47 let signature = obj
48 .peel_to_commit()
49 .context("failed to peel object to commit")?
50 .committer()
51 .to_owned();
52
53 Ok(signature)
54 })?
55 .to_owned(),
56 })
57 }
58}
59
60pub(crate) struct Repository {
61 inner: git2::Repository,
62}
63
64impl Repository {
65 #[tracing::instrument(skip_all)]
66 pub(crate) fn open(config: &Config, name: &RepoName) -> Result<Option<Self>> {
67 Self::open_path(config, &PathBuf::from(&name.0))
68 }
69
70 pub(crate) fn open_path(config: &Config, path: &Path) -> Result<Option<Self>> {
71 let path = config.project_root.join(path);
72
73 if !path.exists() {
74 return Ok(None);
75 }
76
77 let path = path.canonicalize().context("failed to canonicalize path")?;
78
79 if !path.starts_with(&config.project_root) {
80 tracing::warn!(
81 root=?config.project_root.display(),
82 requested=?path.display(),
83 "attempted path traversal",
84 );
85
86 return Ok(None);
87 }
88
89 if path == config.project_root {
90 return Ok(None);
91 }
92
93 if !path.exists() {
94 return Ok(None);
95 }
96
97 let inner = git2::Repository::open(path).context("failed to actually open the repo")?;
98
99 if !inner.path().join(&config.export_ok).exists() {
100 tracing::warn!("tried to access private repo");
101
102 return Ok(None);
103 }
104
105 Ok(Some(Self { inner }))
106 }
107
108 #[must_use]
109 pub(crate) const fn as_inner(&self) -> &git2::Repository {
110 &self.inner
111 }
112}
113