cgitize 6.0.3__tar.gz → 6.0.4__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.
- {cgitize-6.0.3 → cgitize-6.0.4}/.github/workflows/ci.yml +14 -0
- cgitize-6.0.4/.github/workflows/maintenance.yml +37 -0
- {cgitize-6.0.3/cgitize.egg-info → cgitize-6.0.4}/PKG-INFO +1 -1
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize/bitbucket.py +3 -1
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize/cgit.py +23 -5
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize/config.py +12 -4
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize/git.py +18 -7
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize/main.py +25 -14
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize/repo.py +47 -13
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize/utils.py +5 -3
- {cgitize-6.0.3 → cgitize-6.0.4/cgitize.egg-info}/PKG-INFO +1 -1
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize.egg-info/SOURCES.txt +1 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/frontend/Dockerfile +1 -1
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/get_output_dir.py +1 -2
- {cgitize-6.0.3 → cgitize-6.0.4}/pyproject.toml +3 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/requirements.txt +7 -7
- {cgitize-6.0.3 → cgitize-6.0.4}/test/unit/test_bitbucket.py +24 -8
- {cgitize-6.0.3 → cgitize-6.0.4}/test/unit/test_gitlab.py +8 -4
- {cgitize-6.0.3 → cgitize-6.0.4}/.dockerignore +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/.gitattributes +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/.github/dependabot.yml +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/.gitignore +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/.pylintrc +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/.python-version +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/DEVELOPMENT.md +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/LICENSE.txt +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/Makefile +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/README.md +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize/__init__.py +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize/github.py +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize/gitlab.py +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize/version.py +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize.egg-info/dependency_links.txt +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize.egg-info/entry_points.txt +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize.egg-info/requires.txt +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/cgitize.egg-info/top_level.txt +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/Dockerfile +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/README.md +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/cgitize.sh +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/frontend/cmd.sh +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/frontend/etc/cgit/common +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/frontend/etc/cgitrc +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/frontend/etc/nginx/conf.d/default.conf +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/frontend/usr/lib/cgit/filters/about-formatting.sh +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/frontend/usr/lib/cgit/filters/html-converters/.gitattributes +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/frontend/usr/lib/cgit/filters/html-converters/md2html +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker/in_cron.sh +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docker-compose.yml +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docs/api.md +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/docs/auth.md +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/examples/cgitize.toml +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/examples/docker/cgitize.toml +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/prelude.mk +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/setup.cfg +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/__init__.py +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/integration/docker/.dockerignore +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/integration/docker/cgitize/.gitignore +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/integration/docker/cgitize/etc/cgitize.toml +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/integration/docker/docker-compose.yml +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/integration/docker/git_server/Dockerfile +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/integration/docker/git_server/setup_repo.sh +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/integration/docker/test.sh +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/integration/example/test.sh +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/integration/local/test.sh +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/set_credentials.sh +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/unit/__init__.py +0 -0
- {cgitize-6.0.3 → cgitize-6.0.4}/test/unit/test_github.py +0 -0
|
@@ -7,7 +7,21 @@ on:
|
|
|
7
7
|
workflow_dispatch:
|
|
8
8
|
|
|
9
9
|
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
name: Linting
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout
|
|
15
|
+
uses: actions/checkout@v6
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v6
|
|
18
|
+
with:
|
|
19
|
+
python-version: '3.x'
|
|
20
|
+
- name: Run black
|
|
21
|
+
uses: psf/black@stable
|
|
22
|
+
|
|
10
23
|
test_local:
|
|
24
|
+
needs: [lint]
|
|
11
25
|
runs-on: ubuntu-latest
|
|
12
26
|
strategy:
|
|
13
27
|
matrix:
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Maintenance
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
# Weekly, at 02:30 UTC on Friday (somewhat randomly chosen).
|
|
6
|
+
- cron: '30 02 * * 5'
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
maintenance:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
name: Maintenance
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout
|
|
15
|
+
uses: actions/checkout@v6
|
|
16
|
+
with:
|
|
17
|
+
# I think that by default, pushes don't trigger other workflows, and
|
|
18
|
+
# you need a separate token for that: https://github.com/orgs/community/discussions/37103
|
|
19
|
+
#
|
|
20
|
+
# Re-using the Jekyll projects token here BTW:
|
|
21
|
+
token: '${{ secrets.GH_TOKEN }}'
|
|
22
|
+
- name: Set up ssh-agent
|
|
23
|
+
uses: webfactory/ssh-agent@v0.10.0
|
|
24
|
+
with:
|
|
25
|
+
ssh-private-key: '${{ secrets.SSH_KEY }}'
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
uses: actions/setup-python@v6
|
|
28
|
+
with:
|
|
29
|
+
python-version-file: .python-version
|
|
30
|
+
- name: Run maintenance
|
|
31
|
+
id: maintenance
|
|
32
|
+
run: |
|
|
33
|
+
# See https://github.com/actions/checkout#push-a-commit-using-the-built-in-token
|
|
34
|
+
git config user.name "github-actions[bot]"
|
|
35
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
36
|
+
|
|
37
|
+
make maintenance
|
|
@@ -19,7 +19,9 @@ class Bitbucket:
|
|
|
19
19
|
try:
|
|
20
20
|
parts = repo.id.split('/')
|
|
21
21
|
if len(parts) != 2:
|
|
22
|
-
raise ValueError(
|
|
22
|
+
raise ValueError(
|
|
23
|
+
f'repository ID must be in the USER/NAME format: {repo.id}'
|
|
24
|
+
)
|
|
23
25
|
user, name = parts
|
|
24
26
|
return self._impl.repositories.get(user, name)
|
|
25
27
|
except HTTPError:
|
|
@@ -70,9 +70,17 @@ class AgeFile:
|
|
|
70
70
|
# Except I think that the committer date, not author date better
|
|
71
71
|
# represents activity.
|
|
72
72
|
with chdir(repo_dir):
|
|
73
|
-
success, output = Git.capture(
|
|
73
|
+
success, output = Git.capture(
|
|
74
|
+
'for-each-ref',
|
|
75
|
+
'--sort=-committerdate',
|
|
76
|
+
'--count=1',
|
|
77
|
+
'--format=%(committerdate:iso8601)',
|
|
78
|
+
)
|
|
74
79
|
if not success:
|
|
75
|
-
logging.error(
|
|
80
|
+
logging.error(
|
|
81
|
+
"Couldn't get the timestamp of the newest commit in repository: %s",
|
|
82
|
+
repo_dir,
|
|
83
|
+
)
|
|
76
84
|
return None
|
|
77
85
|
return output
|
|
78
86
|
|
|
@@ -118,7 +126,10 @@ class CGitRepositories:
|
|
|
118
126
|
success, output = Git.capture('rev-parse', '--is-inside-work-tree')
|
|
119
127
|
if not success:
|
|
120
128
|
# Overwrite the existing directory if it's not a Git repository.
|
|
121
|
-
logging.warning(
|
|
129
|
+
logging.warning(
|
|
130
|
+
'Local directory is not a repository, going to overwrite it: %s',
|
|
131
|
+
repo_dir,
|
|
132
|
+
)
|
|
122
133
|
return self._mirror(repo)
|
|
123
134
|
|
|
124
135
|
success, output = Git.capture('config', '--get', 'remote.origin.url')
|
|
@@ -126,14 +137,21 @@ class CGitRepositories:
|
|
|
126
137
|
# Every repository managed by this script should have the
|
|
127
138
|
# 'origin' remote. If it doesn't, it's trash. Overwrite the
|
|
128
139
|
# existing directory, mirroring the repository in it.
|
|
129
|
-
logging.warning(
|
|
140
|
+
logging.warning(
|
|
141
|
+
"Local repository doesn't have remote 'origin', going to overwrite it: %s",
|
|
142
|
+
repo_dir,
|
|
143
|
+
)
|
|
130
144
|
return self._mirror(repo)
|
|
131
145
|
|
|
132
146
|
if f'{repo.clone_url}\n' != output:
|
|
133
147
|
# Jeez, there's a proper local repository in the target
|
|
134
148
|
# directory already with a different upstream; something's
|
|
135
149
|
# wrong, fix it manually.
|
|
136
|
-
logging.warning(
|
|
150
|
+
logging.warning(
|
|
151
|
+
"Existing repository '%s' doesn't match the specified clone URL: %s",
|
|
152
|
+
repo.name,
|
|
153
|
+
repo.clone_url,
|
|
154
|
+
)
|
|
137
155
|
if self.force:
|
|
138
156
|
# Unless --force was specified, in which case we overwrite
|
|
139
157
|
# the repository.
|
|
@@ -41,7 +41,9 @@ class MainSection(Section):
|
|
|
41
41
|
|
|
42
42
|
@property
|
|
43
43
|
def output_dir(self):
|
|
44
|
-
return self._get_config_path(
|
|
44
|
+
return self._get_config_path(
|
|
45
|
+
'output_dir', default=MainSection.DEFAULT_OUTPUT_DIR
|
|
46
|
+
)
|
|
45
47
|
|
|
46
48
|
@property
|
|
47
49
|
def error_header_path(self):
|
|
@@ -126,7 +128,9 @@ class GitHubSection(ServiceSection):
|
|
|
126
128
|
username = self.username
|
|
127
129
|
token = self.token
|
|
128
130
|
if (username is None) != (token is None):
|
|
129
|
-
raise RuntimeError(
|
|
131
|
+
raise RuntimeError(
|
|
132
|
+
'please set either both the GitHub username & token, or neither'
|
|
133
|
+
)
|
|
130
134
|
return GitHub(username, token)
|
|
131
135
|
|
|
132
136
|
|
|
@@ -153,7 +157,9 @@ class BitbucketSection(ServiceSection):
|
|
|
153
157
|
username = self.username
|
|
154
158
|
token = self.token
|
|
155
159
|
if (username is None) != (token is None):
|
|
156
|
-
raise RuntimeError(
|
|
160
|
+
raise RuntimeError(
|
|
161
|
+
'please set either both the Bitbucket username & token, or neither'
|
|
162
|
+
)
|
|
157
163
|
return Bitbucket(username, token)
|
|
158
164
|
|
|
159
165
|
|
|
@@ -174,7 +180,9 @@ class GitLabSection(ServiceSection):
|
|
|
174
180
|
username = self.username
|
|
175
181
|
token = self.token
|
|
176
182
|
if (username is None) != (token is None):
|
|
177
|
-
raise RuntimeError(
|
|
183
|
+
raise RuntimeError(
|
|
184
|
+
'please set either both the GitLab username & token, or neither'
|
|
185
|
+
)
|
|
178
186
|
return GitLab(token)
|
|
179
187
|
|
|
180
188
|
|
|
@@ -8,9 +8,10 @@ import os
|
|
|
8
8
|
|
|
9
9
|
from cgitize import utils
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
GIT_ENV = os.environ.copy()
|
|
13
|
-
GIT_ENV['GIT_SSH_COMMAND'] =
|
|
12
|
+
GIT_ENV['GIT_SSH_COMMAND'] = (
|
|
13
|
+
'ssh -oBatchMode=yes -oLogLevel=QUIET -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null'
|
|
14
|
+
)
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class Config:
|
|
@@ -55,7 +56,9 @@ class Config:
|
|
|
55
56
|
for c in name:
|
|
56
57
|
if c.isalnum() or c == '-' or c == '.':
|
|
57
58
|
continue
|
|
58
|
-
raise RuntimeError(
|
|
59
|
+
raise RuntimeError(
|
|
60
|
+
f'section names must only contain alphanumeric characters, . or -: {name}'
|
|
61
|
+
)
|
|
59
62
|
|
|
60
63
|
@staticmethod
|
|
61
64
|
def format_name(name):
|
|
@@ -89,7 +92,9 @@ class Config:
|
|
|
89
92
|
return f'"{name}"'
|
|
90
93
|
|
|
91
94
|
def format(self):
|
|
92
|
-
result =
|
|
95
|
+
result = (
|
|
96
|
+
f'[{Config.Section.format_name(self.section)} {self.format_name()}]\n'
|
|
97
|
+
)
|
|
93
98
|
result += ''.join((var.format() for var in self.variables))
|
|
94
99
|
return result
|
|
95
100
|
|
|
@@ -107,9 +112,13 @@ class Config:
|
|
|
107
112
|
for c in name:
|
|
108
113
|
if c.isalnum() or c == '-':
|
|
109
114
|
continue
|
|
110
|
-
raise RuntimeError(
|
|
115
|
+
raise RuntimeError(
|
|
116
|
+
f'variable name can only contain alphanumeric characters or -: {name}'
|
|
117
|
+
)
|
|
111
118
|
if not name[0].isalnum():
|
|
112
|
-
raise RuntimeError(
|
|
119
|
+
raise RuntimeError(
|
|
120
|
+
f'variable name must start with an alphanumeric character: {name}'
|
|
121
|
+
)
|
|
113
122
|
|
|
114
123
|
@staticmethod
|
|
115
124
|
def validate_value(value):
|
|
@@ -161,7 +170,9 @@ class Git:
|
|
|
161
170
|
with utils.protected_file(config.path):
|
|
162
171
|
with config.backup() as old_contents:
|
|
163
172
|
variables = [Config.Variable('insteadOf', repo.clone_url)]
|
|
164
|
-
subsection = Config.Subsection(
|
|
173
|
+
subsection = Config.Subsection(
|
|
174
|
+
'url', repo.clone_url_with_auth, variables
|
|
175
|
+
)
|
|
165
176
|
new_contents = f'{old_contents}\n{subsection.format()}'
|
|
166
177
|
config.write(new_contents)
|
|
167
178
|
yield
|
|
@@ -17,18 +17,25 @@ def parse_args(argv=None):
|
|
|
17
17
|
if argv is None:
|
|
18
18
|
argv = sys.argv[1:]
|
|
19
19
|
parser = ArgumentParser()
|
|
20
|
-
parser.add_argument(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
parser.add_argument(
|
|
31
|
-
|
|
20
|
+
parser.add_argument(
|
|
21
|
+
'--config',
|
|
22
|
+
'-c',
|
|
23
|
+
metavar='PATH',
|
|
24
|
+
default=Config.DEFAULT_PATH,
|
|
25
|
+
help='config file path',
|
|
26
|
+
)
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
'--repo', metavar='REPO_ID', nargs='*', dest='repos', help='repos to pull'
|
|
29
|
+
)
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
'--force', '-f', action='store_true', help='overwrite existing repositories'
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
'--verbose', '-v', action='store_true', help='verbose log output'
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
'--version', '-V', action='version', version=f'%(prog)s {__version__}'
|
|
38
|
+
)
|
|
32
39
|
return parser.parse_args(argv)
|
|
33
40
|
|
|
34
41
|
|
|
@@ -36,7 +43,9 @@ def setup_error_header_file(error_header_path, success, error=None):
|
|
|
36
43
|
with open(error_header_path, 'w') as file:
|
|
37
44
|
contents = ''
|
|
38
45
|
if not success:
|
|
39
|
-
contents =
|
|
46
|
+
contents = (
|
|
47
|
+
'''<p style="text-align: center; color: red; font-weight: bold;">'''
|
|
48
|
+
)
|
|
40
49
|
contents += '''Some repositories couldn't be updated, please check application logs for details.'''
|
|
41
50
|
if error is not None:
|
|
42
51
|
contents += f'''<br>{type(error).__name__}: {error}'''
|
|
@@ -53,7 +62,9 @@ def main(argv=None):
|
|
|
53
62
|
|
|
54
63
|
try:
|
|
55
64
|
cgit_server = CGitServer(config.main.clone_url)
|
|
56
|
-
output = CGitRepositories(
|
|
65
|
+
output = CGitRepositories(
|
|
66
|
+
config.main.output_dir, cgit_server, force=args.force
|
|
67
|
+
)
|
|
57
68
|
for repo in config.parse_repositories():
|
|
58
69
|
if args.repos is None or repo.name in args.repos:
|
|
59
70
|
success = success and output.update(repo)
|
|
@@ -21,8 +21,9 @@ class Repo:
|
|
|
21
21
|
raise ValueError("every repository must have 'clone_url'")
|
|
22
22
|
clone_url = src['clone_url']
|
|
23
23
|
subdir = src.get('dir')
|
|
24
|
-
return Repo(
|
|
25
|
-
|
|
24
|
+
return Repo(
|
|
25
|
+
name, clone_url, owner=owner, desc=desc, homepage=homepage, subdir=subdir
|
|
26
|
+
)
|
|
26
27
|
|
|
27
28
|
@staticmethod
|
|
28
29
|
def from_github(src, config, subdir=None):
|
|
@@ -36,8 +37,15 @@ class Repo:
|
|
|
36
37
|
clone_url = ssh_url if config.main.clone_via_ssh else https_url
|
|
37
38
|
url_auth = None if config.main.clone_via_ssh else config.github.url_auth
|
|
38
39
|
|
|
39
|
-
return Repo(
|
|
40
|
-
|
|
40
|
+
return Repo(
|
|
41
|
+
name,
|
|
42
|
+
clone_url,
|
|
43
|
+
owner=owner,
|
|
44
|
+
desc=desc,
|
|
45
|
+
homepage=homepage,
|
|
46
|
+
url_auth=url_auth,
|
|
47
|
+
subdir=subdir,
|
|
48
|
+
)
|
|
41
49
|
|
|
42
50
|
@staticmethod
|
|
43
51
|
def from_bitbucket(src, config, subdir=None):
|
|
@@ -46,13 +54,17 @@ class Repo:
|
|
|
46
54
|
homepage = src.get_link('html')
|
|
47
55
|
owner = src.data['owner']['display_name']
|
|
48
56
|
|
|
49
|
-
https_urls = [
|
|
57
|
+
https_urls = [
|
|
58
|
+
link for link in src.data['links']['clone'] if link['name'] == 'https'
|
|
59
|
+
]
|
|
50
60
|
if len(https_urls) != 1:
|
|
51
61
|
raise RuntimeError(f"no https:// clone URL for repository '{name}'?!")
|
|
52
62
|
# Bitbucket leaves the username in the URL... Sigh.
|
|
53
63
|
https_url = url_remove_auth(https_urls[0]['href'])
|
|
54
64
|
|
|
55
|
-
ssh_urls = [
|
|
65
|
+
ssh_urls = [
|
|
66
|
+
link for link in src.data['links']['clone'] if link['name'] == 'ssh'
|
|
67
|
+
]
|
|
56
68
|
if len(ssh_urls) != 1:
|
|
57
69
|
raise RuntimeError(f"no ssh:// clone URL for repository '{name}'?!")
|
|
58
70
|
ssh_url = ssh_urls[0]['href']
|
|
@@ -60,8 +72,15 @@ class Repo:
|
|
|
60
72
|
clone_url = ssh_url if config.main.clone_via_ssh else https_url
|
|
61
73
|
url_auth = None if config.main.clone_via_ssh else config.bitbucket.url_auth
|
|
62
74
|
|
|
63
|
-
return Repo(
|
|
64
|
-
|
|
75
|
+
return Repo(
|
|
76
|
+
name,
|
|
77
|
+
clone_url,
|
|
78
|
+
owner=owner,
|
|
79
|
+
desc=desc,
|
|
80
|
+
homepage=homepage,
|
|
81
|
+
url_auth=url_auth,
|
|
82
|
+
subdir=subdir,
|
|
83
|
+
)
|
|
65
84
|
|
|
66
85
|
@staticmethod
|
|
67
86
|
def from_gitlab(src, config, subdir=None):
|
|
@@ -75,11 +94,26 @@ class Repo:
|
|
|
75
94
|
clone_url = ssh_url if config.main.clone_via_ssh else https_url
|
|
76
95
|
url_auth = None if config.main.clone_via_ssh else config.gitlab.url_auth
|
|
77
96
|
|
|
78
|
-
return Repo(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
97
|
+
return Repo(
|
|
98
|
+
name,
|
|
99
|
+
clone_url,
|
|
100
|
+
owner=owner,
|
|
101
|
+
desc=desc,
|
|
102
|
+
homepage=homepage,
|
|
103
|
+
url_auth=url_auth,
|
|
104
|
+
subdir=subdir,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def __init__(
|
|
108
|
+
self,
|
|
109
|
+
name,
|
|
110
|
+
clone_url,
|
|
111
|
+
owner=None,
|
|
112
|
+
desc=None,
|
|
113
|
+
homepage=None,
|
|
114
|
+
url_auth=None,
|
|
115
|
+
subdir=None,
|
|
116
|
+
):
|
|
83
117
|
self._name = name
|
|
84
118
|
self._desc = desc
|
|
85
119
|
self._homepage = homepage
|
|
@@ -21,7 +21,8 @@ def setup_logging(verbose=False):
|
|
|
21
21
|
format='%(asctime)s | %(levelname)s | %(message)s',
|
|
22
22
|
# Log to stdout, because that's where subprocess's output goes (so that
|
|
23
23
|
# the don't get interleaved).
|
|
24
|
-
stream=sys.stdout
|
|
24
|
+
stream=sys.stdout,
|
|
25
|
+
)
|
|
25
26
|
try:
|
|
26
27
|
yield
|
|
27
28
|
except Exception as e:
|
|
@@ -37,8 +38,9 @@ def run(*args, capture_output=False, **kwargs):
|
|
|
37
38
|
stderr = subprocess.STDOUT
|
|
38
39
|
|
|
39
40
|
logging.debug('%s', args)
|
|
40
|
-
result = subprocess.run(
|
|
41
|
-
|
|
41
|
+
result = subprocess.run(
|
|
42
|
+
args, check=True, stdout=stdout, stderr=stderr, encoding='utf-8', **kwargs
|
|
43
|
+
)
|
|
42
44
|
|
|
43
45
|
if result.stdout is not None:
|
|
44
46
|
logging.debug('\n%s', result.stdout)
|
|
@@ -10,8 +10,7 @@ def parse_args(argv=None):
|
|
|
10
10
|
if argv is None:
|
|
11
11
|
argv = sys.argv[1:]
|
|
12
12
|
parser = ArgumentParser()
|
|
13
|
-
parser.add_argument('config', metavar='PATH',
|
|
14
|
-
help='config file path')
|
|
13
|
+
parser.add_argument('config', metavar='PATH', help='config file path')
|
|
15
14
|
return parser.parse_args(argv)
|
|
16
15
|
|
|
17
16
|
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
atlassian-python-api==4.0.7
|
|
2
2
|
beautifulsoup4==4.14.3
|
|
3
|
-
certifi==2026.
|
|
3
|
+
certifi==2026.4.22
|
|
4
4
|
cffi==2.0.0
|
|
5
|
-
charset-normalizer==3.4.
|
|
6
|
-
cryptography==
|
|
5
|
+
charset-normalizer==3.4.7
|
|
6
|
+
cryptography==48.0.0
|
|
7
7
|
Deprecated==1.3.1
|
|
8
|
-
idna==3.
|
|
8
|
+
idna==3.15
|
|
9
9
|
jmespath==1.1.0
|
|
10
10
|
oauthlib==3.3.1
|
|
11
11
|
pycparser==3.0
|
|
12
|
-
PyGithub==2.9.
|
|
12
|
+
PyGithub==2.9.1
|
|
13
13
|
PyJWT==2.12.1
|
|
14
14
|
PyNaCl==1.6.2
|
|
15
15
|
python-gitlab==6.5.0
|
|
16
|
-
requests==2.
|
|
16
|
+
requests==2.34.2
|
|
17
17
|
requests-oauthlib==2.0.0
|
|
18
18
|
requests-toolbelt==1.0.0
|
|
19
19
|
soupsieve==2.8.3
|
|
20
20
|
tomli==2.4.1
|
|
21
21
|
typing_extensions==4.15.0
|
|
22
|
-
urllib3==2.
|
|
22
|
+
urllib3==2.7.0
|
|
23
23
|
wrapt==2.1.2
|
|
@@ -15,7 +15,8 @@ class BitbucketTests(unittest.TestCase):
|
|
|
15
15
|
self.bitbucket = Cloud(
|
|
16
16
|
username=os.environ.get('CGITIZE_BITBUCKET_USERNAME'),
|
|
17
17
|
password=os.environ.get('CGITIZE_BITBUCKET_TOKEN'),
|
|
18
|
-
cloud=True
|
|
18
|
+
cloud=True,
|
|
19
|
+
)
|
|
19
20
|
|
|
20
21
|
def test_nonexistent_repo(self):
|
|
21
22
|
with self.assertRaises(HTTPError):
|
|
@@ -29,15 +30,24 @@ class BitbucketTests(unittest.TestCase):
|
|
|
29
30
|
self.assertEqual(r.data['workspace']['name'], 'Test cgitize workspace')
|
|
30
31
|
self.assertEqual(r.data['workspace']['slug'], 'cgitize-test-workspace')
|
|
31
32
|
|
|
32
|
-
self.assertEqual(
|
|
33
|
+
self.assertEqual(
|
|
34
|
+
r.get_link('html'), 'https://bitbucket.org/cgitize-test-workspace/public'
|
|
35
|
+
)
|
|
33
36
|
|
|
34
|
-
clone_urls = [
|
|
37
|
+
clone_urls = [
|
|
38
|
+
link for link in r.data['links']['clone'] if link['name'] == 'https'
|
|
39
|
+
]
|
|
35
40
|
self.assertEqual(len(clone_urls), 1)
|
|
36
|
-
self.assertEqual(
|
|
41
|
+
self.assertEqual(
|
|
42
|
+
clone_urls[0]['href'],
|
|
43
|
+
'https://cgitize-test@bitbucket.org/cgitize-test-workspace/public.git',
|
|
44
|
+
)
|
|
37
45
|
|
|
38
46
|
ssh_urls = [link for link in r.data['links']['clone'] if link['name'] == 'ssh']
|
|
39
47
|
self.assertEqual(len(ssh_urls), 1)
|
|
40
|
-
self.assertEqual(
|
|
48
|
+
self.assertEqual(
|
|
49
|
+
ssh_urls[0]['href'], 'git@bitbucket.org:cgitize-test-workspace/public.git'
|
|
50
|
+
)
|
|
41
51
|
|
|
42
52
|
def test_private_repo(self):
|
|
43
53
|
r = self.bitbucket.repositories.get('cgitize-test-workspace', 'private')
|
|
@@ -47,12 +57,18 @@ class BitbucketTests(unittest.TestCase):
|
|
|
47
57
|
self.assertEqual(r.data['workspace']['name'], 'Test cgitize workspace')
|
|
48
58
|
self.assertEqual(r.data['workspace']['slug'], 'cgitize-test-workspace')
|
|
49
59
|
|
|
50
|
-
self.assertEqual(
|
|
60
|
+
self.assertEqual(
|
|
61
|
+
r.get_link('html'), 'https://bitbucket.org/cgitize-test-workspace/private'
|
|
62
|
+
)
|
|
51
63
|
|
|
52
64
|
def test_user(self):
|
|
53
65
|
w = self.bitbucket.workspaces.get('cgitize-test-workspace')
|
|
54
|
-
self.assertEqual(
|
|
55
|
-
|
|
66
|
+
self.assertEqual(
|
|
67
|
+
len([r for r in w.repositories.each() if r.name == 'public']), 1
|
|
68
|
+
)
|
|
69
|
+
self.assertEqual(
|
|
70
|
+
len([r for r in w.repositories.each() if r.name == 'private']), 1
|
|
71
|
+
)
|
|
56
72
|
|
|
57
73
|
|
|
58
74
|
class BitbucketTestPrivateRepo(unittest.TestCase):
|
|
@@ -13,8 +13,8 @@ from gitlab.exceptions import GitlabGetError
|
|
|
13
13
|
class GitLabTests(unittest.TestCase):
|
|
14
14
|
def setUp(self):
|
|
15
15
|
self.gitlab = Gitlab(
|
|
16
|
-
'https://gitlab.com',
|
|
17
|
-
|
|
16
|
+
'https://gitlab.com', private_token=os.environ.get('CGITIZE_GITLAB_TOKEN')
|
|
17
|
+
)
|
|
18
18
|
|
|
19
19
|
def test_nonexistent_repo(self):
|
|
20
20
|
with self.assertRaises(GitlabGetError):
|
|
@@ -27,7 +27,9 @@ class GitLabTests(unittest.TestCase):
|
|
|
27
27
|
self.assertEqual(r.namespace['name'], 'Test cgitize user')
|
|
28
28
|
self.assertEqual(r.namespace['path'], 'cgitize-test')
|
|
29
29
|
self.assertEqual(r.web_url, 'https://gitlab.com/cgitize-test/public')
|
|
30
|
-
self.assertEqual(
|
|
30
|
+
self.assertEqual(
|
|
31
|
+
r.http_url_to_repo, 'https://gitlab.com/cgitize-test/public.git'
|
|
32
|
+
)
|
|
31
33
|
self.assertEqual(r.ssh_url_to_repo, 'git@gitlab.com:cgitize-test/public.git')
|
|
32
34
|
|
|
33
35
|
def test_private_repo(self):
|
|
@@ -37,7 +39,9 @@ class GitLabTests(unittest.TestCase):
|
|
|
37
39
|
self.assertEqual(r.namespace['name'], 'Test cgitize user')
|
|
38
40
|
self.assertEqual(r.namespace['path'], 'cgitize-test')
|
|
39
41
|
self.assertEqual(r.web_url, 'https://gitlab.com/cgitize-test/private')
|
|
40
|
-
self.assertEqual(
|
|
42
|
+
self.assertEqual(
|
|
43
|
+
r.http_url_to_repo, 'https://gitlab.com/cgitize-test/private.git'
|
|
44
|
+
)
|
|
41
45
|
self.assertEqual(r.ssh_url_to_repo, 'git@gitlab.com:cgitize-test/private.git')
|
|
42
46
|
|
|
43
47
|
def test_user(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cgitize-6.0.3 → cgitize-6.0.4}/docker/frontend/usr/lib/cgit/filters/html-converters/.gitattributes
RENAMED
|
File without changes
|
{cgitize-6.0.3 → cgitize-6.0.4}/docker/frontend/usr/lib/cgit/filters/html-converters/md2html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|