diff --git a/Jenkins/daily-cleanup/Jenkinsfile b/Jenkins/daily-cleanup/Jenkinsfile new file mode 100644 index 0000000..09a4618 --- /dev/null +++ b/Jenkins/daily-cleanup/Jenkinsfile @@ -0,0 +1,55 @@ +// Copyright 2019 Google LLC +// +// Licensed under the the Apache License v2.0 with LLVM Exceptions (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://llvm.org/LICENSE.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/* Pipeline executed daily to do cleanup work */ + +def success = true +def failure_message = "" + +pipeline { + agent { label 'linux' } + triggers { + cron('@daily') + } + environment { + SCRIPT_DIR = "${WORKSPACE}/llvm-premerge-checks/scripts" + } + options { + timeout(time:10, unit:'MINUTES') + } + stages { + stage("git checkout"){ + steps { + dir("${LLVM_DIR}"){ + git url: 'git@github.com:llvm-premerge-tests/llvm-project.git' + } + dir("llvm-premerge-checks") + { + git url: 'https://github.com/google/llvm-premerge-checks.git' + } + } + } + stage('cleanup repo'){ + // delete old branches after 30 days + steps { + dir("${LLVM_DIR}"){ + sh """ + python ${SCRIPT_DIR}/cleanup_branches.py --days 30 --pattern "phab-diff-.*" + """ + } + } + } + } +} diff --git a/scripts/cleanup_branches.py b/scripts/cleanup_branches.py new file mode 100644 index 0000000..655ad3b --- /dev/null +++ b/scripts/cleanup_branches.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# Copyright 2020 Google LLC +# +# Licensed under the the Apache License v2.0 with LLVM Exceptions (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The script will delete old git branches.""" + +import argparse +import datetime +import functools +import git +import operator +import os +import re +from typing import List + + +def delete_old_branches(repo_path: str, max_age: datetime.datetime, branch_patterns: List[re.Pattern], + *, dry_run: bool = True, remote_name: str = 'origin'): + """Deletes 'old' branches from a git repo. + + This script assumes that $repo_path contains a current checkout of the repository ot be cleaned up. + """ + repo = git.Repo(repo_path) + refs = repo.remotes[remote_name].refs + print('Found {} references at {} in total.'.format(len(refs), remote_name)) + del_count = 0 + for reference in refs: + committed_date = datetime.datetime.fromtimestamp(reference.commit.committed_date) + if committed_date < max_age and _has_pattern_match(reference.name, branch_patterns): + if dry_run: + print('dryrun: would have deleted {}'.format(reference.name)) + else: + print('Deleting {}'.format(reference.name)) + git.RemoteReference.delete(repo.remotes[remote_name].repo, reference) + del_count += 1 + + print('Deleted {} references.'.format(del_count)) + if not dry_run: + print('Pushing to remote...') + repo.remotes[remote_name].push() + print('Done.') + + +def _has_pattern_match(name: str, patterns) -> bool: + """Check if name matches any of the patterns""" + return functools.reduce( + operator.or_, + map(lambda r: r.search(name) is not None, patterns), + False) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Clean a git repository') + parser.add_argument('repo_path', type=str, nargs='?', default=os.getcwd()) + parser.add_argument('--days', type=int, default=30) + parser.add_argument('--pattern', action='append', type=str) + parser.add_argument('--dryrun', action='store_true') + args = parser.parse_args() + + max_age = datetime.datetime.now() - datetime.timedelta(days=args.days) + branch_pattern = [re.compile(r) for r in args.pattern] + delete_old_branches(args.repo_path, max_age, branch_pattern, dry_run=args.dryrun)