Files
@ 42155469350e
Branch filter:
Location: majic-scripts/hooks/thebuggenie_hg_remote.py
42155469350e
9.1 KiB
text/x-python
Noticket: Added a small script for creating quick backups of files before making changes to them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | #
# Hook for submitting Mercurial pushed commits to The Bug Genie using the HTTP
# Access mode.
#
# The hook can be used either as a 'changegroup' or 'incoming' hook. When
# executed as a changegroup hook, all pushed changes will be processed.
#
# In order to use same hook configuration for all of user's repositories, it is
# required to modify the the user's ~/.hgrc Mercurial configuration file. In
# order to have per-repository configuration for the hook, use induvidual
# repository's Mercurial configuration file (.hg/hgrc file, relative to
# repository's root directory). The recommended way is to use the per-repository
# settings.
#
# The following parameters have to be defined (under the specified sections in
# the configuration file):
#
# [extensions]
#
# buggenie = /path/to/thebuggenie_hg_remote.py
#
# [buggenie]
# program = [curl|wget]
# url = tbg_url
# passkey = project_passkey
# project = project_id
# nosslverify = [true|false]
#
# [hooks]
# changegroup.buggenie = python:buggenie.hook
#
# The following parameters are mandatory:
#
# program
# Specifies the program that should be used for submitting the report to
# TBG. Supported programs are 'wget' and 'curl'.
# url
# Specifies the base URL where The Bug Genie installation can be reached
# at.
# passkey
# Specifies the passkey that should be used for authenticating with The Bug
# Genie instance for the specified project. The passkey can be obtained from
# the project's 'VCS Integration' page.
# project
# Specifies the project identifier that should be used when submitting a
# report to The Bug Genie. The project ID can be obtained from the project's
# 'VCS Integration' page.
#
# The following parameters are optional:
#
# nosslverify
# Specifies that the calling program should _not_ verify the certificate (if
# the URL specified begins with https). This can be used in conjunction with
# the self-signed certificates.
#
from urllib import quote
import subprocess
import mercurial.node
def call_program(program, arguments, url):
"""
Calls the specified program, passing it the arguments. The URL is passed
through stdin to the program.
Arguments:
program - Binary that should be called.
arguments - Arguments that should be passed to the program.
url - TBG report submit URL that should be passed for processing to the
program.
Returns:
Tuple consisting out of error_code, stdout, and stderr generated by call.
"""
# Prefix the arguments with program.
arguments.insert(0, program)
# Set-up the subprocess for execution.
process = subprocess.Popen(arguments, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
# curl expects slightly different format when reading URL from stdin.
if program == "curl":
url = "url=%s" % url
# Obtain the contents of stdout and stderr.
stdout, stderr = process.communicate(url)
# Return the exit code of process, and message that should be shown to user.
return process.returncode, stdout, stderr
def extract_report(repo, context):
"""
Extract the commit report from provided context.
Arguments:
repo - Mercurial repository object.
context - Mercurial commit context for which the report should be extracted.
Returns:
Dictionary containing the key/value pairs that should be used for
constructing the GET arguments of a TBG update URL.
"""
# Extract some basic information first.
report = {}
report['author'] = context.user()
report['rev'] = "%d:%s" % (context.rev(), mercurial.node.short(context.node()))
report['commit_msg'] = context.description()
report['date'] = str(context.date()[0]).split(".0")[0]
# Extract parent revision identifier.
parent = context.parents()[0]
report['oldrev'] = "%d:%s" % (parent.rev(), mercurial.node.short(parent.node()))
# Get the list of changed files compared to previous commit.
files_changed = repo.status(parent.node(), context.node())
# Create a string containing information about all changes, one file per
# line, first column being type of change, second column being filename.
files_changed_status = []
for f in files_changed[0]:
files_changed_status.append("U %s" % f)
for f in files_changed[1]:
files_changed_status.append("A %s" % f)
for f in files_changed[2]:
files_changed_status.append("D %s" % f)
report['changed'] = "\n".join(files_changed_status)
# Finally return the report.
return report
def submit_report(commit, repo, ui, program, program_params, base_url, project_id, passkey):
"""
Submits report to TBG for a single commit.
Arguments:
commit - Mercurial commit identifier.
repo - Mercurial repository object.
ui - Mercurial user interface object.
program - Name of the program that should be used.
program_params - Parameters that should be passed to the program.
base_url - Base URL at which TBG can be found.
project_id - TBG project identifier.
passkey - Passkey that should be used for authentication.
"""
# Get the commit hash and context.
commit_hash = repo.changelog.node(commit)
commit_context = repo.changectx(commit_hash)
# Extract information for the commit.
report = extract_report(repo, commit_context)
# Add the passkey to report, it will be used for constructing the URL.
report["passkey"] = passkey
# Generate the URL.
# Strip the trailing slash.
base_url = base_url.rstrip("/")
# Set-up the base link for project.
url = "%s/vcs_integration/report/%s/?" % (base_url, project_id)
# Pass the GET arguments.
args = [ "%s=%s" % (quote(key), quote(value)) for key, value in report.iteritems()]
url += "&".join(args)
# Call the program.
exit_code, stdout, stderr = call_program(program, program_params, url)
# Since wget has a bit poor way of outputting error, filter out the URL used
# (in order to avoid passkey leakage).
if program == "wget":
stderr = "\n".join(stderr.split("\n")[1:])
# Output the stderr content if program returned with an error. Otherwise
# output the information retrieved from TBG.
if exit_code != 0:
ui.warn("TBG Hook: An error happened while trying to submit the data.\n")
ui.warn("TBG Hook: Program returned error message:\n")
# Strip out the whitespaces and newlines from the end, and make sure we
# have a single final newline.
stderr.rstrip()
stderr += "\n"
ui.warn(stderr)
else:
# Strip out the whitespaces and newlines from the end, and make sure we
# have a single final newline.
stdout.rstrip()
stdout += "\n"
ui.warn(stdout)
def hook(ui, repo, hooktype, node = None, url = None, **kwargs):
"""
Commit and changegroup hook for Mercurial.
Arguments:
ui - Mercurial user interface object.
repo - Mercurial repository object.
node - Mercurial revision ID.
url - Mercurial path/URL.
kwargs - Additional keyword arguments.
"""
# Read the configuration options.
config = dict((param, value) for param, value in ui.configitems('buggenie'))
# Make sure the required settings were specified.
if not "program" in config:
ui.warn("TBG HG Hook: No program was specified. Check your settings.\n")
return
if not "url" in config:
ui.warn("TBG HG Hook: No base URL was specified. Check your settings.\n")
return
if not "project" in config:
ui.warn("TBG HG Hook: No project ID was specified. Check your settings.\n")
return
if not "passkey" in config:
ui.warn("TBG HG Hook: No passkey was specified. Check your settings.\n")
return
# Set-up parameters to be passed onto program.
if config["program"] == "wget":
# Use dots for progress, output to stdout, read link from stdin.
program_params = [ "--progress=dot", "-O", "-", "-i", "-" ]
if config.get("nosslverify") == "true":
program_params.append("--no-check-certificate")
elif config["program"] == "curl":
# Quiet, but show errors, fail on server error, and read link from stdin.
program_params = [ "-s", "-S", "-f", "-K", "-" ]
if config.get("nosslverify") == "true":
program_params.append("--insecure")
else:
ui.warn("TBG HG Hook: Specified program '%s' is not supported. Check your settings.\n")
return
# Get the node hash.
node_hash = mercurial.node.bin(node)
# If the hook is called as a changegroup hook, process all related commits.
if hooktype == "changegroup":
start = repo.changelog.rev(node_hash)
end = len(repo.changelog)
for commit in xrange(start, end):
submit_report(commit, repo, ui, config["program"], program_params, config["url"], config["project"], config["passkey"])
# If the hook is called as a commit, hook, process just the single commit.
elif hooktype == "commit":
submit_report(commit, repo, ui, config["program"], program_params, config["url"], config["project"], config["passkey"])
|