mirror of
https://github.com/etlegacy/LegacyTransifexBot.git
synced 2024-11-22 04:11:06 +00:00
Initial import of prfromtransifex script.
Meant to be run regularily as part of a user cronjob. Pulls updated translations from transifex and if anything relevant changed creates a pull request with the translation update to the mumble master repository. Completely untested at this stage. Very unlikely to even run.
This commit is contained in:
commit
c658a41775
3 changed files with 261 additions and 0 deletions
5
README.txt
Normal file
5
README.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Requires pygithub, plumbum
|
||||||
|
|
||||||
|
Meant to be run regularily as part of a root cronjob. Pulls updated
|
||||||
|
translations from transifex and if anything relevant changed creates
|
||||||
|
a pull request with the translation update to the mumble master repository.
|
58
prfromtransifex.ini
Normal file
58
prfromtransifex.ini
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
[github]
|
||||||
|
; Github username of bot
|
||||||
|
user = MumbleTransifexBot
|
||||||
|
; Github password of bot
|
||||||
|
password =
|
||||||
|
|
||||||
|
[transifex]
|
||||||
|
; Modes: default fetches all string, reviewed only reviewed ones
|
||||||
|
mode = default
|
||||||
|
; Minimum number of percent needed for a translation to be included
|
||||||
|
minpercent = 0
|
||||||
|
|
||||||
|
|
||||||
|
[workingrepo]
|
||||||
|
owner = MumbleTransifexBot
|
||||||
|
repo = mumble
|
||||||
|
branch = master
|
||||||
|
|
||||||
|
; Clone URL for working repo
|
||||||
|
url = git@github.com:MumbleTransifexBot/mumble.git
|
||||||
|
; Local path the repo can be found
|
||||||
|
path = /var/transifex/mumble
|
||||||
|
|
||||||
|
[targetrepo]
|
||||||
|
owner = mumble-voip
|
||||||
|
repo = mumble
|
||||||
|
branch = master
|
||||||
|
|
||||||
|
; Clone URL for pull request target repository
|
||||||
|
url = https://github.com/mumble-voip/mumble.git
|
||||||
|
|
||||||
|
[pullrequest]
|
||||||
|
; Template string for pull request title
|
||||||
|
title = Transifex translation update
|
||||||
|
; Template string for pull request body
|
||||||
|
body = New translation updates available from transifex
|
||||||
|
https://www.transifex.com/organization/mumble/dashboard/mumble
|
||||||
|
; Template string for commits in PR. Available variables
|
||||||
|
; %(mode)s See transifex.mode above
|
||||||
|
; %(minpercent)s See transifex.minpercent above
|
||||||
|
; %(langcount)d Number of languages matched with given settings
|
||||||
|
commit = Transifex translation update
|
||||||
|
Mode: %(mode)s
|
||||||
|
Minimum percent translated: %(minpercent)s
|
||||||
|
Matched %(langcount)d languages
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
; File to store language bookkeeping data in
|
||||||
|
; Will be re-written and commited to the repository
|
||||||
|
; if changes occur.
|
||||||
|
file = src/mumble/translations.pri
|
||||||
|
; Template to generate file from, available variables $(files)s
|
||||||
|
; for a space separated list of all files.
|
||||||
|
template = # Do not change manually
|
||||||
|
# Autogenerated by mumble transifex bot
|
||||||
|
TRANSLATIONS = %(files)s
|
||||||
|
; Space separated translation files not retrieved from transifex
|
||||||
|
additionaltsfiles = mumble_en.ts
|
198
prfromtransifex.py
Normal file
198
prfromtransifex.py
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8
|
||||||
|
|
||||||
|
# Copyright (C) 2014 Stefan Hacker <dd0t@users.sourceforge.net>
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
|
||||||
|
# - Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
# - Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
# - Neither the name of the Mumble Developers nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from this
|
||||||
|
# software without specific prior written permission.
|
||||||
|
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||||
|
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Meant to be run regularily as part of a root cronjob. Pulls updated translations from
|
||||||
|
transifex and if anything relevant changed creates a pull request with the translation
|
||||||
|
update to the mumble master repository.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import ConfigParser
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from logging import basicConfig, getLogger, DEBUG, INFO, WARNING, ERROR, debug, error, info, warning, exception
|
||||||
|
from github import Github
|
||||||
|
from plumbum.cmd import git, tx, cd
|
||||||
|
from plumbum import ProcessExecutionError
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
def getExistingPullRequest(g, user, repo):
|
||||||
|
s = g.search("type:pr is:open repo:%(repo)s author:%(user)s" % {'user': user,
|
||||||
|
'repo': repo})
|
||||||
|
pullrequests = s.get_page(0)
|
||||||
|
total = len(pullrequests)
|
||||||
|
if total == 0:
|
||||||
|
# We have to create a PR
|
||||||
|
debug("No open pull request found for %s in %s", user, repo)
|
||||||
|
return None
|
||||||
|
elif total == 1:
|
||||||
|
# We can reuse the existing PR
|
||||||
|
pr = pullrequests[0]
|
||||||
|
debug("Reusing existing PR %d from %s", pr.number, pr.created_at)
|
||||||
|
return pr
|
||||||
|
else:
|
||||||
|
# More then one PR pending. That's not right. Abort
|
||||||
|
raise Exception("Have %d PRs pending. This is unexpected." % total)
|
||||||
|
|
||||||
|
def createNewPullRequest(g,
|
||||||
|
target_owner, target_repo, target_branch,
|
||||||
|
base_owner, base_branch,
|
||||||
|
request_title, request_body):
|
||||||
|
|
||||||
|
u = g.get_user(target_owner)
|
||||||
|
r = u.get_repo(target_repo)
|
||||||
|
pr = r.create_pull(title = request_title,
|
||||||
|
body = request_body,
|
||||||
|
head = base_owner + ":" + base_branch,
|
||||||
|
base = target_branch)
|
||||||
|
return pr
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parent_parser = ArgumentParser(
|
||||||
|
description = 'Create pull requests to mumble from transifex translation updates',
|
||||||
|
epilog = __doc__)
|
||||||
|
|
||||||
|
parent_parser.add_argument('-c', '--config', help = 'Configuration file (default: %(default)s)', default = '/etc/prfromtransifex.ini')
|
||||||
|
parent_parser.add_argument('--setup', help = "If set sets up needed git clone and then exits", action='store_true')
|
||||||
|
parent_parser.add_argument('-v', '--verbose', help = 'Verbose logging', action='store_true')
|
||||||
|
|
||||||
|
args = parent_parser.parse_args()
|
||||||
|
basicConfig(level = (DEBUG if args.verbose else INFO),
|
||||||
|
format='%(asctime)s %(levelname)s %(message)s')
|
||||||
|
|
||||||
|
debug("Loading configuration from: %s", args.config)
|
||||||
|
|
||||||
|
cfg = ConfigParser.RawConfigParser()
|
||||||
|
cfg.read(args.config)
|
||||||
|
|
||||||
|
user = cfg.get('github', 'user')
|
||||||
|
password = cfg.get('github', 'password')
|
||||||
|
|
||||||
|
mode = cfg.get('transifex', 'mode')
|
||||||
|
minpercent = cfg.get('transifex', 'minpercent')
|
||||||
|
|
||||||
|
wr_owner = cfg.get('workingrepo', 'owner')
|
||||||
|
wr_repo = cfg.get('workingrepo', 'repo')
|
||||||
|
wr_branch = cfg.get('workingrepo', 'branch')
|
||||||
|
wr_url = cfg.get('workingrepo', 'url')
|
||||||
|
wr_path = cfg.get('workingrepo', 'path')
|
||||||
|
|
||||||
|
tr_owner = cfg.get('targetrepo', 'owner')
|
||||||
|
tr_repo = cfg.get('targetrepo', 'repo')
|
||||||
|
tr_branch = cfg.get('targetrepo', 'branch')
|
||||||
|
tr_url = cfg.get('targetrepo', 'url')
|
||||||
|
|
||||||
|
pr_title = cfg.get('pullrequest', 'title')
|
||||||
|
pr_body = cfg.get('pullrequest', 'body')
|
||||||
|
pr_commit = cfg.get('pullrequest', 'commit')
|
||||||
|
|
||||||
|
translationsfile = cfg.get('misc', 'file')
|
||||||
|
translationstemplate = cfg.get('misc', 'template')
|
||||||
|
additionaltsfiles = cfg.get('misc', 'additionaltsfiles')
|
||||||
|
|
||||||
|
if args.setup:
|
||||||
|
info("Setting up git repo")
|
||||||
|
debug(git["clone", wr_url, wr_path]())
|
||||||
|
debug(git["remote", "add", "target", tr_url]())
|
||||||
|
info("Done")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
info("Checking for pending PR")
|
||||||
|
g = Github(user, password)
|
||||||
|
pr = getExistingPullRequest(user = user,
|
||||||
|
repo = tr_repo)
|
||||||
|
if pr:
|
||||||
|
info("Already have pending PR %d", pr.number)
|
||||||
|
# As long as we have a pending PR we want to make sure we
|
||||||
|
# keep working on that basis so potential review inside of
|
||||||
|
# of the PR isn't disturbed. Changes should be added on as
|
||||||
|
# additional commits on top of the existing PR.
|
||||||
|
remote = "origin"
|
||||||
|
branch = wr_branch
|
||||||
|
else:
|
||||||
|
info("No pending PR, will be creating a new one")
|
||||||
|
# When we have no pending requests we want to base our branch
|
||||||
|
# on the most recent mumble version to make fast-forward application
|
||||||
|
# of our patches as easy as possible.
|
||||||
|
remote = "target"
|
||||||
|
branch = tr_branch
|
||||||
|
|
||||||
|
info("Updating remote '%s'", remote)
|
||||||
|
debug(git["fetch", remote]()))
|
||||||
|
info("Resetting to branch '%s'", branch
|
||||||
|
debug(git["reset", remote + "/" + branch, "--hard"]())
|
||||||
|
info("Cleaning repository")
|
||||||
|
debug(git["clean", "-f", "-x", "-d"]())
|
||||||
|
info("Pulling translations")
|
||||||
|
|
||||||
|
txout = tx["pull", "-f", "--mode=" + mode, "--minimum-perc" + minpercent]()
|
||||||
|
debug(txout)
|
||||||
|
|
||||||
|
tfilepath = os.path.join(wr_path, translationsfile)
|
||||||
|
info("Updating translations listing file '%s'", tfilepath)
|
||||||
|
paths, files = zip(*re.findall(r"^\s->\s[\w_]+:\s([\w/\_]+/([\w_]+\.ts))$", t, flags=re.MULTILINE))
|
||||||
|
debug(git["add"](*paths))
|
||||||
|
translations = additionaltsfiles + " ".join(files)"
|
||||||
|
|
||||||
|
with open(tfilepath, "w") as f:
|
||||||
|
f.write(translationstemplate % {'files': translations})
|
||||||
|
|
||||||
|
debug(git["add"](tfilepath))
|
||||||
|
|
||||||
|
debug("Checking for modifications")
|
||||||
|
changed, changedfiles, _ = git["diff", "--cached", "--name-only", "--exit-code"].run()
|
||||||
|
if not changed:
|
||||||
|
info("No changes to translations, done")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
debug("Changed files: %s", " ".join(os.linesep.split(changedfiles)))
|
||||||
|
|
||||||
|
info("Things changed & force pushing")
|
||||||
|
debug(git["commit", "-m", pr_commit % {'mode': mode,
|
||||||
|
'minpercent': minpercent,
|
||||||
|
'langcount': len(files)}]())
|
||||||
|
|
||||||
|
debug(git["push", "-f", "origin", wr_branch]())
|
||||||
|
|
||||||
|
if not pr:
|
||||||
|
info("No existing PR, creating new one")
|
||||||
|
pr = createNewPullRequest(g,
|
||||||
|
target_owner = tr_owner,
|
||||||
|
target_repo = tr_repo,
|
||||||
|
target_branch = tr_branch,
|
||||||
|
base_owner = wr_owner,
|
||||||
|
base_branch = wr_branch,
|
||||||
|
request_title = pr_title,
|
||||||
|
request_body = pr_body)
|
||||||
|
|
||||||
|
info("Created PR %d", pr.number)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue