mirror of
https://github.com/rclone/rclone.git
synced 2025-08-05 12:25:59 +02:00
Before this change new backend docs would have their changes flagged which is undesirable for the first revision.
138 lines
4.1 KiB
Python
Executable File
138 lines
4.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
This script checks for unauthorized modifications in autogenerated sections of markdown files.
|
|
It is designed to be used in a GitHub Actions workflow or a local pre-commit hook.
|
|
|
|
Features:
|
|
- Detects markdown files changed in the last commit.
|
|
- Identifies modified autogenerated sections marked by specific comments.
|
|
- Reports violations using GitHub Actions error messages.
|
|
- Exits with a nonzero status code if unauthorized changes are found.
|
|
|
|
It currently only checks the last commit.
|
|
"""
|
|
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
def run_git(args):
|
|
"""
|
|
Run a Git command with the provided arguments and return its output as a string.
|
|
"""
|
|
return subprocess.run(["git"] + args, stdout=subprocess.PIPE, text=True, check=True).stdout.strip()
|
|
|
|
def get_changed_files():
|
|
"""
|
|
Retrieve a list of markdown files that were changed in the last commit.
|
|
"""
|
|
files = run_git(["diff", "--name-only", "HEAD~1", "HEAD"]).splitlines()
|
|
return [f for f in files if f.endswith(".md")]
|
|
|
|
def get_diff(file):
|
|
"""
|
|
Get the diff of a given file between the last commit and the current version.
|
|
"""
|
|
return run_git(["diff", "-U0", "HEAD~1", "HEAD", "--", file]).splitlines()
|
|
|
|
def get_file_content(ref, file):
|
|
"""
|
|
Retrieve the content of a file from a given Git reference.
|
|
"""
|
|
try:
|
|
return run_git(["show", f"{ref}:{file}"]).splitlines()
|
|
except Exception:
|
|
return []
|
|
|
|
def find_regions(lines):
|
|
"""
|
|
Identify the start and end line numbers of autogenerated regions in a file.
|
|
"""
|
|
regions = []
|
|
start = None
|
|
for i, line in enumerate(lines, 1):
|
|
if "rem autogenerated options start" in line:
|
|
start = i
|
|
elif "rem autogenerated options stop" in line and start is not None:
|
|
regions.append((start, i))
|
|
start = None
|
|
return regions
|
|
|
|
def in_region(ln, regions):
|
|
"""
|
|
Check if a given line number falls within an autogenerated region.
|
|
"""
|
|
return any(start <= ln <= end for start, end in regions)
|
|
|
|
def show_error(file_name, line, message):
|
|
"""
|
|
Print an error message in a GitHub Actions-compatible format.
|
|
"""
|
|
print(f"::error file={file_name},line={line}::{message} at {file_name} line {line}")
|
|
|
|
def check_file(file):
|
|
"""
|
|
Check a markdown file for modifications in autogenerated regions.
|
|
"""
|
|
viol = False
|
|
new_lines = get_file_content("HEAD", file)
|
|
old_lines = get_file_content("HEAD~1", file)
|
|
|
|
# If old file did not exist or was empty then don't check
|
|
if not old_lines:
|
|
return
|
|
|
|
# Entire autogenerated file check.
|
|
if any("autogenerated - DO NOT EDIT" in l for l in new_lines[:10]):
|
|
if get_diff(file):
|
|
show_error(file, 1, "Autogenerated file modified")
|
|
return True
|
|
return False
|
|
|
|
# Partial autogenerated regions.
|
|
regions_new = find_regions(new_lines)
|
|
regions_old = find_regions(old_lines)
|
|
diff = get_diff(file)
|
|
hunk_re = re.compile(r"^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@")
|
|
new_ln = old_ln = None
|
|
|
|
for line in diff:
|
|
if line.startswith("@@"):
|
|
m = hunk_re.match(line)
|
|
if m:
|
|
old_ln = int(m.group(1))
|
|
new_ln = int(m.group(3))
|
|
elif new_ln is None:
|
|
continue
|
|
elif line.startswith("+"):
|
|
if in_region(new_ln, regions_new):
|
|
show_error(file, new_ln, "Autogenerated region of file modified")
|
|
viol = True
|
|
new_ln += 1
|
|
elif line.startswith("-"):
|
|
if in_region(old_ln, regions_old):
|
|
show_error(file, old_ln, "Autogenerated region of file modified")
|
|
viol = True
|
|
old_ln += 1
|
|
else:
|
|
new_ln += 1
|
|
old_ln += 1
|
|
|
|
return viol
|
|
|
|
def main():
|
|
"""
|
|
Main function that iterates over changed files and checks them for violations.
|
|
"""
|
|
found = False
|
|
for f in get_changed_files():
|
|
if check_file(f):
|
|
found = True
|
|
if found:
|
|
sys.exit(1)
|
|
print("No unauthorized edits found in autogenerated sections.")
|
|
sys.exit(0)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|