pyOpenSourceProjects 0.2.3__tar.gz → 0.3.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 (32) hide show
  1. pyopensourceprojects-0.3.0/PKG-INFO +42 -0
  2. pyopensourceprojects-0.3.0/README.md +15 -0
  3. pyopensourceprojects-0.3.0/osprojects/__init__.py +1 -0
  4. pyopensourceprojects-0.2.3/osprojects/checkos.py → pyopensourceprojects-0.3.0/osprojects/check_project.py +165 -178
  5. pyopensourceprojects-0.3.0/osprojects/checkos.py +137 -0
  6. pyopensourceprojects-0.3.0/osprojects/github_api.py +353 -0
  7. pyopensourceprojects-0.3.0/osprojects/osproject.py +580 -0
  8. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/pyproject.toml +11 -6
  9. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/tests/basetest.py +6 -0
  10. pyopensourceprojects-0.3.0/tests/test_github.py +120 -0
  11. pyopensourceprojects-0.3.0/tests/test_github_api.py +87 -0
  12. pyopensourceprojects-0.3.0/tests/test_osproject.py +85 -0
  13. pyopensourceprojects-0.2.3/PKG-INFO +0 -43
  14. pyopensourceprojects-0.2.3/README.md +0 -18
  15. pyopensourceprojects-0.2.3/osprojects/__init__.py +0 -1
  16. pyopensourceprojects-0.2.3/osprojects/osproject.py +0 -564
  17. pyopensourceprojects-0.2.3/tests/test_osproject.py +0 -193
  18. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/.github/workflows/build.yml +0 -0
  19. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/.github/workflows/upload-to-pypi.yml +0 -0
  20. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/.gitignore +0 -0
  21. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/.project +0 -0
  22. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/.pydevproject +0 -0
  23. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/LICENSE +0 -0
  24. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/mkdocs.yml +0 -0
  25. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/osprojects/editor.py +0 -0
  26. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/scripts/blackisort +0 -0
  27. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/scripts/doc +0 -0
  28. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/scripts/install +0 -0
  29. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/scripts/installAndTest +0 -0
  30. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/scripts/release +0 -0
  31. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/scripts/test +0 -0
  32. {pyopensourceprojects-0.2.3 → pyopensourceprojects-0.3.0}/tests/__init__.py +0 -0
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyOpenSourceProjects
3
+ Version: 0.3.0
4
+ Dynamic: Summary
5
+ Project-URL: Home, https://github.com/WolfgangFahl/pyOpenSourceProjects
6
+ Project-URL: Documentation, http://wiki.bitplan.com/index.php/pyOpenSourceProjects
7
+ Project-URL: Source, https://github.com/WolfgangFahl/pyOpenSourceProjects
8
+ Author-email: Wolfgang Fahl <wf@bitplan.com>
9
+ Maintainer-email: Wolfgang Fahl <wf@bitplan.com>
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ Classifier: Programming Language :: Python
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: gitpython
19
+ Requires-Dist: packaging>=24.1
20
+ Requires-Dist: py-3rdparty-mediawiki>=0.17.0
21
+ Requires-Dist: pylodstorage>=0.17.0
22
+ Requires-Dist: python-dateutil>=2.8.2
23
+ Requires-Dist: requests
24
+ Requires-Dist: tqdm>=4.66.5
25
+ Provides-Extra: test
26
+ Description-Content-Type: text/markdown
27
+
28
+ # pyOpenSourceProjects
29
+ Helper Library to organize open source Projects
30
+
31
+ | | |
32
+ | :--- | :--- |
33
+ | **GitHub** | [![Github Actions Build](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions/workflows/build.yml/badge.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions/workflows/build.yml) [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyOpenSourceProjects.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/issues) [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyOpenSourceProjects.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/issues/?q=is%3Aissue+is%3Aclosed) |
34
+ | **PyPi** | [![PyPI Status](https://img.shields.io/pypi/v/pyOpenSourceProjects.svg)](https://pypi.python.org/pypi/pyOpenSourceProjects/) [![License](https://img.shields.io/github/license/WolfgangFahl/pyOpenSourceProjects.svg)](https://www.apache.org/licenses/LICENSE-2.0) [![pypi](https://img.shields.io/pypi/pyversions/pyOpenSourceProjects)](https://pypi.org/project/pyOpenSourceProjects/) |
35
+ | **Docs** | [![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://WolfgangFahl.github.io/pyOpenSourceProjects/) |
36
+
37
+ ## Documentation
38
+ [Wiki](https://wiki.bitplan.com/index.php/PyOpenSourceProjects)
39
+
40
+ ### Authors
41
+ * [Tim Holzheim](https://www.semantic-mediawiki.org/wiki/Tim_Holzheim)
42
+ * [Wolfgang Fahl](http://www.bitplan.com/Wolfgang_Fahl)
@@ -0,0 +1,15 @@
1
+ # pyOpenSourceProjects
2
+ Helper Library to organize open source Projects
3
+
4
+ | | |
5
+ | :--- | :--- |
6
+ | **GitHub** | [![Github Actions Build](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions/workflows/build.yml/badge.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions/workflows/build.yml) [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyOpenSourceProjects.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/issues) [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyOpenSourceProjects.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/issues/?q=is%3Aissue+is%3Aclosed) |
7
+ | **PyPi** | [![PyPI Status](https://img.shields.io/pypi/v/pyOpenSourceProjects.svg)](https://pypi.python.org/pypi/pyOpenSourceProjects/) [![License](https://img.shields.io/github/license/WolfgangFahl/pyOpenSourceProjects.svg)](https://www.apache.org/licenses/LICENSE-2.0) [![pypi](https://img.shields.io/pypi/pyversions/pyOpenSourceProjects)](https://pypi.org/project/pyOpenSourceProjects/) |
8
+ | **Docs** | [![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://WolfgangFahl.github.io/pyOpenSourceProjects/) |
9
+
10
+ ## Documentation
11
+ [Wiki](https://wiki.bitplan.com/index.php/PyOpenSourceProjects)
12
+
13
+ ### Authors
14
+ * [Tim Holzheim](https://www.semantic-mediawiki.org/wiki/Tim_Holzheim)
15
+ * [Wolfgang Fahl](http://www.bitplan.com/Wolfgang_Fahl)
@@ -0,0 +1 @@
1
+ __version__ = "0.3.0"
@@ -1,22 +1,23 @@
1
- #!/usr/bin/env python
2
1
  """
3
- Created on 2024-07-30
2
+ Created on 2024-08-28
4
3
 
5
4
  @author: wf
6
5
  """
7
- import argparse
6
+
8
7
  import os
9
- from argparse import Namespace
8
+ import tomllib
10
9
  from dataclasses import dataclass
10
+ from typing import List
11
+
11
12
  from git import Repo
12
13
  from git.exc import InvalidGitRepositoryError, NoSuchPathError
13
- from typing import List
14
+ from packaging import version
15
+
16
+ from osprojects.github_api import GitHubAction
17
+
14
18
  # original at ngwidgets - use redundant local copy ...
15
19
  from osprojects.editor import Editor
16
- from osprojects.osproject import GitHub, OsProject
17
- import tomllib
18
- import traceback
19
- from packaging import version
20
+
20
21
 
21
22
  @dataclass
22
23
  class Check:
@@ -39,20 +40,22 @@ class Check:
39
40
  check = Check(ok, path, msg=path, content=content)
40
41
  return check
41
42
 
42
- class CheckOS:
43
+
44
+ class CheckProject:
43
45
  """
44
- check the open source projects
46
+ Checker for an individual open source project
45
47
  """
46
48
 
47
- def __init__(self, args: Namespace, project: OsProject):
48
- self.args = args
49
- self.verbose = args.verbose
50
- self.workspace = args.workspace
49
+ def __init__(self, parent, project, args):
50
+ self.parent = parent
51
51
  self.project = project
52
- self.project_path = os.path.join(self.workspace, project.id)
53
- self.checks = []
54
- # python 3.12 is max version
55
- self.max_python_version_minor=12
52
+ self.args = args
53
+ self.checks: List[Check] = []
54
+ self.project_path = project.folder
55
+ self.project_name = None
56
+ self.requires_python = None
57
+ self.min_python_version_minor = None
58
+ self.max_python_version_minor = 13 # python 3.13 is max version
56
59
 
57
60
  @property
58
61
  def total(self) -> int:
@@ -68,20 +71,28 @@ class CheckOS:
68
71
  failed_checks = [check for check in self.checks if not check.ok]
69
72
  return failed_checks
70
73
 
71
- def add_check(self, ok, msg:str="",path: str=None,negative:bool=False) -> Check:
74
+ def add_error(self, ex, path: str):
75
+ self.parent.handle_exception(ex)
76
+ self.add_check(False, msg=f"{str(ex)}", path=path)
77
+
78
+ def add_check(
79
+ self, ok, msg: str = "", path: str = None, negative: bool = False
80
+ ) -> Check:
72
81
  if not path:
73
82
  raise ValueError("path parameter missing")
74
- marker=""
83
+ marker = ""
75
84
  if negative:
76
- ok=not ok
77
- marker="⚠ ️"
85
+ ok = not ok
86
+ marker = "⚠ ️"
78
87
  check = Check(ok=ok, path=path, msg=f"{marker}{msg}{path}")
79
88
  self.checks.append(check)
80
89
  return check
81
90
 
82
- def add_content_check(self, content: str, needle: str, path: str, negative:bool=False) -> Check:
83
- ok=needle in content
84
- check=self.add_check(ok, msg=f"{needle} in ", path=path,negative=negative)
91
+ def add_content_check(
92
+ self, content: str, needle: str, path: str, negative: bool = False
93
+ ) -> Check:
94
+ ok = needle in content
95
+ check = self.add_check(ok, msg=f"{needle} in ", path=path, negative=negative)
85
96
  return check
86
97
 
87
98
  def add_path_check(self, path) -> Check:
@@ -111,8 +122,14 @@ class CheckOS:
111
122
  content = file_exists.content
112
123
 
113
124
  if file == "build.yml":
114
- min_python_version_minor = int(self.requires_python.split('.')[-1])
115
- self.add_check(min_python_version_minor==self.min_python_version_minor,msg=f"{min_python_version_minor} (build.yml)!={self.min_python_version_minor} (pyprojec.toml)",path=file_path)
125
+ min_python_version_minor = int(
126
+ self.requires_python.split(".")[-1]
127
+ )
128
+ self.add_check(
129
+ min_python_version_minor == self.min_python_version_minor,
130
+ msg=f"{min_python_version_minor} (build.yml)!={self.min_python_version_minor} (pyprojec.toml)",
131
+ path=file_path,
132
+ )
116
133
  python_versions = f"""python-version: [ {', '.join([f"'3.{i}'" for i in range(self.min_python_version_minor, self.max_python_version_minor+1)])} ]"""
117
134
  self.add_content_check(
118
135
  content,
@@ -124,7 +141,9 @@ class CheckOS:
124
141
  "os: [ubuntu-latest, macos-latest, windows-latest]",
125
142
  file_path,
126
143
  )
127
- self.add_content_check(content, "uses: actions/checkout@v4", file_path)
144
+ self.add_content_check(
145
+ content, "uses: actions/checkout@v4", file_path
146
+ )
128
147
  self.add_content_check(
129
148
  content,
130
149
  "uses: actions/setup-python@v5",
@@ -132,17 +151,20 @@ class CheckOS:
132
151
  )
133
152
 
134
153
  self.add_content_check(
135
- content,
136
- "sphinx",
137
- file_path,
138
- negative=True
154
+ content, "sphinx", file_path, negative=True
155
+ )
156
+ scripts_ok = (
157
+ "scripts/install" in content
158
+ and "scripts/test" in content
159
+ or "scripts/installAndTest" in content
139
160
  )
140
- scripts_ok="scripts/install" in content and "scripts/test" in content or "scripts/installAndTest" in content
141
- self.add_check(scripts_ok,"install and test", file_path)
161
+ self.add_check(scripts_ok, "install and test", file_path)
142
162
 
143
163
  elif file == "upload-to-pypi.yml":
144
164
  self.add_content_check(content, "id-token: write", file_path)
145
- self.add_content_check(content, "uses: actions/checkout@v4", file_path)
165
+ self.add_content_check(
166
+ content, "uses: actions/checkout@v4", file_path
167
+ )
146
168
  self.add_content_check(
147
169
  content,
148
170
  "uses: actions/setup-python@v5",
@@ -164,19 +186,27 @@ class CheckOS:
164
186
  file_exists = self.add_path_check(file_path)
165
187
  if file_exists.ok:
166
188
  content = file_exists.content
167
- if file=="doc":
168
- self.add_content_check(content, "sphinx", file_path, negative=True)
169
- self.add_content_check(content,"WF 2024-07-30 - updated",file_path)
170
- if file=="test":
171
- self.add_content_check(content,"WF 2024-08-03",file_path)
172
- if file=="release":
189
+ if file == "doc":
190
+ self.add_content_check(
191
+ content, "sphinx", file_path, negative=True
192
+ )
193
+ self.add_content_check(
194
+ content, "WF 2024-07-30 - updated", file_path
195
+ )
196
+ if file == "test":
197
+ self.add_content_check(content, "WF 2024-08-03", file_path)
198
+ if file == "release":
173
199
  self.add_content_check(content, "scripts/doc -d", file_path)
174
200
 
175
201
  def check_readme(self):
176
202
  readme_path = os.path.join(self.project_path, "README.md")
177
203
  readme_exists = self.add_path_check(readme_path)
178
204
  if not hasattr(self, "project_name"):
179
- self.add_check(False, "project_name from pyproject.toml needed for README.md check", self.project_path)
205
+ self.add_check(
206
+ False,
207
+ "project_name from pyproject.toml needed for README.md check",
208
+ self.project_path,
209
+ )
180
210
  return
181
211
  if readme_exists.ok:
182
212
  readme_content = readme_exists.content
@@ -186,7 +216,7 @@ class CheckOS:
186
216
  "[![PyPI Status](https://img.shields.io/pypi/v/{self.project_name}.svg)](https://pypi.python.org/pypi/{self.project_name}/)",
187
217
  "[![GitHub issues](https://img.shields.io/github/issues/{self.project.fqid}.svg)](https://github.com/{self.project.fqid}/issues)",
188
218
  "[![GitHub closed issues](https://img.shields.io/github/issues-closed/{self.project.fqid}.svg)](https://github.com/{self.project.fqid}/issues/?q=is%3Aissue+is%3Aclosed)",
189
- "[![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://{self.project.owner}.github.io/{self.project.id}/)",
219
+ "[![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://{self.project.owner}.github.io/{self.project.project_id}/)",
190
220
  "[![License](https://img.shields.io/github/license/{self.project.fqid}.svg)](https://www.apache.org/licenses/LICENSE-2.0)",
191
221
  ]
192
222
  for line in badge_lines:
@@ -196,81 +226,115 @@ class CheckOS:
196
226
  needle=formatted_line,
197
227
  path=readme_path,
198
228
  )
199
- self.add_content_check(readme_content, "readthedocs", readme_path, negative=True)
229
+ self.add_content_check(
230
+ readme_content, "readthedocs", readme_path, negative=True
231
+ )
200
232
 
201
- def check_pyproject_toml(self)->bool:
233
+ def check_pyproject_toml(self) -> bool:
202
234
  """
203
235
  pyproject.toml
204
236
  """
205
237
  toml_path = os.path.join(self.project_path, "pyproject.toml")
206
238
  toml_exists = self.add_path_check(toml_path)
207
239
  if toml_exists.ok:
208
- content=toml_exists.content
240
+ content = toml_exists.content
209
241
  toml_dict = tomllib.loads(content)
210
- project_check=self.add_check("project" in toml_dict, "[project]", toml_path)
242
+ project_check = self.add_check(
243
+ "project" in toml_dict, "[project]", toml_path
244
+ )
211
245
  if project_check.ok:
212
- self.project_name=toml_dict["project"]["name"]
213
- requires_python_check=self.add_check("requires-python" in toml_dict["project"], "requires-python", toml_path)
246
+ self.project_name = toml_dict["project"]["name"]
247
+ requires_python_check = self.add_check(
248
+ "requires-python" in toml_dict["project"],
249
+ "requires-python",
250
+ toml_path,
251
+ )
214
252
  if requires_python_check.ok:
215
253
  self.requires_python = toml_dict["project"]["requires-python"]
216
- min_python_version = version.parse(self.requires_python.replace(">=", ""))
217
- min_version_needed="3.9"
218
- version_ok=min_python_version >= version.parse(min_version_needed)
219
- self.add_check(version_ok, f"requires-python>={min_version_needed}", toml_path)
220
- self.min_python_version_minor=int(str(min_python_version).split('.')[-1])
221
- for minor_version in range(self.min_python_version_minor, self.max_python_version_minor+1):
222
- needle=f"Programming Language :: Python :: 3.{minor_version}"
254
+ min_python_version = version.parse(
255
+ self.requires_python.replace(">=", "")
256
+ )
257
+ min_version_needed = "3.9"
258
+ version_ok = min_python_version >= version.parse(min_version_needed)
259
+ self.add_check(
260
+ version_ok, f"requires-python>={min_version_needed}", toml_path
261
+ )
262
+ self.min_python_version_minor = int(
263
+ str(min_python_version).split(".")[-1]
264
+ )
265
+ for minor_version in range(
266
+ self.min_python_version_minor, self.max_python_version_minor + 1
267
+ ):
268
+ needle = f"Programming Language :: Python :: 3.{minor_version}"
223
269
  self.add_content_check(content, needle, toml_path)
224
270
  self.add_content_check(content, "hatchling", toml_path)
225
- self.add_content_check(content,"[tool.hatch.build.targets.wheel.sources]",toml_path)
271
+ self.add_content_check(
272
+ content, "[tool.hatch.build.targets.wheel.sources]", toml_path
273
+ )
226
274
  return toml_exists.ok
227
275
 
228
- def check_git(self):
276
+ def check_git(self) -> bool:
229
277
  """
230
- Check git repository information using gitpython
278
+ Check git repository information using GitHub class
279
+
280
+ Returns:
281
+ bool: True if git owner matches project owner and the repo is not a fork
231
282
  """
283
+ owner_match = False
284
+ is_fork = False
232
285
  try:
233
- repo = Repo(self.project_path)
234
-
235
- # Check if it's actually a git repository
236
- if not repo.bare:
237
- self.add_check(True, "Is a git repository", self.project_path)
238
-
239
- # Get the remote URL
240
- try:
241
- remote_url = repo.remotes.origin.url
242
- self.add_check(True, "Has remote origin", self.project_path)
243
-
244
- # Extract owner and repository name from the URL
245
- parts = remote_url.split('/')
246
- git_owner = parts[-2]
247
- git_repo = parts[-1].replace('.git', '')
248
-
249
- # Compare with the project information we have
250
- owner_match = git_owner.lower() == self.project.owner.lower()
251
- self.add_check(owner_match, f"Git owner ({git_owner}) matches project owner ({self.project.owner})", self.project_path)
252
-
253
- repo_match = git_repo.lower() == self.project.id.lower()
254
- self.add_check(repo_match, f"Git repo name ({git_repo}) matches project id ({self.project.id})", self.project_path)
286
+ local_owner = self.project.owner
287
+ remote_owner = self.project.repo_info["owner"]["login"]
288
+ is_fork = self.project.repo_info["fork"]
289
+ owner_match = local_owner.lower() == remote_owner.lower() and not is_fork
290
+ self.add_check(
291
+ owner_match,
292
+ f"Git owner ({remote_owner}) matches project owner ({local_owner}) and is not a fork",
293
+ self.project_path,
294
+ )
255
295
 
256
- except AttributeError:
257
- self.add_check(False, "No remote origin found", self.project_path)
296
+ local_project_id = self.project.project_id
297
+ remote_repo_name = self.project.repo_info["name"]
298
+ repo_match = local_project_id.lower() == remote_repo_name.lower()
299
+ self.add_check(
300
+ repo_match,
301
+ f"Git repo name ({remote_repo_name}) matches project id ({local_project_id})",
302
+ self.project_path,
303
+ )
258
304
 
259
- # Check if there are uncommitted changes
260
- if repo.is_dirty():
261
- self.add_check(False, "Repository has uncommitted changes", self.project_path)
262
- else:
263
- self.add_check(True, "Repository is clean", self.project_path)
305
+ # Check if there are uncommitted changes (this still requires local git access)
306
+ local_repo = Repo(self.project_path)
307
+ self.add_check(
308
+ not local_repo.is_dirty(), "uncomitted changes for", self.project_path
309
+ )
264
310
 
311
+ # Check latest GitHub Actions workflow run
312
+ latest_run = GitHubAction.get_latest_workflow_run(self.project)
313
+ if latest_run:
314
+ self.add_check(
315
+ latest_run["conclusion"] == "success",
316
+ f"Latest GitHub Actions run: {latest_run['conclusion']}",
317
+ latest_run["html_url"],
318
+ )
265
319
  else:
266
- self.add_check(False, "Not a valid git repository (bare repository)", self.project_path)
320
+ self.add_check(
321
+ False,
322
+ "No GitHub Actions runs found",
323
+ self.project.repo.ticketUrl(),
324
+ )
267
325
 
268
326
  except InvalidGitRepositoryError:
269
327
  self.add_check(False, "Not a valid git repository", self.project_path)
270
328
  except NoSuchPathError:
271
- self.add_check(False, "Git repository path does not exist", self.project_path)
329
+ self.add_check(
330
+ False, "Git repository path does not exist", self.project_path
331
+ )
332
+ except Exception as ex:
333
+ self.add_error(ex, self.project_path)
272
334
 
273
- def check(self, title:str):
335
+ return owner_match and not is_fork
336
+
337
+ def check(self, title: str):
274
338
  """
275
339
  Check the given project and print results
276
340
  """
@@ -283,7 +347,11 @@ class CheckOS:
283
347
 
284
348
  # ok_count=len(ok_checks)
285
349
  failed_count = len(self.failed_checks)
286
- summary = f"❌ {failed_count:2}/{self.total:2}" if failed_count > 0 else f"✅ {self.total:2}/{self.total:2}"
350
+ summary = (
351
+ f"❌ {failed_count:2}/{self.total:2}"
352
+ if failed_count > 0
353
+ else f"✅ {self.total:2}/{self.total:2}"
354
+ )
287
355
  print(f"{title}{summary}:{self.project}→{self.project.url}")
288
356
  if failed_count > 0:
289
357
  # Sort checks by path
@@ -301,97 +369,16 @@ class CheckOS:
301
369
  path_failed = sum(1 for c in path_checks if not c.ok)
302
370
  if path_failed > 0 or self.args.debug:
303
371
  print(f"❌ {path}: {path_failed}")
304
- i=0
372
+ i = 0
305
373
  for check in path_checks:
306
- show=not check.ok or self.args.debug
374
+ show = not check.ok or self.args.debug
307
375
  if show:
308
- i+=1
376
+ i += 1
309
377
  print(f" {i:3}{check.marker}:{check.msg}")
310
378
 
311
379
  if self.args.editor and path_failed > 0:
312
380
  if os.path.isfile(path):
313
381
  # @TODO Make editor configurable
314
- Editor.open(path,default_editor_cmd="/usr/local/bin/atom")
382
+ Editor.open(path, default_editor_cmd="/usr/local/bin/atom")
315
383
  else:
316
384
  Editor.open_filepath(path)
317
-
318
-
319
- def main(_argv=None):
320
- """
321
- main command line entry point
322
- """
323
- parser = argparse.ArgumentParser(description="Check open source projects")
324
- parser.add_argument(
325
- "-d",
326
- "--debug",
327
- action="store_true",
328
- help="add debug output",
329
- )
330
- parser.add_argument(
331
- "-e",
332
- "--editor",
333
- action="store_true",
334
- help="open default editor on failed files",
335
- )
336
- parser.add_argument(
337
- "-o", "--owner", help="project owner or organization", required=True
338
- )
339
- parser.add_argument("-p", "--project", help="name of the project")
340
- parser.add_argument("-l", "--language", help="filter projects by language")
341
- parser.add_argument(
342
- "--local", action="store_true", help="check only locally available projects"
343
- )
344
- parser.add_argument(
345
- "-v", "--verbose", action="store_true", help="show verbose output"
346
- )
347
- parser.add_argument(
348
- "-ws",
349
- "--workspace",
350
- help="(Eclipse) workspace directory",
351
- default=os.path.expanduser("~/py-workspace"),
352
- )
353
-
354
- args = parser.parse_args(args=_argv)
355
-
356
- try:
357
- github = GitHub()
358
- if args.project:
359
- # Check specific project
360
- projects = github.list_projects_as_os_projects(
361
- args.owner, project_name=args.project
362
- )
363
- else:
364
- # Check all projects
365
- projects = github.list_projects_as_os_projects(args.owner)
366
-
367
- if args.language:
368
- projects = [p for p in projects if p.language == args.language]
369
-
370
- if args.local:
371
- local_projects = []
372
- for project in projects:
373
- checker = CheckOS(args=args, project=project)
374
- if checker.check_local().ok:
375
- local_projects.append(project)
376
- projects = local_projects
377
-
378
- # filter for git ownership
379
- filtered_projects = []
380
- for project in projects:
381
- checker = CheckOS(args=args, project=project)
382
- checker.check_git()
383
- git_owner_check = next((check for check in checker.checks if "Git owner" in check.msg), None)
384
- if git_owner_check and git_owner_check.ok:
385
- filtered_projects.append(project)
386
- projects = filtered_projects
387
-
388
- for i,project in enumerate(projects):
389
- checker = CheckOS(args=args, project=project)
390
- checker.check(f"{i+1:3}:")
391
- except Exception as ex:
392
- if args.debug:
393
- print(traceback.format_exc())
394
- raise ex
395
-
396
- if __name__ == "__main__":
397
- main()