diff --git a/.github/fixtures/test-custom-remote-api-url/cliff.toml b/.github/fixtures/test-custom-remote-api-url/cliff.toml new file mode 100644 index 0000000000..4136d1fcb8 --- /dev/null +++ b/.github/fixtures/test-custom-remote-api-url/cliff.toml @@ -0,0 +1,55 @@ +[remote.gitlab] +owner = "archlinux" +repo = "arch-repro-status" +api_url = "https://gitlab.archlinux.org/api/v4" + +[changelog] +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +## What's Changed + +{%- if version %} in {{ version }}{%- endif -%} +{% for commit in commits %} + * {{ commit.message | split(pat="\n") | first | trim }}\ + {% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif -%} + {% if commit.remote.pr_number %} in #{{ commit.remote.pr_number }}{%- endif %} +{%- endfor -%} + +{% if gitlab.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} + {% raw %}\n{% endraw -%} + ### New Contributors +{%- endif %}\ +{% for contributor in gitlab.contributors | filter(attribute="is_first_time", value=true) %} + * @{{ contributor.username }} made their first contribution + {%- if contributor.pr_number %} in \ + [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ + {%- endif %} +{%- endfor -%} + +{% if version %} + {% if previous.version %} + **Full Changelog**: https://gitlab.com/{{ remote.gitlab.owner }}/{{ remote.gitlab.repo }}/compare/{{ previous.version }}...{{ version }} + {% endif %} +{% else -%} + {% raw %}\n{% endraw %} +{% endif %} +""" +# remove the leading and trailing whitespace from the template +trim = true +# changelog footer +footer = """ + +""" + +[git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# filter out the commits that are not conventional +filter_unconventional = false +# process each line of a commit as an individual commit +split_commits = false +# regex for preprocessing the commit messages +commit_preprocessors = [{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" }] +# protect breaking changes from being skipped due to matching a skipping commit_parser +protect_breaking_commits = false diff --git a/.github/fixtures/test-custom-remote-api-url/commit.sh b/.github/fixtures/test-custom-remote-api-url/commit.sh new file mode 100755 index 0000000000..fc3b7b7016 --- /dev/null +++ b/.github/fixtures/test-custom-remote-api-url/commit.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -e + +git remote add origin https://gitlab.archlinux.org/archlinux/arch-repro-status +git fetch +git checkout 5fe2f324db566756ccaf066fe186100a09a87625 diff --git a/.github/fixtures/test-custom-remote-api-url/expected.md b/.github/fixtures/test-custom-remote-api-url/expected.md new file mode 100644 index 0000000000..d20126a97f --- /dev/null +++ b/.github/fixtures/test-custom-remote-api-url/expected.md @@ -0,0 +1,9 @@ +## What's Changed in v1.4.1 +* Update the copyright year in license by @Orhun Parmaksız +* bump dependencies by @Orhun Parmaksız +* update cargo-deny config by @Orhun Parmaksız +* prepare for 1.4.1 by @Orhun Parmaksız + +**Full Changelog**: https://gitlab.com/archlinux/arch-repro-status/compare/v1.4.0...v1.4.1 + + diff --git a/.github/workflows/test-fixtures.yml b/.github/workflows/test-fixtures.yml index 09e7d138d8..00b09a68f9 100644 --- a/.github/workflows/test-fixtures.yml +++ b/.github/workflows/test-fixtures.yml @@ -102,6 +102,8 @@ jobs: command: --unreleased --tag v0.2.0 - fixtures-name: test-unchanged-tag-date command: --tag v0.2.0 + - fixtures-name: test-custom-remote-api-url + command: --latest steps: - name: Checkout diff --git a/git-cliff-core/src/changelog.rs b/git-cliff-core/src/changelog.rs index a505a127a5..4688d587d1 100644 --- a/git-cliff-core/src/changelog.rs +++ b/git-cliff-core/src/changelog.rs @@ -842,24 +842,28 @@ mod test { repo: String::from("awesome"), token: None, is_custom: false, + api_url: None, }, gitlab: Remote { owner: String::from("coolguy"), repo: String::from("awesome"), token: None, is_custom: false, + api_url: None, }, gitea: Remote { owner: String::from("coolguy"), repo: String::from("awesome"), token: None, is_custom: false, + api_url: None, }, bitbucket: Remote { owner: String::from("coolguy"), repo: String::from("awesome"), token: None, is_custom: false, + api_url: None, }, }, bump: Bump::default(), diff --git a/git-cliff-core/src/command.rs b/git-cliff-core/src/command.rs index 71a3c652a1..8f4038bc4d 100644 --- a/git-cliff-core/src/command.rs +++ b/git-cliff-core/src/command.rs @@ -72,6 +72,7 @@ pub fn run( #[cfg(test)] mod test { use super::*; + #[test] #[cfg(target_family = "unix")] fn run_os_command() -> Result<()> { diff --git a/git-cliff-core/src/config.rs b/git-cliff-core/src/config.rs index 2266bb3444..54fc56a94c 100644 --- a/git-cliff-core/src/config.rs +++ b/git-cliff-core/src/config.rs @@ -182,6 +182,8 @@ pub struct Remote { /// Whether if the remote is set manually. #[serde(skip_deserializing, default = "default_true")] pub is_custom: bool, + /// Remote API URL. + pub api_url: Option, } /// Returns `true` for serde's `default` attribute. @@ -209,6 +211,7 @@ impl Remote { repo: repo.into(), token: None, is_custom: false, + api_url: None, } } diff --git a/git-cliff-core/src/remote/bitbucket.rs b/git-cliff-core/src/remote/bitbucket.rs index 5fe0a763ae..d12e9d70dd 100644 --- a/git-cliff-core/src/remote/bitbucket.rs +++ b/git-cliff-core/src/remote/bitbucket.rs @@ -5,16 +5,9 @@ use serde::{ Deserialize, Serialize, }; -use std::env; use super::*; -/// Bitbucket REST API url. -const BITBUCKET_API_URL: &str = "https://api.bitbucket.org/2.0/repositories"; - -/// Environment variable for overriding the Bitbucket REST API url. -const BITBUCKET_API_URL_ENV: &str = "BITBUCKET_API_URL"; - /// Log message to show while fetching data from Bitbucket. pub const START_FETCHING_MSG: &str = "Retrieving data from Bitbucket..."; @@ -187,11 +180,8 @@ impl TryFrom for BitbucketClient { } impl RemoteClient for BitbucketClient { - fn api_url() -> String { - env::var(BITBUCKET_API_URL_ENV) - .ok() - .unwrap_or_else(|| BITBUCKET_API_URL.to_string()) - } + const API_URL: &str = "https://api.bitbucket.org/2.0/repositories"; + const API_URL_ENV: &str = "BITBUCKET_API_URL"; fn remote(&self) -> Remote { self.remote.clone() diff --git a/git-cliff-core/src/remote/gitea.rs b/git-cliff-core/src/remote/gitea.rs index cc9cf349cc..c7a6b09a89 100644 --- a/git-cliff-core/src/remote/gitea.rs +++ b/git-cliff-core/src/remote/gitea.rs @@ -5,16 +5,9 @@ use serde::{ Deserialize, Serialize, }; -use std::env; use super::*; -/// Gitea API url. -const GITEA_API_URL: &str = "https://codeberg.org"; - -/// Environment variable for overriding the Gitea REST API url. -const GITEA_API_URL_ENV: &str = "GITEA_API_URL"; - /// Log message to show while fetching data from Gitea. pub const START_FETCHING_MSG: &str = "Retrieving data from Gitea..."; @@ -151,11 +144,8 @@ impl TryFrom for GiteaClient { } impl RemoteClient for GiteaClient { - fn api_url() -> String { - env::var(GITEA_API_URL_ENV) - .ok() - .unwrap_or_else(|| GITEA_API_URL.to_string()) - } + const API_URL: &str = "https://codeberg.org"; + const API_URL_ENV: &str = "GITEA_API_URL"; fn remote(&self) -> Remote { self.remote.clone() diff --git a/git-cliff-core/src/remote/github.rs b/git-cliff-core/src/remote/github.rs index 80fc2125d7..2548844b8b 100644 --- a/git-cliff-core/src/remote/github.rs +++ b/git-cliff-core/src/remote/github.rs @@ -5,16 +5,9 @@ use serde::{ Deserialize, Serialize, }; -use std::env; use super::*; -/// GitHub REST API url. -const GITHUB_API_URL: &str = "https://api.github.com"; - -/// Environment variable for overriding the GitHub REST API url. -const GITHUB_API_URL_ENV: &str = "GITHUB_API_URL"; - /// Log message to show while fetching data from GitHub. pub const START_FETCHING_MSG: &str = "Retrieving data from GitHub..."; @@ -166,11 +159,8 @@ impl TryFrom for GitHubClient { } impl RemoteClient for GitHubClient { - fn api_url() -> String { - env::var(GITHUB_API_URL_ENV) - .ok() - .unwrap_or_else(|| GITHUB_API_URL.to_string()) - } + const API_URL: &str = "https://api.github.com"; + const API_URL_ENV: &str = "GITHUB_API_URL"; fn remote(&self) -> Remote { self.remote.clone() diff --git a/git-cliff-core/src/remote/gitlab.rs b/git-cliff-core/src/remote/gitlab.rs index 84c2553966..78c9bde8c6 100644 --- a/git-cliff-core/src/remote/gitlab.rs +++ b/git-cliff-core/src/remote/gitlab.rs @@ -5,16 +5,9 @@ use serde::{ Deserialize, Serialize, }; -use std::env; use super::*; -/// GitLab REST API url. -const GITLAB_API_URL: &str = "https://gitlab.com/api/v4"; - -/// Environment variable for overriding the GitLab REST API url. -const GITLAB_API_URL_ENV: &str = "GITLAB_API_URL"; - /// Log message to show while fetching data from GitLab. pub const START_FETCHING_MSG: &str = "Retrieving data from GitLab..."; @@ -248,11 +241,8 @@ impl TryFrom for GitLabClient { } impl RemoteClient for GitLabClient { - fn api_url() -> String { - env::var(GITLAB_API_URL_ENV) - .ok() - .unwrap_or_else(|| GITLAB_API_URL.to_string()) - } + const API_URL: &str = "https://gitlab.com/api/v4"; + const API_URL_ENV: &str = "GITLAB_API_URL"; fn remote(&self) -> Remote { self.remote.clone() diff --git a/git-cliff-core/src/remote/mod.rs b/git-cliff-core/src/remote/mod.rs index 5ae8d114c5..5527d645cf 100644 --- a/git-cliff-core/src/remote/mod.rs +++ b/git-cliff-core/src/remote/mod.rs @@ -48,6 +48,7 @@ use serde::{ Deserialize, Serialize, }; +use std::env; use std::fmt::Debug; use std::time::Duration; use time::{ @@ -168,8 +169,20 @@ fn create_remote_client( /// Trait for handling the API connection and fetching. pub trait RemoteClient { + /// API URL for a particular client + const API_URL: &'static str; + + /// Name of the environment variable used to set the API URL to a + /// self-hosted instance (if applicable). + const API_URL_ENV: &'static str; + /// Returns the API url. - fn api_url() -> String; + fn api_url(&self) -> String { + env::var(Self::API_URL_ENV) + .ok() + .or(self.remote().api_url) + .unwrap_or_else(|| Self::API_URL.to_string()) + } /// Returns the remote repository information. fn remote(&self) -> Remote; @@ -188,7 +201,7 @@ pub trait RemoteClient { project_id: i64, page: i32, ) -> Result { - let url = T::url(project_id, &Self::api_url(), &self.remote(), page); + let url = T::url(project_id, &self.api_url(), &self.remote(), page); debug!("Sending request to: {url}"); let response = self.client().get(&url).send().await?; let response_text = if response.status().is_success() { @@ -209,7 +222,7 @@ pub trait RemoteClient { project_id: i64, page: i32, ) -> Result> { - let url = T::url(project_id, &Self::api_url(), &self.remote(), page); + let url = T::url(project_id, &self.api_url(), &self.remote(), page); debug!("Sending request to: {url}"); let response = self.client().get(&url).send().await?; let response_text = if response.status().is_success() { diff --git a/git-cliff-core/src/repo.rs b/git-cliff-core/src/repo.rs index bcd55e048c..e76ded3650 100644 --- a/git-cliff-core/src/repo.rs +++ b/git-cliff-core/src/repo.rs @@ -475,6 +475,7 @@ fn url_path_segments(url: &str) -> Result { repo: repo.to_string(), token: None, is_custom: false, + api_url: None, }) } @@ -504,6 +505,7 @@ fn ssh_path_segments(url: &str) -> Result { repo: repo.to_string(), token: None, is_custom: false, + api_url: None, }) } @@ -664,6 +666,7 @@ mod test { repo: String::from("git-cliff"), token: None, is_custom: false, + api_url: remote.api_url.clone(), }, remote ); diff --git a/website/docs/configuration/remote.md b/website/docs/configuration/remote.md index 7d5706917c..e0a0e1ff03 100644 --- a/website/docs/configuration/remote.md +++ b/website/docs/configuration/remote.md @@ -51,3 +51,19 @@ git cliff --github-token ``` Same applies for GitLab/Bitbucket with `--gitlab-token`/`--gitea-token`/`--bitbucket-token` and `GITLAB_TOKEN`/`GITEA_TOKEN`/`BITBUCKET_TOKEN` environment variables. + +### api_url + +Sets the API URL for a particular remote. + +--- + +Here is a complete example for a project hosted on GitLab: + +```toml +[remote.gitlab] +owner = "archlinux" +repo = "arch-repro-status" +api_url = "https://gitlab.archlinux.org/api/v4" +token = "deadbeef" +```