From 0cb4c993b335652821eac583f0ea9cbcb68105b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Saparelli?= Date: Fri, 2 Sep 2022 16:00:46 +1200 Subject: [PATCH] Explode out even more variants of default download URLs (#329) --- SUPPORT.md | 89 ++++++++----------- crates/lib/src/fetchers/gh_crate_meta.rs | 12 +-- .../lib/src/fetchers/gh_crate_meta/hosting.rs | 85 ++++++++++++------ .../src/helpers/download/async_extracter.rs | 2 +- 4 files changed, 98 insertions(+), 90 deletions(-) diff --git a/SUPPORT.md b/SUPPORT.md index 5b77791d2..dc84940b9 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -55,80 +55,61 @@ The default value for `pkg-url` will depend on the repository of the package. It is setup to work with github releases, gitlab releases, bitbucket downloads and source forge downloads. -#### Github +If your package already uses any of these URLs, you shouldn't need to set anything. -For github, the `pkg-url` is set to +The URLs are derived from a set of filenames and a set of paths, which are +"multiplied together": every filename appended to every path. The filenames +are: -```rust -[ - "{ repo }/releases/download/v{ version }/{ name }-{ target }-v{ version }.{ archive-format }", - "{ repo }/releases/download/v{ version }/{ name }-v{ version }-{ target }.{ archive-format }", - "{ repo }/releases/download/v{ version }/{ name }-{ version }-{ target }.{ archive-format }", - "{ repo }/releases/download/v{ version }/{ name }-{ target }.{ archive-format }", -] -``` +- `{ name }-{ target }-{ version }.{ archive-format }` +- `{ name }-{ target }-v{ version }.{ archive-format }` +- `{ name }-{ version }-{ target }.{ archive-format }` +- `{ name }-v{ version }-{ target }.{ archive-format }` +- `{ name }-{ version }-{ target }.{ archive-format }` +- `{ name }-v{ version }-{ target }.{ archive-format }` +- `{ name }-{ target }.{ archive-format }` ("versionless") -The first 3 versions does not overwrite different targets or versions when manually downloaded. +The paths are: -All `pkg-url` templates download binaries located at `{ repo }/releases/download/v{ version }/`, which -is compatible with github tags / releases. +#### for GitHub -If your package already uses this approach, you shouldn't need to set anything. +- `{ repo }/releases/download/{ version }/` +- `{ repo }/releases/download/v{ version }/` -#### GitLab +#### for GitLab -For gitlab, the `pkg-url` is set to +- `{ repo }/-/releases/{ version }/downloads/binaries/` +- `{ repo }/-/releases/v{ version }/downloads/binaries/` -```rust -[ - "{ repo }/-/releases/v{ version }/downloads/binaries/{ name }-{ target }-v{ version }.{ archive-format }", - "{ repo }/-/releases/v{ version }/downloads/binaries/{ name }-v{ version }-{ target }.{ archive-format }", - "{ repo }/-/releases/v{ version }/downloads/binaries/{ name }-{ version }-{ target }.{ archive-format }", - "{ repo }/-/releases/v{ version }/downloads/binaries/{ name }-{ target }.{ archive-format }", -] -``` +Note that this uses the [Permanent links to release assets][gitlab-permalinks] +feature of GitLab EE: it requires you to create an asset as a link with a +`filepath`, which, as of writing, can only be set using GitLab's API. -This will attempt to find the release assets with `filepath` set to -`binaries/{ name }-{ target }.{ archive-format }` +[gitlab-permalinks]: https://docs.gitlab.com/ee/user/project/releases/index.html#permanent-links-to-latest-release-assets -Note that this uses the [Permanent links to release assets](https://gitlab.kitware.com/help/user/project/releases/index#permanent-links-to-latest-release-assets) feature of gitlab, it requires you to -create an asset as a link with a `filepath`, which can be set only using gitlab api as of the writing. +#### for BitBucket -#### BitBucket +- `{ repo }/downloads/` -For bitbucket, the `pkg-url` is set to +Binaries must be uploaded into the project's "Downloads" page on BitBucket. -```rust -[ - "{ repo }/downloads/{ name }-{ target }-v{ version }.{ archive-format }", - "{ repo }/downloads/{ name }-v{ version }-{ target }.{ archive-format }", - "{ repo }/downloads/{ name }-{ version }-{ target }.{ archive-format }", -] -``` +Also note that as there are no per-release downloads, the "versionless" +filename is not considered here. -To setup the package for binstall, upload the binary into bitbucket downloads page of your project, -with its name set to be `{ name }-{ target }-v{ version }.{ archive-format }`. +#### for SourceForge -#### SourceForge +- `{ repo }/files/binaries/{ version }` +- `{ repo }/files/binaries/v{ version }` -For source forge, the `pkg-url` is set to - -```rust -[ - "{ repo }/files/binaries/v{ version }/{ name }-{ target }-v{ version }.{ archive-format }/download", - "{ repo }/files/binaries/v{ version }/{ name }-v{ version }-{ target }.{ archive-format }/download", - "{ repo }/files/binaries/v{ version }/{ name }-{ version }-{ target }.{ archive-format }/download", - "{ repo }/files/binaries/v{ version }/{ name }-{ target }.{ archive-format }/download", -] -``` +The URLs also have `/download` appended as per SourceForge's schema. -To setup the package for binstall, upload the binary to the file page of your project, -under the directory `binaries/v{ version }` with the filename `{ name }-{ target }.{ archive-format }`. +Binary must be uploaded to the "File" page of your project, under the directory +`binaries/v{ version }`. #### Others -For all other situations, `binstall` does not provide a default `pkg-url` and you need to manually -specify it. +For all other situations, `binstall` does not provide a default `pkg-url` and +you need to manually specify it. ### QuickInstall diff --git a/crates/lib/src/fetchers/gh_crate_meta.rs b/crates/lib/src/fetchers/gh_crate_meta.rs index 532d40601..d55d033e8 100644 --- a/crates/lib/src/fetchers/gh_crate_meta.rs +++ b/crates/lib/src/fetchers/gh_crate_meta.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, path::Path, sync::Arc}; +use std::{path::Path, sync::Arc}; use compact_str::{CompactString, ToCompactString}; use futures_util::stream::{FuturesUnordered, StreamExt}; @@ -23,7 +23,7 @@ use crate::{ use super::Data; mod hosting; -use hosting::GitHostingServices; +use hosting::RepositoryHost; pub struct GhCrateMeta { client: Client, @@ -86,13 +86,13 @@ impl super::Fetcher for GhCrateMeta { None }; - let pkg_urls = if let Some(pkg_url) = self.data.meta.pkg_url.as_deref() { - Cow::Owned(vec![pkg_url]) + let pkg_urls = if let Some(pkg_url) = self.data.meta.pkg_url.clone() { + vec![pkg_url] } else if let Some(repo) = repo.as_ref() { if let Some(pkg_urls) = - GitHostingServices::guess_git_hosting_services(repo)?.get_default_pkg_url_template() + RepositoryHost::guess_git_hosting_services(repo)?.get_default_pkg_url_template() { - Cow::Borrowed(pkg_urls) + pkg_urls } else { warn!( concat!( diff --git a/crates/lib/src/fetchers/gh_crate_meta/hosting.rs b/crates/lib/src/fetchers/gh_crate_meta/hosting.rs index 265fab797..521e062c8 100644 --- a/crates/lib/src/fetchers/gh_crate_meta/hosting.rs +++ b/crates/lib/src/fetchers/gh_crate_meta/hosting.rs @@ -3,16 +3,17 @@ use url::Url; use crate::errors::BinstallError; #[derive(Copy, Clone, Debug)] -pub enum GitHostingServices { +pub enum RepositoryHost { GitHub, GitLab, BitBucket, SourceForge, Unknown, } -impl GitHostingServices { + +impl RepositoryHost { pub fn guess_git_hosting_services(repo: &Url) -> Result { - use GitHostingServices::*; + use RepositoryHost::*; match repo.domain() { Some(domain) if domain.starts_with("github") => Ok(GitHub), @@ -23,34 +24,60 @@ impl GitHostingServices { } } - pub fn get_default_pkg_url_template(self) -> Option<&'static [&'static str]> { - use GitHostingServices::*; + pub fn get_default_pkg_url_template(self) -> Option> { + use RepositoryHost::*; + + let full_filenames = &[ + "{ name }-{ target }-v{ version }.{ archive-format }", + "{ name }-{ target }-{ version }.{ archive-format }", + "{ name }-{ version }-{ target }.{ archive-format }", + "{ name }-v{ version }-{ target }.{ archive-format }", + "{ name }-{ version }-{ target }.{ archive-format }", + "{ name }-v{ version }-{ target }.{ archive-format }", + ]; + + let noversion_filenames = &["{ name }-{ target }.{ archive-format }"]; match self { - GitHub => Some(&[ - "{ repo }/releases/download/v{ version }/{ name }-{ target }-v{ version }.{ archive-format }", - "{ repo }/releases/download/v{ version }/{ name }-v{ version }-{ target }.{ archive-format }", - "{ repo }/releases/download/v{ version }/{ name }-{ version }-{ target }.{ archive-format }", - "{ repo }/releases/download/v{ version }/{ name }-{ target }.{ archive-format }", - ]), - GitLab => Some(&[ - "{ repo }/-/releases/v{ version }/downloads/binaries/{ name }-{ target }-v{ version }.{ archive-format }", - "{ repo }/-/releases/v{ version }/downloads/binaries/{ name }-v{ version }-{ target }.{ archive-format }", - "{ repo }/-/releases/v{ version }/downloads/binaries/{ name }-{ version }-{ target }.{ archive-format }", - "{ repo }/-/releases/v{ version }/downloads/binaries/{ name }-{ target }.{ archive-format }", - ]), - BitBucket => Some(&[ - "{ repo }/downloads/{ name }-{ target }-v{ version }.{ archive-format }", - "{ repo }/downloads/{ name }-v{ version }-{ target }.{ archive-format }", - "{ repo }/downloads/{ name }-{ version }-{ target }.{ archive-format }", - ]), - SourceForge => Some(&[ - "{ repo }/files/binaries/v{ version }/{ name }-{ target }-v{ version }.{ archive-format }/download", - "{ repo }/files/binaries/v{ version }/{ name }-v{ version }-{ target }.{ archive-format }/download", - "{ repo }/files/binaries/v{ version }/{ name }-{ version }-{ target }.{ archive-format }/download", - "{ repo }/files/binaries/v{ version }/{ name }-{ target }.{ archive-format }/download", - ]), - Unknown => None, + GitHub => Some(apply_filenames_to_paths( + &[ + "{ repo }/releases/download/{ version }", + "{ repo }/releases/download/v{ version }", + ], + &[full_filenames, noversion_filenames], + )), + GitLab => Some(apply_filenames_to_paths( + &[ + "{ repo }/-/releases/{ version }/downloads/binaries", + "{ repo }/-/releases/v{ version }/downloads/binaries", + ], + &[full_filenames, noversion_filenames], + )), + BitBucket => Some(apply_filenames_to_paths( + &["{ repo }/downloads"], + &[full_filenames], + )), + SourceForge => Some( + apply_filenames_to_paths( + &[ + "{ repo }/files/binaries/{ version }", + "{ repo }/files/binaries/v{ version }", + ], + &[full_filenames, noversion_filenames], + ) + .into_iter() + .map(|url| format!("{url}/download")) + .collect(), + ), + Unknown => None, } } } + +fn apply_filenames_to_paths(paths: &[&str], filenames: &[&[&str]]) -> Vec { + filenames + .iter() + .flat_map(|fs| fs.iter()) + .flat_map(|filename| paths.iter().map(move |path| format!("{path}/{filename}"))) + .collect() +} diff --git a/crates/lib/src/helpers/download/async_extracter.rs b/crates/lib/src/helpers/download/async_extracter.rs index 682c2c5ff..840cbf210 100644 --- a/crates/lib/src/helpers/download/async_extracter.rs +++ b/crates/lib/src/helpers/download/async_extracter.rs @@ -25,7 +25,7 @@ where block_in_place(move || { fs::create_dir_all(path.parent().unwrap())?; - let mut file = fs::File::create(&path)?; + let mut file = fs::File::create(path)?; // remove it unless the operation isn't aborted and no write // fails.