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.
Files changed (59) hide show
  1. rsmetacheck_bot-0.6.0/LICENSE +21 -0
  2. rsmetacheck_bot-0.6.0/PKG-INFO +187 -0
  3. rsmetacheck_bot-0.6.0/README.md +148 -0
  4. rsmetacheck_bot-0.6.0/pyproject.toml +123 -0
  5. rsmetacheck_bot-0.6.0/setup.cfg +4 -0
  6. rsmetacheck_bot-0.6.0/src/conversion/__init__.py +5 -0
  7. rsmetacheck_bot-0.6.0/src/conversion/convert_legacy_outputs.py +149 -0
  8. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/__init__.py +11 -0
  9. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/analysis_runtime.py +818 -0
  10. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/check_parsing.py +83 -0
  11. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/codemeta_runtime.py +131 -0
  12. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/commit_lookup.py +150 -0
  13. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/config/__init__.py +1 -0
  14. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/config/config_utils.py +99 -0
  15. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/config/rsmetacheck_non_failing.toml +2 -0
  16. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/config/schemas.py +186 -0
  17. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/constants.py +286 -0
  18. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/convert_legacy.py +26 -0
  19. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/fetch.py +361 -0
  20. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/github_api.py +204 -0
  21. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/gitlab_api.py +253 -0
  22. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/history.py +102 -0
  23. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/incremental.py +124 -0
  24. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/main.py +41 -0
  25. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/pipeline.py +406 -0
  26. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/pitfalls.py +161 -0
  27. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/platform_api.py +131 -0
  28. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/publish.py +733 -0
  29. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/query_history.py +41 -0
  30. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/repo_state.py +224 -0
  31. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/report_summary.py +141 -0
  32. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/reporting.py +396 -0
  33. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/rsmetacheck_wrapper.py +67 -0
  34. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/token_resolver.py +33 -0
  35. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/utils.py +85 -0
  36. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot/verify_tokens.py +138 -0
  37. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot.egg-info/PKG-INFO +187 -0
  38. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot.egg-info/SOURCES.txt +57 -0
  39. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot.egg-info/dependency_links.txt +1 -0
  40. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot.egg-info/entry_points.txt +2 -0
  41. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot.egg-info/requires.txt +26 -0
  42. rsmetacheck_bot-0.6.0/src/rsmetacheck_bot.egg-info/top_level.txt +2 -0
  43. rsmetacheck_bot-0.6.0/tests/test_check_parsing.py +147 -0
  44. rsmetacheck_bot-0.6.0/tests/test_codemeta_runtime.py +92 -0
  45. rsmetacheck_bot-0.6.0/tests/test_config_schemas.py +278 -0
  46. rsmetacheck_bot-0.6.0/tests/test_config_utils.py +63 -0
  47. rsmetacheck_bot-0.6.0/tests/test_conversion.py +164 -0
  48. rsmetacheck_bot-0.6.0/tests/test_fetch.py +249 -0
  49. rsmetacheck_bot-0.6.0/tests/test_gitlab_api.py +21 -0
  50. rsmetacheck_bot-0.6.0/tests/test_gitlab_integration.py +18 -0
  51. rsmetacheck_bot-0.6.0/tests/test_history.py +107 -0
  52. rsmetacheck_bot-0.6.0/tests/test_incremental.py +131 -0
  53. rsmetacheck_bot-0.6.0/tests/test_init.py +16 -0
  54. rsmetacheck_bot-0.6.0/tests/test_legacy_report_parsing.py +22 -0
  55. rsmetacheck_bot-0.6.0/tests/test_pipeline.py +1358 -0
  56. rsmetacheck_bot-0.6.0/tests/test_pitfalls.py +300 -0
  57. rsmetacheck_bot-0.6.0/tests/test_publish.py +874 -0
  58. rsmetacheck_bot-0.6.0/tests/test_report_summary.py +75 -0
  59. 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
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19468976.svg)](https://doi.org/10.5281/zenodo.19468976)
43
+ [![SWH](https://archive.softwareheritage.org/badge/swh:1:dir:9f3d474c040158cc675e8db135767e0d2d3fba7b/)](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: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
45
+ [![Version](https://img.shields.io/badge/version-0.6.0-blue.svg)](https://github.com/SoftwareUnderstanding/rsmetacheck-bot/releases)
46
+ [![Python 3.11--3.12](https://img.shields.io/badge/python-3.11--3.12-blue.svg)](https://www.python.org/downloads/)
47
+ [![CI (build)](https://github.com/SoftwareUnderstanding/rsmetacheck-bot/actions/workflows/ci.yml/badge.svg)](https://github.com/SoftwareUnderstanding/rsmetacheck-bot/actions/workflows/ci.yml)
48
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
49
+ ![coverage](coverage.svg)
50
+ ![interrogate](interrogate_badge.svg)
51
+ [![rsmetacheck Validation](https://github.com/SoftwareUnderstanding/sw-metadata-bot/actions/workflows/rsmetacheck.yml/badge.svg)](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
+ [![Subscribe](https://img.shields.io/badge/subscribe%20to%20the%20bot-blue?style=for-the-badge)](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
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19468976.svg)](https://doi.org/10.5281/zenodo.19468976)
4
+ [![SWH](https://archive.softwareheritage.org/badge/swh:1:dir:9f3d474c040158cc675e8db135767e0d2d3fba7b/)](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: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+ [![Version](https://img.shields.io/badge/version-0.6.0-blue.svg)](https://github.com/SoftwareUnderstanding/rsmetacheck-bot/releases)
7
+ [![Python 3.11--3.12](https://img.shields.io/badge/python-3.11--3.12-blue.svg)](https://www.python.org/downloads/)
8
+ [![CI (build)](https://github.com/SoftwareUnderstanding/rsmetacheck-bot/actions/workflows/ci.yml/badge.svg)](https://github.com/SoftwareUnderstanding/rsmetacheck-bot/actions/workflows/ci.yml)
9
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
10
+ ![coverage](coverage.svg)
11
+ ![interrogate](interrogate_badge.svg)
12
+ [![rsmetacheck Validation](https://github.com/SoftwareUnderstanding/sw-metadata-bot/actions/workflows/rsmetacheck.yml/badge.svg)](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
+ [![Subscribe](https://img.shields.io/badge/subscribe%20to%20the%20bot-blue?style=for-the-badge)](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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ """Public conversion package for legacy snapshot migration."""
2
+
3
+ from .convert_legacy_outputs import convert_legacy_output_tree, main
4
+
5
+ __all__ = ["convert_legacy_output_tree", "main"]
@@ -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__"]