cgitize 9.0.0__tar.gz → 9.1.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.
- {cgitize-9.0.0 → cgitize-9.1.0}/.github/workflows/ci.yml +10 -2
- cgitize-9.1.0/.gitmodules +3 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/CHANGELOG.md +9 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/DEVELOPMENT.md +16 -14
- {cgitize-9.0.0 → cgitize-9.1.0}/Makefile +12 -12
- {cgitize-9.0.0/cgitize.egg-info → cgitize-9.1.0}/PKG-INFO +1 -1
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/cgit.py +8 -9
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/git.py +25 -45
- {cgitize-9.0.0 → cgitize-9.1.0/cgitize.egg-info}/PKG-INFO +1 -1
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize.egg-info/SOURCES.txt +18 -2
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize.egg-info/scm_file_list.json +18 -2
- cgitize-9.1.0/cgitize.egg-info/scm_version.json +8 -0
- cgitize-9.1.0/test/integration/lib/basic.sh +72 -0
- cgitize-9.1.0/test/integration/lib/common.sh +92 -0
- cgitize-9.1.0/test/integration/lib/example.sh +47 -0
- cgitize-9.1.0/test/integration/runner/.gitattributes +3 -0
- cgitize-9.1.0/test/integration/runner/LICENSE.txt +21 -0
- cgitize-9.1.0/test/integration/runner/README.md +95 -0
- cgitize-9.1.0/test/integration/runner/harness +148 -0
- cgitize-9.1.0/test/integration/runner/src/log.sh +139 -0
- cgitize-9.1.0/test/integration/runner/src/main.sh +148 -0
- cgitize-9.1.0/test/integration/runner/src/test.sh +23 -0
- cgitize-9.1.0/test/integration/tst-basic-error.sh +21 -0
- cgitize-9.1.0/test/integration/tst-basic.sh +21 -0
- cgitize-9.1.0/test/integration/tst-example-https-private.sh +33 -0
- cgitize-9.1.0/test/integration/tst-example-https.sh +31 -0
- cgitize-9.1.0/test/integration/tst-example-one-repo.sh +10 -0
- cgitize-9.1.0/test/integration/tst-example-ssh-private.sh +33 -0
- cgitize-9.1.0/test/integration/tst-example-ssh.sh +31 -0
- cgitize-9.0.0/cgitize.egg-info/scm_version.json +0 -8
- cgitize-9.0.0/test/integration/example/test.sh +0 -312
- cgitize-9.0.0/test/integration/local/test.sh +0 -244
- {cgitize-9.0.0 → cgitize-9.1.0}/.git-blame-ignore-revs +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/.gitattributes +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/.github/dependabot.yml +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/.github/workflows/maintenance.yml +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/.gitignore +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/.python-version +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/LICENSE.txt +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/README.md +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/__init__.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/bitbucket.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/config.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/github.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/gitlab.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/header.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/main.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/repo.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/url.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/utils.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/version.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize.egg-info/dependency_links.txt +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize.egg-info/entry_points.txt +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize.egg-info/requires.txt +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/cgitize.egg-info/top_level.txt +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/Dockerfile +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/README.md +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/Dockerfile +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/cmd.sh +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/etc/cgit/common +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/etc/cgitrc +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/etc/nginx/conf.d/default.conf +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/usr/lib/cgit/filters/about-formatting.sh +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/usr/lib/cgit/filters/html-converters/.gitattributes +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/usr/lib/cgit/filters/html-converters/md2html +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/get_output_dir.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/in_cron.sh +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docker/run.sh +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docs/api.md +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/docs/auth.md +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/examples/cgitize.toml +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/prelude.mk +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/pyproject.toml +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/requirements.txt +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/setup.cfg +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/__init__.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/.gitignore +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/docker-compose.yml +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/etc/cgitize.toml +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/git_server/Dockerfile +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/git_server/entrypoint.sh +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/git_server/setup.sh +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/test.sh +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/set_credentials.sh +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/unit/__init__.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/unit/test_bitbucket.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/unit/test_github.py +0 -0
- {cgitize-9.0.0 → cgitize-9.1.0}/test/unit/test_gitlab.py +0 -0
|
@@ -43,6 +43,7 @@ jobs:
|
|
|
43
43
|
uses: actions/checkout@v7
|
|
44
44
|
with:
|
|
45
45
|
fetch-depth: 0
|
|
46
|
+
submodules: recursive
|
|
46
47
|
- name: Set up Python
|
|
47
48
|
uses: actions/setup-python@v7
|
|
48
49
|
with:
|
|
@@ -61,6 +62,8 @@ jobs:
|
|
|
61
62
|
steps:
|
|
62
63
|
- name: Checkout
|
|
63
64
|
uses: actions/checkout@v7
|
|
65
|
+
with:
|
|
66
|
+
submodules: recursive
|
|
64
67
|
- name: Integration test (Docker)
|
|
65
68
|
run: sudo make test/docker
|
|
66
69
|
|
|
@@ -80,6 +83,8 @@ jobs:
|
|
|
80
83
|
steps:
|
|
81
84
|
- name: Checkout
|
|
82
85
|
uses: actions/checkout@v7
|
|
86
|
+
with:
|
|
87
|
+
submodules: recursive
|
|
83
88
|
- name: Set up Python
|
|
84
89
|
uses: actions/setup-python@v7
|
|
85
90
|
with:
|
|
@@ -90,8 +95,8 @@ jobs:
|
|
|
90
95
|
uses: webfactory/ssh-agent@v0.10.0
|
|
91
96
|
with:
|
|
92
97
|
ssh-private-key: '${{ secrets.SSH_KEY }}'
|
|
93
|
-
- name: Integration
|
|
94
|
-
run: make test/
|
|
98
|
+
- name: Integration tests
|
|
99
|
+
run: make test/integration
|
|
95
100
|
|
|
96
101
|
publish_docker:
|
|
97
102
|
needs:
|
|
@@ -110,6 +115,8 @@ jobs:
|
|
|
110
115
|
steps:
|
|
111
116
|
- name: Checkout
|
|
112
117
|
uses: actions/checkout@v7
|
|
118
|
+
with:
|
|
119
|
+
submodules: recursive
|
|
113
120
|
# https://github.com/docker/metadata-action#semver
|
|
114
121
|
- id: meta
|
|
115
122
|
name: Docker Hub metadata
|
|
@@ -168,6 +175,7 @@ jobs:
|
|
|
168
175
|
uses: actions/checkout@v7
|
|
169
176
|
with:
|
|
170
177
|
fetch-depth: 0
|
|
178
|
+
submodules: recursive
|
|
171
179
|
- name: Set up Python
|
|
172
180
|
uses: actions/setup-python@v7
|
|
173
181
|
- name: Verify package can be installed
|
|
@@ -6,6 +6,15 @@ v9.0
|
|
|
6
6
|
* Docker images: SSH agent should now be mapped to /ssh/agent.sock (instead of
|
|
7
7
|
/ssh-agent.sock).
|
|
8
8
|
|
|
9
|
+
v9.1
|
|
10
|
+
----
|
|
11
|
+
|
|
12
|
+
* The global ~/.gitconfig is no longer used for HTTPS authentication.
|
|
13
|
+
Instead, a temporary file is used.
|
|
14
|
+
* Internally, `git` is now executed with the `GIT_CONFIG_NOSYSTEM` and
|
|
15
|
+
`GIT_CONFIG_GLOBAL` environment variables set.
|
|
16
|
+
This might break existing setups in some way.
|
|
17
|
+
|
|
9
18
|
v8.0
|
|
10
19
|
====
|
|
11
20
|
|
|
@@ -8,8 +8,8 @@ Workspace setup
|
|
|
8
8
|
Testing
|
|
9
9
|
-------
|
|
10
10
|
|
|
11
|
-
N.B.: `make test` is a shortcut for unit tests and the
|
|
12
|
-
|
|
11
|
+
N.B.: `make test` is a shortcut for unit tests and the "basic" integration
|
|
12
|
+
tests.
|
|
13
13
|
|
|
14
14
|
### Unit tests
|
|
15
15
|
|
|
@@ -29,22 +29,24 @@ You can run `. test/set_credentials.sh`; it'll ask you to fill in the secrets.
|
|
|
29
29
|
|
|
30
30
|
### Integration
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
Run them using
|
|
32
|
+
make test/integration
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
sudo make test/docker
|
|
37
|
-
make test/example
|
|
38
|
-
|
|
39
|
-
* The `example` test uses the forges' APIs and might easily hit their rate
|
|
34
|
+
* The "example" tests use the forges' APIs and might easily hit their rate
|
|
40
35
|
limits.
|
|
41
36
|
Use the tokens used for unit tests.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
* Additionally, it tests cloning via SSH (the `clone_via_ssh` setting) too, so
|
|
38
|
+
you need to add the secret project-specific SSH key to your agent as well (make
|
|
39
|
+
sure it's the only one in the agent; if there's a key associated with another
|
|
40
|
+
user, it could get used instead).
|
|
41
|
+
* Run only basic integration tests:
|
|
42
|
+
|
|
43
|
+
make test/integration/basic
|
|
44
|
+
|
|
45
|
+
### Docker setup
|
|
46
|
+
|
|
47
|
+
sudo make test/docker
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
It's a separate test to check Docker container building & setup.
|
|
48
50
|
|
|
49
51
|
Upgrading dependencies
|
|
50
52
|
----------------------
|
|
@@ -50,7 +50,7 @@ python:
|
|
|
50
50
|
repl: python
|
|
51
51
|
|
|
52
52
|
.PHONY: test
|
|
53
|
-
test: test/unit test/
|
|
53
|
+
test: test/unit test/integration/basic
|
|
54
54
|
|
|
55
55
|
.PHONY: test/unit
|
|
56
56
|
test/unit:
|
|
@@ -59,12 +59,19 @@ test/unit:
|
|
|
59
59
|
@echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
60
60
|
python -m unittest --verbose --buffer
|
|
61
61
|
|
|
62
|
-
.PHONY: test/
|
|
63
|
-
test/
|
|
62
|
+
.PHONY: test/integration
|
|
63
|
+
test/integration:
|
|
64
64
|
@echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
65
|
-
@echo Integration
|
|
65
|
+
@echo Integration tests
|
|
66
66
|
@echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
67
|
-
./test/integration/
|
|
67
|
+
./test/integration/runner/harness
|
|
68
|
+
|
|
69
|
+
.PHONY: test/integration/basic
|
|
70
|
+
test/integration/basic:
|
|
71
|
+
@echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
72
|
+
@echo Basic integration tests
|
|
73
|
+
@echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
74
|
+
./test/integration/runner/harness -f '*basic*'
|
|
68
75
|
|
|
69
76
|
.PHONY: test/docker
|
|
70
77
|
test/docker:
|
|
@@ -72,10 +79,3 @@ test/docker:
|
|
|
72
79
|
@echo Integration test: docker
|
|
73
80
|
@echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
74
81
|
./test/integration/docker/test.sh
|
|
75
|
-
|
|
76
|
-
.PHONY: test/example
|
|
77
|
-
test/example:
|
|
78
|
-
@echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
79
|
-
@echo Integration test: example
|
|
80
|
-
@echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
81
|
-
./test/integration/example/test.sh
|
|
@@ -70,7 +70,7 @@ 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
74
|
"for-each-ref",
|
|
75
75
|
"--sort=-committerdate",
|
|
76
76
|
"--count=1",
|
|
@@ -123,7 +123,7 @@ class CGitRepositories:
|
|
|
123
123
|
return self._mirror(repo)
|
|
124
124
|
|
|
125
125
|
with chdir(repo_dir):
|
|
126
|
-
success, output = Git.capture("rev-parse", "--is-inside-work-tree")
|
|
126
|
+
success, output = Git().capture("rev-parse", "--is-inside-work-tree")
|
|
127
127
|
if not success:
|
|
128
128
|
# Overwrite the existing directory if it's not a Git repository.
|
|
129
129
|
logging.warning(
|
|
@@ -132,7 +132,7 @@ class CGitRepositories:
|
|
|
132
132
|
)
|
|
133
133
|
return self._mirror(repo)
|
|
134
134
|
|
|
135
|
-
success, output = Git.capture("config", "--get", "remote.origin.url")
|
|
135
|
+
success, output = Git().capture("config", "--get", "remote.origin.url")
|
|
136
136
|
if not success:
|
|
137
137
|
# Every repository managed by this script should have the
|
|
138
138
|
# 'origin' remote. If it doesn't, it's trash. Overwrite the
|
|
@@ -171,17 +171,16 @@ class CGitRepositories:
|
|
|
171
171
|
except Exception as e:
|
|
172
172
|
logging.exception(e)
|
|
173
173
|
return False
|
|
174
|
-
with Git.setup_auth(repo):
|
|
175
|
-
return
|
|
174
|
+
with Git.setup_auth(repo) as git:
|
|
175
|
+
return git.check("clone", "--mirror", "--quiet", repo.clone_url, repo_dir)
|
|
176
176
|
|
|
177
177
|
def _fix_upstream_url(self, repo):
|
|
178
178
|
repo_dir = self.get_repo_dir(repo)
|
|
179
179
|
with chdir(repo_dir):
|
|
180
|
-
return Git.check("remote", "set-url", "origin", repo.clone_url)
|
|
180
|
+
return Git().check("remote", "set-url", "origin", repo.clone_url)
|
|
181
181
|
|
|
182
182
|
def _update_existing(self, repo):
|
|
183
183
|
logging.info("Updating repository '%s'", repo.name)
|
|
184
184
|
repo_dir = self.get_repo_dir(repo)
|
|
185
|
-
with chdir(repo_dir):
|
|
186
|
-
|
|
187
|
-
return Git.check("remote", "update", "--prune")
|
|
185
|
+
with chdir(repo_dir), Git.setup_auth(repo) as git:
|
|
186
|
+
return git.check("remote", "update", "--prune")
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
from contextlib import contextmanager
|
|
7
7
|
import os
|
|
8
|
+
import tempfile
|
|
8
9
|
|
|
9
10
|
from cgitize import utils
|
|
10
11
|
|
|
@@ -12,37 +13,21 @@ GIT_ENV = os.environ.copy()
|
|
|
12
13
|
GIT_ENV["GIT_SSH_COMMAND"] = (
|
|
13
14
|
"ssh -oBatchMode=yes -oLogLevel=QUIET -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null"
|
|
14
15
|
)
|
|
16
|
+
GIT_ENV["GIT_CONFIG_NOSYSTEM"] = "true"
|
|
17
|
+
GIT_ENV["GIT_CONFIG_GLOBAL"] = "/dev/null"
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
class Config:
|
|
18
|
-
def __init__(self, path):
|
|
19
|
-
self.path = path
|
|
20
|
-
|
|
21
|
-
def exists(self):
|
|
22
|
-
return os.path.exists(self.path)
|
|
23
|
-
|
|
24
|
-
def open(self, mode="r"):
|
|
25
|
-
return open(self.path, mode=mode, encoding="utf-8")
|
|
26
|
-
|
|
27
|
-
def read(self):
|
|
28
|
-
with self.open(mode="r") as fd:
|
|
29
|
-
return fd.read()
|
|
30
|
-
|
|
31
|
-
def write(self, contents):
|
|
32
|
-
with self.open(mode="w") as fd:
|
|
33
|
-
fd.write(contents)
|
|
34
|
-
|
|
35
|
-
@contextmanager
|
|
36
|
-
def backup(self):
|
|
37
|
-
old_contents = self.read()
|
|
38
|
-
try:
|
|
39
|
-
yield old_contents
|
|
40
|
-
finally:
|
|
41
|
-
self.write(old_contents)
|
|
42
|
-
|
|
43
21
|
# What follows is an exteremely loose interpretation of what the .gitconfig
|
|
44
22
|
# syntax is. The source was git-config(1).
|
|
45
23
|
|
|
24
|
+
@staticmethod
|
|
25
|
+
@contextmanager
|
|
26
|
+
def make_tmp():
|
|
27
|
+
tmp = tempfile.NamedTemporaryFile(mode="w", encoding="utf-8")
|
|
28
|
+
with tmp as result, utils.protected_file(result.name):
|
|
29
|
+
yield result
|
|
30
|
+
|
|
46
31
|
class Section:
|
|
47
32
|
def __init__(self, name, variables):
|
|
48
33
|
Config.Section.validate_name(name)
|
|
@@ -148,31 +133,26 @@ class Config:
|
|
|
148
133
|
class Git:
|
|
149
134
|
EXE = "git"
|
|
150
135
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
136
|
+
def __init__(self, config=None):
|
|
137
|
+
self._env = GIT_ENV.copy()
|
|
138
|
+
if config is not None:
|
|
139
|
+
self._env["GIT_CONFIG_GLOBAL"] = config
|
|
154
140
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return utils.try_capture(Git.EXE, *args, env=GIT_ENV, **kwargs)
|
|
141
|
+
def check(self, *args, **kwargs):
|
|
142
|
+
return utils.try_run(Git.EXE, *args, env=self._env, **kwargs)
|
|
158
143
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return Config(os.path.expanduser("~/.gitconfig"))
|
|
144
|
+
def capture(self, *args, **kwargs):
|
|
145
|
+
return utils.try_capture(Git.EXE, *args, env=self._env, **kwargs)
|
|
162
146
|
|
|
163
147
|
@staticmethod
|
|
164
148
|
@contextmanager
|
|
165
149
|
def setup_auth(repo):
|
|
166
150
|
if not repo.url_auth:
|
|
167
|
-
yield
|
|
151
|
+
yield Git()
|
|
168
152
|
return
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
)
|
|
176
|
-
new_contents = f"{old_contents}\n{subsection.format()}"
|
|
177
|
-
config.write(new_contents)
|
|
178
|
-
yield
|
|
153
|
+
with Config.make_tmp() as config:
|
|
154
|
+
variables = [Config.Variable("insteadOf", repo.clone_url)]
|
|
155
|
+
subsection = Config.Subsection("url", repo.clone_url_with_auth, variables)
|
|
156
|
+
config.write(subsection.format())
|
|
157
|
+
config.flush()
|
|
158
|
+
yield Git(config.name)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
.git-blame-ignore-revs
|
|
2
2
|
.gitattributes
|
|
3
3
|
.gitignore
|
|
4
|
+
.gitmodules
|
|
4
5
|
.python-version
|
|
5
6
|
CHANGELOG.md
|
|
6
7
|
DEVELOPMENT.md
|
|
@@ -52,6 +53,13 @@ docs/auth.md
|
|
|
52
53
|
examples/cgitize.toml
|
|
53
54
|
test/__init__.py
|
|
54
55
|
test/set_credentials.sh
|
|
56
|
+
test/integration/tst-basic-error.sh
|
|
57
|
+
test/integration/tst-basic.sh
|
|
58
|
+
test/integration/tst-example-https-private.sh
|
|
59
|
+
test/integration/tst-example-https.sh
|
|
60
|
+
test/integration/tst-example-one-repo.sh
|
|
61
|
+
test/integration/tst-example-ssh-private.sh
|
|
62
|
+
test/integration/tst-example-ssh.sh
|
|
55
63
|
test/integration/docker/.gitignore
|
|
56
64
|
test/integration/docker/docker-compose.yml
|
|
57
65
|
test/integration/docker/test.sh
|
|
@@ -59,8 +67,16 @@ test/integration/docker/etc/cgitize.toml
|
|
|
59
67
|
test/integration/docker/git_server/Dockerfile
|
|
60
68
|
test/integration/docker/git_server/entrypoint.sh
|
|
61
69
|
test/integration/docker/git_server/setup.sh
|
|
62
|
-
test/integration/
|
|
63
|
-
test/integration/
|
|
70
|
+
test/integration/lib/basic.sh
|
|
71
|
+
test/integration/lib/common.sh
|
|
72
|
+
test/integration/lib/example.sh
|
|
73
|
+
test/integration/runner/.gitattributes
|
|
74
|
+
test/integration/runner/LICENSE.txt
|
|
75
|
+
test/integration/runner/README.md
|
|
76
|
+
test/integration/runner/harness
|
|
77
|
+
test/integration/runner/src/log.sh
|
|
78
|
+
test/integration/runner/src/main.sh
|
|
79
|
+
test/integration/runner/src/test.sh
|
|
64
80
|
test/unit/__init__.py
|
|
65
81
|
test/unit/test_bitbucket.py
|
|
66
82
|
test/unit/test_github.py
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
".github/workflows/ci.yml",
|
|
7
7
|
".github/workflows/maintenance.yml",
|
|
8
8
|
".gitignore",
|
|
9
|
+
".gitmodules",
|
|
9
10
|
".python-version",
|
|
10
11
|
"CHANGELOG.md",
|
|
11
12
|
"DEVELOPMENT.md",
|
|
@@ -52,8 +53,23 @@
|
|
|
52
53
|
"test/integration/docker/git_server/entrypoint.sh",
|
|
53
54
|
"test/integration/docker/git_server/setup.sh",
|
|
54
55
|
"test/integration/docker/test.sh",
|
|
55
|
-
"test/integration/
|
|
56
|
-
"test/integration/
|
|
56
|
+
"test/integration/lib/basic.sh",
|
|
57
|
+
"test/integration/lib/common.sh",
|
|
58
|
+
"test/integration/lib/example.sh",
|
|
59
|
+
"test/integration/runner/.gitattributes",
|
|
60
|
+
"test/integration/runner/LICENSE.txt",
|
|
61
|
+
"test/integration/runner/README.md",
|
|
62
|
+
"test/integration/runner/harness",
|
|
63
|
+
"test/integration/runner/src/log.sh",
|
|
64
|
+
"test/integration/runner/src/main.sh",
|
|
65
|
+
"test/integration/runner/src/test.sh",
|
|
66
|
+
"test/integration/tst-basic-error.sh",
|
|
67
|
+
"test/integration/tst-basic.sh",
|
|
68
|
+
"test/integration/tst-example-https-private.sh",
|
|
69
|
+
"test/integration/tst-example-https.sh",
|
|
70
|
+
"test/integration/tst-example-one-repo.sh",
|
|
71
|
+
"test/integration/tst-example-ssh-private.sh",
|
|
72
|
+
"test/integration/tst-example-ssh.sh",
|
|
57
73
|
"test/set_credentials.sh",
|
|
58
74
|
"test/unit/__init__.py",
|
|
59
75
|
"test/unit/test_bitbucket.py",
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Copyright (c) 2026 Egor Tensin <egor@tensin.name>
|
|
2
|
+
# This file is part of the "cgitize" project.
|
|
3
|
+
# For details, see https://github.com/egor-tensin/cgitize
|
|
4
|
+
# Distributed under the MIT License.
|
|
5
|
+
|
|
6
|
+
test_basic_setup() {
|
|
7
|
+
test_repo_dir="$test_root_dir/repo"
|
|
8
|
+
test_output_dir="$test_root_dir/output"
|
|
9
|
+
test_config_path="$test_root_dir/cgitize.toml"
|
|
10
|
+
test_mirror_name=repo_mirror
|
|
11
|
+
test_mirror_dir="$test_output_dir/$test_mirror_name.git"
|
|
12
|
+
|
|
13
|
+
log "Repo directory: $test_repo_dir"
|
|
14
|
+
log "Output directory: $test_output_dir"
|
|
15
|
+
log "Config path: $test_config_path"
|
|
16
|
+
|
|
17
|
+
mkdir -- "$test_repo_dir"
|
|
18
|
+
git -C "$test_repo_dir" init -q
|
|
19
|
+
git -C "$test_repo_dir" config user.name 'Test user'
|
|
20
|
+
git -C "$test_repo_dir" config user.email 'test@example.com'
|
|
21
|
+
|
|
22
|
+
test_basic_make_commit
|
|
23
|
+
test_basic_make_commit
|
|
24
|
+
|
|
25
|
+
cat > "$test_config_path" <<EOF
|
|
26
|
+
output_dir = "$test_output_dir"
|
|
27
|
+
|
|
28
|
+
[repositories.repo]
|
|
29
|
+
name = "$test_mirror_name"
|
|
30
|
+
clone_url = "$test_repo_dir"
|
|
31
|
+
EOF
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_test_commit_counter=0
|
|
35
|
+
|
|
36
|
+
test_basic_make_commit() {
|
|
37
|
+
_test_commit_counter=$((_test_commit_counter + 1))
|
|
38
|
+
|
|
39
|
+
local file
|
|
40
|
+
file="$_test_commit_counter.txt"
|
|
41
|
+
echo "$_test_commit_counter" > "$test_repo_dir/$file"
|
|
42
|
+
|
|
43
|
+
log "Commiting file $file in $test_repo_dir..."
|
|
44
|
+
|
|
45
|
+
touch -- "$test_repo_dir/file"
|
|
46
|
+
git -C "$test_repo_dir" add "$file"
|
|
47
|
+
git -C "$test_repo_dir" commit -q -m "committing $file"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
test_basic_verify_commits() {
|
|
51
|
+
log "Verifying commits in $test_mirror_dir..."
|
|
52
|
+
|
|
53
|
+
local msg
|
|
54
|
+
git -C "$test_mirror_dir" log --reverse --pretty=tformat:%s |
|
|
55
|
+
while IFS= read -r msg; do
|
|
56
|
+
if [ "$#" -eq 0 ]; then
|
|
57
|
+
fail "Unexpected commit found: $msg"
|
|
58
|
+
return 1
|
|
59
|
+
fi
|
|
60
|
+
if [ "$msg" != "$1" ]; then
|
|
61
|
+
fail "Unexpected commit found: $msg"
|
|
62
|
+
fail_details "Expected commit: $1"
|
|
63
|
+
return 1
|
|
64
|
+
fi
|
|
65
|
+
shift
|
|
66
|
+
done
|
|
67
|
+
|
|
68
|
+
if [ "$#" -ne 0 ]; then
|
|
69
|
+
fail "Missing commit: $1"
|
|
70
|
+
return 1
|
|
71
|
+
fi
|
|
72
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Copyright (c) 2026 Egor Tensin <egor@tensin.name>
|
|
2
|
+
# This file is part of the "cgitize" project.
|
|
3
|
+
# For details, see https://github.com/egor-tensin/cgitize
|
|
4
|
+
# Distributed under the MIT License.
|
|
5
|
+
|
|
6
|
+
export GIT_CONFIG_NOSYSTEM=true
|
|
7
|
+
export GIT_CONFIG_GLOBAL=/dev/null
|
|
8
|
+
|
|
9
|
+
test_run_cgitize() {
|
|
10
|
+
local cmd=(python3 -m cgitize.main --config "$test_config_path" "$@")
|
|
11
|
+
log_run "${cmd[@]}"
|
|
12
|
+
PYTHONPATH="$tests_dir/../.." "${cmd[@]}"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
test_verify_repos() {
|
|
16
|
+
local repo
|
|
17
|
+
for repo; do
|
|
18
|
+
log "Verifying repository: $repo..."
|
|
19
|
+
|
|
20
|
+
local repo_dir
|
|
21
|
+
repo_dir="$test_output_dir/$repo.git"
|
|
22
|
+
GIT_DIR="$repo_dir" git rev-parse HEAD > /dev/null
|
|
23
|
+
log 'HEAD is fine.'
|
|
24
|
+
|
|
25
|
+
if test -s "$repo_dir/info/web/last-modified"; then
|
|
26
|
+
log 'last-modified is fine.'
|
|
27
|
+
else
|
|
28
|
+
fail 'last-modified is missing!'
|
|
29
|
+
return 1
|
|
30
|
+
fi
|
|
31
|
+
done
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test_verify_no_repos() {
|
|
35
|
+
local repo
|
|
36
|
+
for repo; do
|
|
37
|
+
log "Verifying repository doesn't exist: $repo"
|
|
38
|
+
|
|
39
|
+
local repo_dir
|
|
40
|
+
repo_dir="$test_output_dir/$repo.git"
|
|
41
|
+
|
|
42
|
+
if [ -e "$repo_dir" ]; then
|
|
43
|
+
fail "Exists, but it shouldn't"
|
|
44
|
+
return 1
|
|
45
|
+
fi
|
|
46
|
+
done
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
test_verify_origin() {
|
|
50
|
+
if [ "$#" -ne 2 ]; then
|
|
51
|
+
log "usage: ${FUNCNAME[0]} REPO URL"
|
|
52
|
+
return 1
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
local repo="$1"
|
|
56
|
+
local expected="$2"
|
|
57
|
+
|
|
58
|
+
log "Verifying origin for $repo..."
|
|
59
|
+
|
|
60
|
+
local repo_dir
|
|
61
|
+
repo_dir="$test_output_dir/$repo.git"
|
|
62
|
+
|
|
63
|
+
local actual
|
|
64
|
+
actual="$( GIT_DIR="$repo_dir" git config --get remote.origin.url )"
|
|
65
|
+
|
|
66
|
+
if [ "$expected" != "$actual" ]; then
|
|
67
|
+
fail "Origin doesn't match!"
|
|
68
|
+
fail_details "Expected: $expected"
|
|
69
|
+
fail_details "Actual: $actual"
|
|
70
|
+
return 1
|
|
71
|
+
fi
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
test_verify_error_header_empty() {
|
|
75
|
+
log 'Verifying the error header...'
|
|
76
|
+
if [ -s "$test_output_dir/error.html" ]; then
|
|
77
|
+
fail "The error header should be empty"
|
|
78
|
+
return 1
|
|
79
|
+
fi
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
test_verify_error_header() {
|
|
83
|
+
log 'Verifying the error header...'
|
|
84
|
+
|
|
85
|
+
local msg="Some repositories couldn't be updated"
|
|
86
|
+
|
|
87
|
+
if ! grep -F -- "$msg" "$test_output_dir/error.html" > /dev/null; then
|
|
88
|
+
fail "The error header doesn't contain the required error"
|
|
89
|
+
fail_details "Expected error: $msg"
|
|
90
|
+
return 1
|
|
91
|
+
fi
|
|
92
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Copyright (c) 2026 Egor Tensin <egor@tensin.name>
|
|
2
|
+
# This file is part of the "cgitize" project.
|
|
3
|
+
# For details, see https://github.com/egor-tensin/cgitize
|
|
4
|
+
# Distributed under the MIT License.
|
|
5
|
+
|
|
6
|
+
_sed_config() {
|
|
7
|
+
if [ "$#" -ne 1 ]; then
|
|
8
|
+
log "usage: ${FUNCNAME[0]} EXPR"
|
|
9
|
+
return 1
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
sed -i -E -e "$1" -- "$test_config_path"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
_test_example_setup_config() {
|
|
16
|
+
test_config_path="$test_root_dir/cgitize.toml"
|
|
17
|
+
log "Config path: $test_config_path"
|
|
18
|
+
|
|
19
|
+
cp -- "$tests_dir/../../examples/cgitize.toml" "$test_config_path"
|
|
20
|
+
|
|
21
|
+
_sed_config 's@^output_dir = .*$@output_dir = "'"$test_output_dir"'"@'
|
|
22
|
+
test_example_clone_ssh
|
|
23
|
+
test_example_private_skip
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
test_example_setup() {
|
|
27
|
+
test_output_dir="$test_root_dir/output"
|
|
28
|
+
log "Output directory: $test_output_dir"
|
|
29
|
+
|
|
30
|
+
_test_example_setup_config
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
test_example_clone_ssh() {
|
|
34
|
+
_sed_config 's/^clone_via_ssh = (true|false)$/clone_via_ssh = true/'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
test_example_clone_https() {
|
|
38
|
+
_sed_config 's/^clone_via_ssh = (true|false)$/clone_via_ssh = false/'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
test_example_private_include() {
|
|
42
|
+
_sed_config 's/^private = (true|false)$/private = true/'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
test_example_private_skip() {
|
|
46
|
+
_sed_config 's/^private = (true|false)$/private = false/'
|
|
47
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Egor Tensin <egor@tensin.name>
|
|
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.
|