rsmetacheck-bot 0.6.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.
- rsmetacheck_bot-0.6.0/LICENSE +21 -0
- rsmetacheck_bot-0.6.0/PKG-INFO +187 -0
- rsmetacheck_bot-0.6.0/README.md +148 -0
- rsmetacheck_bot-0.6.0/pyproject.toml +123 -0
- rsmetacheck_bot-0.6.0/setup.cfg +4 -0
- rsmetacheck_bot-0.6.0/src/conversion/__init__.py +5 -0
- rsmetacheck_bot-0.6.0/src/conversion/convert_legacy_outputs.py +149 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/__init__.py +11 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/analysis_runtime.py +818 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/check_parsing.py +83 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/codemeta_runtime.py +131 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/commit_lookup.py +150 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/config/__init__.py +1 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/config/config_utils.py +99 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/config/rsmetacheck_non_failing.toml +2 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/config/schemas.py +186 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/constants.py +286 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/convert_legacy.py +26 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/fetch.py +361 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/github_api.py +204 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/gitlab_api.py +253 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/history.py +102 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/incremental.py +124 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/main.py +41 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/pipeline.py +406 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/pitfalls.py +161 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/platform_api.py +131 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/publish.py +733 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/query_history.py +41 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/repo_state.py +224 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/report_summary.py +141 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/reporting.py +396 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/rsmetacheck_wrapper.py +67 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/token_resolver.py +33 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/utils.py +85 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/verify_tokens.py +138 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot.egg-info/PKG-INFO +187 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot.egg-info/SOURCES.txt +57 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot.egg-info/dependency_links.txt +1 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot.egg-info/entry_points.txt +2 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot.egg-info/requires.txt +26 -0
- rsmetacheck_bot-0.6.0/src/rsmetacheck_bot.egg-info/top_level.txt +2 -0
- rsmetacheck_bot-0.6.0/tests/test_check_parsing.py +147 -0
- rsmetacheck_bot-0.6.0/tests/test_codemeta_runtime.py +92 -0
- rsmetacheck_bot-0.6.0/tests/test_config_schemas.py +278 -0
- rsmetacheck_bot-0.6.0/tests/test_config_utils.py +63 -0
- rsmetacheck_bot-0.6.0/tests/test_conversion.py +164 -0
- rsmetacheck_bot-0.6.0/tests/test_fetch.py +249 -0
- rsmetacheck_bot-0.6.0/tests/test_gitlab_api.py +21 -0
- rsmetacheck_bot-0.6.0/tests/test_gitlab_integration.py +18 -0
- rsmetacheck_bot-0.6.0/tests/test_history.py +107 -0
- rsmetacheck_bot-0.6.0/tests/test_incremental.py +131 -0
- rsmetacheck_bot-0.6.0/tests/test_init.py +16 -0
- rsmetacheck_bot-0.6.0/tests/test_legacy_report_parsing.py +22 -0
- rsmetacheck_bot-0.6.0/tests/test_pipeline.py +1358 -0
- rsmetacheck_bot-0.6.0/tests/test_pitfalls.py +300 -0
- rsmetacheck_bot-0.6.0/tests/test_publish.py +874 -0
- rsmetacheck_bot-0.6.0/tests/test_report_summary.py +75 -0
- rsmetacheck_bot-0.6.0/tests/test_token_resolution.py +57 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Software Understanding
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rsmetacheck-bot
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: Metadata quality bot for software repositories, leveraging metacheck for analysis and GitHub/GitLab APIs for issue management.
|
|
5
|
+
Author-email: Tom François <tom.francois@lapp.in2p3.fr>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/SoftwareUnderstanding/rsmetacheck-bot
|
|
8
|
+
Project-URL: Repository, https://github.com/SoftwareUnderstanding/rsmetacheck-bot
|
|
9
|
+
Project-URL: Issues, https://github.com/SoftwareUnderstanding/rsmetacheck-bot/issues
|
|
10
|
+
Keywords: codemeta,metadata,bot,quality software
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: <3.13,>=3.11
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: click>=8.3.1
|
|
18
|
+
Requires-Dist: rsmetacheck>=0.3.6
|
|
19
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
20
|
+
Requires-Dist: requests>=2.32.5
|
|
21
|
+
Requires-Dist: setuptools[test]<83.0.0
|
|
22
|
+
Requires-Dist: pydantic>=2.13.4
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: bandit>=1.9.3; extra == "dev"
|
|
25
|
+
Requires-Dist: interrogate>=1.7.0; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff>=0.14.13; extra == "dev"
|
|
27
|
+
Requires-Dist: ty>=0.0.12; extra == "dev"
|
|
28
|
+
Provides-Extra: test
|
|
29
|
+
Requires-Dist: pytest; extra == "test"
|
|
30
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
31
|
+
Requires-Dist: setuptools<85.0.0; extra == "test"
|
|
32
|
+
Requires-Dist: genbadge[coverage]>=1.0.0; extra == "test"
|
|
33
|
+
Provides-Extra: docs
|
|
34
|
+
Requires-Dist: furo>=2025.12.19; extra == "docs"
|
|
35
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
36
|
+
Requires-Dist: sphinxcontrib-mermaid>=1.0.0; extra == "docs"
|
|
37
|
+
Requires-Dist: tomli>=2.0.1; python_version < "3.11" and extra == "docs"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# rsmetacheck-bot
|
|
41
|
+
|
|
42
|
+
[](https://doi.org/10.5281/zenodo.19468976)
|
|
43
|
+
[](https://archive.softwareheritage.org/swh:1:dir:9f3d474c040158cc675e8db135767e0d2d3fba7b;origin=https://github.com/SoftwareUnderstanding/rsmetacheck-bot;visit=swh:1:snp:03da984544982fdf5aa1f556012b917250f1e4c3;anchor=swh:1:rev:fc64150edf2e35f045cf3858d81b00ca6e7f6e68)
|
|
44
|
+
[](LICENSE)
|
|
45
|
+
[](https://github.com/SoftwareUnderstanding/rsmetacheck-bot/releases)
|
|
46
|
+
[](https://www.python.org/downloads/)
|
|
47
|
+
[](https://github.com/SoftwareUnderstanding/rsmetacheck-bot/actions/workflows/ci.yml)
|
|
48
|
+
[](https://github.com/astral-sh/ruff)
|
|
49
|
+

|
|
50
|
+

|
|
51
|
+
[](https://github.com/SoftwareUnderstanding/sw-metadata-bot/actions/workflows/rsmetacheck.yml)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
An automated bot that analyzes repository metadata quality and creates issues with improvement suggestions.
|
|
55
|
+
|
|
56
|
+
Part of the [CodeMetaSoft](https://w3id.org/codemetasoft) project to improve research software metadata quality.
|
|
57
|
+
|
|
58
|
+
[](https://github.com/SoftwareUnderstanding/rsmetacheck-bot/issues/new?template=subscribe.yml)
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 📋 What This Bot Does
|
|
63
|
+
|
|
64
|
+
If you received an issue from this bot, it means your repository's metadata was automatically analyzed and some improvements were detected on the main default branch.
|
|
65
|
+
|
|
66
|
+
The issue contains:
|
|
67
|
+
|
|
68
|
+
- **Pitfalls**: Critical metadata issues that should be fixed
|
|
69
|
+
- **Warnings**: Metadata improvements that are recommended
|
|
70
|
+
- **Suggestions**: Specific recommendations on how to fix each issue
|
|
71
|
+
- **Codemeta.json generation**: if your repo does not contain any `codemeta.json` file, the bot suggests one.
|
|
72
|
+
|
|
73
|
+
### Example Issues You Might See
|
|
74
|
+
|
|
75
|
+
```md
|
|
76
|
+
### [P002](https://w3id.org/rsmetacheck/catalog/#P002)
|
|
77
|
+
**Evidence:** P002 detected: LICENSE file contains unreplaced template placeholders
|
|
78
|
+
|
|
79
|
+
**Suggestion:** Update the copyright section with accurate names, organizations, and the current year. Personalizing this section ensures clarity and legal accuracy.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 💬 How to Respond
|
|
85
|
+
|
|
86
|
+
### If You Agree with the Suggestions
|
|
87
|
+
|
|
88
|
+
Fix the identified issues and **close the issue** with a comment explaining what you fixed. Your improvements help your software become more discoverable and citable!
|
|
89
|
+
|
|
90
|
+
### If You Disagree or Have Questions
|
|
91
|
+
|
|
92
|
+
Feel free to **comment on the issue**. We're happy to discuss the suggestions and help clarify what's needed.
|
|
93
|
+
|
|
94
|
+
### If You're Not Interested
|
|
95
|
+
|
|
96
|
+
Simply comment **"unsubscribe"** on the issue and we'll remove your repository from future analysis.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 🔍 What Analysis Is Used
|
|
101
|
+
|
|
102
|
+
This bot uses [rsmetacheck](https://github.com/SoftwareUnderstanding/rsmetacheck), which analyzes:
|
|
103
|
+
|
|
104
|
+
- Software metadata completeness
|
|
105
|
+
- Citation and documentation quality
|
|
106
|
+
- Repository structure and best practices
|
|
107
|
+
|
|
108
|
+
The bot **does not**:
|
|
109
|
+
|
|
110
|
+
- Modify your code or files
|
|
111
|
+
- Make pull requests
|
|
112
|
+
- Have access to your repository secrets
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Install
|
|
117
|
+
|
|
118
|
+
### pip install
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
pip install rsmetacheck-bot
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### uv
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
uv add rsmetacheck-bot
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 📚 Learn More
|
|
131
|
+
|
|
132
|
+
- [CodeMetaSoft Project](https://w3id.org/codemetasoft) - About the initiative
|
|
133
|
+
- [rsmetacheck](https://github.com/SoftwareUnderstanding/rsmetacheck) - The analysis tool
|
|
134
|
+
- [Citation File Format](https://citation-file-format.github.io/) - How to add CITATION.cff
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 🛠️ For Maintainers Running This Bot
|
|
139
|
+
|
|
140
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and usage instructions.
|
|
141
|
+
|
|
142
|
+
Project storage policy: the repo-centric layout and migration rules are described in [docs/output_storage.rst](docs/output_storage.rst).
|
|
143
|
+
|
|
144
|
+
The pipeline is config driven: one JSON file defines the repository list, issue message, inline opt-outs, and output layout.
|
|
145
|
+
|
|
146
|
+
Supported platforms:
|
|
147
|
+
|
|
148
|
+
- ✅ GitHub.com
|
|
149
|
+
- ✅ Gitlab.com
|
|
150
|
+
|
|
151
|
+
The bot is handling self-hosted gitlab platform but requires providing a token to this server (and not gitlab.com).
|
|
152
|
+
|
|
153
|
+
### Safe workflow for local validation
|
|
154
|
+
|
|
155
|
+
Use these commands when validating the bot without creating live issues:
|
|
156
|
+
|
|
157
|
+
1. Legacy snapshot conversion
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
uv run sw-metadata-bot convert-legacy outputs/legacy_snapshot --target-root outputs/converted
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
This converts a legacy snapshot tree into the current repo-centric layout without publishing anything.
|
|
164
|
+
|
|
165
|
+
2. Fresh dry-run analysis from scratch
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
uv run sw-metadata-bot run-analysis --config-file config.json --snapshot-tag 202603
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
This writes analysis outputs and run reports, but remains dry-run and does not create issues.
|
|
172
|
+
|
|
173
|
+
3. Simulated publish review
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
uv run sw-metadata-bot simulate-publish --analysis-root outputs/batch-a/202603
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
This replays the analysis decisions with a fake issue client so you can review the generated issue bodies and opt-out logic without spamming real repositories.
|
|
180
|
+
|
|
181
|
+
> Avoid the live `publish` command unless you explicitly want to create or update real repository issues.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 📝 License
|
|
186
|
+
|
|
187
|
+
See [LICENSE](LICENSE) file.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# rsmetacheck-bot
|
|
2
|
+
|
|
3
|
+
[](https://doi.org/10.5281/zenodo.19468976)
|
|
4
|
+
[](https://archive.softwareheritage.org/swh:1:dir:9f3d474c040158cc675e8db135767e0d2d3fba7b;origin=https://github.com/SoftwareUnderstanding/rsmetacheck-bot;visit=swh:1:snp:03da984544982fdf5aa1f556012b917250f1e4c3;anchor=swh:1:rev:fc64150edf2e35f045cf3858d81b00ca6e7f6e68)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://github.com/SoftwareUnderstanding/rsmetacheck-bot/releases)
|
|
7
|
+
[](https://www.python.org/downloads/)
|
|
8
|
+
[](https://github.com/SoftwareUnderstanding/rsmetacheck-bot/actions/workflows/ci.yml)
|
|
9
|
+
[](https://github.com/astral-sh/ruff)
|
|
10
|
+

|
|
11
|
+

|
|
12
|
+
[](https://github.com/SoftwareUnderstanding/sw-metadata-bot/actions/workflows/rsmetacheck.yml)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
An automated bot that analyzes repository metadata quality and creates issues with improvement suggestions.
|
|
16
|
+
|
|
17
|
+
Part of the [CodeMetaSoft](https://w3id.org/codemetasoft) project to improve research software metadata quality.
|
|
18
|
+
|
|
19
|
+
[](https://github.com/SoftwareUnderstanding/rsmetacheck-bot/issues/new?template=subscribe.yml)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 📋 What This Bot Does
|
|
24
|
+
|
|
25
|
+
If you received an issue from this bot, it means your repository's metadata was automatically analyzed and some improvements were detected on the main default branch.
|
|
26
|
+
|
|
27
|
+
The issue contains:
|
|
28
|
+
|
|
29
|
+
- **Pitfalls**: Critical metadata issues that should be fixed
|
|
30
|
+
- **Warnings**: Metadata improvements that are recommended
|
|
31
|
+
- **Suggestions**: Specific recommendations on how to fix each issue
|
|
32
|
+
- **Codemeta.json generation**: if your repo does not contain any `codemeta.json` file, the bot suggests one.
|
|
33
|
+
|
|
34
|
+
### Example Issues You Might See
|
|
35
|
+
|
|
36
|
+
```md
|
|
37
|
+
### [P002](https://w3id.org/rsmetacheck/catalog/#P002)
|
|
38
|
+
**Evidence:** P002 detected: LICENSE file contains unreplaced template placeholders
|
|
39
|
+
|
|
40
|
+
**Suggestion:** Update the copyright section with accurate names, organizations, and the current year. Personalizing this section ensures clarity and legal accuracy.
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 💬 How to Respond
|
|
46
|
+
|
|
47
|
+
### If You Agree with the Suggestions
|
|
48
|
+
|
|
49
|
+
Fix the identified issues and **close the issue** with a comment explaining what you fixed. Your improvements help your software become more discoverable and citable!
|
|
50
|
+
|
|
51
|
+
### If You Disagree or Have Questions
|
|
52
|
+
|
|
53
|
+
Feel free to **comment on the issue**. We're happy to discuss the suggestions and help clarify what's needed.
|
|
54
|
+
|
|
55
|
+
### If You're Not Interested
|
|
56
|
+
|
|
57
|
+
Simply comment **"unsubscribe"** on the issue and we'll remove your repository from future analysis.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 🔍 What Analysis Is Used
|
|
62
|
+
|
|
63
|
+
This bot uses [rsmetacheck](https://github.com/SoftwareUnderstanding/rsmetacheck), which analyzes:
|
|
64
|
+
|
|
65
|
+
- Software metadata completeness
|
|
66
|
+
- Citation and documentation quality
|
|
67
|
+
- Repository structure and best practices
|
|
68
|
+
|
|
69
|
+
The bot **does not**:
|
|
70
|
+
|
|
71
|
+
- Modify your code or files
|
|
72
|
+
- Make pull requests
|
|
73
|
+
- Have access to your repository secrets
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Install
|
|
78
|
+
|
|
79
|
+
### pip install
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install rsmetacheck-bot
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### uv
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
uv add rsmetacheck-bot
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 📚 Learn More
|
|
92
|
+
|
|
93
|
+
- [CodeMetaSoft Project](https://w3id.org/codemetasoft) - About the initiative
|
|
94
|
+
- [rsmetacheck](https://github.com/SoftwareUnderstanding/rsmetacheck) - The analysis tool
|
|
95
|
+
- [Citation File Format](https://citation-file-format.github.io/) - How to add CITATION.cff
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 🛠️ For Maintainers Running This Bot
|
|
100
|
+
|
|
101
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and usage instructions.
|
|
102
|
+
|
|
103
|
+
Project storage policy: the repo-centric layout and migration rules are described in [docs/output_storage.rst](docs/output_storage.rst).
|
|
104
|
+
|
|
105
|
+
The pipeline is config driven: one JSON file defines the repository list, issue message, inline opt-outs, and output layout.
|
|
106
|
+
|
|
107
|
+
Supported platforms:
|
|
108
|
+
|
|
109
|
+
- ✅ GitHub.com
|
|
110
|
+
- ✅ Gitlab.com
|
|
111
|
+
|
|
112
|
+
The bot is handling self-hosted gitlab platform but requires providing a token to this server (and not gitlab.com).
|
|
113
|
+
|
|
114
|
+
### Safe workflow for local validation
|
|
115
|
+
|
|
116
|
+
Use these commands when validating the bot without creating live issues:
|
|
117
|
+
|
|
118
|
+
1. Legacy snapshot conversion
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
uv run sw-metadata-bot convert-legacy outputs/legacy_snapshot --target-root outputs/converted
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This converts a legacy snapshot tree into the current repo-centric layout without publishing anything.
|
|
125
|
+
|
|
126
|
+
2. Fresh dry-run analysis from scratch
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
uv run sw-metadata-bot run-analysis --config-file config.json --snapshot-tag 202603
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
This writes analysis outputs and run reports, but remains dry-run and does not create issues.
|
|
133
|
+
|
|
134
|
+
3. Simulated publish review
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
uv run sw-metadata-bot simulate-publish --analysis-root outputs/batch-a/202603
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
This replays the analysis decisions with a fake issue client so you can review the generated issue bodies and opt-out logic without spamming real repositories.
|
|
141
|
+
|
|
142
|
+
> Avoid the live `publish` command unless you explicitly want to create or update real repository issues.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 📝 License
|
|
147
|
+
|
|
148
|
+
See [LICENSE](LICENSE) file.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel>=0.47.0,<0.49.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "rsmetacheck-bot"
|
|
7
|
+
version = "0.6.0"
|
|
8
|
+
description = "Metadata quality bot for software repositories, leveraging metacheck for analysis and GitHub/GitLab APIs for issue management."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11,<3.13"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Tom François", email = "tom.francois@lapp.in2p3.fr"}
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"click>=8.3.1",
|
|
17
|
+
"rsmetacheck>=0.3.6",
|
|
18
|
+
"python-dotenv>=1.0.0",
|
|
19
|
+
"requests>=2.32.5",
|
|
20
|
+
"setuptools[test]<83.0.0",
|
|
21
|
+
"pydantic>=2.13.4",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
keywords = ["codemeta", "metadata", "bot", "quality software"]
|
|
25
|
+
classifiers = [
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Operating System :: OS Independent",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/SoftwareUnderstanding/rsmetacheck-bot"
|
|
33
|
+
Repository = "https://github.com/SoftwareUnderstanding/rsmetacheck-bot"
|
|
34
|
+
Issues = "https://github.com/SoftwareUnderstanding/rsmetacheck-bot/issues"
|
|
35
|
+
|
|
36
|
+
[project.scripts]
|
|
37
|
+
rsmetacheck-bot = "rsmetacheck_bot.main:main"
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
dev = [
|
|
41
|
+
"bandit>=1.9.3",
|
|
42
|
+
"interrogate>=1.7.0",
|
|
43
|
+
"ruff>=0.14.13",
|
|
44
|
+
"ty>=0.0.12",
|
|
45
|
+
]
|
|
46
|
+
test = [
|
|
47
|
+
"pytest",
|
|
48
|
+
"pytest-cov",
|
|
49
|
+
"setuptools<85.0.0",
|
|
50
|
+
"genbadge[coverage]>=1.0.0",
|
|
51
|
+
]
|
|
52
|
+
docs = [
|
|
53
|
+
"furo>=2025.12.19",
|
|
54
|
+
"sphinx",
|
|
55
|
+
"sphinxcontrib-mermaid>=1.0.0",
|
|
56
|
+
"tomli>=2.0.1; python_version < '3.11'",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
[tool.uv]
|
|
60
|
+
package = true
|
|
61
|
+
|
|
62
|
+
[tool.setuptools]
|
|
63
|
+
package-dir = {"" = "src"}
|
|
64
|
+
packages = { find = { where = ["src"] } }
|
|
65
|
+
include-package-data = true
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
[tool.setuptools.package-data]
|
|
69
|
+
rsmetacheck_bot = ["config/rsmetacheck_non_failing.toml"]
|
|
70
|
+
|
|
71
|
+
[tool.rsmetacheck_bot.docs]
|
|
72
|
+
# Start year for copyright range (displayed as "YYYY–present" in docs)
|
|
73
|
+
copyright_year = "2026"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
[tool.pytest.ini_options]
|
|
77
|
+
pythonpath = [
|
|
78
|
+
"src",
|
|
79
|
+
"scripts"
|
|
80
|
+
]
|
|
81
|
+
testpaths = ["tests"]
|
|
82
|
+
python_files = ["test_*.py"]
|
|
83
|
+
python_classes = ["Test*"]
|
|
84
|
+
python_functions = ["test_*"]
|
|
85
|
+
addopts = "-v --tb=short"
|
|
86
|
+
markers = [
|
|
87
|
+
"integration_gitlab: real GitLab API integration tests requiring a valid GITLAB_API_TOKEN",
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
[tool.bandit]
|
|
91
|
+
exclude_dirs = ["tests",".venv", ".ruff-cache"]
|
|
92
|
+
tests = ["B201", "B301"]
|
|
93
|
+
skips = ["B101", "B601"]
|
|
94
|
+
|
|
95
|
+
[tool.interrogate]
|
|
96
|
+
ignore-init-method = false
|
|
97
|
+
ignore-init-module = false
|
|
98
|
+
ignore-magic = false
|
|
99
|
+
ignore-semiprivate = false
|
|
100
|
+
ignore-private = false
|
|
101
|
+
ignore-property-decorators = false
|
|
102
|
+
ignore-module = false
|
|
103
|
+
ignore-nested-functions = false
|
|
104
|
+
ignore-nested-classes = false
|
|
105
|
+
ignore-setters = false
|
|
106
|
+
ignore-overloaded-functions = false
|
|
107
|
+
fail-under = 90
|
|
108
|
+
# example values
|
|
109
|
+
exclude = ["setup.py", "docs", "build"]
|
|
110
|
+
# example regex
|
|
111
|
+
ignore-regex = ["^get$", "^mock_.*", ".*BaseClass.*"]
|
|
112
|
+
ext = []
|
|
113
|
+
# possible values: sphinx (default), google
|
|
114
|
+
style = "sphinx"
|
|
115
|
+
# possible values: 0 (minimal output), 1 (-v), 2 (-vv)
|
|
116
|
+
verbose = 0
|
|
117
|
+
quiet = false
|
|
118
|
+
whitelist-regex = []
|
|
119
|
+
color = true
|
|
120
|
+
omit-covered-files = false
|
|
121
|
+
# output file location
|
|
122
|
+
generate-badge = "."
|
|
123
|
+
badge-format = "svg"
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Convert legacy snapshot-based output trees into the repo-centric state layout."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import shutil
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from rsmetacheck_bot import constants, repo_state
|
|
12
|
+
from rsmetacheck_bot.config.config_utils import sanitize_repo_name
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _iter_legacy_repo_dirs(snapshot_root: Path) -> list[Path]:
|
|
16
|
+
"""Return repository directories inside a legacy snapshot root."""
|
|
17
|
+
if not snapshot_root.exists():
|
|
18
|
+
return []
|
|
19
|
+
|
|
20
|
+
repo_dirs = [
|
|
21
|
+
child
|
|
22
|
+
for child in snapshot_root.iterdir()
|
|
23
|
+
if child.is_dir() and not child.name.startswith(".")
|
|
24
|
+
]
|
|
25
|
+
return sorted(repo_dirs)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _load_json_if_present(path: Path) -> Any | None:
|
|
29
|
+
"""Load JSON from disk when present, otherwise return None."""
|
|
30
|
+
if not path.exists():
|
|
31
|
+
return None
|
|
32
|
+
try:
|
|
33
|
+
with open(path, encoding="utf-8") as handle:
|
|
34
|
+
return json.load(handle)
|
|
35
|
+
except (json.JSONDecodeError, OSError):
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _copy_if_present(source: Path, destination: Path) -> None:
|
|
40
|
+
"""Copy a file to the destination when it exists."""
|
|
41
|
+
if source.exists():
|
|
42
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
shutil.copy2(source, destination)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def convert_legacy_output_tree(
|
|
47
|
+
snapshot_root: Path, *, target_root: Path | None = None
|
|
48
|
+
) -> list[Path]:
|
|
49
|
+
"""Convert a legacy snapshot root into repo-centric state folders.
|
|
50
|
+
|
|
51
|
+
The converter copies repository artifacts from legacy per-repo directories into
|
|
52
|
+
a new layout rooted at ``target_root``. It also writes current-state and
|
|
53
|
+
event-log files for each converted repository and mirrors the legacy
|
|
54
|
+
run_report.json into the target root when present.
|
|
55
|
+
"""
|
|
56
|
+
snapshot_root = snapshot_root.resolve()
|
|
57
|
+
target_root = (target_root or snapshot_root).resolve()
|
|
58
|
+
target_root.mkdir(parents=True, exist_ok=True)
|
|
59
|
+
|
|
60
|
+
repo_dirs = _iter_legacy_repo_dirs(snapshot_root)
|
|
61
|
+
print(f"[convert-legacy] Converting legacy snapshot: {snapshot_root}")
|
|
62
|
+
print(f"[convert-legacy] Target root: {target_root}")
|
|
63
|
+
|
|
64
|
+
converted: list[Path] = []
|
|
65
|
+
for repo_dir in repo_dirs:
|
|
66
|
+
repo_name = sanitize_repo_name(repo_dir.name)
|
|
67
|
+
repo_folder = target_root / repo_name
|
|
68
|
+
repo_paths = {
|
|
69
|
+
"repo_folder": repo_folder,
|
|
70
|
+
"event_log": repo_folder / constants.FILENAME_EVENT_LOG,
|
|
71
|
+
"current_state": repo_folder / constants.FILENAME_CURRENT_STATE,
|
|
72
|
+
"analyses_folder": repo_folder / constants.DIRNAME_ANALYSES,
|
|
73
|
+
"issues_folder": repo_folder / constants.DIRNAME_ISSUES,
|
|
74
|
+
}
|
|
75
|
+
repo_state.ensure_repo_state_dirs(repo_paths)
|
|
76
|
+
|
|
77
|
+
copied_files = []
|
|
78
|
+
for filename in (
|
|
79
|
+
constants.FILENAME_PITFALL,
|
|
80
|
+
constants.FILENAME_SOMEF_OUTPUT,
|
|
81
|
+
constants.FILENAME_REPORT,
|
|
82
|
+
constants.FILENAME_ISSUE_REPORT,
|
|
83
|
+
constants.FILENAME_CODEMETA_STATUS,
|
|
84
|
+
constants.FILENAME_CODEMETA_GENERATED,
|
|
85
|
+
):
|
|
86
|
+
source = repo_dir / filename
|
|
87
|
+
if source.exists():
|
|
88
|
+
_copy_if_present(source, repo_folder / filename)
|
|
89
|
+
copied_files.append(filename)
|
|
90
|
+
|
|
91
|
+
legacy_report = _load_json_if_present(repo_dir / constants.FILENAME_REPORT)
|
|
92
|
+
if isinstance(legacy_report, dict):
|
|
93
|
+
records = legacy_report.get("records")
|
|
94
|
+
if isinstance(records, list) and records:
|
|
95
|
+
record = records[0]
|
|
96
|
+
if isinstance(record, dict):
|
|
97
|
+
repo_state.write_current_state(
|
|
98
|
+
repo_folder, repo_state.build_analysis_current_state(record)
|
|
99
|
+
)
|
|
100
|
+
repo_state.append_event_log(
|
|
101
|
+
repo_folder,
|
|
102
|
+
{
|
|
103
|
+
"event": "analysis_completed",
|
|
104
|
+
"commit_id": record.get("current_commit_id") or "unknown",
|
|
105
|
+
"analysis_file": str(
|
|
106
|
+
(repo_folder / constants.DIRNAME_ANALYSES).relative_to(
|
|
107
|
+
repo_folder
|
|
108
|
+
)
|
|
109
|
+
),
|
|
110
|
+
},
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
print(
|
|
114
|
+
f"[convert-legacy] - {repo_dir.name} -> {repo_folder} "
|
|
115
|
+
f"({len(copied_files)} file(s) copied)"
|
|
116
|
+
)
|
|
117
|
+
converted.append(snapshot_root)
|
|
118
|
+
|
|
119
|
+
legacy_run_report = snapshot_root / constants.FILENAME_RUN_REPORT
|
|
120
|
+
if legacy_run_report.exists():
|
|
121
|
+
target_run_report = target_root / constants.FILENAME_RUN_REPORT
|
|
122
|
+
shutil.copy2(legacy_run_report, target_run_report)
|
|
123
|
+
print(f"[convert-legacy] - copied run_report.json to {target_run_report}")
|
|
124
|
+
|
|
125
|
+
print(
|
|
126
|
+
f"[convert-legacy] Finished: {len(repo_dirs)} repository directory(s) converted."
|
|
127
|
+
)
|
|
128
|
+
return converted
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def main() -> None:
|
|
132
|
+
"""CLI entry point for the conversion utility."""
|
|
133
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
134
|
+
parser.add_argument(
|
|
135
|
+
"snapshot_root", type=Path, help="Legacy snapshot root to convert"
|
|
136
|
+
)
|
|
137
|
+
parser.add_argument(
|
|
138
|
+
"--target-root",
|
|
139
|
+
type=Path,
|
|
140
|
+
default=None,
|
|
141
|
+
help="Destination root for the converted repo-centric layout",
|
|
142
|
+
)
|
|
143
|
+
args = parser.parse_args()
|
|
144
|
+
|
|
145
|
+
convert_legacy_output_tree(args.snapshot_root, target_root=args.target_root)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if __name__ == "__main__":
|
|
149
|
+
main()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""rsmetacheck-bot: RSMetaCheck bot for pushing issues with existing repository metadata."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = version("rsmetacheck-bot")
|
|
7
|
+
except PackageNotFoundError:
|
|
8
|
+
# Package is not installed
|
|
9
|
+
__version__ = "unknown"
|
|
10
|
+
|
|
11
|
+
__all__ = ["__version__"]
|