summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorEgor Tensin <egor@tensin.name>2026-10-15 09:28:25 +0500
committerEgor Tensin <egor@tensin.name>2026-10-15 09:40:47 +0500
commitda49ca88c279fd766d5cd30719af9327e3aa2757 (patch)
treef49b544fa2df5846eead42eeac70df80b87e2b25
parentt/i: migrate to test-harness (diff)
downloadcgitize-da49ca88c279fd766d5cd30719af9327e3aa2757.tar.gz
cgitize-da49ca88c279fd766d5cd30719af9327e3aa2757.zip
don't touch the global ~/.gitconfig
This practice was sus from the very beginning.
-rw-r--r--cgitize/cgit.py17
-rw-r--r--cgitize/git.py70
-rw-r--r--test/integration/lib/common.sh2
3 files changed, 34 insertions, 55 deletions
diff --git a/cgitize/cgit.py b/cgitize/cgit.py
index 547235c..d4a8ae2 100644
--- a/cgitize/cgit.py
+++ b/cgitize/cgit.py
@@ -70,7 +70,7 @@ class AgeFile:
# Except I think that the committer date, not author date better
# represents activity.
with chdir(repo_dir):
- success, output = Git.capture(
+ success, output = Git().capture(
"for-each-ref",
"--sort=-committerdate",
"--count=1",
@@ -123,7 +123,7 @@ class CGitRepositories:
return self._mirror(repo)
with chdir(repo_dir):
- success, output = Git.capture("rev-parse", "--is-inside-work-tree")
+ success, output = Git().capture("rev-parse", "--is-inside-work-tree")
if not success:
# Overwrite the existing directory if it's not a Git repository.
logging.warning(
@@ -132,7 +132,7 @@ class CGitRepositories:
)
return self._mirror(repo)
- success, output = Git.capture("config", "--get", "remote.origin.url")
+ success, output = Git().capture("config", "--get", "remote.origin.url")
if not success:
# Every repository managed by this script should have the
# 'origin' remote. If it doesn't, it's trash. Overwrite the
@@ -171,17 +171,16 @@ class CGitRepositories:
except Exception as e:
logging.exception(e)
return False
- with Git.setup_auth(repo):
- return Git.check("clone", "--mirror", "--quiet", repo.clone_url, repo_dir)
+ with Git.setup_auth(repo) as git:
+ return git.check("clone", "--mirror", "--quiet", repo.clone_url, repo_dir)
def _fix_upstream_url(self, repo):
repo_dir = self.get_repo_dir(repo)
with chdir(repo_dir):
- return Git.check("remote", "set-url", "origin", repo.clone_url)
+ return Git().check("remote", "set-url", "origin", repo.clone_url)
def _update_existing(self, repo):
logging.info("Updating repository '%s'", repo.name)
repo_dir = self.get_repo_dir(repo)
- with chdir(repo_dir):
- with Git.setup_auth(repo):
- return Git.check("remote", "update", "--prune")
+ with chdir(repo_dir), Git.setup_auth(repo) as git:
+ return git.check("remote", "update", "--prune")
diff --git a/cgitize/git.py b/cgitize/git.py
index 4f5eb19..fdb2fef 100644
--- a/cgitize/git.py
+++ b/cgitize/git.py
@@ -5,6 +5,7 @@
from contextlib import contextmanager
import os
+import tempfile
from cgitize import utils
@@ -12,37 +13,21 @@ GIT_ENV = os.environ.copy()
GIT_ENV["GIT_SSH_COMMAND"] = (
"ssh -oBatchMode=yes -oLogLevel=QUIET -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null"
)
+GIT_ENV["GIT_CONFIG_NOSYSTEM"] = "true"
+GIT_ENV["GIT_CONFIG_GLOBAL"] = "/dev/null"
class Config:
- def __init__(self, path):
- self.path = path
-
- def exists(self):
- return os.path.exists(self.path)
-
- def open(self, mode="r"):
- return open(self.path, mode=mode, encoding="utf-8")
-
- def read(self):
- with self.open(mode="r") as fd:
- return fd.read()
-
- def write(self, contents):
- with self.open(mode="w") as fd:
- fd.write(contents)
-
- @contextmanager
- def backup(self):
- old_contents = self.read()
- try:
- yield old_contents
- finally:
- self.write(old_contents)
-
# What follows is an exteremely loose interpretation of what the .gitconfig
# syntax is. The source was git-config(1).
+ @staticmethod
+ @contextmanager
+ def make_tmp():
+ tmp = tempfile.NamedTemporaryFile(mode="w", encoding="utf-8")
+ with tmp as result, utils.protected_file(result.name):
+ yield result
+
class Section:
def __init__(self, name, variables):
Config.Section.validate_name(name)
@@ -148,31 +133,26 @@ class Config:
class Git:
EXE = "git"
- @staticmethod
- def check(*args, **kwargs):
- return utils.try_run(Git.EXE, *args, env=GIT_ENV, **kwargs)
+ def __init__(self, config=None):
+ self._env = GIT_ENV.copy()
+ if config is not None:
+ self._env["GIT_CONFIG_GLOBAL"] = config
- @staticmethod
- def capture(*args, **kwargs):
- return utils.try_capture(Git.EXE, *args, env=GIT_ENV, **kwargs)
+ def check(self, *args, **kwargs):
+ return utils.try_run(Git.EXE, *args, env=self._env, **kwargs)
- @staticmethod
- def get_global_config():
- return Config(os.path.expanduser("~/.gitconfig"))
+ def capture(self, *args, **kwargs):
+ return utils.try_capture(Git.EXE, *args, env=self._env, **kwargs)
@staticmethod
@contextmanager
def setup_auth(repo):
if not repo.url_auth:
- yield
+ yield Git()
return
- config = Git.get_global_config()
- with utils.protected_file(config.path):
- with config.backup() as old_contents:
- variables = [Config.Variable("insteadOf", repo.clone_url)]
- subsection = Config.Subsection(
- "url", repo.clone_url_with_auth, variables
- )
- new_contents = f"{old_contents}\n{subsection.format()}"
- config.write(new_contents)
- yield
+ with Config.make_tmp() as config:
+ variables = [Config.Variable("insteadOf", repo.clone_url)]
+ subsection = Config.Subsection("url", repo.clone_url_with_auth, variables)
+ config.write(subsection.format())
+ config.flush()
+ yield Git(config.name)
diff --git a/test/integration/lib/common.sh b/test/integration/lib/common.sh
index 10774a7..45dad8c 100644
--- a/test/integration/lib/common.sh
+++ b/test/integration/lib/common.sh
@@ -4,7 +4,7 @@
# Distributed under the MIT License.
export GIT_CONFIG_NOSYSTEM=true
-#export GIT_CONFIG_GLOBAL=/dev/null
+export GIT_CONFIG_GLOBAL=/dev/null
test_run_cgitize() {
local cmd=(python3 -m cgitize.main --config "$test_config_path" "$@")