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.
Files changed (88) hide show
  1. {cgitize-9.0.0 → cgitize-9.1.0}/.github/workflows/ci.yml +10 -2
  2. cgitize-9.1.0/.gitmodules +3 -0
  3. {cgitize-9.0.0 → cgitize-9.1.0}/CHANGELOG.md +9 -0
  4. {cgitize-9.0.0 → cgitize-9.1.0}/DEVELOPMENT.md +16 -14
  5. {cgitize-9.0.0 → cgitize-9.1.0}/Makefile +12 -12
  6. {cgitize-9.0.0/cgitize.egg-info → cgitize-9.1.0}/PKG-INFO +1 -1
  7. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/cgit.py +8 -9
  8. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/git.py +25 -45
  9. {cgitize-9.0.0 → cgitize-9.1.0/cgitize.egg-info}/PKG-INFO +1 -1
  10. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize.egg-info/SOURCES.txt +18 -2
  11. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize.egg-info/scm_file_list.json +18 -2
  12. cgitize-9.1.0/cgitize.egg-info/scm_version.json +8 -0
  13. cgitize-9.1.0/test/integration/lib/basic.sh +72 -0
  14. cgitize-9.1.0/test/integration/lib/common.sh +92 -0
  15. cgitize-9.1.0/test/integration/lib/example.sh +47 -0
  16. cgitize-9.1.0/test/integration/runner/.gitattributes +3 -0
  17. cgitize-9.1.0/test/integration/runner/LICENSE.txt +21 -0
  18. cgitize-9.1.0/test/integration/runner/README.md +95 -0
  19. cgitize-9.1.0/test/integration/runner/harness +148 -0
  20. cgitize-9.1.0/test/integration/runner/src/log.sh +139 -0
  21. cgitize-9.1.0/test/integration/runner/src/main.sh +148 -0
  22. cgitize-9.1.0/test/integration/runner/src/test.sh +23 -0
  23. cgitize-9.1.0/test/integration/tst-basic-error.sh +21 -0
  24. cgitize-9.1.0/test/integration/tst-basic.sh +21 -0
  25. cgitize-9.1.0/test/integration/tst-example-https-private.sh +33 -0
  26. cgitize-9.1.0/test/integration/tst-example-https.sh +31 -0
  27. cgitize-9.1.0/test/integration/tst-example-one-repo.sh +10 -0
  28. cgitize-9.1.0/test/integration/tst-example-ssh-private.sh +33 -0
  29. cgitize-9.1.0/test/integration/tst-example-ssh.sh +31 -0
  30. cgitize-9.0.0/cgitize.egg-info/scm_version.json +0 -8
  31. cgitize-9.0.0/test/integration/example/test.sh +0 -312
  32. cgitize-9.0.0/test/integration/local/test.sh +0 -244
  33. {cgitize-9.0.0 → cgitize-9.1.0}/.git-blame-ignore-revs +0 -0
  34. {cgitize-9.0.0 → cgitize-9.1.0}/.gitattributes +0 -0
  35. {cgitize-9.0.0 → cgitize-9.1.0}/.github/dependabot.yml +0 -0
  36. {cgitize-9.0.0 → cgitize-9.1.0}/.github/workflows/maintenance.yml +0 -0
  37. {cgitize-9.0.0 → cgitize-9.1.0}/.gitignore +0 -0
  38. {cgitize-9.0.0 → cgitize-9.1.0}/.python-version +0 -0
  39. {cgitize-9.0.0 → cgitize-9.1.0}/LICENSE.txt +0 -0
  40. {cgitize-9.0.0 → cgitize-9.1.0}/README.md +0 -0
  41. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/__init__.py +0 -0
  42. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/bitbucket.py +0 -0
  43. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/config.py +0 -0
  44. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/github.py +0 -0
  45. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/gitlab.py +0 -0
  46. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/header.py +0 -0
  47. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/main.py +0 -0
  48. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/repo.py +0 -0
  49. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/url.py +0 -0
  50. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/utils.py +0 -0
  51. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize/version.py +0 -0
  52. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize.egg-info/dependency_links.txt +0 -0
  53. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize.egg-info/entry_points.txt +0 -0
  54. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize.egg-info/requires.txt +0 -0
  55. {cgitize-9.0.0 → cgitize-9.1.0}/cgitize.egg-info/top_level.txt +0 -0
  56. {cgitize-9.0.0 → cgitize-9.1.0}/docker/Dockerfile +0 -0
  57. {cgitize-9.0.0 → cgitize-9.1.0}/docker/README.md +0 -0
  58. {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/Dockerfile +0 -0
  59. {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/cmd.sh +0 -0
  60. {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/etc/cgit/common +0 -0
  61. {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/etc/cgitrc +0 -0
  62. {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/etc/nginx/conf.d/default.conf +0 -0
  63. {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/usr/lib/cgit/filters/about-formatting.sh +0 -0
  64. {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/usr/lib/cgit/filters/html-converters/.gitattributes +0 -0
  65. {cgitize-9.0.0 → cgitize-9.1.0}/docker/frontend/usr/lib/cgit/filters/html-converters/md2html +0 -0
  66. {cgitize-9.0.0 → cgitize-9.1.0}/docker/get_output_dir.py +0 -0
  67. {cgitize-9.0.0 → cgitize-9.1.0}/docker/in_cron.sh +0 -0
  68. {cgitize-9.0.0 → cgitize-9.1.0}/docker/run.sh +0 -0
  69. {cgitize-9.0.0 → cgitize-9.1.0}/docs/api.md +0 -0
  70. {cgitize-9.0.0 → cgitize-9.1.0}/docs/auth.md +0 -0
  71. {cgitize-9.0.0 → cgitize-9.1.0}/examples/cgitize.toml +0 -0
  72. {cgitize-9.0.0 → cgitize-9.1.0}/prelude.mk +0 -0
  73. {cgitize-9.0.0 → cgitize-9.1.0}/pyproject.toml +0 -0
  74. {cgitize-9.0.0 → cgitize-9.1.0}/requirements.txt +0 -0
  75. {cgitize-9.0.0 → cgitize-9.1.0}/setup.cfg +0 -0
  76. {cgitize-9.0.0 → cgitize-9.1.0}/test/__init__.py +0 -0
  77. {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/.gitignore +0 -0
  78. {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/docker-compose.yml +0 -0
  79. {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/etc/cgitize.toml +0 -0
  80. {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/git_server/Dockerfile +0 -0
  81. {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/git_server/entrypoint.sh +0 -0
  82. {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/git_server/setup.sh +0 -0
  83. {cgitize-9.0.0 → cgitize-9.1.0}/test/integration/docker/test.sh +0 -0
  84. {cgitize-9.0.0 → cgitize-9.1.0}/test/set_credentials.sh +0 -0
  85. {cgitize-9.0.0 → cgitize-9.1.0}/test/unit/__init__.py +0 -0
  86. {cgitize-9.0.0 → cgitize-9.1.0}/test/unit/test_bitbucket.py +0 -0
  87. {cgitize-9.0.0 → cgitize-9.1.0}/test/unit/test_github.py +0 -0
  88. {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 test (example config)
94
- run: make test/example
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
@@ -0,0 +1,3 @@
1
+ [submodule "test/integration/local/runner"]
2
+ path = test/integration/runner
3
+ url = https://github.com/egor-tensin/test-harness.git
@@ -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 `local` integration
12
- test.
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
- * There're three integration tests: `local`, `docker` and `example`.
33
- Run them using
32
+ make test/integration
34
33
 
35
- make test/local
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
- * Additionally, it tests cloning via SSH (the `clone_via_ssh` setting) too,
43
- so you need to add the secret project-specific SSH key to your agent as well
44
- (make sure it's the only one in the agent; if there's a key associated with
45
- another user, it could get used instead).
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
- [examples/cgitize.toml]: examples/cgitize.toml
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/local
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/local
63
- test/local:
62
+ .PHONY: test/integration
63
+ test/integration:
64
64
  @echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
65
- @echo Integration test: local
65
+ @echo Integration tests
66
66
  @echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67
- ./test/integration/local/test.sh
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cgitize
3
- Version: 9.0.0
3
+ Version: 9.1.0
4
4
  Summary: Self-host your repositories using cgit
5
5
  Author-email: Egor Tensin <egor@tensin.name>
6
6
  License-Expression: MIT
@@ -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 Git.check("clone", "--mirror", "--quiet", repo.clone_url, repo_dir)
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
- with Git.setup_auth(repo):
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
- @staticmethod
152
- def check(*args, **kwargs):
153
- return utils.try_run(Git.EXE, *args, env=GIT_ENV, **kwargs)
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
- @staticmethod
156
- def capture(*args, **kwargs):
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
- @staticmethod
160
- def get_global_config():
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
- config = Git.get_global_config()
170
- with utils.protected_file(config.path):
171
- with config.backup() as old_contents:
172
- variables = [Config.Variable("insteadOf", repo.clone_url)]
173
- subsection = Config.Subsection(
174
- "url", repo.clone_url_with_auth, variables
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,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cgitize
3
- Version: 9.0.0
3
+ Version: 9.1.0
4
4
  Summary: Self-host your repositories using cgit
5
5
  Author-email: Egor Tensin <egor@tensin.name>
6
6
  License-Expression: MIT
@@ -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/example/test.sh
63
- test/integration/local/test.sh
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/example/test.sh",
56
- "test/integration/local/test.sh",
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,8 @@
1
+ {
2
+ "tag": "9.1.0",
3
+ "distance": 0,
4
+ "node": "gc233bd4a3aa3025394c80ccd3c8efc81d0baca75",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-15"
8
+ }
@@ -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,3 @@
1
+ * text=auto
2
+
3
+ *.sh text eol=lf
@@ -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.