Beispiel #1
0
 def __init__(self, config):
     self.config = config
     self.phab = Phabricator(host=config.phabricator_url,
                             token=config.phabricator_token)
     self.phab.connect()
     self.phab.update_interfaces()
     self.instance_string = config.phabricator_revision_url
Beispiel #2
0
class Phab:
    def __init__(self, config):
        self.config = config
        self.phab = Phabricator(host=config.phabricator_url,
                                token=config.phabricator_token)
        self.phab.connect()
        self.phab.update_interfaces()
        self.instance_string = config.phabricator_revision_url

    def notify_harbourmaster(self, key, success):
        key_number = re.findall(r"D(\d+)", key)
        differntial = self.phab.differential.query(ids=key_number)
        self.phab.harbormaster.sendmessage(
            buildTargetPHID=differntial[0]["phid"], type=success)

    def apply_patch(self, id):
        try:
            subprocess.check_output(["arc", "patch", "--nobranch", id],
                                    cwd=self.config.repository_path)
        except subprocess.CalledProcessError as e:
            raise Exception("git phab application failed: " + e.output)

    def get_id_from_commit_message(self, msg):
        revision_numbers = re.findall(r"" + self.instance_string + "/D(\d+)",
                                      msg)
        if len(revision_numbers) == 0:
            raise Exception(
                "Revision ID cannot be fetched from commit message")
        return "D" + revision_numbers[-1]
Beispiel #3
0
    def __init__(self, config):
        self.running = True
        self.timer_in_seconds = config['phabricator']['listener']['interval']
        self.latest = None

        self.phab = Phabricator(host='https://phabricator.services.mozilla.com/api/',
                                token=config['phabricator']['token'])
        self.phab.update_interfaces()
Beispiel #4
0
def init():
    global phab, tag_map

    phab = Phabricator(host=os.environ['PHABRICATOR_API_URL'],
                       token=os.environ.get('PHABRICATOR_API_TOKEN'))
    phab.update_interfaces()

    tag_map = task_tagger.resolve_tags(phab)
Beispiel #5
0
def setup_phab(projectphid):
    phab = Phabricator()
    phab.update_interfaces()
    print "Fetching tasks from Phab"
    tasks = phab.maniphest.query(projectPHIDs=[projectphid],
                                 status="status-open",
                                 limit=3000)
    return (phab, tasks)
Beispiel #6
0
 def __init__(self,
              token: Optional[str],
              host: Optional[str] = 'https://reviews.llvm.org/api/',
              dry_run_updates: bool = False):
     self._phab = None  # type: Optional[Phabricator]
     self.dry_run_updates = dry_run_updates
     self._phab = Phabricator(token=token, host=host)
     self.update_interfaces()
def main(args):
    parser = argparse.ArgumentParser(prog=args[0], add_help=True,
                                     usage='%(prog)s [script] [reports...]',
                                     description=__doc__)
    parser.add_argument('--no-report', action='store_true',
                        help='Do not report test results to phabricator')
    parser.add_argument('script', type=str, help='script to execute')
    parser.add_argument('reports', type=str, nargs='*',
                        help='list of test result files')
    parsed = parser.parse_args(args[1:])

    token = os.environ.get("TEAMCITY_CONDUIT_TOKEN", None)
    if not token and not parsed.no_report:
        print("Please provide a conduit token in the environment variable ""TEAMCITY_CONDUIT_TOKEN""")
        sys.exit(1)

    arcconfig = get_arcconfig()
    phabricatorUrl = urlparse.urljoin(arcconfig['conduit_uri'], "api/")
    buildUrl = os.environ.get('BUILD_URL', '')

    failures, exitcode = execute_and_parse(parsed.script)
    for file in parsed.reports:
        # All inputs may not exist if the build fails prematurely
        if not os.path.isfile(file):
            continue

        elif file.endswith(".xml"):
            failures.update(get_failures(file))

    build_status = "success"
    if len(failures) != 0:
        build_status = "failure"

    if len(failures) != 0 and exitcode == 0:
        exitcode = 1

    if parsed.no_report:
        print("Build terminated with {}".format(build_status))
        sys.exit(exitcode)

    phab = Phabricator(host=phabricatorUrl, token=token)
    phab.update_interfaces()

    revisionID = get_revision(phab, get_commit_message())
    authorPHID = get_author(phab, revisionID)

    if build_status != "success":
        task_body = create_task_body(buildUrl, revisionID, failures)
        create_task(phab, authorPHID, revisionID, task_body)
        create_comment(phab, revisionID, build_status, buildUrl)

    sys.exit(exitcode)
Beispiel #8
0
 def __init__(self, comment_file_path: str, git_hash: str):
     # TODO: turn os.environ parameter into command line arguments
     # this would be much clearer and easier for testing
     self.comment_file_path = comment_file_path
     self.conduit_token = os.environ.get('CONDUIT_TOKEN')  # type: Optional[str]
     self.host = os.environ.get('PHABRICATOR_HOST')  # type: Optional[str]
     self._load_arcrc()
     self.diff_id = os.environ['DIFF_ID']  # type: str
     self.diff_json_path = os.environ['DIFF_JSON']  # type: str
     if not self.host.endswith('/api/'):
         self.host += '/api/'
     self.phab = Phabricator(token=self.conduit_token, host=self.host)
     self.git_hash = git_hash  # type: Optional[str]
     self.msg = []  # type: List[str]
def get_comments(phid, author_phids, exclude_author_phid, days=None):
    phab = Phabricator()
    constraints = {
        'authorPHIDs': author_phids,
    }
    transactions = phab.transaction.search(
        objectIdentifier='D%s' % phid,
        constraints=constraints,
    )
    comments = []
    for transaction in transactions.get('data'):
        if not transaction.get('comments'):
            continue
        created = transaction.get('comments')[0].get('dateCreated')
        if days and created < get_epoc_days_ago(days):
            continue
        author_phid = transaction.get('authorPHID')
        if author_phid == exclude_author_phid:
            continue
        comments.append({
            'authorPHID':
            author_phid,
            'comments':
            transaction.get('comments')[0].get('content').get('raw'),
        })
    list.reverse(comments)
    return comments
Beispiel #10
0
    def __init__(self):

        # Get super-early debugging by `export PHABFIVE_DEBUG=1`
        if "PHABFIVE_DEBUG" in os.environ:
            log.setLevel(logging.DEBUG)
            log.info("Loglevel is: {}".format(
                logging.getLevelName(log.getEffectiveLevel())))

        self.conf = self.load_config()

        maxlen = 8 + len(max(dict(self.conf).keys(), key=len))
        for k, v in dict(self.conf).items():
            log.debug("{} {} {}".format(k, "." * (maxlen - len(k)), v))

        # check for required configurables
        for k, v in dict(self.conf).items():
            if k in REQUIRED and not v:
                error = "{} is not configured".format(k)
                example = CONFIG_EXAMPLES.get(k)
                if example:
                    error += ", " + example
                raise PhabfiveConfigException(error)

        # check validity of configurables
        for k in VALIDATORS.keys():
            if not re.match(VALIDATORS[k], self.conf[k]):
                error = "{} is malformed".format(k)
                example = VALID_EXAMPLES.get(k)
                if example:
                    error += ", " + example
                raise PhabfiveConfigException(error)
        self.phab = Phabricator(host=self.conf.get("PHAB_URL"),
                                token=self.conf.get("PHAB_TOKEN"))

        self.verify_connection()
def get_task(version):
    p = Phabricator()
    constraints = {
        'query': 'title:"{} deployment blockers"'.format(version)
    }
    phid = p.maniphest.search(constraints=constraints)['data'][0]['id']
    return 'https://phabricator.wikimedia.org/T{}'.format(phid)
Beispiel #12
0
def loadConfig():

    global PuppetRoot
    global PhabricatorAPIToken
    global PhabricatorHost
    global WireguardManifestFilePath
    global RepositoryName
    global RepositoryCommit
    global PHIDsOfProjectsToNotify

    click.echo('Loading config...')
    config = configparser.ConfigParser()
    config.read('config.ini')

    params = config['Params']

    PuppetRoot = params['puppet_root']

    PhabricatorAPIToken = params['phabricator_api_token']
    PhabricatorHost = params['phabricator_host']
    WireguardManifestFilePath = params['wireguard_manifest_file_path']
    RepositoryName = params['puppet_repository_name']
    RepositoryCommit = params['puppet_repository_commit']

    projects = config['Projects_To_Tag']
    PHIDsOfProjectsToNotify = [projects[slug] for slug in projects.keys()]

    repo = git.Repo(PuppetRoot)
    phab = Phabricator(host=PhabricatorHost, token=PhabricatorAPIToken)
    return (repo, phab)
Beispiel #13
0
def get_phabricator_client():
    """Return a Phabricator client instance"""

    client = Phabricator(username='******',
                         token='<%= @icingabot_password %>',
                         host='https://phabricator.miraheze.org/api/')

    return client
	def connect_to_phab(self):
		self.phab = Phabricator(config.phab_user,
						   config.phab_cert,
						   config.phab_host)

		self.phab.update_interfaces()
		# DEBUG to verify the API connection worked: print phab.user.whoami()
		vlog('Looks like we connected to the phab API \o/')
Beispiel #15
0
 def test_author_data_full(self):
     phab = Phabricator(host='dummmy.com', token='dummy.token')
     response, raw_response = PhabricatorService().author_data(
         author_phid="PHID-USER-test",
         raw_response=test_mock.mock_phabricator_raw_response(),
         phab=phab)
     print(response)
     self.assertTrue(True, False)
Beispiel #16
0
def main(args):

    if len(args) < 2:
        print(
            "Please provide a list of junit reports as command line arguments."
        )
        sys.exit(1)

    token = os.getenv("TEAMCITY_CONDUIT_TOKEN", None)
    if not token:
        print("Please provide a conduit token in the environment variable "
              "TEAMCITY_CONDUIT_TOKEN"
              "")
        sys.exit(1)

    arcconfig = get_arcconfig()
    phabricatorUrl = urlparse.urljoin(arcconfig['conduit_uri'], "api/")
    buildUrl = os.getenv('BUILD_URL', '')

    build_status = "success"
    failures = {}
    for arg in args:
        # All inputs may not exist if the build fails prematurely
        if not os.path.isfile(arg):
            continue

        if arg.endswith(".status"):
            with open(arg, "r") as f:
                build_status = f.read().strip()
        elif arg.endswith(".xml"):
            failures.update(get_failures(arg))

    if len(failures) != 0:
        build_status = "failure"

    phab = Phabricator(host=phabricatorUrl, token=token)
    phab.update_interfaces()

    revisionID = get_revision(phab, get_commit_message())
    authorPHID = get_author(phab, revisionID)

    if build_status != "success":
        task_body = create_task_body(buildUrl, revisionID, failures)
        create_task(phab, authorPHID, revisionID, task_body)
        create_comment(phab, revisionID, build_status, buildUrl)
Beispiel #17
0
def test_resolve_tags__should_find_standard_tags():
    phab = Phabricator(host=os.environ['PHABRICATOR_API_URL'],
                       token=os.environ.get('PHABRICATOR_API_TOKEN'))
    tag_map = resolve_tags(phab)

    eq_(tag_map['has_revision_required_diff_tag'],
        "PHID-PROJ-kx4rkibaeu6rt72oupll")
    eq_(tag_map['has_diff_tag'], "PHID-PROJ-segawqxql2j3qedl7cqc")
    eq_(tag_map['has_accepted_diff_tag'], "PHID-PROJ-72kxzxxqoak5bp2llx3m")
Beispiel #18
0
def test_get_diff():
    phab = Phabricator(host=api_url_base, token=credentials.api_key)
    response = phab.differential.getdiff(diff_id="178")
    parsed = response.__dict__

    filename = 'diff.json'
    if filename:
        with open(filename, 'r') as f:
            data = json.load(f)
    assert parsed == data
def main():
    args = parse_args()

    fmt = '%(name)s - %(levelname)s - %(message)s'
    if not args.log_notime:
        fmt = '%(asctime)s - ' + fmt
    logging.basicConfig(format=fmt, level=logging.INFO)

    phab = Phabricator(host=args.phabricator, token=args.phabricator_token)
    logger.info('updating phabricator interfaces...')
    phab.update_interfaces()
    logger.info(f'working with {phab.host}')

    interactor = PhabGchat(phab, args.phabricator_hmac, args.gchat_webhook)

    ws = TornadoServer(int(args.port), [
        tw.url('/post', PhabReciever, dict(pg=interactor, )),
    ])
    ws.run()
Beispiel #20
0
def main(args):
    if len(args) < 2:
        print("Usage: {} <script> [<output> ...]".format(args[0]))
        print("")
        print("  <script>: Script to run and parse output from.")
        print("  <output>: Test result file for parsing.")
        sys.exit(1)

    token = os.environ.get("TEAMCITY_CONDUIT_TOKEN", None)
    if not token:
        print("Please provide a conduit token in the environment variable "
              "TEAMCITY_CONDUIT_TOKEN"
              "")
        sys.exit(1)

    arcconfig = get_arcconfig()
    phabricatorUrl = urlparse.urljoin(arcconfig['conduit_uri'], "api/")
    buildUrl = os.environ.get('BUILD_URL', '')

    failures = execute_and_parse(args[1])
    for file in args[2:]:
        # All inputs may not exist if the build fails prematurely
        if not os.path.isfile(arg):
            continue

        elif arg.endswith(".xml"):
            failures.update(get_failures(arg))

    build_status = "success"
    if len(failures) != 0:
        build_status = "failure"

    phab = Phabricator(host=phabricatorUrl, token=token)
    phab.update_interfaces()

    revisionID = get_revision(phab, get_commit_message())
    authorPHID = get_author(phab, revisionID)

    if build_status != "success":
        task_body = create_task_body(buildUrl, revisionID, failures)
        create_task(phab, authorPHID, revisionID, task_body)
        create_comment(phab, revisionID, build_status, buildUrl)
Beispiel #21
0
def publish_log():

    if not env.logger:
        return

    log_output = env.logger.getvalue()
    env.logger.close()

    phabricator = Phabricator()
    phabricator.update_interfaces()
    deployer = phabricator.user.whoami()


    info_dict = {
        "arguments": " ".join(sys.argv[1:]),
        "user": deployer.userName,
        "date": datetime.now().strftime('%Y-%m-%d %H:%M'),
    }

    paste_title = "fab %(arguments)s # %(user)s at %(date)s" % info_dict

    paste = phabricator.paste.create(
        content=log_output,
        title=paste_title,
    )

    info_dict.update({
        "paste_id": paste.id,
        "branch": env.branch,
        "branch_repo_url": "%(repo_browser_url)shistory/%(branch)s/" % {
            "repo_browser_url": env.repo_browser,
            "branch": env.branch,
        }
    })

    msg = "P%(paste_id)d `fab %(arguments)s` # @%(user)s at %(date)s " \
          "from branch [%(branch)s](%(branch_repo_url)s) " % info_dict

    phabricator.conpherence.updatethread(
        id=env.log_compherence,
        message=msg,
    )
Beispiel #22
0
 def test_get_last_comment(self, mock_phabricator_author_data):
     phab = Phabricator(host='dummy.com', token='dummy.token')
     comments = test_mock.mock_phabricator_get_comments(1201, phab)
     response = PhabricatorService().get_last_comment(comments,
                                                      phab,
                                                      raw_response=[])
     createdAt = datetime.datetime.fromtimestamp(float(1551763640))
     res = LastComment(author='dummy_user',
                       body='This is some content',
                       created_at=createdAt)
     self.assertEqual(response, res)
Beispiel #23
0
def test_get_raw_diff():
    phab = Phabricator(host=api_url_base, token=credentials.api_key)
    response = phab.differential.getrawdiff(diffID="178")
    parsed = response.__dict__
    with open('rawdiff.json', 'w') as outfile:
        json.dump(parsed, outfile)

    filename = 'rawdiff.json'
    if filename:
        with open(filename, 'r') as f:
            data = json.load(f)
    assert parsed == data
Beispiel #24
0
def get_phabricator_client():
    """Return a Phabricator client instance"""

    parser = ConfigParser.SafeConfigParser()
    parser_mode = 'phabricator_bot'
    parser.read(PHABRICATOR_CONFIG_FILE)

    client = Phabricator(username=parser.get(parser_mode, 'username'),
                         token=parser.get(parser_mode, 'token'),
                         host=parser.get(parser_mode, 'host'))

    return client
def phid_to_name(phid):
    if phid is None:
        return None
    if cache.has(phid):
        return cache.get(phid)
    phab = Phabricator()
    data = phab.phid.lookup(names=[
        phid,
    ])
    name = data[phid]['name']
    cache.set(phid, name)
    return name
Beispiel #26
0
    def __init__(self, version, crs):
        self.version = version
        self.crs = crs
        self.last_five_version_ids = None
        self.version = version
        phab = Phabricator()
        self.train_blocker = trainblockers.TrainBlockers(self.version, phab)
        try:
            self.conductor = trainblockers.get_phab_user(
                self.train_blocker.blocker_task['fields']['ownerPHID'], phab)
        except APIError:
            self.conductor = 'Release Engineer Team'

        backups = self.train_blocker.blocker_task['fields'][
            'custom.train.backup']
        if backups:
            self.backup = trainblockers.get_phab_user(backups[0], phab)
        else:
            self.backup = 'Nobody .·´¯`(>▂<)´¯`·.'
        self.stats = TrainsStats(
            self.crs.execute(
                '''
            SELECT
                id,
                version,
                patches,
                rollbacks,
                (group0_delay_days + group1_delay_days + group2_delay_days) as days_delay,
                (SELECT count(*) from blocker b WHERE b.train_id = train.id) as blocker_count
            FROM train
            WHERE start_time <= (
                SELECT start_time
                FROM train
                WHERE version = ?
            )
            ORDER BY start_time desc
            LIMIT 5
        ''', (self.version, )).fetchall())
        self.thanks = set()
        for row in self.crs.execute(
                '''
            SELECT
                blocker,
                unblocker
            FROM train t
            JOIN blocker b ON b.train_id = t.id
            WHERE version = ?
        ''', (self.version, )).fetchall():
            if row[0] not in [self.conductor, self.backup, 'null']:
                self.thanks.add(row[0])
            if row[1] not in [self.conductor, self.backup, 'null']:
                self.thanks.add(row[1])
Beispiel #27
0
 def test_get_reviews_no_raw(self, mock_get_comments,
                             mock_phabricator_get_last_comment,
                             mock_phabricator_author_data,
                             mock_phabricator_time_from_epoch):
     reviews = test_mock.mock_phabricator_differential_query(
         None, None, None)
     phab = Phabricator(host='dummmy.com', token='dummy.token')
     response = PhabricatorService().get_reviews(phab,
                                                 reviews,
                                                 host="www.google.com",
                                                 raw_response=[])
     self.assertEqual(response[0]._format_oneline(1, 1),
                      self.config['msg2'])
def get_diffs(author_phids, days=None):
    phab = Phabricator()
    constraints = {
        'authorPHIDs': author_phids,
    }
    if settings.DIFF_STATUS:
        constraints['statuses'] = settings.DIFF_STATUS
    if days:
        constraints['createdStart'] = get_epoc_days_ago(days)
    results = phab.differential.revision.search(constraints=constraints,
                                                #order='newest',
                                                )
    return results.get('data')
Beispiel #29
0
def test_on_task_update__for_task_with_needs_changes_diff__should_remove_undesired_tags(
        cassette):
    phab = Phabricator(host=os.environ['PHABRICATOR_API_URL'],
                       token=os.environ.get('PHABRICATOR_API_TOKEN'))
    tag_map = resolve_tags(phab)

    on_task_update(phab, 2895, tag_map=tag_map)

    eq_(cassette.requests[-1].url,
        "https://truecode.trueship.com/api/maniphest.edit")
    eq_(
        cassette.requests[-1].body,
        b"params=%7B%22objectIdentifier%22%3A+%22PHID-TASK-bdaz2w2ue4nlotls6wqn%22%2C+%22transactions%22%3A+%5B%7B%22value%22%3A+%5B%22PHID-PROJ-kx4rkibaeu6rt72oupll%22%5D%2C+%22type%22%3A+%22projects.add%22%7D%2C+%7B%22value%22%3A+%5B%22PHID-PROJ-segawqxql2j3qedl7cqc%22%2C+%22PHID-PROJ-72kxzxxqoak5bp2llx3m%22%5D%2C+%22type%22%3A+%22projects.remove%22%7D%5D%2C+%22__conduit__%22%3A+%7B%22token%22%3A+%22123%22%7D%7D&output=json"
    )
Beispiel #30
0
def test_on_task_update__for_task_with_unreviewed_diff__should_tag_with_diff_tag(
        cassette):
    phab = Phabricator(host=os.environ['PHABRICATOR_API_URL'],
                       token=os.environ.get('PHABRICATOR_API_TOKEN'))
    tag_map = resolve_tags(phab)

    on_task_update(phab, 3109, tag_map=tag_map)

    eq_(cassette.requests[-1].url,
        "https://truecode.trueship.com/api/maniphest.edit")
    eq_(
        cassette.requests[-1].body,
        b"params=%7B%22transactions%22%3A+%5B%7B%22value%22%3A+%5B%22PHID-PROJ-segawqxql2j3qedl7cqc%22%5D%2C+%22type%22%3A+%22projects.add%22%7D%5D%2C+%22objectIdentifier%22%3A+%22PHID-TASK-s3scdbecgvk23b4uwsfp%22%2C+%22__conduit__%22%3A+%7B%22token%22%3A+%22123%22%7D%7D&output=json"
    )
Beispiel #31
0
def test_on_task_update__for_landed_task_with_accepted_diff__should_tag_with_accepted_tag(
        cassette):
    phab = Phabricator(host=os.environ['PHABRICATOR_API_URL'],
                       token=os.environ.get('PHABRICATOR_API_TOKEN'))
    tag_map = resolve_tags(phab)

    on_task_update(phab, 3135, tag_map=tag_map)

    eq_(cassette.requests[-1].url,
        "https://truecode.trueship.com/api/maniphest.edit")
    eq_(
        cassette.requests[-1].body,
        b"output=json&params=%7B%22transactions%22%3A+%5B%7B%22type%22%3A+%22projects.add%22%2C+%22value%22%3A+%5B%22PHID-PROJ-t2mvgyqoolxpakgeyngn%22%5D%7D%2C+%7B%22type%22%3A+%22projects.remove%22%2C+%22value%22%3A+%5B%22PHID-PROJ-segawqxql2j3qedl7cqc%22%5D%7D%5D%2C+%22__conduit__%22%3A+%7B%22token%22%3A+%22123%22%7D%2C+%22objectIdentifier%22%3A+%22PHID-TASK-zjxys23b7zuuojohnfrl%22%7D"
    )
Beispiel #32
0
def test_on_task_update__for_task_with_accepted_diff__should_tag_with_accepted_tag(
        cassette):
    phab = Phabricator(host=os.environ['PHABRICATOR_API_URL'],
                       token=os.environ.get('PHABRICATOR_API_TOKEN'))
    tag_map = resolve_tags(phab)

    on_task_update(phab, 3070, tag_map=tag_map)

    eq_(cassette.requests[-1].url,
        "https://truecode.trueship.com/api/maniphest.edit")
    eq_(
        cassette.requests[-1].body,
        b"params=%7B%22objectIdentifier%22%3A+%22PHID-TASK-djleagj2aiegvb7kzivn%22%2C+%22__conduit__%22%3A+%7B%22token%22%3A+%22123%22%7D%2C+%22transactions%22%3A+%5B%7B%22value%22%3A+%5B%22PHID-PROJ-72kxzxxqoak5bp2llx3m%22%5D%2C+%22type%22%3A+%22projects.add%22%7D%5D%7D&output=json"
    )
#!/usr/bin/env python

import sys

from phabricator import Phabricator

if len(sys.argv) == 1:
    print "Syntax: %s ProjectName" % sys.argv[0]
    sys.exit(1)

project_name = sys.argv[1]

phab = Phabricator()
phab.update_interfaces()
proj = phab.project


def phid_from_name(name):
    all_projects = proj.query()

    for phid in all_projects:
        if all_projects[phid]['name'] == name:
            return phid

projectPHID = phid_from_name(project_name)
tasks = phab.maniphest.find(
    projectPHIDs=[projectPHID],
)

print "Project %s" % project_name
Beispiel #34
0
def setup_phab(projectphid):
	phab = Phabricator()
	phab.update_interfaces()
	print "Fetching tasks from Phab"
	tasks = phab.maniphest.query(projectPHIDs=[projectphid], status="status-open", limit=3000)
 	return(phab, tasks)
class Phidify:

	def __init__(self, args):
		self.args = args	# FIXME unpack all the args into member variables?
		if config.phab_host.find('phab-01') != -1:
                        self.host = 'phab-01'
		elif config.phab_host.find('phabricator.wikimedia.org') != -1:
                        self.host = 'phabricator'
		else:
			self.json_error('Unrecognized host %s in config' % (config.phab_host))
			sys.exit(1)
		self.userMap = 'data/trello_names_' + self.host + '.yaml'
                with open(self.userMap) as f:
                    yam = yaml.load(f)
		self.trelloUserMap = yam['trelloUserMap']
		vlog('Looks like we loaded self.trelloUserMap OK \o/')


	def connect_to_phab(self):
		self.phab = Phabricator(config.phab_user,
						   config.phab_cert,
						   config.phab_host)

		self.phab.update_interfaces()
		# DEBUG to verify the API connection worked: print phab.user.whoami()
		vlog('Looks like we connected to the phab API \o/')

	# Returns dict mapping Trello usernames to phab PHIDs
	def get_trelloUserPHIDs(self, trelloUserMap):
		self.connect_to_phab()
		# trelloUserMap maps trello usernames to phabricator usernames, e.g.
		#  {'spage1': 'spage',  'Tisza': 'Tgr'}
		# We can query to get the PHID of the phab username
		#  {'spage': 'PHID-USER-rwvw2dwvvjiyrlzy4iho',  'Tisza': 'PHID-USER-66kvyuekkkwkqbpze2uk'}
		# we want to return {'spage1' : 'PHID-USER-rwvw2dwvvjiyrlzy4iho'}

		# Get list of unique Phabricator usernames to query, excluding 'None' and empty
		lookupNames = [u for u in set(trelloUserMap.values()) if u]

		vlog('get_trelloUserPHIDs querying Phab for %d usernames: %s ' % (len(lookupNames), lookupNames))
		response = self.phab.user.query(usernames = lookupNames)

		# That conduit query returns an array of users' data, each containing phabUsername and phid.
		# Turn it into a dict.
		phabUserPHIDMap = {}
		for userInfo in response:
			if userInfo["userName"] and userInfo["phid"]:
				phabUserPHIDMap[userInfo["userName"]] = userInfo["phid"]

		# Now create what we want, a dict of trelloUsername -> phid
		trelloUserPHIDMap = {}
		for trelloUsername, phabUsername in trelloUserMap.iteritems():
			if (phabUsername
				and phabUsername in phabUserPHIDMap
				and phabUserPHIDMap[phabUsername]
				and phabUserPHIDMap[phabUsername] != 'None'): # XXX fires?
				trelloUserPHIDMap[trelloUsername] = phabUserPHIDMap[phabUsername]

		return trelloUserPHIDMap

	def writeUserPHIDs(self):
		trelloUserPHIDMap = self.get_trelloUserPHIDs(self.trelloUserMap)
		fname = 'conf/trello-scrub_' + self.host + '.yaml'
		if os.path.exists(fname):
			elog('ERROR: ' + fname + ' already exists')
			sys.exit(2)
		stream = file(fname, 'w')
		trelloScrub = {
			'uid-map': trelloUserPHIDMap,
			'uid-cheatsheet': {}
		}
		# safe_dump() avoids gtisza: !!python/unicode 'PHID-USER-66kvyuekkkwkqbpze2uk'
		yaml.safe_dump( trelloScrub, stream, width=30)
		log('SUCCESS: wrote trello usernames->PHIDs to ' + fname)
class TrelloImporter:
	# Map from Trello username to phabricator user.
	userMapPhab01 = {
		'gtisza': 'Tgr',
		"legoktm": "legoktm",
		"matthewflaschen": "mattflaschen",
		"pauginer": None,
		"spage1": "spage",
	}
	userMapPhabWMF = {
		# from mingleterminator.py
		'fflorin': 'Fabrice_Florin',
		'gdubuc': 'Gilles',
		'mholmquist': 'MarkTraceur',
		'gtisza': 'Tgr',
		'pginer': 'Pginer-WMF',
		# From ack username trabulous_dir/flow-current-iteration_lOh4XCy7_fm.json  \
		# | perl -pe 's/^\s+//' | sort | uniq
		# Collaboration-Team members:     Eloquence, DannyH, Pginer-WMF, Spage, Quiddity, Mattflaschen, matthiasmullie, EBernhardson
		"alexandermonk": None,
		"antoinemusso": None,
		"benmq": None,
		"bsitu": None,
		"dannyhorn1": "DannyH",
		"erikbernhardson": "EBernhardson",
		"jaredmzimmerman": None,
		"jonrobson1": None,
		"kaityhammerstein": None,
		"legoktm": None,
		"matthewflaschen": "mattflaschen",
		"matthiasmullie": "matthiasmullie",
		"maygalloway": None,
		"moizsyed_": None,
		"oliverkeyes": None,
		"pauginer": "Pginer-WMF",
		"quiddity1": "Quiddity",
		"shahyarghobadpour": None,
		"spage1": "Spage",
		"wctaiwan": None,
		"werdnum": None,
	}

	def __init__(self, jsonName, args):
		self.jsonName = jsonName
		self.args = args	# FIXME unpack all the args into member variables?
		self.verbose = args.verbose
		if config.phab_host.find('phab-01') != -1:
			self.host = 'phab-01'
		elif config.phab_host.find('phabricator.wikimedia.org') != -1:
			self.host = 'phabricator'
		else:
			self.json_error('Unrecognized host %s in config' % (config.phab_host))
			sys.exit(3)
		self.board = TrelloDAO(self.jsonName)
		trelloBoardName = self.board.get_board_name();
		vlog('Trello board = %s' % (trelloBoardName))


	def connect_to_phab(self):
		self.phab = Phabricator(config.phab_user,
						   config.phab_cert,
						   config.phab_host)

		self.phab.update_interfaces()
		self.phabm = phabmacros('', '', '')
		self.phabm.con = self.phab
		# DEBUG to verify the API connection worked: print phab.user.whoami()
		vlog('Looks like we connected to the phab API \o/')


	def sanity_check(self):
		if not 'cards' in self.trelloJson:
			self.json_error('No cards in input file')
			sys.exit(1)

	def testify(self, str):
		if self.args.test_run:
			str = "TEST Trello_create RUN: " + str

		return str

	def json_error(self, str):
		elog('ERROR: %s in input file %s' % (str, self.jsonName))

	# Determine projectPHID for the project name in which this will create tasks.
	def get_projectPHID(self, phabProjectName):
		# Similar conduit code in trello_makePHIDs.py get_trelloUserPHIDs
		response = self.phab.project.query(names = [phabProjectName])
		for projInfo in response.data.values():
		    if projInfo["name"] == phabProjectName:
			vlog('Phabricator project %s has PHID %s' % (phabProjectName, projInfo["phid"] ) )
			return projInfo["phid"]

		elog('Phabricator project %s not found' % (phabProjectName))
		sys.exit(4)
		return # not reached

	# This is the workhorse
	def createTask(self, card):
		# Default some keys we always pass to createtask.
		taskinfo = {
			'ownerPHID'	: None,
			'ccPHIDs'	  : [],
			'projectPHIDs' : [self.projectPHID],
		}

		taskinfo["title"] = self.testify(card.name)

		# TODO: if Trello board is using scrum for Trello browser extension,
		# could extract story points /\s+\((\d+)\)' from card title to feed into Sprint extension.

		# TODO: process attachments
		# TODO: taskinfo["assignee"]
		desc = self.testify(card.desc)

		if card.checklist_strs:
			desc += '\n' + '\n\n'.join(card.checklist_strs)
		desc_tail = '\n--------------------------'
		desc_tail += '\n**Trello card**: [[ %s | %s ]]\n' % (card.url, card.shortLink)
		# Mention column the same way as the card.final_comment_fields below from export_trello.py.
		desc_tail += '\n * column: %s\n' % (unicode(card.column))
		if len(card.final_comment_fields) > 0:
			s = ''
			s += '\n'
			for key in sorted(card.final_comment_fields):
				s += ' * %s: %s\n' % (str(key), unicode(card.final_comment_fields[key]))
			desc_tail += s

		# TODO: could add additional info (main attachment, etc.) to desc_tail.
		taskinfo["description"] = desc + '\n' + desc_tail
		# TODO: chasemp: what priority?
		taskinfo['priority'] = 50
		# TODO: chasemp: can I put "Trello lOh4XCy7" in "Reference" field?

		# Take the set of members
		idMembers = card.idMembers
		# Get the Trello username for the idMember
		# memberNames = [ TrelloDAO.get_username(id) for id in idMembers if TrelloDAO.get_username(id)]

		# export_trello.py sets names it can't match to 'import-john-doe'
		if not 'FAILED' in card.owner and not card.owner == 'import-john-doe':
			taskinfo['ownerPHID'] = card.owner
		taskinfo['ccPHIDs'] = [u for u in card.subscribers if not 'FAILED' in u and not u == 'import-john-doe']

		# TODO: Add any other members with a PHID to the ccPHIDs
		# TODO: Note remaining Trello members in desc_tail

		# TODO: bugzilla_create.py and wmfphablib/phabapi.py use axuiliary for
		# BZ ref, but it doesn't work for Trello ref?
		taskinfo["auxiliary"] = {"std:maniphest:external_reference":"Trello %s" % (card.shortLink)}

		if self.args.conduit:
			# This prints fields for maniphest.createtask
			print '"%s"\n"%s"\n\n' % (taskinfo["title"].encode('unicode-escape'),
			                          taskinfo["description"].encode('unicode-escape'))
		else:
			if self.args.dry_run:
				log("dry-run to create a task for Trello card %s ('%s')" %
					(card.shortLink, taskinfo["title"]))
			else:
				ticket = self.phab.maniphest.createtask(
											 title = taskinfo['title'],
											 description = taskinfo['description'],
											 projectPHIDs = taskinfo['projectPHIDs'],
											 ownerPHID = taskinfo['ownerPHID'],
											 ccPHIDs = taskinfo['ccPHIDs'],
											 auxiliary = taskinfo['auxiliary']
				)

				log("Created task: T%s (%s) from Trello card %s ('%s')" %
					(ticket['id'], ticket['phid'], card.shortLink, taskinfo["title"]))


			# Here bugzilla_create goes on to log actual creating user and view/edit policy,
			# then set_task_ctime to creation_time.

			# Should I add comments to the card here,
			# or a separate step that goes through action in self.board.blob["actions"]
			# handling type="commentCard"?


	# Here are the types of objects in the "actions" array.
	#     20   "type": "addAttachmentToCard",
	#      9   "type": "addChecklistToCard",
	#      2   "type": "addMemberToBoard",
	#     38   "type": "addMemberToCard",
	#     69   "type": "commentCard",
	#      1   "type": "copyCard",
	#     25   "type": "createCard",
	#      3   "type": "createList",
	#      6   "type": "deleteAttachmentFromCard",
	#     29   "type": "moveCardFromBoard",
	#     18   "type": "moveCardToBoard",
	#      4   "type": "moveListFromBoard",
	#      2   "type": "moveListToBoard",
	#      3   "type": "removeChecklistFromCard",
	#     14   "type": "removeMemberFromCard",
	#      3   "type": "updateBoard",
	#    698   "type": "updateCard",
	#     48   "type": "updateCheckItemStateOnCard",
	#      8   "type": "updateList",
	# def getCardCreationMeta(self, cardId):
		# Look around in JSON for ["actions"] array for member with type:"createCard"
		# with ["card"]["id"] = cardId
		# and use the siblings ["date"] and ["memberCreator"]["id"]

	# def getCardComments(self, cardId):
		# Look around in JSON ["actions"] for member with type:""commentCard"
		# with ["card"]["id"] = cardId
		# and use the siblings ["date"] and ["memberCreator"]["id"]

	def process_cards(self):
		self.connect_to_phab()

		self.projectPHID = self.get_projectPHID(self.args.phab_project);

		# This file has Trello_username: user_PHID mapping created by trello_makePHIDs.py.
		scrubber = TrelloScrubber('conf/trello-scrub.yaml')
		for j_card in self.board.blob["cards"]:
			card = TrelloCard(j_card, scrubber)
			card.figure_stuff_out(self.board)
			if self.args.column and not card.column == self.args.column:
				continue
			# Skip archived cards ("closed" seems to correspond?)
			# But I think archive all cards in column doesn't set this.
			if card.closed:
				continue

			# TODO: skip cards that are bugs
			# TODO: skip cards that already exist in Phab.
			self.createTask(card)
Beispiel #37
0
def init_phab_connection():
    phab = Phabricator()
    phab.update_interfaces()
    return phab