Skip to content

Commit b2712e7

Browse files
Emmadbanty
Emma
andauthored
Add gitea support (#759)
Took me a little longer, since i noticed that my initial idea would literally have required a full refactor, which was a little too large in scope. the above checklist is for keeping track of what will be added and this PR is here to track the progress and eventual questions. closes #743 ## Plans - [x] Extending the configuration format and adding a `[gitea]` section - [x] Allowing to generate the configuration from known gitea remotes - [x] using gitea on the `CreatePullRequest` step - [x] using gitea on the `Release` step - [x] creating a new step `SelectGiteaIssue` - [x] adding a new `forge` concept to the documentation to link forge integrations together in a convenient place - [x] adding a new `gitea` section to the config file reference - [x] multi-forge support as mentioned in #743 (comment) ## Testing - PR generated by knope on a copy of one of the projects i work on. (fixes will be pushed in a bit) https://codeberg.org/FallenValkyrie/testing/pulls/5 - generated release https://codeberg.org/FallenValkyrie/testing/releases/tag/v0.3.0 - Adding tests in `tests::gitea_release` and `tests::generate` - selecting gitea issues (token has already been deleted, so no need to censor it) ![An image showing knope successfully selecting gitea issues](https://github.com/knope-dev/knope/assets/42149402/3e538d1a-ccdb-4b74-92f3-868070af8dc6) - Using the same testing repo to see if knope correctly asks for the `GITEA_TOKEN` ![An image of a Knope prepare-release workflow doing a lot of things and then asking for the Gitea token](https://github.com/knope-dev/knope/assets/42149402/dc849e58-20b6-4d18-8a0d-244f0d0ecd8a) ## Notes - Commits will be reorganized after review. This is simply because i don't feel like constantly reorganizing commits. - The PR looks huge, but most of that is tests and test assets. - Currently `ureq` (as far as i can tell) doesn't support sending multipart form data and the only library i could find, [`ureq_multipart`](https://docs.rs/ureq_multipart/latest/ureq_multipart/index.html), panics on IO errors and has messages in chinese (see [in its own codebase](https://gitee.com/eshangrao/ureq-multipart-toolkit/blob/master/src/lib.rs#L186-196)). Together i find those unacceptable design choices. issue: algesten/ureq#698 --------- Co-authored-by: Dylan Anthony <[email protected]>
1 parent e70bd1e commit b2712e7

File tree

89 files changed

+2061
-105
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+2061
-105
lines changed

.changeset/gitea_support.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
default: minor
3+
---
4+
5+
# Gitea support
6+
7+
PR #759 closed issue #743. Thank you, @FallenValkyrie!
8+
9+
- Added Support for Gitea in the `CreatePullRequest` step
10+
- Added Support for Gitea in the `Release` step
11+
- Added A new `SelectGiteaIssue` step
12+
- Add support to generate Gitea config from known public Gitea instances
13+
14+
## How it works
15+
16+
To be able to use these new steps, just add a new section to your configuration, like this:
17+
18+
```toml
19+
[gitea]
20+
repo = "knope"
21+
owner = "knope-dev"
22+
host = "https://codeberg.org"
23+
```
24+
25+
You can now use the supported steps in the same way as their GitHub equivalents.
26+
27+
## Generating a configuration
28+
29+
Knope can now generate a configuration for you, if your repository's remote is one of the known
30+
public Gitea instances. Currently only [Codeberg](https://codeberg.org) is supported,
31+
but feel free to add more [here](https://github.com/knope-dev/knope/blob/main/src/config/toml/config.rs#L90).

.github/vale/config/vocabularies/Custom/accept.txt

+2
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ GitHub Actions
1111
musl
1212
TOML
1313
rebasing
14+
Gitea
15+
Codeberg
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
title: "Forge"
3+
---
4+
5+
A forge is a fully featured Git server with a web interface, like GitHub, Gitea or GitLab.
6+
Currently Knope can interact with the following forges:
7+
8+
- [Gitea]
9+
- [GitHub]
10+
11+
:::caution
12+
If you configure more than one forge, Knope will assume that you wish to push to all of them.
13+
Keep that in mind, when writing your configuration.
14+
:::
15+
16+
[Gitea]: /reference/config-file/gitea
17+
[GitHub]: /reference/config-file/github

docs/src/content/docs/reference/Concepts/release.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ In Knope, releasing a package consists of:
1010
2. Update all versioned files with the new version
1111
3. Add the details of all changes since the last release to the [changelog]
1212
4. Create a [Git tag](#git-tags)
13-
5. Optionally create a GitHub release (as part of the previous step) if [GitHub is configured](/reference/config-file/github)
13+
5. Optionally create a release (as part of the previous step) if [a forge is configured](/reference/concepts/forge)
1414

1515
:::tip
1616

docs/src/content/docs/reference/Config File/Steps/create-pull-request.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,16 @@
22
title: CreatePullRequest
33
---
44

5-
Create a pull request on GitHub from the current branch to a specified branch. If a pull request for those already exists, this step will overwrite the title and body of the existing pull request.
5+
Create a pull request on every configured forge from the current branch to a specified branch. If a pull request for those already exists, this step will overwrite the title and body of the existing pull request.
6+
7+
:::caution
8+
If you configure more than one forge, Knope will assume that you wish to create a PR on all of them.
9+
Keep that in mind, when writing your configuration.
10+
:::
11+
12+
## Prerequisites
13+
14+
To use the `CreatePullRequest` step, you must configure a forge first. See [configuring a Forge] for more information.
615

716
## Parameters
817

@@ -52,3 +61,4 @@ For a full example of how to use this with GitHub Actions to help automate relea
5261

5362
[Knope's prepare-release workflow]: https://github.com/knope-dev/knope/blob/e7292fa746fe1d81b84e5848815c02a0d8fc6f95/.github/workflows/prepare_release.yml
5463
[knope's release workflow]: https://github.com/knope-dev/knope/blob/e7292fa746fe1d81b84e5848815c02a0d8fc6f95/.github/workflows/release.yml
64+
[configuring a forge]: /reference/concepts/forge

docs/src/content/docs/reference/Config File/Steps/release.md

+16-11
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ title: Release
33
---
44

55
Release the configured [packages] which have pending changes.
6-
If there is a [GitHub config] set,
7-
this creates a release on GitHub with the same release notes that it added to the changelog (if any).
6+
If there is a [forge config] set,
7+
this creates a release with the same release notes that it added to the changelog (if any).
88
Otherwise, this tags the current commit as a release.
99
In either case, this step adds a new Git tag with the package's tag format.
1010
You should run [`PrepareRelease`] before this step, though not necessarily in the same workflow.
@@ -21,15 +21,15 @@ If the config file is using the `[packages.{name}]` syntax,
2121
each package gets its own tag in the format `{name}/v{version}` (this is the syntax required for Go modules).
2222
See examples below for more illustration.
2323

24-
## GitHub release notes
24+
## Release notes
2525

2626
There are several different possible release notes formats:
2727

2828
1. If run after a [`PrepareRelease`] step in the same workflow, the release notes will be the same as the changelog section created by [`PrepareRelease`] even if there is no changelog file configured—with the exception that headers are one level higher (for example, `####` becomes `###`).
2929
2. If run in a workflow with no [`PrepareRelease`] step before it (the new version was set another way), and there is a changelog file for the package, the release notes will be taken from the relevant changelog section. This section header must match exactly what [`PrepareRelease`] would have created. Headers will one level higher (for example, `####` becomes `###`).
30-
3. If run in a workflow with no [`PrepareRelease`] step before it (the new version was set another way), and there is no changelog file for the package, the step will use GitHub's automatic release notes generation.
30+
3. If run in a workflow with no [`PrepareRelease`] step before it (the new version was set another way), and there is no changelog file for the package, the step will use automatic release notes generation.
3131

32-
## GitHub release assets
32+
## Release assets
3333

3434
You can optionally include any number of assets to include in a release via [package assets].
3535
If you do this, this step will:
@@ -42,15 +42,20 @@ If you have any follow-up workflows triggered by GitHub releases,
4242
you can use `on: release: created` to run as soon as the step creates the draft
4343
(without assets) or `on: release: published` to run only after the assets are uploaded.
4444

45+
:::caution
46+
[Package assets] are currently unsupported when used together with Gitea.
47+
This is due to one of Knope's dependencies not supporting `multipart/form-data` requests.
48+
:::
49+
4550
## Errors
4651

4752
This step will fail if:
4853

49-
1. [GitHub config] exists but Knope can't create a release on GitHub. For example:
50-
1. There is no GitHub token set.
51-
2. The GitHub token doesn't have permission to create releases.
52-
3. The release already exists on GitHub (causing a conflict).
53-
2. There is no [GitHub config] set and Knope can't tag the current commit as a release.
54+
1. [forge config] exists but Knope can't create a release on the forge. For example:
55+
1. There is no token set.
56+
2. The token doesn't have permission to create releases.
57+
3. The release already exists on the forge (causing a conflict).
58+
2. There is no [forge config] set and Knope can't tag the current commit as a release.
5459
3. Could not find the correct changelog section in the configured changelog file for loading release notes.
5560
4. One of the configured package assets doesn't exist.
5661

@@ -179,7 +184,7 @@ See [Knope's release workflow] and [knope.toml] where we:
179184
3. Fan out into several jobs which each check out the changes and build a different binary
180185
4. Create a GitHub release with the new version, changelog, and the binary assets
181186

182-
[github config]: /reference/config-file/github
187+
[forge config]: /reference/concepts/forge
183188
[`preparerelease`]: /reference/config-file/steps/prepare-release
184189
[packages]: /reference/concepts/package
185190
[package assets]: /reference/config-file/packages#assets
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
title: SelectGiteaIssue
3+
---
4+
5+
Search for Gitea issues by status and display the list of them in the terminal.
6+
Selecting an issue enables other steps to use the issue's information (for example, [`SwitchBranches`]).
7+
8+
## Errors
9+
10+
This step will fail if any of the following are true:
11+
12+
1. Knope can't communicate with the Gitea instance.
13+
2. There is no [Gitea config] set.
14+
3. User doesn't select an issue.
15+
16+
## Example
17+
18+
```toml
19+
[[workflows]]
20+
name = "Start some work"
21+
[[workflows.steps]]
22+
type = "SelectGiteaIssue"
23+
label = "selected"
24+
```
25+
26+
[Gitea config]: /reference/config-file/gitea
27+
[`switchbranches`]: /reference/config-file/steps/switch-branches
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
title: "Gitea"
3+
---
4+
5+
Details needed to use steps that reference Gitea repositories.
6+
7+
## Example
8+
9+
```toml
10+
# knope.toml
11+
12+
[gitea]
13+
owner = "knope-dev"
14+
repo = "knope"
15+
host = "https://codeberg.org"
16+
```
17+
18+
The first time you use a step which requires this config,
19+
you will be prompted to generate a Gitea API token so Knope can perform actions on your behalf.
20+
To bypass this prompt, you can manually set the `GITEA_TOKEN` environment variable.

docs/src/content/docs/reference/Config File/packages.md

+4
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ Assets is a list of files to upload to a GitHub release. They do nothing without
135135
Assets are per-package. Each asset can optionally have a `name`, this is what it will appear as in GitHub releases.
136136
The `name` defaults to the final part of the path.
137137

138+
:::caution
139+
Knope doesn't yet support uploading assets to Gitea, declaring both `[gitea]` and assets is an error.
140+
:::
141+
138142
```toml
139143
[package]
140144

src/app_config.rs

+13
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ pub(crate) fn get_or_prompt_for_github_token() -> Result<String, Error> {
2424
})
2525
}
2626

27+
pub(crate) fn get_or_prompt_for_gitea_token(host: &str) -> Result<String, Error> {
28+
std::env::var("GITEA_TOKEN").or_else(|_| {
29+
let prompt = format!(
30+
"\
31+
No Gitea token found, generate one from {host}/user/settings/applications with\n\
32+
`repository` permissions set to `Read and Write`\
33+
and `issue` permissions set to `Read` and input here\
34+
"
35+
);
36+
load_value_or_prompt("gitea_token", &prompt)
37+
})
38+
}
39+
2740
pub(crate) fn load_value_or_prompt(key: &str, prompt: &str) -> Result<String, Error> {
2841
let app_dirs = AppDirs::new(Some("knope"), true).ok_or(Error::CouldNotOpenConfigPath)?;
2942
let config_path = app_dirs.config_dir.join(key);

src/config/mod.rs

+68-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ use crate::{
1919

2020
pub(crate) mod toml;
2121

22-
pub(crate) use self::toml::{ChangeLogSectionName, CommitFooter, CustomChangeType, GitHub, Jira};
22+
pub(crate) use self::toml::{
23+
ChangeLogSectionName, CommitFooter, CustomChangeType, GitHub, Gitea, Jira,
24+
};
2325

2426
/// A valid config, loaded from a supported file (or detected via default)
2527
#[derive(Debug)]
@@ -31,6 +33,8 @@ pub(crate) struct Config {
3133
pub(crate) jira: Option<Jira>,
3234
/// Optional configuration to talk to GitHub
3335
pub(crate) github: Option<GitHub>,
36+
/// Optional configuration to communicate with a Gitea instance
37+
pub(crate) gitea: Option<Gitea>,
3438
}
3539

3640
impl Config {
@@ -65,12 +69,14 @@ impl Config {
6569
package: Option<toml::Package>,
6670
workflows: Vec<Workflow>,
6771
github: Option<GitHub>,
72+
gitea: Option<Gitea>,
6873
}
6974

7075
let config = SimpleConfig {
7176
package: self.packages.pop().map(toml::Package::from),
7277
workflows: self.workflows,
7378
github: self.github,
79+
gitea: self.gitea,
7480
};
7581
#[allow(clippy::unwrap_used)] // because serde is annoying... I know it will serialize
7682
let serialized = to_string(&config).unwrap();
@@ -123,6 +129,18 @@ impl TryFrom<(ConfigLoader, String)> for Config {
123129
.collect::<Result<Vec<Package>, Error>>()?,
124130
(None, None) => Vec::new(),
125131
};
132+
133+
if config.gitea.is_some()
134+
&& packages.iter().any(|package| {
135+
package
136+
.assets
137+
.as_ref()
138+
.is_some_and(|assets| !assets.is_empty())
139+
})
140+
{
141+
return Err(Error::GiteaAssetUploads);
142+
}
143+
126144
Ok(Self {
127145
packages,
128146
workflows: config
@@ -133,6 +151,7 @@ impl TryFrom<(ConfigLoader, String)> for Config {
133151
.collect(),
134152
jira: config.jira.map(Spanned::into_inner),
135153
github: config.github.map(Spanned::into_inner),
154+
gitea: config.gitea.map(Spanned::into_inner),
136155
})
137156
}
138157
}
@@ -196,10 +215,17 @@ pub(crate) enum Error {
196215
url("https://knope.tech/reference/config-file/packages/")
197216
)]
198217
EmptyPackages,
218+
#[error("Asset uploads for Gitea are not supported")]
219+
#[diagnostic(
220+
code(config::gitea_asset_uploads),
221+
help("Remove the `[[package.assets]]` key from your config."),
222+
url("https://github.com/knope-dev/knope/issues/779")
223+
)]
224+
GiteaAssetUploads,
199225
}
200226

201227
#[cfg(test)]
202-
mod test_package_configs {
228+
mod test_errors {
203229

204230
use super::Config;
205231

@@ -219,15 +245,39 @@ mod test_package_configs {
219245
let config = Config::try_from((config, toml_string));
220246
assert!(config.is_err(), "Expected an error, got {config:?}");
221247
}
248+
249+
#[test]
250+
fn gitea_asset_error() {
251+
let toml_string = r#"
252+
[packages.something]
253+
[[packages.something.assets]]
254+
name = "something"
255+
path = "something"
256+
[[workflows]]
257+
name = "default"
258+
[[workflows.steps]]
259+
type = "Command"
260+
command = "echo this is nothing, really"
261+
[gitea]
262+
host = "https://gitea.example.com"
263+
owner = "knope"
264+
repo = "knope"
265+
"#
266+
.to_string();
267+
let config: super::toml::ConfigLoader = toml::from_str(&toml_string).unwrap();
268+
let config = Config::try_from((config, toml_string));
269+
assert!(config.is_err(), "Expected an error, got {config:?}");
270+
}
222271
}
223272

224273
/// Generate a brand new Config for the project in the current directory.
225274
pub(crate) fn generate() -> Config {
226275
let mut variables = IndexMap::new();
227276
variables.insert(String::from("$version"), Variable::Version);
228277

229-
let github = match git::get_first_remote() {
230-
Some(remote) if remote.contains("github.com") => {
278+
let first_remote = git::get_first_remote();
279+
let github = match first_remote {
280+
Some(ref remote) if remote.contains("github.com") => {
231281
let parts = remote.split('/').collect::<Vec<_>>();
232282
let owner = parts.get(parts.len() - 2).map(|owner| {
233283
owner
@@ -246,7 +296,19 @@ pub(crate) fn generate() -> Config {
246296
}
247297
_ => None,
248298
};
249-
let mut release_steps = if github.is_some() {
299+
300+
let gitea = first_remote.as_ref().and_then(|remote| {
301+
if Gitea::KNOWN_PUBLIC_GITEA_HOSTS
302+
.iter()
303+
.any(|known_host| remote.contains(known_host))
304+
{
305+
Gitea::try_from_remote(remote)
306+
} else {
307+
None
308+
}
309+
});
310+
311+
let mut release_steps = if github.is_some() || gitea.is_some() {
250312
vec![
251313
Step::Command {
252314
command: String::from(
@@ -284,6 +346,7 @@ pub(crate) fn generate() -> Config {
284346
],
285347
jira: None,
286348
github,
349+
gitea,
287350
packages: find_packages().ok().into_iter().collect(),
288351
}
289352
}

0 commit comments

Comments
 (0)