Skip to content

Commit 2953508

Browse files
authored
Merge pull request #18 from hartwork/pre-commit
Make GitHub Actions enforce pre-commit clean code + address some ruff warnings
2 parents a28f07f + b5e9e86 commit 2953508

File tree

5 files changed

+186
-38
lines changed

5 files changed

+186
-38
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Copyright (c) 2025 Sebastian Pipping <[email protected]>
2+
# Licensed under GNU Affero GPL v3 or later
3+
4+
name: Detect outdated pre-commit hooks
5+
6+
on:
7+
schedule:
8+
- cron: '0 2 * * 5' # Every Friday at 2am
9+
workflow_dispatch:
10+
11+
# NOTE: This will drop all permissions from GITHUB_TOKEN except metadata read,
12+
# and then (re)add the ones listed below:
13+
permissions:
14+
contents: write
15+
pull-requests: write
16+
17+
jobs:
18+
pre_commit_detect_outdated:
19+
name: Detect outdated pre-commit hooks
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
23+
24+
- name: Set up Python 3.13
25+
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
26+
with:
27+
python-version: 3.13
28+
29+
- name: Install pre-commit
30+
run: |-
31+
pip install \
32+
--disable-pip-version-check \
33+
--no-warn-script-location \
34+
--user \
35+
pre-commit
36+
echo "PATH=${HOME}/.local/bin:${PATH}" >> "${GITHUB_ENV}"
37+
38+
- name: Check for outdated hooks
39+
run: |-
40+
pre-commit autoupdate
41+
git diff -- .pre-commit-config.yaml
42+
43+
- name: Create pull request from changes (if any)
44+
id: create-pull-request
45+
uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5
46+
with:
47+
author: 'pre-commit <[email protected]>'
48+
base: main
49+
body: |-
50+
For your consideration.
51+
52+
:warning: Please **CLOSE AND RE-OPEN** this pull request so that [further workflow runs get triggered](https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#triggering-further-workflow-runs) for this pull request.
53+
branch: precommit-autoupdate
54+
commit-message: "pre-commit: Autoupdate"
55+
delete-branch: true
56+
draft: true
57+
labels: enhancement
58+
title: "pre-commit: Autoupdate"
59+
60+
- name: Log pull request URL
61+
if: "${{ steps.create-pull-request.outputs.pull-request-url }}"
62+
run: |
63+
echo "Pull request URL is: ${{ steps.create-pull-request.outputs.pull-request-url }}"

.github/workflows/pre-commit.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright (c) 2025 Sebastian Pipping <[email protected]>
2+
# Licensed under GNU Affero GPL v3 or later
3+
4+
name: Run pre-commit
5+
6+
on:
7+
pull_request:
8+
push:
9+
schedule:
10+
- cron: '0 2 * * 5' # Every Friday at 2am
11+
workflow_dispatch:
12+
13+
# Drop permissions to minimum for security
14+
permissions:
15+
contents: read
16+
17+
jobs:
18+
pre-commit:
19+
name: Run pre-commit
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
23+
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
24+
with:
25+
python-version: 3.13
26+
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1

.pre-commit-config.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright (c) 2025 Sebastian Pipping <[email protected]>
2+
# Licensed under GNU Affero GPL v3 or later
3+
4+
repos:
5+
- repo: https://github.com/pre-commit/pre-commit-hooks
6+
rev: v5.0.0
7+
hooks:
8+
- id: check-merge-conflict
9+
- id: check-toml
10+
- id: check-yaml
11+
- id: end-of-file-fixer
12+
- id: trailing-whitespace
13+
14+
- repo: https://github.com/astral-sh/ruff-pre-commit
15+
rev: v0.8.4
16+
hooks:
17+
- id: ruff
18+
args:
19+
- --fix
20+
- id: ruff-format

Caddyfile.generate

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,22 @@
55
import subprocess
66
import sys
77
from argparse import ArgumentParser
8+
from collections import namedtuple
89
from configparser import ConfigParser, NoOptionError
910
from contextlib import suppress
10-
from collections import namedtuple
1111
from tempfile import NamedTemporaryFile
1212
from textwrap import dedent
1313

1414

1515
class CaddyfileGenerator:
16-
17-
Site = namedtuple('Site', [
18-
'alias_domains',
19-
'backend_authority',
20-
'domain',
21-
])
16+
Site = namedtuple(
17+
"Site",
18+
[
19+
"alias_domains",
20+
"backend_authority",
21+
"domain",
22+
],
23+
)
2224

2325
def __init__(self):
2426
self._redir_target_of = {}
@@ -30,31 +32,41 @@ class CaddyfileGenerator:
3032
self._redir_target_of[domain] = site.domain
3133

3234
def write_to(self, fp):
33-
print(dedent("""\
35+
print(
36+
dedent("""\
3437
# NOTE: This file has been generated, do not edit
3538
(common) {
3639
encode zstd gzip
3740
log {
3841
output stdout
3942
}
40-
}"""), file=fp)
43+
}"""),
44+
file=fp,
45+
)
4146

4247
for domain, backend_authority in sorted(self._backend_of.items()):
43-
print(dedent("""
48+
print(
49+
dedent("""
4450
%s {
4551
import common
4652
reverse_proxy %s {
4753
header_down +Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
4854
}
49-
}""") % (domain, backend_authority), file=fp)
50-
51-
for source_domain, target_domain in sorted(
52-
self._redir_target_of.items()):
53-
print(dedent("""
55+
}""") # noqa: E501
56+
% (domain, backend_authority),
57+
file=fp,
58+
)
59+
60+
for source_domain, target_domain in sorted(self._redir_target_of.items()):
61+
print(
62+
dedent("""
5463
%s {
5564
import common
5665
redir https://%s{uri}
57-
}""") % (source_domain, target_domain), file=fp)
66+
}""")
67+
% (source_domain, target_domain),
68+
file=fp,
69+
)
5870

5971

6072
def run(options):
@@ -65,51 +77,59 @@ def run(options):
6577
caddyfile = CaddyfileGenerator()
6678
for domain in config.sections():
6779
try:
68-
alias_domains = config.get(domain, 'aliases').split()
80+
alias_domains = config.get(domain, "aliases").split()
6981
except NoOptionError:
7082
alias_domains = []
71-
backend_authority = config.get(domain, 'backend')
83+
backend_authority = config.get(domain, "backend")
7284

73-
site = CaddyfileGenerator.Site(alias_domains, backend_authority,
74-
domain)
85+
site = CaddyfileGenerator.Site(alias_domains, backend_authority, domain)
7586
caddyfile.add(site)
7687

7788
with NamedTemporaryFile() as temp_file:
7889
# The idea is to diff against previous content or against
7990
# empty file when there is no previos content
80-
with suppress(OSError):
81-
with open(options.output_filename, 'r+b') as fin:
91+
with suppress(OSError): # noqa: SIM117
92+
with open(options.output_filename, "r+b") as fin:
8293
temp_file.file.write(fin.read())
8394
temp_file.file.flush()
8495

85-
with open(options.output_filename, 'w') as fout:
96+
with open(options.output_filename, "w") as fout:
8697
caddyfile.write_to(fout)
8798

88-
exit_code = subprocess.call(['diff', '-u', temp_file.name,
89-
options.output_filename])
99+
exit_code = subprocess.call( # noqa: S603
100+
["diff", "-u", temp_file.name, options.output_filename],
101+
)
90102

91103
# Write stateful output in the format expected by SaltStack
92104
if exit_code and options.saltstack:
93105
print()
94106
print("changed=yes comment='Caddyfile changed'")
95107

96108

97-
if __name__ == '__main__':
109+
if __name__ == "__main__":
98110
parser = ArgumentParser()
99-
parser.add_argument('--config', dest='config_filename', metavar='FILENAME',
100-
default='sites.cfg',
101-
help='Path to config file to read'
102-
' (default: %(default)s)')
103-
parser.add_argument('--output', dest='output_filename', metavar='FILENAME',
104-
default='Caddyfile',
105-
help='Path to write Caddyfile to'
106-
' (default: %(default)s)')
107-
parser.add_argument('--saltstack', action='store_true',
108-
help='Add lines signaling changes to SaltStack'
109-
' (default: omitted)')
111+
parser.add_argument(
112+
"--config",
113+
dest="config_filename",
114+
metavar="FILENAME",
115+
default="sites.cfg",
116+
help="Path to config file to read (default: %(default)s)",
117+
)
118+
parser.add_argument(
119+
"--output",
120+
dest="output_filename",
121+
metavar="FILENAME",
122+
default="Caddyfile",
123+
help="Path to write Caddyfile to (default: %(default)s)",
124+
)
125+
parser.add_argument(
126+
"--saltstack",
127+
action="store_true",
128+
help="Add lines signaling changes to SaltStack (default: omitted)",
129+
)
110130
options = parser.parse_args()
111131

112132
try:
113133
run(options)
114-
except IOError as e:
134+
except OSError as e:
115135
sys.exit(e)

ruff.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
indent-width = 4
2+
line-length = 99
3+
target-version = "py39"
4+
5+
[lint]
6+
select = [
7+
"ALL",
8+
]
9+
10+
ignore = [
11+
"ANN", # flake8-annotations
12+
"D1", # Missing docstring in public ...
13+
"D203", # one-blank-line-before-class, to unclash with D211
14+
"D212", # multi-line-summary-first-line, to unclash with D213
15+
"PTH123", # `open()` should be replaced by `Path.open()`
16+
"PYI024", # Use `typing.NamedTuple` instead of `collections.namedtuple`
17+
"S607", # Starting a process with a partial executable path
18+
"T201", # `print` found
19+
]

0 commit comments

Comments
 (0)