diff --git a/hooks/invent.update b/hooks/invent.update index 444ad92..737e107 100755 --- a/hooks/invent.update +++ b/hooks/invent.update @@ -1,205 +1,212 @@ #!/usr/bin/env python # Load dependencies import os import re import sys import subprocess from hooklib import Repository, RepositoryMetadataLoader, Commit, CommitAuditor, CommitNotifier, MessageBuilder, CommitChecker, CiaNotifier, RepoType, ChangeType, RefType def usage(): print "Information needed to run could not be gathered successfully." print "Required environment variables: GIT_DIR, GL_USERNAME, HOME" print "Syntax: update.secondary " exit(1) def maintenance(): print "Sorry, but the repository you are trying to access is currently unavailable." print "This is to allow for maintenance, we apologise for any inconvience caused." print "If you believe this not to be the case, please contact sysadmin@kde.org." exit(1) ##### # Initialisation ##### # Read arguments... try: ref_name = sys.argv[1] old_sha1 = sys.argv[2] new_sha1 = sys.argv[3] except IndexError: usage() # Do we need to make sure GIT_DIR is around? # With Gitaly this isn't always the case if 'GIT_DIR' not in os.environ: os.environ['GIT_DIR'] = os.getenv('PWD') # Read needed environment variables git_dir = os.getenv('GIT_DIR') push_user = os.getenv('GL_USERNAME') user_home = os.getenv('HOME') # Initialise the repository if os.path.exists("/srv/gitlab/repositories"): Repository.BaseDir = "/srv/gitlab/repositories/" Commit.UrlPattern = "https://invent.kde.org/{0}/commit/{1}" else: print "Base directory could not be found" exit(1) repository = Repository( ref_name, old_sha1, new_sha1, push_user ) ##### # Redetermine the repository type # Because things are structured differently in a Gitlab world we need a different set of checks to determine the type of repository we have here ##### # Starting position is that repositories should be treated as if they are scratch (personal) repositories repository.repo_type = RepoType.Scratch # Is this potentially a mainline (kde/) repository? mainlineRepository = re.match("^kde/(.+)$", repository.path) if mainlineRepository: repository.repo_type = RepoType.Normal repository.path = mainlineRepository.group(1) # Website live in the websites/ namespace elif re.match("^websites/(.+)$", repository.path): repository.repo_type = RepoType.Website # Sysadmin repositories live in the sysadmin/ namespace elif re.match("^sysadmin/(.+)$", repository.path): repository.repo_type = RepoType.Sysadmin ##### # Auditing ##### # Repository change checks... push_size_restricted = [RepoType.Normal, RepoType.Website, RepoType.Sysadmin] if repository.ref_type == RefType.Backup: print "Pushing to backup refs is not supported for security reasons" print "Push declined - attempted repository integrity violation" exit(1) elif repository.ref_type == RefType.Upstream: print "Pushing to server maintained upstreams is not permitted" print "Push declined - attempted repository integrity violation" exit(1) elif repository.ref_type == RefType.Unknown: print "Sorry, but the ref you are trying to push to could not be recognised." print "Only pushes to branches, tags and notes are permitted." exit(1) elif repository.ref_name == "HEAD": print "Creating refs which conflict with internally used names is not permitted." print "Push declined - attempted repository integrity violation" exit(1) elif repository.change_type == ChangeType.Create and re.match("^origin/(.+)$", repository.ref_name): print "Creating refs starting with the name origin/ is not permitted." print "This is usually caused by incorrectly pushing a branch or tag." print "Please ensure your remote branch name does not contain 'origin/' at the beginning" print "Push declined - attempted repository integrity violation" exit(1) if repository.ref_type == RefType.Tag and repository.change_type != ChangeType.Delete and repository.commit_type != "tag": print "Pushing an unannotated tag is not permitted." print "Push declined - attempted repository integrity violation" exit(1) +# Force pushes must be specially allowed +force_push_base = repository.management_directory + "/repo-configs/force-push/" +if repository.change_type == ChangeType.Forced and repository.repo_type in push_size_restricted and not os.path.exists(force_push_base + repository.path): + print "Force pushes to mainline KDE repositories is only permitted for specific situations." + print "Please contact the KDE Sysadmin team for further assistance" + exit(1) + # New commits... notification_base = repository.management_directory + "/repo-configs/notifications/" if len(repository.commits) > 100 and not os.path.exists(notification_base + repository.path + ".git/skip-notifications") and repository.repo_type in push_size_restricted: print "More than 100 commits are being pushed" print "Push declined - excessive notifications would be sent" print "Please file a KDE Sysadmin ticket to continue" exit(1) # Lets check the commits themselves now auditor = CommitAuditor( repository ) audit_base = repository.management_directory + "/repo-configs/audit/" if not os.path.exists(audit_base + repository.path + ".git/skip-eol"): auditor.audit_eol() if not os.path.exists(audit_base + repository.path + ".git/skip-filename"): auditor.audit_filename() if not os.path.exists(audit_base + repository.path + ".git/skip-author-names"): auditor.audit_names_in_metadata() if not os.path.exists(audit_base + repository.path + ".git/skip-author-emails"): auditor.audit_emails_in_metadata() blocked_path = repository.management_directory + "/repo-configs/blocked/" + repository.path if os.path.exists(blocked_path): auditor.audit_hashes(blocked_path) # Did we have any commit audit failures? if auditor.audit_failed: print "Push declined - commits failed audit" exit(1) ##### # Pre-acceptance ##### # Do we need to back it up?? if repository.change_type == ChangeType.Forced or repository.change_type == ChangeType.Delete: repository.backup_ref() ##### # Post acceptance ##### # Are post commands supposed to be run? if os.path.exists(notification_base + repository.path + ".git/skip-notifications"): print "Hooks are currently disabled!" exit(0) # Does this user need a special post-update skip? post_exceptions = ["scripty"] if push_user in post_exceptions: exit(0) # Output a helpful url.... if repository.new_sha1 in repository.commits: if len(repository.commits) == 1: print "This commit is available for viewing at:" else: print "The last commit in this series is available for viewing at:" print repository.commits[ repository.new_sha1 ].url # Are we allowed to send notifications on this repo? notify_allowed = [RepoType.Normal, RepoType.Website, RepoType.Sysadmin] if not repository.repo_type in notify_allowed: exit(0) # Prepare to send notifications notifier = CommitNotifier() cia = CiaNotifier(repository) checker = CommitChecker() # Perform notifications for (commit, diff) in notifier.handler(repository): # Check for license, etc problems in the commit checker.check_commit_problems( commit, diff ) # Create the message builder in preperation to send notifications builder = MessageBuilder( repository, commit, checker ) builder.determine_keywords() # Do CIA (IRC Notifications) cia.notify(builder) if repository.repo_type == RepoType.Sysadmin: notify_address = "sysadmin-svn@kde.org" else: notify_address = "kde-commits@kde.org" notifier.notify_email( builder, notify_address, diff ) # Handle Bugzilla notifier.notify_bugzilla( builder ) # Everything is done.... exit(0)