aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--cgitize/bitbucket.py4
-rw-r--r--cgitize/cgit.py28
-rw-r--r--cgitize/config.py16
-rw-r--r--cgitize/git.py25
-rw-r--r--cgitize/main.py39
-rw-r--r--cgitize/repo.py58
-rw-r--r--cgitize/utils.py8
-rwxr-xr-xdocker/get_output_dir.py3
-rw-r--r--test/unit/test_bitbucket.py32
-rw-r--r--test/unit/test_gitlab.py12
10 files changed, 165 insertions, 60 deletions
diff --git a/cgitize/bitbucket.py b/cgitize/bitbucket.py
index 5c55a2a..f3cc4d4 100644
--- a/cgitize/bitbucket.py
+++ b/cgitize/bitbucket.py
@@ -19,7 +19,9 @@ class Bitbucket:
try:
parts = repo.id.split('/')
if len(parts) != 2:
- raise ValueError(f'repository ID must be in the USER/NAME format: {repo.id}')
+ raise ValueError(
+ f'repository ID must be in the USER/NAME format: {repo.id}'
+ )
user, name = parts
return self._impl.repositories.get(user, name)
except HTTPError:
diff --git a/cgitize/cgit.py b/cgitize/cgit.py
index 8c8a8ca..7691c61 100644
--- a/cgitize/cgit.py
+++ b/cgitize/cgit.py
@@ -70,9 +70,17 @@ class AgeFile:
# Except I think that the committer date, not author date better
# represents activity.
with chdir(repo_dir):
- success, output = Git.capture('for-each-ref', '--sort=-committerdate', '--count=1', '--format=%(committerdate:iso8601)')
+ success, output = Git.capture(
+ 'for-each-ref',
+ '--sort=-committerdate',
+ '--count=1',
+ '--format=%(committerdate:iso8601)',
+ )
if not success:
- logging.error("Couldn't get the timestamp of the newest commit in repository: %s", repo_dir)
+ logging.error(
+ "Couldn't get the timestamp of the newest commit in repository: %s",
+ repo_dir,
+ )
return None
return output
@@ -118,7 +126,10 @@ class CGitRepositories:
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('Local directory is not a repository, going to overwrite it: %s', repo_dir)
+ logging.warning(
+ 'Local directory is not a repository, going to overwrite it: %s',
+ repo_dir,
+ )
return self._mirror(repo)
success, output = Git.capture('config', '--get', 'remote.origin.url')
@@ -126,14 +137,21 @@ class CGitRepositories:
# Every repository managed by this script should have the
# 'origin' remote. If it doesn't, it's trash. Overwrite the
# existing directory, mirroring the repository in it.
- logging.warning("Local repository doesn't have remote 'origin', going to overwrite it: %s", repo_dir)
+ logging.warning(
+ "Local repository doesn't have remote 'origin', going to overwrite it: %s",
+ repo_dir,
+ )
return self._mirror(repo)
if f'{repo.clone_url}\n' != output:
# Jeez, there's a proper local repository in the target
# directory already with a different upstream; something's
# wrong, fix it manually.
- logging.warning("Existing repository '%s' doesn't match the specified clone URL: %s", repo.name, repo.clone_url)
+ logging.warning(
+ "Existing repository '%s' doesn't match the specified clone URL: %s",
+ repo.name,
+ repo.clone_url,
+ )
if self.force:
# Unless --force was specified, in which case we overwrite
# the repository.
diff --git a/cgitize/config.py b/cgitize/config.py
index 4de7701..0e5fe6e 100644
--- a/cgitize/config.py
+++ b/cgitize/config.py
@@ -41,7 +41,9 @@ class MainSection(Section):
@property
def output_dir(self):
- return self._get_config_path('output_dir', default=MainSection.DEFAULT_OUTPUT_DIR)
+ return self._get_config_path(
+ 'output_dir', default=MainSection.DEFAULT_OUTPUT_DIR
+ )
@property
def error_header_path(self):
@@ -126,7 +128,9 @@ class GitHubSection(ServiceSection):
username = self.username
token = self.token
if (username is None) != (token is None):
- raise RuntimeError('please set either both the GitHub username & token, or neither')
+ raise RuntimeError(
+ 'please set either both the GitHub username & token, or neither'
+ )
return GitHub(username, token)
@@ -153,7 +157,9 @@ class BitbucketSection(ServiceSection):
username = self.username
token = self.token
if (username is None) != (token is None):
- raise RuntimeError('please set either both the Bitbucket username & token, or neither')
+ raise RuntimeError(
+ 'please set either both the Bitbucket username & token, or neither'
+ )
return Bitbucket(username, token)
@@ -174,7 +180,9 @@ class GitLabSection(ServiceSection):
username = self.username
token = self.token
if (username is None) != (token is None):
- raise RuntimeError('please set either both the GitLab username & token, or neither')
+ raise RuntimeError(
+ 'please set either both the GitLab username & token, or neither'
+ )
return GitLab(token)
diff --git a/cgitize/git.py b/cgitize/git.py
index 5ee3177..aba1f2b 100644
--- a/cgitize/git.py
+++ b/cgitize/git.py
@@ -8,9 +8,10 @@ import os
from cgitize import utils
-
GIT_ENV = os.environ.copy()
-GIT_ENV['GIT_SSH_COMMAND'] = 'ssh -oBatchMode=yes -oLogLevel=QUIET -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null'
+GIT_ENV['GIT_SSH_COMMAND'] = (
+ 'ssh -oBatchMode=yes -oLogLevel=QUIET -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null'
+)
class Config:
@@ -55,7 +56,9 @@ class Config:
for c in name:
if c.isalnum() or c == '-' or c == '.':
continue
- raise RuntimeError(f'section names must only contain alphanumeric characters, . or -: {name}')
+ raise RuntimeError(
+ f'section names must only contain alphanumeric characters, . or -: {name}'
+ )
@staticmethod
def format_name(name):
@@ -89,7 +92,9 @@ class Config:
return f'"{name}"'
def format(self):
- result = f'[{Config.Section.format_name(self.section)} {self.format_name()}]\n'
+ result = (
+ f'[{Config.Section.format_name(self.section)} {self.format_name()}]\n'
+ )
result += ''.join((var.format() for var in self.variables))
return result
@@ -107,9 +112,13 @@ class Config:
for c in name:
if c.isalnum() or c == '-':
continue
- raise RuntimeError(f'variable name can only contain alphanumeric characters or -: {name}')
+ raise RuntimeError(
+ f'variable name can only contain alphanumeric characters or -: {name}'
+ )
if not name[0].isalnum():
- raise RuntimeError(f'variable name must start with an alphanumeric character: {name}')
+ raise RuntimeError(
+ f'variable name must start with an alphanumeric character: {name}'
+ )
@staticmethod
def validate_value(value):
@@ -161,7 +170,9 @@ class Git:
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)
+ subsection = Config.Subsection(
+ 'url', repo.clone_url_with_auth, variables
+ )
new_contents = f'{old_contents}\n{subsection.format()}'
config.write(new_contents)
yield
diff --git a/cgitize/main.py b/cgitize/main.py
index 8e1f638..1d7245a 100644
--- a/cgitize/main.py
+++ b/cgitize/main.py
@@ -17,18 +17,25 @@ def parse_args(argv=None):
if argv is None:
argv = sys.argv[1:]
parser = ArgumentParser()
- parser.add_argument('--config', '-c', metavar='PATH',
- default=Config.DEFAULT_PATH,
- help='config file path')
- parser.add_argument('--repo', metavar='REPO_ID',
- nargs='*', dest='repos',
- help='repos to pull')
- parser.add_argument('--force', '-f', action='store_true',
- help='overwrite existing repositories')
- parser.add_argument('--verbose', '-v', action='store_true',
- help='verbose log output')
- parser.add_argument('--version', '-V', action='version',
- version=f'%(prog)s {__version__}')
+ parser.add_argument(
+ '--config',
+ '-c',
+ metavar='PATH',
+ default=Config.DEFAULT_PATH,
+ help='config file path',
+ )
+ parser.add_argument(
+ '--repo', metavar='REPO_ID', nargs='*', dest='repos', help='repos to pull'
+ )
+ parser.add_argument(
+ '--force', '-f', action='store_true', help='overwrite existing repositories'
+ )
+ parser.add_argument(
+ '--verbose', '-v', action='store_true', help='verbose log output'
+ )
+ parser.add_argument(
+ '--version', '-V', action='version', version=f'%(prog)s {__version__}'
+ )
return parser.parse_args(argv)
@@ -36,7 +43,9 @@ def setup_error_header_file(error_header_path, success, error=None):
with open(error_header_path, 'w') as file:
contents = ''
if not success:
- contents = '''<p style="text-align: center; color: red; font-weight: bold;">'''
+ contents = (
+ '''<p style="text-align: center; color: red; font-weight: bold;">'''
+ )
contents += '''Some repositories couldn't be updated, please check application logs for details.'''
if error is not None:
contents += f'''<br>{type(error).__name__}: {error}'''
@@ -53,7 +62,9 @@ def main(argv=None):
try:
cgit_server = CGitServer(config.main.clone_url)
- output = CGitRepositories(config.main.output_dir, cgit_server, force=args.force)
+ output = CGitRepositories(
+ config.main.output_dir, cgit_server, force=args.force
+ )
for repo in config.parse_repositories():
if args.repos is None or repo.name in args.repos:
success = success and output.update(repo)
diff --git a/cgitize/repo.py b/cgitize/repo.py
index 23a2b31..cabc7ba 100644
--- a/cgitize/repo.py
+++ b/cgitize/repo.py
@@ -21,8 +21,9 @@ class Repo:
raise ValueError("every repository must have 'clone_url'")
clone_url = src['clone_url']
subdir = src.get('dir')
- return Repo(name, clone_url, owner=owner, desc=desc, homepage=homepage,
- subdir=subdir)
+ return Repo(
+ name, clone_url, owner=owner, desc=desc, homepage=homepage, subdir=subdir
+ )
@staticmethod
def from_github(src, config, subdir=None):
@@ -36,8 +37,15 @@ class Repo:
clone_url = ssh_url if config.main.clone_via_ssh else https_url
url_auth = None if config.main.clone_via_ssh else config.github.url_auth
- return Repo(name, clone_url, owner=owner, desc=desc, homepage=homepage,
- url_auth=url_auth, subdir=subdir)
+ return Repo(
+ name,
+ clone_url,
+ owner=owner,
+ desc=desc,
+ homepage=homepage,
+ url_auth=url_auth,
+ subdir=subdir,
+ )
@staticmethod
def from_bitbucket(src, config, subdir=None):
@@ -46,13 +54,17 @@ class Repo:
homepage = src.get_link('html')
owner = src.data['owner']['display_name']
- https_urls = [link for link in src.data['links']['clone'] if link['name'] == 'https']
+ https_urls = [
+ link for link in src.data['links']['clone'] if link['name'] == 'https'
+ ]
if len(https_urls) != 1:
raise RuntimeError(f"no https:// clone URL for repository '{name}'?!")
# Bitbucket leaves the username in the URL... Sigh.
https_url = url_remove_auth(https_urls[0]['href'])
- ssh_urls = [link for link in src.data['links']['clone'] if link ['name'] == 'ssh']
+ ssh_urls = [
+ link for link in src.data['links']['clone'] if link['name'] == 'ssh'
+ ]
if len(ssh_urls) != 1:
raise RuntimeError(f"no ssh:// clone URL for repository '{name}'?!")
ssh_url = ssh_urls[0]['href']
@@ -60,8 +72,15 @@ class Repo:
clone_url = ssh_url if config.main.clone_via_ssh else https_url
url_auth = None if config.main.clone_via_ssh else config.bitbucket.url_auth
- return Repo(name, clone_url, owner=owner, desc=desc, homepage=homepage,
- url_auth=url_auth, subdir=subdir)
+ return Repo(
+ name,
+ clone_url,
+ owner=owner,
+ desc=desc,
+ homepage=homepage,
+ url_auth=url_auth,
+ subdir=subdir,
+ )
@staticmethod
def from_gitlab(src, config, subdir=None):
@@ -75,11 +94,26 @@ class Repo:
clone_url = ssh_url if config.main.clone_via_ssh else https_url
url_auth = None if config.main.clone_via_ssh else config.gitlab.url_auth
- return Repo(name, clone_url, owner=owner, desc=desc, homepage=homepage,
- url_auth=url_auth, subdir=subdir)
+ return Repo(
+ name,
+ clone_url,
+ owner=owner,
+ desc=desc,
+ homepage=homepage,
+ url_auth=url_auth,
+ subdir=subdir,
+ )
- def __init__(self, name, clone_url, owner=None, desc=None, homepage=None,
- url_auth=None, subdir=None):
+ def __init__(
+ self,
+ name,
+ clone_url,
+ owner=None,
+ desc=None,
+ homepage=None,
+ url_auth=None,
+ subdir=None,
+ ):
self._name = name
self._desc = desc
self._homepage = homepage
diff --git a/cgitize/utils.py b/cgitize/utils.py
index c3a4fff..dd27f14 100644
--- a/cgitize/utils.py
+++ b/cgitize/utils.py
@@ -21,7 +21,8 @@ def setup_logging(verbose=False):
format='%(asctime)s | %(levelname)s | %(message)s',
# Log to stdout, because that's where subprocess's output goes (so that
# the don't get interleaved).
- stream=sys.stdout)
+ stream=sys.stdout,
+ )
try:
yield
except Exception as e:
@@ -37,8 +38,9 @@ def run(*args, capture_output=False, **kwargs):
stderr = subprocess.STDOUT
logging.debug('%s', args)
- result = subprocess.run(args, check=True, stdout=stdout, stderr=stderr,
- encoding='utf-8', **kwargs)
+ result = subprocess.run(
+ args, check=True, stdout=stdout, stderr=stderr, encoding='utf-8', **kwargs
+ )
if result.stdout is not None:
logging.debug('\n%s', result.stdout)
diff --git a/docker/get_output_dir.py b/docker/get_output_dir.py
index 5ca0055..ff0d35a 100755
--- a/docker/get_output_dir.py
+++ b/docker/get_output_dir.py
@@ -10,8 +10,7 @@ def parse_args(argv=None):
if argv is None:
argv = sys.argv[1:]
parser = ArgumentParser()
- parser.add_argument('config', metavar='PATH',
- help='config file path')
+ parser.add_argument('config', metavar='PATH', help='config file path')
return parser.parse_args(argv)
diff --git a/test/unit/test_bitbucket.py b/test/unit/test_bitbucket.py
index efb325d..221fa79 100644
--- a/test/unit/test_bitbucket.py
+++ b/test/unit/test_bitbucket.py
@@ -15,7 +15,8 @@ class BitbucketTests(unittest.TestCase):
self.bitbucket = Cloud(
username=os.environ.get('CGITIZE_BITBUCKET_USERNAME'),
password=os.environ.get('CGITIZE_BITBUCKET_TOKEN'),
- cloud=True)
+ cloud=True,
+ )
def test_nonexistent_repo(self):
with self.assertRaises(HTTPError):
@@ -29,15 +30,24 @@ class BitbucketTests(unittest.TestCase):
self.assertEqual(r.data['workspace']['name'], 'Test cgitize workspace')
self.assertEqual(r.data['workspace']['slug'], 'cgitize-test-workspace')
- self.assertEqual(r.get_link('html'), 'https://bitbucket.org/cgitize-test-workspace/public')
+ self.assertEqual(
+ r.get_link('html'), 'https://bitbucket.org/cgitize-test-workspace/public'
+ )
- clone_urls = [link for link in r.data['links']['clone'] if link['name'] == 'https']
+ clone_urls = [
+ link for link in r.data['links']['clone'] if link['name'] == 'https'
+ ]
self.assertEqual(len(clone_urls), 1)
- self.assertEqual(clone_urls[0]['href'], 'https://cgitize-test@bitbucket.org/cgitize-test-workspace/public.git')
+ self.assertEqual(
+ clone_urls[0]['href'],
+ 'https://cgitize-test@bitbucket.org/cgitize-test-workspace/public.git',
+ )
ssh_urls = [link for link in r.data['links']['clone'] if link['name'] == 'ssh']
self.assertEqual(len(ssh_urls), 1)
- self.assertEqual(ssh_urls[0]['href'], 'git@bitbucket.org:cgitize-test-workspace/public.git')
+ self.assertEqual(
+ ssh_urls[0]['href'], 'git@bitbucket.org:cgitize-test-workspace/public.git'
+ )
def test_private_repo(self):
r = self.bitbucket.repositories.get('cgitize-test-workspace', 'private')
@@ -47,12 +57,18 @@ class BitbucketTests(unittest.TestCase):
self.assertEqual(r.data['workspace']['name'], 'Test cgitize workspace')
self.assertEqual(r.data['workspace']['slug'], 'cgitize-test-workspace')
- self.assertEqual(r.get_link('html'), 'https://bitbucket.org/cgitize-test-workspace/private')
+ self.assertEqual(
+ r.get_link('html'), 'https://bitbucket.org/cgitize-test-workspace/private'
+ )
def test_user(self):
w = self.bitbucket.workspaces.get('cgitize-test-workspace')
- self.assertEqual(len([r for r in w.repositories.each() if r.name == 'public']), 1)
- self.assertEqual(len([r for r in w.repositories.each() if r.name == 'private']), 1)
+ self.assertEqual(
+ len([r for r in w.repositories.each() if r.name == 'public']), 1
+ )
+ self.assertEqual(
+ len([r for r in w.repositories.each() if r.name == 'private']), 1
+ )
class BitbucketTestPrivateRepo(unittest.TestCase):
diff --git a/test/unit/test_gitlab.py b/test/unit/test_gitlab.py
index 7243dbd..08eb808 100644
--- a/test/unit/test_gitlab.py
+++ b/test/unit/test_gitlab.py
@@ -13,8 +13,8 @@ from gitlab.exceptions import GitlabGetError
class GitLabTests(unittest.TestCase):
def setUp(self):
self.gitlab = Gitlab(
- 'https://gitlab.com',
- private_token=os.environ.get('CGITIZE_GITLAB_TOKEN'))
+ 'https://gitlab.com', private_token=os.environ.get('CGITIZE_GITLAB_TOKEN')
+ )
def test_nonexistent_repo(self):
with self.assertRaises(GitlabGetError):
@@ -27,7 +27,9 @@ class GitLabTests(unittest.TestCase):
self.assertEqual(r.namespace['name'], 'Test cgitize user')
self.assertEqual(r.namespace['path'], 'cgitize-test')
self.assertEqual(r.web_url, 'https://gitlab.com/cgitize-test/public')
- self.assertEqual(r.http_url_to_repo, 'https://gitlab.com/cgitize-test/public.git')
+ self.assertEqual(
+ r.http_url_to_repo, 'https://gitlab.com/cgitize-test/public.git'
+ )
self.assertEqual(r.ssh_url_to_repo, 'git@gitlab.com:cgitize-test/public.git')
def test_private_repo(self):
@@ -37,7 +39,9 @@ class GitLabTests(unittest.TestCase):
self.assertEqual(r.namespace['name'], 'Test cgitize user')
self.assertEqual(r.namespace['path'], 'cgitize-test')
self.assertEqual(r.web_url, 'https://gitlab.com/cgitize-test/private')
- self.assertEqual(r.http_url_to_repo, 'https://gitlab.com/cgitize-test/private.git')
+ self.assertEqual(
+ r.http_url_to_repo, 'https://gitlab.com/cgitize-test/private.git'
+ )
self.assertEqual(r.ssh_url_to_repo, 'git@gitlab.com:cgitize-test/private.git')
def test_user(self):