Skip to content

Instantly share code, notes, and snippets.

@jdlrobson
Last active February 20, 2019 19:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jdlrobson/a2dba042579e85e5180e5106d9ecec1a to your computer and use it in GitHub Desktop.
Save jdlrobson/a2dba042579e85e5180e5106d9ecec1a to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
'''
Copyright [2017] [Jon Robson]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
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.
'''
import json
import os
import subprocess
import select
import time
import datetime
import signal
import argparse
DRY_RUN = False
busy = False
HOST_NAME = 'gerrit.wikimedia.org'
ORIGIN = 'gerrit' #make origin
def get_parser():
helper = {
'project': 'A valid project name e.g. `mediawiki/extensions/Popups`',
}
parser = argparse.ArgumentParser()
for key, msg in helper.items():
parser.add_argument('--%s'%key, help=msg)
return parser
def log(msg):
print msg
f = open('pixies.log', 'a')
f.write('[%s] %s\n'%(datetime.datetime.utcnow(), msg))
f.close()
def getCommit():
process = subprocess.Popen('git rev-parse HEAD', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
output, error = process.communicate()
return output.strip()
def submitReview( score, msg = None ):
if DRY_RUN:
log("Operating in dry run mode. Message not sent.")
return
args = ['ssh', '-p 29418',
HOST_NAME, 'gerrit', 'review',
'--code-review', score ]
if msg is None:
if score == "-1":
msg = "\I cannot rebase this manually."
else:
msg = "\I have rebased your patch for you against current master."
args.extend( [ '--message', '"%s"'%msg ] )
args.append( getCommit() )
subprocess.Popen( args ).communicate()
def runCommand(cmd):
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
output, error = process.communicate()
log("run %s"%cmd)
return output, error
def rebaser(gerritId, parent):
log("rebasing %s for %s"%(gerritId, parent))
runCommand("pwd")
runCommand("git fetch %s"%ORIGIN)
runCommand("git stash")
runCommand("git checkout %s/master"%ORIGIN)
msg, err = runCommand("git branch -r --contains %s"%parent)
runCommand("git review -d %s"%gerritId)
npmInstall, err = runCommand("git diff HEAD^ package.json")
if npmInstall:
log("package.json update detected. Running `npm install`.")
runCommand("npm install")
if not msg:
log("Null op - submitting review")
submitReview("0", "This patch has dependencies. Please ask me again when it doesn't.")
else:
log("Informing requestor of progress")
submitReview("0", "message received master!")
runCommand("git rebase %s/master"%ORIGIN)
runCommand("npm run build")
dirty, err = runCommand("git diff | grep \"<<<<\"")
if not dirty:
log("Diff is not dirty")
runCommand("git add -u")
runCommand("git rebase --continue")
# Just in case the patch didn't need a rebase..
runCommand("git commit --amend -e --no-verify --no-edit")
runCommand("git review")
log("Submit review")
submitReview("0")
else:
log("Diff is dirty")
runCommand("git rebase --abort")
log("Submit review")
submitReview("-1")
def processEvent(event):
busy = True
if "change" in event:
gerritId = event["change"]["number"]
log("Processing change %s"%gerritId)
plusTwo = False
if "comment" in event and event["comment"].endswith("\nrebase") or plusTwo:
if "patchSet" in event and "parents" in event["patchSet"]:
parents = event["patchSet"]["parents"]
if len(parents) == 1:
parentCommit = parents[0]
rebaser(gerritId, parentCommit)
else:
log("Several parents, cannot rebase: %s"%parents)
else:
log("Ignoring")
else:
log("Unknown event. Cannot process.")
log("accepting new jobs")
busy = False
# 1 tick = 1s
TICK = 1
# every 4 hrs reboot
RESTART_TICKS = ( 60 * 60 ) * 4
def watch(project):
log("Watching")
ticks = 0
args = ['ssh', '-p 29418', HOST_NAME, 'gerrit', 'stream-events' ]
process = subprocess.Popen( args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid )
p = select.poll()
p.register(process.stdout)
while True:
if busy:
log("busy")
elif p.poll(1):
line = process.stdout.readline()
try:
event = json.loads(line)
if "project" in event and event["project"] == project:
log("Process event")
processEvent(event)
except ValueError:
log("Cannot read" + line)
pass
log("sleeping")
ticks += 1
if ticks > RESTART_TICKS:
log("refreshing ssh connection")
os.killpg( os.getpgid( process.pid ), signal.SIGTERM )
process = subprocess.Popen( args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid )
p = select.poll()
p.register(process.stdout)
ticks = 0
log("refreshed ssh connection")
time.sleep(TICK)
if __name__ == '__main__':
parser = get_parser()
args = parser.parse_args()
try:
project = args.project
runCommand("nvm use")
log('Running npm install, please wait')
runCommand("npm install")
log('Begin watching')
watch(project)
except KeyError:
log("ERROR: Please define a project")
@jdlrobson
Copy link
Author

jdlrobson commented Jun 16, 2017

L105 - I want to turn this on but I can't seem to get the watch method working. It just prints to the terminal... that's the final piece of the puzzle

Then all you need to do is run this from inside the extension directory...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment