Skip to content

Commit

Permalink
Support for self-hosted Gitea (#164)
Browse files Browse the repository at this point in the history
deps.rs is now available for self-hosted Gitea at
`/repo/gitea/<DOMAIN>/owner/repo`, e. g.
`/repo/gitea/git.example.org/deps-rs/deps.rs`,
`/repo/gitea/git.example.org:1234/deps-rs/deps.rs`,
`/repo/gitea/http://unsafe-gitea.org/deps-rs/deps.rs`.

This _should_ also include support for Gitea hosted in subdirectories,
e. g. `www.example.org/gitea`, though I haven't tested this yet.

If no protocol (`https://`/`http://`) is specified, `https://` is
automatically added to the beginning of the gitea server's URL.
However I could also change this to only accept https. Another
option might be the use of URL-encoding.
I am open for feedback, feel free to suggest changes.

Implementation notes:

- The Router now matches `/repo/*site/:qual/:name` instead of
  `/repo/:site/:qual/:name` to allow for an arbitrary number of
  `/`s before qual and name.
- `RepoSite` now has a new variant `Gitea(GiteaDomain)`.
- `RepoSite` no longer implements `Copy`. However this should not
  be problematic because `Copy`ing was only used for `to_base_uri`,
  `to_usercontent_base_uri` and `to_usercontent_repo_suffix` which
  now accept `&self` references.
- `RepoSite` no longer implements `AsRef` and now uses `Display`
  instead.

- updated test `correct_raw_url_generation`
- updated readme

Related to #84, #141
  • Loading branch information
nitn3lav authored Aug 21, 2022
1 parent a991fa8 commit ba7647d
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 28 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ We currently support projects and crates hosted on crates.io, Github, Gitlab, Bi
To analyze the state of your dependencies you can use the following URLs:

- for projects on crates.io: `https://deps.rs/crate/<NAME>`
- for projects on Github, Gitlab, Bitbucket, SourceHut, or Codeberg: `https://deps.rs/repo/<HOSTER>/<USER>/<REPO>` (where `<HOSTER>` is either `github`, `gitlab`, `bitbucket`, `sourcehut`, or `codeberg`)
- for projects on Github, Gitlab, Bitbucket, SourceHut, Codeberg, or Gitea: `https://deps.rs/repo/<HOSTER>/<USER>/<REPO>` (where `<HOSTER>` is either `github`, `gitlab`, `bitbucket`, `sourcehut`, `codeberg`, or `gitea/<DOMAIN>`)

## Badges

Expand Down
98 changes: 78 additions & 20 deletions src/models/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,49 +42,52 @@ impl fmt::Display for RepoPath {
write!(
f,
"{} => {}/{}",
self.site.as_ref(),
self.site.to_string(),
self.qual.as_ref(),
self.name.as_ref()
)
}
}

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum RepoSite {
Github,
Gitlab,
Bitbucket,
Sourcehut,
Codeberg,
Gitea(GiteaDomain),
}

impl RepoSite {
pub fn to_base_uri(self) -> &'static str {
pub fn to_base_uri(&self) -> &str {
match self {
RepoSite::Github => "https://github.com",
RepoSite::Gitlab => "https://gitlab.com",
RepoSite::Bitbucket => "https://bitbucket.org",
RepoSite::Sourcehut => "https://git.sr.ht",
RepoSite::Codeberg => "https://codeberg.org",
RepoSite::Gitea(domain) => domain.as_ref(),
}
}

pub fn to_usercontent_base_uri(self) -> &'static str {
pub fn to_usercontent_base_uri(&self) -> &str {
match self {
RepoSite::Github => "https://raw.githubusercontent.com",
RepoSite::Gitlab => "https://gitlab.com",
RepoSite::Bitbucket => "https://bitbucket.org",
RepoSite::Sourcehut => "https://git.sr.ht",
RepoSite::Codeberg => "https://codeberg.org",
RepoSite::Gitea(domain) => domain.as_ref(),
}
}

pub fn to_usercontent_repo_suffix(self) -> &'static str {
pub fn to_usercontent_repo_suffix(&self) -> &'static str {
match self {
RepoSite::Github => "HEAD",
RepoSite::Gitlab | RepoSite::Bitbucket => "raw/HEAD",
RepoSite::Sourcehut => "blob/HEAD",
RepoSite::Codeberg => "raw",
RepoSite::Codeberg | RepoSite::Gitea(_) => "raw",
}
}
}
Expand All @@ -93,25 +96,64 @@ impl FromStr for RepoSite {
type Err = Error;

fn from_str(input: &str) -> Result<RepoSite, Error> {
match input {
"github" => Ok(RepoSite::Github),
"gitlab" => Ok(RepoSite::Gitlab),
"bitbucket" => Ok(RepoSite::Bitbucket),
"sourcehut" => Ok(RepoSite::Sourcehut),
"codeberg" => Ok(RepoSite::Codeberg),
_ => Err(anyhow!("unknown repo site identifier")),
if let Some((site, domain)) = input.split_once("/") {
match site {
"gitea" => Ok(RepoSite::Gitea(domain.parse()?)),
_ => Err(anyhow!("unknown repo site identifier")),
}
} else {
match input {
"github" => Ok(RepoSite::Github),
"gitlab" => Ok(RepoSite::Gitlab),
"bitbucket" => Ok(RepoSite::Bitbucket),
"sourcehut" => Ok(RepoSite::Sourcehut),
"codeberg" => Ok(RepoSite::Codeberg),
_ => Err(anyhow!("unknown repo site identifier")),
}
}
}
}

impl AsRef<str> for RepoSite {
fn as_ref(&self) -> &str {
impl fmt::Display for RepoSite {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RepoSite::Github => "github",
RepoSite::Gitlab => "gitlab",
RepoSite::Bitbucket => "bitbucket",
RepoSite::Sourcehut => "sourcehut",
RepoSite::Codeberg => "codeberg",
RepoSite::Github => write!(f, "github"),
RepoSite::Gitlab => write!(f, "gitlab"),
RepoSite::Bitbucket => write!(f, "bitbucket"),
RepoSite::Sourcehut => write!(f, "sourcehut"),
RepoSite::Codeberg => write!(f, "codeberg"),
RepoSite::Gitea(s) => write!(f, "gitea/{s}"),
}
}
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct GiteaDomain(String);

impl FromStr for GiteaDomain {
type Err = Error;

fn from_str(input: &str) -> Result<GiteaDomain, Error> {
if input.starts_with("https://") || input.starts_with("http://") {
Ok(GiteaDomain(input.to_string()))
} else {
Ok(GiteaDomain(format!("https://{input}")))
}
}
}

impl AsRef<str> for GiteaDomain {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}

impl fmt::Display for GiteaDomain {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.starts_with("https://") {
f.write_str(&self.0["https://".len()..])
} else {
self.0.fmt(f)
}
}
}
Expand Down Expand Up @@ -215,5 +257,21 @@ mod tests {
let exp = format!("https://codeberg.org/deps-rs/deps.rs/raw/{}", expected);
assert_eq!(out.to_string(), exp);
}

for (input, expected) in &paths {
let repo = RepoPath::from_parts("gitea/gitea.com", "deps-rs", "deps.rs").unwrap();
let out = repo.to_usercontent_file_url(RelativePath::new(input));

let exp = format!("https://gitea.com/deps-rs/deps.rs/raw/{}", expected);
assert_eq!(out.to_string(), exp);
}

for (input, expected) in &paths {
let repo = RepoPath::from_parts("gitea/example.com/git", "deps-rs", "deps.rs").unwrap();
let out = repo.to_usercontent_file_url(RelativePath::new(input));

let exp = format!("https://example.com/git/deps-rs/deps.rs/raw/{}", expected);
assert_eq!(out.to_string(), exp);
}
}
}
4 changes: 2 additions & 2 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ impl App {
router.add("/static/logo.svg", Route::Static(StaticFile::FaviconPng));

router.add(
"/repo/:site/:qual/:name",
"/repo/*site/:qual/:name",
Route::RepoStatus(StatusFormat::Html),
);
router.add(
"/repo/:site/:qual/:name/status.svg",
"/repo/*site/:qual/:name/status.svg",
Route::RepoStatus(StatusFormat::Svg),
);

Expand Down
4 changes: 2 additions & 2 deletions src/server/views/html/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ fn popular_table(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>)
@for repo in popular_repos.into_iter().take(10) {
tr {
td {
a href=(format!("{}/repo/{}/{}/{}", &super::SELF_BASE_URL as &str, repo.path.site.as_ref(), repo.path.qual.as_ref(), repo.path.name.as_ref())) {
a href=(format!("{}/repo/{}/{}/{}", &super::SELF_BASE_URL as &str, repo.path.site, repo.path.qual.as_ref(), repo.path.name.as_ref())) {
(format!("{} / {}", repo.path.qual.as_ref(), repo.path.name.as_ref()))
}
}
td class="has-text-right" {
img src=(format!("{}/repo/{}/{}/{}/status.svg", &super::SELF_BASE_URL as &str, repo.path.site.as_ref(), repo.path.qual.as_ref(), repo.path.name.as_ref()));
img src=(format!("{}/repo/{}/{}/{}/status.svg", &super::SELF_BASE_URL as &str, repo.path.site, repo.path.qual.as_ref(), repo.path.name.as_ref()));
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/server/views/html/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,11 @@ fn get_site_icon(site: &RepoSite) -> (FaType, &'static str) {
RepoSite::Github => (FaType::Brands, "github"),
RepoSite::Gitlab => (FaType::Brands, "gitlab"),
RepoSite::Bitbucket => (FaType::Brands, "bitbucket"),
// FIXME: There is no brands/{sourcehut, codeberg} icon, so just use a
// FIXME: There is no brands/{sourcehut, codeberg, gitea} icon, so just use a
// regular circle which looks close enough.
RepoSite::Sourcehut | RepoSite::Codeberg => (FaType::Regular, "circle"),
RepoSite::Sourcehut | RepoSite::Codeberg | RepoSite::Gitea(_) => {
(FaType::Regular, "circle")
}
}
}

Expand Down Expand Up @@ -334,7 +336,7 @@ fn render_success(
let self_path = match subject_path {
SubjectPath::Repo(ref repo_path) => format!(
"repo/{}/{}/{}",
repo_path.site.as_ref(),
repo_path.site,
repo_path.qual.as_ref(),
repo_path.name.as_ref()
),
Expand Down

0 comments on commit ba7647d

Please sign in to comment.