git-bot-feedback 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/.pre-commit-config.yaml +4 -1
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/CHANGELOG.md +12 -3
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/Cargo.lock +2 -2
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/Cargo.toml +5 -2
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/PKG-INFO +10 -3
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/README.md +17 -3
- {git_bot_feedback-0.1.0/bindings/python → git_bot_feedback-0.2.0}/README.rst +9 -2
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/bindings/python/CHANGELOG.md +26 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/bindings/python/Cargo.toml +2 -2
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0/bindings/python}/README.rst +9 -2
- git_bot_feedback-0.2.0/src/client/common.rs +30 -0
- git_bot_feedback-0.2.0/src/client/gitea/mod.rs +302 -0
- git_bot_feedback-0.2.0/src/client/gitea/serde_structs.rs +105 -0
- git_bot_feedback-0.2.0/src/client/gitea/specific_api.rs +437 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/client/github/graphql.rs +2 -3
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/client/github/mod.rs +15 -5
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/client/github/serde_structs.rs +0 -27
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/client/github/specific_api.rs +5 -9
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/client/mod.rs +50 -4
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/error.rs +7 -1
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/file_utils/file_filter.rs +1 -1
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/file_utils/mod.rs +3 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/git_diff.rs +30 -12
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/lib.rs +7 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/.gitattributes +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/.gitignore +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/CONTRIBUTING.md +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/LICENSE +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/bindings/python/git_bot_feedback.pyi +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/bindings/python/src/lib.rs +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/bindings/python/src/wrapper.rs +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/build.rs +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/codecov.yml +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/cspell.config.yml +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/docs/API/client.rst +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/docs/API/file-annotations.rst +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/docs/API/file-changes.rst +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/docs/API/index.rst +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/docs/API/output-variables.rst +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/docs/API/pr-reviews.rst +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/docs/API/thread-comments.rst +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/docs/_static/favicon.ico +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/docs/_static/logo.png +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/docs/conf.py +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/docs/index.rst +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/docs/logo.xcf +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/git_bot_feedback.pyi +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/nurfile +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/pyproject.toml +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/client/local.rs +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/comments/mod.rs +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/comments/review_comments.rs +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/comments/thread_comments.rs +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/file_annotations.rs +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/src/output_variable.rs +0 -0
- {git_bot_feedback-0.1.0 → git_bot_feedback-0.2.0}/uv.lock +0 -0
|
@@ -5,17 +5,20 @@ repos:
|
|
|
5
5
|
rev: v6.0.0
|
|
6
6
|
hooks:
|
|
7
7
|
- id: trailing-whitespace
|
|
8
|
+
exclude: ^tests/assets/.*.diff$
|
|
8
9
|
- id: end-of-file-fixer
|
|
10
|
+
exclude: ^tests/assets/.*.diff$
|
|
9
11
|
- id: check-yaml
|
|
10
12
|
- id: check-toml
|
|
11
13
|
- id: check-added-large-files
|
|
12
14
|
- id: mixed-line-ending
|
|
13
15
|
args: ['--fix=lf']
|
|
16
|
+
exclude: ^tests/assets/.*.diff$
|
|
14
17
|
- repo: https://github.com/streetsidesoftware/cspell-cli
|
|
15
18
|
rev: v10.0.1
|
|
16
19
|
hooks:
|
|
17
20
|
- id: cspell
|
|
18
|
-
exclude: CHANGELOG.md$
|
|
21
|
+
exclude: CHANGELOG.md$|.*\.diff$
|
|
19
22
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
20
23
|
# Ruff version.
|
|
21
24
|
rev: v0.15.20
|
|
@@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
<!-- markdownlint-disable MD024 -->
|
|
8
8
|
|
|
9
|
-
## [git-bot-feedback/v0.
|
|
9
|
+
## [git-bot-feedback/v0.8.0] - 2026-07-01
|
|
10
10
|
|
|
11
11
|
### <!-- 1 --> 🚀 Added
|
|
12
12
|
|
|
@@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
25
25
|
- Revise `FileFilter::walk_dir()` by @2bndy5 in [#78](https://github.com/2bndy5/git-bot-feedback/pull/78)
|
|
26
26
|
- Strip parent path from `walk_dir()` results by @2bndy5 in [#83](https://github.com/2bndy5/git-bot-feedback/pull/83)
|
|
27
27
|
- Add python bindings by @2bndy5 in [#90](https://github.com/2bndy5/git-bot-feedback/pull/90)
|
|
28
|
+
- Add Gitea support by @2bndy5 in [#94](https://github.com/2bndy5/git-bot-feedback/pull/94)
|
|
28
29
|
|
|
29
30
|
### <!-- 10 --> 💥 Breaking Changes
|
|
30
31
|
|
|
@@ -40,14 +41,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
40
41
|
- Honor `ignore_index` when asserted by @2bndy5 in [#75](https://github.com/2bndy5/git-bot-feedback/pull/75)
|
|
41
42
|
- Strip relative path from `FileFilter::walk_dir()` results by @2bndy5 in [#84](https://github.com/2bndy5/git-bot-feedback/pull/84)
|
|
42
43
|
- Use correct name of PR review state that is "COMMENTED" by @2bndy5 in [#89](https://github.com/2bndy5/git-bot-feedback/pull/89)
|
|
44
|
+
- Fail on unsuccessful return code from request for file changes by @2bndy5 in [#95](https://github.com/2bndy5/git-bot-feedback/pull/95)
|
|
45
|
+
- Trim line endings from file names when parsing diff strings by @2bndy5 in [#97](https://github.com/2bndy5/git-bot-feedback/pull/97)
|
|
46
|
+
- Fail on parsing erroneous diff by @2bndy5 in [#98](https://github.com/2bndy5/git-bot-feedback/pull/98)
|
|
47
|
+
- Move option's value instead of cloning it by @2bndy5 in [#99](https://github.com/2bndy5/git-bot-feedback/pull/99)
|
|
48
|
+
|
|
49
|
+
### <!-- 8 --> 📝 Documentation
|
|
50
|
+
|
|
51
|
+
- Expand on some methods' doc comments by @2bndy5 in [#96](https://github.com/2bndy5/git-bot-feedback/pull/96)
|
|
43
52
|
|
|
44
53
|
### <!-- 9 --> 🗨️ Changed
|
|
45
54
|
|
|
46
55
|
- Revise `FileAnnotations` impl by @2bndy5 in [#64](https://github.com/2bndy5/git-bot-feedback/pull/64)
|
|
47
56
|
|
|
48
|
-
[git-bot-feedback/v0.
|
|
57
|
+
[git-bot-feedback/v0.8.0]: https://github.com/2bndy5/git-bot-feedback/compare/git-bot-feedback/v0.7.1...git-bot-feedback/v0.8.0
|
|
49
58
|
|
|
50
|
-
Full commit diff: [`git-bot-feedback/v0.7.
|
|
59
|
+
Full commit diff: [`git-bot-feedback/v0.7.1...git-bot-feedback/v0.8.0`][git-bot-feedback/v0.8.0]
|
|
51
60
|
|
|
52
61
|
|
|
53
62
|
<!-- generated by git-cliff -->
|
|
@@ -400,7 +400,7 @@ dependencies = [
|
|
|
400
400
|
|
|
401
401
|
[[package]]
|
|
402
402
|
name = "git-bot-feedback"
|
|
403
|
-
version = "0.
|
|
403
|
+
version = "0.8.0"
|
|
404
404
|
dependencies = [
|
|
405
405
|
"async-trait",
|
|
406
406
|
"chrono",
|
|
@@ -420,7 +420,7 @@ dependencies = [
|
|
|
420
420
|
|
|
421
421
|
[[package]]
|
|
422
422
|
name = "git-bot-feedback-py"
|
|
423
|
-
version = "0.
|
|
423
|
+
version = "0.2.0"
|
|
424
424
|
dependencies = [
|
|
425
425
|
"git-bot-feedback",
|
|
426
426
|
"pyo3",
|
|
@@ -21,7 +21,7 @@ features = ["http2", "charset", "system-proxy"]
|
|
|
21
21
|
[package]
|
|
22
22
|
name = "git-bot-feedback"
|
|
23
23
|
description = "A library designed for CI tools that posts comments on a Pull Request."
|
|
24
|
-
version = "0.
|
|
24
|
+
version = "0.8.0"
|
|
25
25
|
edition.workspace = true
|
|
26
26
|
license.workspace = true
|
|
27
27
|
repository.workspace = true
|
|
@@ -58,8 +58,11 @@ pyo3 = ["dep:pyo3"]
|
|
|
58
58
|
# GitHub implementation/support
|
|
59
59
|
github = []
|
|
60
60
|
|
|
61
|
+
# Gitea implementation/support
|
|
62
|
+
gitea = []
|
|
63
|
+
|
|
61
64
|
# features enabled by default
|
|
62
|
-
default = ["github"]
|
|
65
|
+
default = ["github", "gitea"]
|
|
63
66
|
|
|
64
67
|
# optional feature to silence a compiler error if/when
|
|
65
68
|
# no features enable any git server implementations
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: git-bot-feedback
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Classifier: Development Status :: 5 - Production/Stable
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: Intended Audience :: System Administrators
|
|
@@ -51,15 +51,22 @@ loop is required.
|
|
|
51
51
|
Supported git servers
|
|
52
52
|
---------------------
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
But the API is designed to easily add support for other git servers.
|
|
54
|
+
This project is designed to easily add support for various git servers.
|
|
56
55
|
The following is just a list of git servers that are planned (in order or priority).
|
|
57
56
|
|
|
58
57
|
- GitHub
|
|
59
58
|
- GitLab
|
|
60
59
|
- Gitea
|
|
60
|
+
|
|
61
|
+
Gitea does not support
|
|
62
|
+
|
|
63
|
+
- posting thread comments for commits (push events)
|
|
64
|
+
- programmatically deleting a PR reviews' individual comments, rather we can
|
|
65
|
+
only resolve them (currently). However, deleting an entire PR review is supported.
|
|
61
66
|
- BitBucket
|
|
62
67
|
|
|
68
|
+
Currently, only Github and Gitea are supported.
|
|
69
|
+
|
|
63
70
|
LGPL license
|
|
64
71
|
------------
|
|
65
72
|
|
|
@@ -32,15 +32,29 @@ consumers to choose the TLS backend of their choice; see [reqwest's features][re
|
|
|
32
32
|
|
|
33
33
|
## Supported git servers
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
But the API is designed to easily add support for other git servers.
|
|
35
|
+
This project is designed to easily add support for various git servers.
|
|
37
36
|
The following is just a list of git servers that are planned (in order or priority).
|
|
38
37
|
|
|
39
38
|
- [x] GitHub
|
|
40
39
|
- [ ] GitLab
|
|
41
|
-
- [
|
|
40
|
+
- [x] Gitea
|
|
41
|
+
|
|
42
|
+
Gitea does not support
|
|
43
|
+
|
|
44
|
+
- posting thread comments for commits (push events)
|
|
45
|
+
- programmatically deleting a PR reviews' individual comments,
|
|
46
|
+
rather we can only resolve them (currently).
|
|
47
|
+
However, deleting an entire PR review is supported.
|
|
42
48
|
- [ ] BitBucket
|
|
43
49
|
|
|
50
|
+
### Optional support
|
|
51
|
+
|
|
52
|
+
Each supported implementation of the above git servers can be controlled via
|
|
53
|
+
[cargo features][dep-features]. They are enabled by default.
|
|
54
|
+
|
|
55
|
+
- `github` enables support of GitHub implementation
|
|
56
|
+
- `gitea` enables support of Gitea implementation
|
|
57
|
+
|
|
44
58
|
## LGPL license
|
|
45
59
|
|
|
46
60
|
This project is licensed under [LGPL-3.0-or-later].
|
|
@@ -26,15 +26,22 @@ loop is required.
|
|
|
26
26
|
Supported git servers
|
|
27
27
|
---------------------
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
But the API is designed to easily add support for other git servers.
|
|
29
|
+
This project is designed to easily add support for various git servers.
|
|
31
30
|
The following is just a list of git servers that are planned (in order or priority).
|
|
32
31
|
|
|
33
32
|
- GitHub
|
|
34
33
|
- GitLab
|
|
35
34
|
- Gitea
|
|
35
|
+
|
|
36
|
+
Gitea does not support
|
|
37
|
+
|
|
38
|
+
- posting thread comments for commits (push events)
|
|
39
|
+
- programmatically deleting a PR reviews' individual comments, rather we can
|
|
40
|
+
only resolve them (currently). However, deleting an entire PR review is supported.
|
|
36
41
|
- BitBucket
|
|
37
42
|
|
|
43
|
+
Currently, only Github and Gitea are supported.
|
|
44
|
+
|
|
38
45
|
LGPL license
|
|
39
46
|
------------
|
|
40
47
|
|
|
@@ -6,11 +6,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
<!-- markdownlint-disable MD024 -->
|
|
8
8
|
|
|
9
|
+
## [git-bot-feedback-py/v0.2.0] - 2026-07-01
|
|
10
|
+
|
|
11
|
+
### <!-- 1 --> 🚀 Added
|
|
12
|
+
|
|
13
|
+
- Add Gitea support by @2bndy5 in [#94](https://github.com/2bndy5/git-bot-feedback/pull/94)
|
|
14
|
+
|
|
15
|
+
### <!-- 4 --> 🛠️ Fixed
|
|
16
|
+
|
|
17
|
+
- Fail on unsuccessful return code from request for file changes by @2bndy5 in [#95](https://github.com/2bndy5/git-bot-feedback/pull/95)
|
|
18
|
+
- Trim line endings from file names when parsing diff strings by @2bndy5 in [#97](https://github.com/2bndy5/git-bot-feedback/pull/97)
|
|
19
|
+
- Fail on parsing erroneous diff by @2bndy5 in [#98](https://github.com/2bndy5/git-bot-feedback/pull/98)
|
|
20
|
+
- Move option's value instead of cloning it by @2bndy5 in [#99](https://github.com/2bndy5/git-bot-feedback/pull/99)
|
|
21
|
+
|
|
22
|
+
### <!-- 6 --> 📦 Dependency updates
|
|
23
|
+
|
|
24
|
+
- Bump version to git-bot-feedback/v0.8.0 by @2bndy5 in [`d6070e7`](https://github.com/2bndy5/git-bot-feedback/commit/d6070e7d41cdc8a1c01c5f3a69a16aa8bdff0d25)
|
|
25
|
+
|
|
26
|
+
### <!-- 8 --> 📝 Documentation
|
|
27
|
+
|
|
28
|
+
- Expand on some methods' doc comments by @2bndy5 in [#96](https://github.com/2bndy5/git-bot-feedback/pull/96)
|
|
29
|
+
|
|
30
|
+
[git-bot-feedback-py/v0.2.0]: https://github.com/2bndy5/git-bot-feedback/compare/git-bot-feedback-py/v0.1.0...git-bot-feedback-py/v0.2.0
|
|
31
|
+
|
|
32
|
+
Full commit diff: [`git-bot-feedback-py/v0.1.0...git-bot-feedback-py/v0.2.0`][git-bot-feedback-py/v0.2.0]
|
|
33
|
+
|
|
9
34
|
## [git-bot-feedback-py/v0.1.0] - 2026-06-29
|
|
10
35
|
|
|
11
36
|
### <!-- 6 --> 📦 Dependency updates
|
|
12
37
|
|
|
13
38
|
- Bump version to git-bot-feedback/v0.7.1 by @2bndy5 in [`5011a04`](https://github.com/2bndy5/git-bot-feedback/commit/5011a04d4806b08e54a1ed2ba456badb7622e682)
|
|
39
|
+
- Bump version to git-bot-feedback-py/v0.1.0 by @2bndy5 in [`c02c181`](https://github.com/2bndy5/git-bot-feedback/commit/c02c181b8ef8f5c3f825c334e02af2765c2a5cea)
|
|
14
40
|
|
|
15
41
|
### <!-- 9 --> 🗨️ Changed
|
|
16
42
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "git-bot-feedback-py"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
edition.workspace = true
|
|
5
5
|
license.workspace = true
|
|
6
6
|
repository.workspace = true
|
|
@@ -14,7 +14,7 @@ crate-type = ["cdylib"] # spell-checker: disable-line
|
|
|
14
14
|
path = "src/lib.rs"
|
|
15
15
|
|
|
16
16
|
[dependencies]
|
|
17
|
-
git-bot-feedback = { path = "../..", version = "0.
|
|
17
|
+
git-bot-feedback = { path = "../..", version = "0.8.0", features = ["file-changes", "pyo3"] }
|
|
18
18
|
pyo3 = { workspace = true }
|
|
19
19
|
pyo3-async-runtimes = { version = "0.29.0", features = ["tokio-runtime"] }
|
|
20
20
|
pyo3-log = "0.13.4"
|
|
@@ -26,15 +26,22 @@ loop is required.
|
|
|
26
26
|
Supported git servers
|
|
27
27
|
---------------------
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
But the API is designed to easily add support for other git servers.
|
|
29
|
+
This project is designed to easily add support for various git servers.
|
|
31
30
|
The following is just a list of git servers that are planned (in order or priority).
|
|
32
31
|
|
|
33
32
|
- GitHub
|
|
34
33
|
- GitLab
|
|
35
34
|
- Gitea
|
|
35
|
+
|
|
36
|
+
Gitea does not support
|
|
37
|
+
|
|
38
|
+
- posting thread comments for commits (push events)
|
|
39
|
+
- programmatically deleting a PR reviews' individual comments, rather we can
|
|
40
|
+
only resolve them (currently). However, deleting an entire PR review is supported.
|
|
36
41
|
- BitBucket
|
|
37
42
|
|
|
43
|
+
Currently, only Github and Gitea are supported.
|
|
44
|
+
|
|
38
45
|
LGPL license
|
|
39
46
|
------------
|
|
40
47
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#![cfg(any(feature = "gitea", feature = "github"))]
|
|
2
|
+
|
|
3
|
+
use serde::Deserialize;
|
|
4
|
+
|
|
5
|
+
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
|
|
6
|
+
#[serde(rename_all = "lowercase")]
|
|
7
|
+
pub enum PullRequestState {
|
|
8
|
+
Open,
|
|
9
|
+
Closed,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/// PR event payload.
|
|
13
|
+
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
|
14
|
+
pub struct PullRequestEventPayload {
|
|
15
|
+
/// The Pull Request's info.
|
|
16
|
+
pub pull_request: PullRequestInfo,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// A structure for deserializing a Pull Request's info from a response's json.
|
|
20
|
+
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
|
21
|
+
pub struct PullRequestInfo {
|
|
22
|
+
/// Is this PR a draft?
|
|
23
|
+
pub draft: bool,
|
|
24
|
+
/// Is this PR locked?
|
|
25
|
+
pub locked: bool,
|
|
26
|
+
/// The Pull Request's number.
|
|
27
|
+
pub number: u64,
|
|
28
|
+
/// What is current state of this PR?
|
|
29
|
+
pub state: PullRequestState,
|
|
30
|
+
}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
use std::{env, fs::OpenOptions, io::Write};
|
|
2
|
+
|
|
3
|
+
use async_trait::async_trait;
|
|
4
|
+
use reqwest::{Client, Method};
|
|
5
|
+
use url::Url;
|
|
6
|
+
|
|
7
|
+
use super::{ClientError, RestApiClient, RestApiRateLimitHeaders, common::PullRequestInfo};
|
|
8
|
+
use crate::{
|
|
9
|
+
OutputVariable, ReviewAction, ReviewOptions, ThreadCommentOptions,
|
|
10
|
+
client::common::PullRequestState,
|
|
11
|
+
};
|
|
12
|
+
mod serde_structs;
|
|
13
|
+
use serde_structs::{FullReview, ReviewDiffComment};
|
|
14
|
+
mod specific_api;
|
|
15
|
+
|
|
16
|
+
#[cfg(feature = "file-changes")]
|
|
17
|
+
use crate::{FileDiffLines, FileFilter, LinesChangedOnly, parse_diff};
|
|
18
|
+
#[cfg(feature = "file-changes")]
|
|
19
|
+
use reqwest::header::{HeaderMap, HeaderValue};
|
|
20
|
+
#[cfg(feature = "file-changes")]
|
|
21
|
+
use std::collections::HashMap;
|
|
22
|
+
|
|
23
|
+
/// A structure to work with Gitea REST API.
|
|
24
|
+
pub struct GiteaApiClient {
|
|
25
|
+
/// The HTTP request client to be used for all REST API calls.
|
|
26
|
+
client: Client,
|
|
27
|
+
|
|
28
|
+
/// The CI run's event payload from the webhook that triggered the workflow.
|
|
29
|
+
pull_request: Option<PullRequestInfo>,
|
|
30
|
+
|
|
31
|
+
/// The name of the event that was triggered when running cpp_linter.
|
|
32
|
+
pub event_name: String,
|
|
33
|
+
|
|
34
|
+
/// The value of the `GITHUB_API_URL` environment variable.
|
|
35
|
+
api_url: Url,
|
|
36
|
+
|
|
37
|
+
/// The value of the `GITHUB_REPOSITORY` environment variable.
|
|
38
|
+
repo: String,
|
|
39
|
+
|
|
40
|
+
/// The value of the `GITHUB_SHA` environment variable.
|
|
41
|
+
sha: String,
|
|
42
|
+
|
|
43
|
+
/// The value of the `ACTIONS_STEP_DEBUG` environment variable.
|
|
44
|
+
pub debug_enabled: bool,
|
|
45
|
+
|
|
46
|
+
/// The response header names that describe the rate limit status.
|
|
47
|
+
rate_limit_headers: RestApiRateLimitHeaders,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[async_trait]
|
|
51
|
+
impl RestApiClient for GiteaApiClient {
|
|
52
|
+
fn start_log_group(&self, name: &str) {
|
|
53
|
+
log::info!(target: "CI_LOG_GROUPING", "::group::{name}");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fn end_log_group(&self, _name: &str) {
|
|
57
|
+
log::info!(target: "CI_LOG_GROUPING", "::endgroup::");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn is_pr_event(&self) -> bool {
|
|
61
|
+
self.pull_request.is_some()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fn set_user_agent(&mut self, user_agent: &str) -> Result<(), ClientError> {
|
|
65
|
+
self.client = Client::builder()
|
|
66
|
+
.default_headers(Self::make_headers()?)
|
|
67
|
+
.user_agent(user_agent)
|
|
68
|
+
.build()?;
|
|
69
|
+
Ok(())
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// Does not support push events, only PR events.
|
|
73
|
+
async fn post_thread_comment(&self, options: ThreadCommentOptions) -> Result<(), ClientError> {
|
|
74
|
+
let comments_url = match &self.pull_request {
|
|
75
|
+
Some(pr_info) => {
|
|
76
|
+
if pr_info.locked {
|
|
77
|
+
return Ok(()); // cannot comment on locked PRs
|
|
78
|
+
}
|
|
79
|
+
env::var("GITEA_TOKEN").map_err(|e| ClientError::env_var("GITEA_TOKEN", e))?;
|
|
80
|
+
self.api_url.join(
|
|
81
|
+
format!("repos/{}/issues/{}/comments", self.repo, pr_info.number).as_str(),
|
|
82
|
+
)?
|
|
83
|
+
}
|
|
84
|
+
None => {
|
|
85
|
+
// This feature is supported in non-PR events on other git servers.
|
|
86
|
+
// But Gitea only supports comments on PRs or issues.
|
|
87
|
+
// Leave a informative log entry to highlight this and return early.
|
|
88
|
+
log::info!(
|
|
89
|
+
"Gitea support for posting thread comments is limited to pull requests only."
|
|
90
|
+
);
|
|
91
|
+
return Ok(());
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
self.update_comment(comments_url, options).await
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async fn cull_pr_reviews(&mut self, options: &mut ReviewOptions) -> Result<(), ClientError> {
|
|
98
|
+
if let Some(pr_info) = self.pull_request.as_ref() {
|
|
99
|
+
// Guard checks for unsuitable PR states
|
|
100
|
+
if (!options.allow_draft && pr_info.draft)
|
|
101
|
+
|| (!options.allow_closed && pr_info.state == PullRequestState::Closed)
|
|
102
|
+
|| pr_info.locked
|
|
103
|
+
{
|
|
104
|
+
return Ok(());
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Fetch existing reviews from this bot
|
|
108
|
+
let existing_reviews = self
|
|
109
|
+
.get_existing_review_comments(pr_info.number as i64, &options.marker)
|
|
110
|
+
.await?;
|
|
111
|
+
|
|
112
|
+
if existing_reviews.is_empty() {
|
|
113
|
+
return Ok(());
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let mut outdated_comment_ids = Vec::new();
|
|
117
|
+
let mut outdated_review_ids = Vec::new();
|
|
118
|
+
let mut reused_comments = std::collections::HashSet::new();
|
|
119
|
+
|
|
120
|
+
// Check each existing review for reused comments
|
|
121
|
+
for review in &existing_reviews {
|
|
122
|
+
let mut keep_review = false;
|
|
123
|
+
|
|
124
|
+
for existing_comment in &review.comments {
|
|
125
|
+
let mut keep_comment = false;
|
|
126
|
+
|
|
127
|
+
// Try to match against proposed comments
|
|
128
|
+
for proposed_comment in options.comments.iter() {
|
|
129
|
+
if Self::match_review_comment(
|
|
130
|
+
existing_comment,
|
|
131
|
+
proposed_comment,
|
|
132
|
+
&options.marker,
|
|
133
|
+
) {
|
|
134
|
+
log::info!(
|
|
135
|
+
"Using existing review comment: path='{}', line_end={}",
|
|
136
|
+
existing_comment.path,
|
|
137
|
+
existing_comment.new_position
|
|
138
|
+
);
|
|
139
|
+
reused_comments.insert(proposed_comment.clone());
|
|
140
|
+
keep_comment = true;
|
|
141
|
+
keep_review = true;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// If comment doesn't match any proposed comment, mark for deletion
|
|
147
|
+
if !keep_comment {
|
|
148
|
+
outdated_comment_ids.push(existing_comment.id);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// If no comments in this review were kept, mark review for deletion
|
|
153
|
+
if !keep_review {
|
|
154
|
+
outdated_review_ids.push(review.id);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Remove reused comments from proposed comments
|
|
159
|
+
options.comments.retain(|c| !reused_comments.contains(c));
|
|
160
|
+
|
|
161
|
+
// Delete outdated comments and reviews
|
|
162
|
+
if !outdated_comment_ids.is_empty() || !outdated_review_ids.is_empty() {
|
|
163
|
+
self.delete_outdated_review_comments(
|
|
164
|
+
pr_info.number as i64,
|
|
165
|
+
outdated_comment_ids,
|
|
166
|
+
outdated_review_ids,
|
|
167
|
+
options.delete_review_comments,
|
|
168
|
+
)
|
|
169
|
+
.await?;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
Ok(())
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async fn post_pr_review(&mut self, options: &ReviewOptions) -> Result<(), ClientError> {
|
|
176
|
+
if let Some(pr_info) = self.pull_request.as_ref() {
|
|
177
|
+
if (!options.allow_draft && pr_info.draft)
|
|
178
|
+
|| (!options.allow_closed && pr_info.state == PullRequestState::Closed)
|
|
179
|
+
|| pr_info.locked
|
|
180
|
+
{
|
|
181
|
+
return Ok(());
|
|
182
|
+
}
|
|
183
|
+
env::var("GITEA_TOKEN").map_err(|e| ClientError::env_var("GITEA_TOKEN", e))?;
|
|
184
|
+
let url = self
|
|
185
|
+
.api_url
|
|
186
|
+
.join(format!("repos/{}/pulls/{}/reviews", self.repo, pr_info.number).as_str())?;
|
|
187
|
+
let payload = FullReview {
|
|
188
|
+
event: match options.action {
|
|
189
|
+
ReviewAction::Comment => String::from("COMMENT"),
|
|
190
|
+
ReviewAction::Approve => String::from("APPROVED"),
|
|
191
|
+
ReviewAction::RequestChanges => String::from("REQUEST_CHANGES"),
|
|
192
|
+
},
|
|
193
|
+
body: format!("{}{}", options.marker, options.summary),
|
|
194
|
+
comments: options
|
|
195
|
+
.comments
|
|
196
|
+
.iter()
|
|
197
|
+
.map(ReviewDiffComment::from)
|
|
198
|
+
.map(|mut r| {
|
|
199
|
+
if !r.body.starts_with(&options.marker) {
|
|
200
|
+
r.body = format!("{}{}", options.marker, r.body);
|
|
201
|
+
}
|
|
202
|
+
r
|
|
203
|
+
})
|
|
204
|
+
.collect(),
|
|
205
|
+
commit_id: self.sha.clone(),
|
|
206
|
+
};
|
|
207
|
+
let request = self.make_api_request(
|
|
208
|
+
&self.client,
|
|
209
|
+
url,
|
|
210
|
+
Method::POST,
|
|
211
|
+
Some(
|
|
212
|
+
serde_json::to_string(&payload)
|
|
213
|
+
.map_err(|e| ClientError::json("serialize PR review payload", e))?,
|
|
214
|
+
),
|
|
215
|
+
None,
|
|
216
|
+
)?;
|
|
217
|
+
let response = self
|
|
218
|
+
.send_api_request(&self.client, request, &self.rate_limit_headers)
|
|
219
|
+
.await;
|
|
220
|
+
match response {
|
|
221
|
+
Ok(response) => {
|
|
222
|
+
self.log_response(response, "Failed to post PR review")
|
|
223
|
+
.await;
|
|
224
|
+
}
|
|
225
|
+
Err(e) => {
|
|
226
|
+
return Err(e.add_request_context("post PR review"));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
Ok(())
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
fn write_output_variables(&self, vars: &[OutputVariable]) -> Result<(), ClientError> {
|
|
234
|
+
if vars.is_empty() {
|
|
235
|
+
// Should probably be an error. This check is only here to prevent needlessly
|
|
236
|
+
// fetching the env var GITEA_OUTPUT value and opening the referenced file.
|
|
237
|
+
return Ok(());
|
|
238
|
+
}
|
|
239
|
+
if let Ok(gh_out) = env::var("GITEA_OUTPUT") {
|
|
240
|
+
return match OpenOptions::new().append(true).open(gh_out) {
|
|
241
|
+
Ok(mut gh_out_file) => {
|
|
242
|
+
for out_var in vars {
|
|
243
|
+
out_var.validate()?;
|
|
244
|
+
writeln!(&mut gh_out_file, "{}={}\n", out_var.name, out_var.value)
|
|
245
|
+
.map_err(|e| ClientError::io("write to GITEA_OUTPUT file", e))?;
|
|
246
|
+
}
|
|
247
|
+
Ok(())
|
|
248
|
+
}
|
|
249
|
+
Err(e) => Err(ClientError::io("write to GITEA_OUTPUT file", e)),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
Ok(())
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
fn append_step_summary(&self, comment: &str) -> Result<(), ClientError> {
|
|
256
|
+
if let Ok(gh_out) = env::var("GITEA_STEP_SUMMARY") {
|
|
257
|
+
// step summary MD file can be overwritten/removed in CI runners
|
|
258
|
+
return match OpenOptions::new().append(true).open(gh_out) {
|
|
259
|
+
Ok(mut gh_out_file) => {
|
|
260
|
+
let result = writeln!(&mut gh_out_file, "\n{comment}\n");
|
|
261
|
+
result.map_err(|e| ClientError::io("write to GITHUB_STEP_SUMMARY file", e))
|
|
262
|
+
}
|
|
263
|
+
Err(e) => Err(ClientError::io("write to GITHUB_STEP_SUMMARY file", e)),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
Ok(())
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
#[cfg(feature = "file-changes")]
|
|
270
|
+
#[cfg_attr(docsrs, doc(cfg(feature = "file-changes")))]
|
|
271
|
+
async fn get_list_of_changed_files(
|
|
272
|
+
&self,
|
|
273
|
+
file_filter: &FileFilter,
|
|
274
|
+
lines_changed_only: &LinesChangedOnly,
|
|
275
|
+
_base_diff: Option<String>,
|
|
276
|
+
_ignore_index: bool,
|
|
277
|
+
) -> Result<HashMap<String, FileDiffLines>, ClientError> {
|
|
278
|
+
let url_path = match &self.pull_request {
|
|
279
|
+
Some(pr_info) => format!("repos/{}/pulls/{}.diff", self.repo, pr_info.number),
|
|
280
|
+
None => format!("repos/{}/commits/{}.diff", self.repo, self.sha),
|
|
281
|
+
};
|
|
282
|
+
let url = self.api_url.join(&url_path)?;
|
|
283
|
+
let mut headers = HeaderMap::new();
|
|
284
|
+
headers.insert("Accept", HeaderValue::from_str("text/plain")?);
|
|
285
|
+
let request = self.make_api_request(&self.client, url, Method::GET, None, Some(headers))?;
|
|
286
|
+
let response = self
|
|
287
|
+
.send_api_request(&self.client, request, &self.rate_limit_headers)
|
|
288
|
+
.await?;
|
|
289
|
+
if let Err(e) = response.error_for_status_ref() {
|
|
290
|
+
if let Ok(body) = response.text().await {
|
|
291
|
+
log::error!("Failed to get list of changed files: {e:?}\n{body}");
|
|
292
|
+
}
|
|
293
|
+
return Err(ClientError::Request(e).add_request_context("get list of changed files"));
|
|
294
|
+
}
|
|
295
|
+
let body = (response.text()).await?.to_string();
|
|
296
|
+
parse_diff(&body, file_filter, lines_changed_only).map_err(ClientError::DiffError)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
fn client_kind(&self) -> String {
|
|
300
|
+
"gitea".to_string()
|
|
301
|
+
}
|
|
302
|
+
}
|