def menu(self, args):
     workflow().add_item(
         "Search for projects",
         "Search for projects and open the project page in your default browser",
         autocomplete=":projects ",
         icon=icons.PROJECTS,
     )
     workflow().add_item(
         "Search for repositories",
         "Search for repositories and open the repository page in your default browser",
         autocomplete=":repos ",
         icon=icons.REPOSITORIES,
     )
     workflow().add_item(
         "Search or raise pull requests",
         "Search for pull requests or raise new ones based on Stash's suggestions",
         autocomplete=":pullrequests ",
         icon=icons.PULL_REQUESTS,
     )
     workflow().add_item(
         "Preferences",
         "Change Stash connection settings, refresh the cache or the workflow itself",
         autocomplete=":config ",
         icon=icons.SETTINGS,
     )
     workflow().add_item(
         "Help", "Get help about the workflow and how to get support", autocomplete=":help ", icon=icons.HELP
     )
 def _add_to_result_list(self, project):
     workflow().add_item(title=project.name,
                         subtitle=project.description,
                         arg=':projects {}'.format(project.key),
                         valid=True,
                         largetext=project.name,
                         icon=workflow().cachefile('{}/{}'.format(PROJECT_AVATAR_DIR, project.key)))
Exemple #3
0
 def menu(self, args):
     workflow().add_item(
         'Search for projects',
         'Search for projects and open the project page in your default browser',
         autocomplete=':projects ',
         icon=icons.PROJECTS
     )
     workflow().add_item(
         'Search for repositories',
         'Search for repositories and open the repository page in your default browser',
         autocomplete=':repos ',
         icon=icons.REPOSITORIES
     )
     workflow().add_item(
         'Search for pull requests',
         'Search for pull requests and open them in your default browser',
         autocomplete=':pullrequests ',
         icon=icons.PULL_REQUESTS
     )
     workflow().add_item(
         'Preferences',
         'Change Stash connection settings, refresh the cache or the workflow itself',
         autocomplete=':config ',
         icon=icons.SETTINGS
     )
     workflow().add_item(
         'Help',
         'Get help about the workflow and how to get support',
         autocomplete=':help ',
         icon=icons.HELP
     )
 def menu(self, args):
     if len(args) == 2:
         workflow().add_item(
             u"Suggested pull requests {}".format(_num_pull_requests("suggestions")),
             "Suggested pull requests to raise based on your recent commits",
             arg=":pullrequests suggestions",
             icon=icons.PR_SUGGESTIONS,
             valid=True,
         )
         workflow().add_item(
             u"All open pull requests {}".format(_num_pull_requests("open")),
             "Search in all open pull requests",
             arg=":pullrequests open",
             icon=icons.OPEN,
             valid=True,
         )
         workflow().add_item(
             u"Your pull requests to review {}".format(_num_pull_requests("review")),
             "Search in pull requests you have to review",
             arg=":pullrequests review",
             icon=icons.REVIEW,
             valid=True,
         )
         workflow().add_item(
             u"Your created pull requests {}".format(_num_pull_requests("created")),
             "Search in pull requests you created",
             arg=":pullrequests created",
             icon=icons.CREATED,
             valid=True,
         )
         workflow().add_item("Main menu", autocomplete="", icon=icons.GO_BACK)
     else:
         cache_key, update_interval = _pull_request_modes[args[-2]]
         pull_request_menu = PullRequestFilterableMenu(args, update_interval, cache_key)
         return pull_request_menu.run()
 def _add_to_result_list(self, pull_request):
     workflow().add_item(title=u'{} #{}: {} → {}'.format(pull_request.repo_name, pull_request.pull_request_id,
                                                         pull_request.from_branch, pull_request.to_branch),
                         subtitle=pull_request.title,
                         arg=':pullrequests ' + pull_request.link,
                         largetext=pull_request.title,
                         valid=True,
                         icon=workflow().cachefile('{}/{}'.format(PROJECT_AVATAR_DIR, pull_request.project_key)))
def _notify_if_upgrade_available():
    if workflow().update_available:
        new_version = workflow().cached_data('__workflow_update_status', max_age=0)['version']
        workflow().add_item('An update is available!',
                            'Update the workflow from version {} to {}'.format(__version__, new_version),
                            arg=':config update',
                            valid=True,
                            icon=icons.UPDATE)
 def _add_to_result_list(self, plan):
     workflow().add_item(title=u'{} {}'.format(plan.name, FAVOURITE_PLAN if plan.is_favourite else ''),
                         subtitle=plan.description,
                         largetext=plan.name,
                         arg=':plans ' + plan.plan_key,
                         modifier_subtitles={u'shift': u'Trigger build execution for this plan'},
                         # `cmd``, ``ctrl``, ``shift``, ``alt`` and ``fn``
                         copytext='{}/browse/{}'.format(workflow().settings.get(HOST_URL), plan.plan_key),
                         valid=True)
 def _add_to_result_list(self, pull_request):
     workflow().add_item(
         title=unicode(pull_request),
         subtitle=pull_request.title,
         arg=":pullrequests " + pull_request.link,
         largetext=pull_request.title,
         valid=True,
         icon=workflow().cachefile("{}/{}".format(PROJECT_AVATAR_DIR, pull_request.project_key)),
     )
 def _add_to_result_list(self, repo):
     workflow().add_item(title=u'{} / {} {} {}'.format(repo.project_name,
                                                       repo.name,
                                                       PUBLIC if repo.public else '',
                                                       FORK if repo.fork else ''),
                         arg=':repos {}/{}'.format(repo.project_key, repo.slug),
                         valid=True,
                         largetext=repo.name,
                         copytext=repo.clone_url,  # allows to use CMD+C to copy the clone URL
                         icon=workflow().cachefile('{}/{}'.format(PROJECT_AVATAR_DIR, repo.project_key)))
 def _add_to_result_list(self, branch):
     workflow().add_item(title=branch.name,
                         subtitle=branch.description,
                         largetext=branch.name,
                         arg=':branches ' + branch.key,
                         modifier_subtitles={
                             u'shift': u'Trigger build execution for this plan branch'
                         },  # `cmd``, ``ctrl``, ``shift``, ``alt`` and ``fn``
                         copytext='{}/browse/{}'.format(workflow().settings.get(HOST_URL), branch.key),
                         valid=True)
 def execute(self, args, ctrl_pressed, shift_pressed):
     if 'sethost' in args:
         workflow().settings[HOST_URL] = args[-1]
         print('New Bamboo host: {}'.format(args[-1]))
     elif 'setuser' in args:
         workflow().settings[USER_NAME] = args[-1]
         print('New Bamboo user: {}'.format(args[-1]))
     elif 'setpw' in args:
         workflow().save_password(USER_PW, args[-1])
         print('Saved Bamboo password in keychain')
     elif 'verifycert' in args:
         verify_cert = workflow().settings.get(VERIFY_CERT, 'false') == 'true'
         toggle = (str(not verify_cert)).lower()
         workflow().settings[VERIFY_CERT] = toggle
         print('Enabled certificate verification' if toggle == 'true' else 'Disabled certificate verification')
     elif 'sync' in args:
         workflow().clear_cache()
         update_bamboo_cache()
         print('Bamboo data synchronization triggered')
     elif 'update' in args:
         try:
             if workflow().start_update():
                 print('Update of workflow finished')
             else:
                 print('You already have the latest workflow version')
         except Exception, e:
             print('Update of workflow failed: {}'.format(str(e)))
def _fetch_bamboo_data_if_necessary(bamboo_facade):
    # cached_data can only take a bare callable (no args),
    # so we need to wrap callables needing arguments in a function
    # that needs none.
    def wrapper_projects():
        return bamboo_facade.projects()

    projects = workflow().cached_data(PROJECTS_CACHE_KEY, wrapper_projects, max_age=UPDATE_INTERVAL_PROJECTS)
    workflow().logger.debug('{} projects cached'.format(len(projects)))

    def wrapper_plans():
        plans_with_branches = bamboo_facade.plans()
        for plan in plans_with_branches:
            branches = bamboo_facade.branches(plan.plan_key)
            plan.branches = branches
        return plans_with_branches

    plans = workflow().cached_data(PLANS_CACHE_KEY, wrapper_plans, max_age=UPDATE_INTERVAL_PLANS)
    workflow().logger.debug('{} plans cached'.format(len(plans)))

    def wrapper_results():
        return bamboo_facade.results()

    results = workflow().cached_data(STATUS_CACHE_KEY, wrapper_results, max_age=UPDATE_INTERVAL_STATUS)
    workflow().logger.debug('{} build results cached'.format(len(results)))
def build_bamboo_facade():
    bamboo_host = workflow().settings.get(HOST_URL, None)
    if not bamboo_host:
        raise ValueError("Bamboo host URL not set.")
    bamboo_user = workflow().settings.get(USER_NAME, None)
    try:
        bamboo_pw = workflow().get_password(USER_PW)
    except PasswordNotFound:
        bamboo_pw = None
    verify_cert = workflow().settings.get(VERIFY_CERT, "false") == "true"
    return BambooFacade(bamboo_host, bamboo_user, bamboo_pw, verify_cert)
def build_stash_facade():
    stash_host = workflow().settings.get(HOST_URL, None)
    if stash_host is None:
        raise ValueError("Stash host URL not set.")
    stash_user = workflow().settings.get(USER_NAME, None)
    try:
        stash_pw = workflow().get_password(USER_PW)
    except PasswordNotFound:
        stash_pw = None
    verify_cert = workflow().settings.get(VERIFY_CERT, "false") == "true"
    return StashFacade(stash_host, stash_user, stash_pw, verify_cert)
 def menu(self, args):
     if len(args) == 2:
         workflow().add_item(
             u'All open pull requests {}'.format(_num_pull_requests('open')),
             'Search in all open pull requests',
             arg=':pullrequests open',
             icon=icons.OPEN,
             valid=True
         )
         workflow().add_item(
             u'Your pull requests to review {}'.format(_num_pull_requests('review')),
             'Search in pull requests you have to review',
             arg=':pullrequests review',
             icon=icons.REVIEW,
             valid=True
         )
         workflow().add_item(
             u'Your created pull requests {}'.format(_num_pull_requests('created')),
             'Search in pull requests you created',
             arg=':pullrequests created',
             icon=icons.CREATED,
             valid=True
         )
         workflow().add_item(
             'Main menu',
             autocomplete='', icon=icons.GO_BACK
         )
     else:
         cache_key, update_interval = _pull_request_modes[args[-2]]
         pull_request_menu = PullRequestFilterableMenu(args, update_interval, cache_key)
         return pull_request_menu.run()
 def execute(self, args, ctrl_pressed, shift_pressed):
     import webbrowser
     host_url = workflow().settings.get(HOST_URL)
     if args[-1] == 'viewAgents':
         webbrowser.open('{}/admin/agent/viewAgents.action'.format(host_url))
     elif shift_pressed:
         from src.actions import build_bamboo_facade
         bamboo_facade = build_bamboo_facade()
         try:
             bamboo_facade.stop_build(args[-1])
             print('Successfully stopped build for {}'.format(args[-1]))
         except Exception, e:
             workflow().logger.debug('workflow ' + str(e))
             print('Failed to stop build for {}: {}'.format(args[-1], str(e)))
def alfred_is_dark():
    # copied from wunderlist workflow:
    background_rgba = workflow().alfred_env['theme_background']
    if background_rgba:
        rgb = [int(x) for x in background_rgba[5:-6].split(',')]
        return (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]) / 255 < 0.5
    return False
def get_data_from_cache(cache_key, update_interval):
    # Set `data_func` to None, as we don't want to update the cache in this script and `max_age` to 0
    # because we want the cached data regardless of age
    try:
        data = workflow().cached_data(cache_key, None, max_age=0)
    except Exception:
        # this might happen when there are incompatible model changes and the pickle cache cannot be deserialzed
        # anymore => in this case it is better to clear the cache and to re-trigger data syncing
        workflow().clear_cache()
        data = []

    # Start update script if cached data is too old (or doesn't exist)
    if not workflow().cached_data_fresh(cache_key, max_age=update_interval):
        update_stash_cache()

    return data
 def _add_to_result_list(self, build_result):
     build_result_info = u'{} / #{}'.format(build_result.plan_name, str(build_result.build_number))
     workflow().add_item(title=build_result_info,
                         icon=self._icon_for_build_status(build_result.build_state),
                         subtitle=u'{}, duration: {}, {}, {}'.format(build_result.test_summary,
                                                                     build_result.duration_desc,
                                                                     build_result.relative_time,
                                                                     strip_tags(build_result.build_reason)),
                         modifier_subtitles={
                             u'ctrl': u'Open page of artifacts to download',
                             u'shift': u'Trigger build execution for this plan'
                         },  # `cmd``, ``ctrl``, ``shift``, ``alt`` and ``fn``
                         largetext=build_result_info,
                         arg=':results {} {}'.format(build_result.build_result_key, build_result.plan_key),
                         copytext='{}/browse/{}'.format(workflow().settings.get(HOST_URL),
                                                        build_result.build_result_key),
                         valid=True)
def icon_theme():
    global _icon_theme
    if not _icon_theme:
        icon_theme = workflow().settings.get(ICON_THEME)
        if icon_theme:
            _icon_theme = icon_theme
        else:
            _icon_theme = 'light' if alfred_is_dark() else 'dark'
    return _icon_theme
Exemple #21
0
def _fetch_user_avatars(repositories, stash_facade):
    user_repos = set(filter(lambda r: r.project_key.startswith('~'), repositories))
    for r in user_repos:
        try:
            avatar = stash_facade.fetch_user_avatar(r.project_key[1:])
            if avatar is not None:
                with open(workflow().cachefile('{}/{}'.format(PROJECT_AVATAR_DIR, r.project_key)), 'wb') as avatar_file:
                    shutil.copyfileobj(avatar, avatar_file)
        except HTTPError:
            pass  # ignore as not every Stash user might have an avatar configured
Exemple #22
0
def route(args):  # e.g., args = ":config sethost http://localhost,--exec"
    command_string = args[0]  # :config sethost http://localhost
    command = command_string.split(' ')

    if not workflow().settings.get(HOST_URL, None) and 'sethost' not in command:
        call_alfred('stash:config sethost ')
        return

    handler = IndexWorkflowAction
    action = next(iter(command), None)
    if action:
        handler = WORKFLOW_ACTIONS.get(action, IndexWorkflowAction)

    if '--exec' in args:
        handler().execute(command, cmd_pressed='--cmd' in args, shift_pressed='--shift' in args)
    else:  # show menu
        handler().menu(command)
        _notify_if_upgrade_available()
        workflow().send_feedback()
    def menu(self, args):
        from src.actions import build_bamboo_facade
        bamboo_facade = build_bamboo_facade()
        dashboard = bamboo_facade.dashboard()
        workflow().add_item(title=dashboard.agent_summary,
                            largetext=dashboard.agent_summary,
                            arg=':dashboard viewAgents',
                            icon=self._icon_for_overall_status(dashboard.status),
                            valid=True)
        for q in dashboard.builds:
            try:
                agent_name = 'on {}'.format(q['agent']['name']) if 'agent' in q else ''
                workflow().add_item(title='{}: {}'.format(q['planName'], q['jobName']),
                                    subtitle='{}: {} {}'.format(q['messageType'], q['messageText'], agent_name),
                                    largetext=q['messageType'],
                                    icon=self._icon_for_build_status(q['messageType']),
                                    arg=':dashboard {}'.format(q['resultKey']),
                                    modifier_subtitles={u'shift': u'Stop currently running build'},
                                    # `cmd``, ``ctrl``, ``shift``, ``alt`` and ``fn``
                                    valid=True)
            except:
                # sometimes the build results JSON does not contain proper build items
                pass

        workflow().add_item('Main menu', autocomplete='', icon=icons.GO_BACK)
Exemple #24
0
def _find_all_projects(stash_facade):
    workflow().logger.debug('Starting to fetch projects...')
    projects = stash_facade.all_projects()
    if not os.path.exists(workflow().cachefile(PROJECT_AVATAR_DIR)):
        os.makedirs(workflow().cachefile(PROJECT_AVATAR_DIR))
    for p in projects:
        avatar = stash_facade.fetch_project_avatar(p.key)
        with open(workflow().cachefile('{}/{}'.format(PROJECT_AVATAR_DIR, p.key)), 'wb') as avatar_file:
            shutil.copyfileobj(avatar, avatar_file)
    workflow().logger.debug('Found {} projects '.format(len(projects)))
    return projects
Exemple #25
0
 def menu(self, args):
     workflow().add_item(
         'About this version',
         'Current version: {}. See here for the changelog.'.format(__version__),
         arg=':help version', valid=True, icon=icons.WHATS_NEW
     )
     workflow().add_item(
         'Found a bug or miss a feature?',
         'Go through our issue database or create a new issue.',
         arg=':help issues', valid=True, icon=icons.ISSUES
     )
     workflow().add_item(
         'About us',
         'We are Mibex Software. Find out more about us.',
         arg=':help about', valid=True, icon=icons.INFO
     )
     workflow().add_item(
         'Credits',
         'Dean Jackson: Python Alfred Workflow, Ian Paterson: inspiration for a menu-based Alfred workflow.',
         arg=':help credits', valid=False, icon=icons.CREDITS
     )
     workflow().add_item('Main menu', autocomplete='', icon=icons.GO_BACK)
def try_bamboo_connection(show_success=True):
    try:
        bamboo_facade = build_bamboo_facade()
        bamboo_facade.is_running()
        if show_success:
            workflow().add_item("Congratulations, connection to Bamboo was successful!", icon=icons.OK)
        return True
    except SSLError:
        workflow().add_item("SSL error: Try with certificate verification disabled", icon=icons.ERROR)
        return False
    except Exception, e:
        workflow().add_item("Error when connecting Bamboo server", str(e), icon=icons.ERROR)
        return False
def try_stash_connection(show_success=True):
    try:
        stash_facade = build_stash_facade()
        stash_facade.verify_stash_connection()
        if show_success:
            workflow().add_item("Congratulations, connection to Stash was successful!", icon=icons.OK)
        return True
    except SSLError:
        workflow().add_item("SSL error: Try with certificate verification disabled", icon=icons.ERROR)
        return False
    except Exception, e:
        workflow().add_item("Error when connecting Stash server", str(e), icon=icons.ERROR)
        return False
    def run(self):
        workflow().logger.debug("workflow args: {}".format(self.args))

        _notify_if_cache_update_in_progress()
        data = get_data_from_cache(self.cache_key, self.update_interval)
        query = self.args[-1]

        # query may not be empty or contain only whitespace. This will raise a ValueError.
        if query and data:
            data = workflow().filter(query, data, key=self._get_result_filter(), min_score=SEARCH_MIN_SCORE)
            workflow().logger.debug("{} {} matching `{}`".format(self.entity_name, len(data), query))

        if not data:
            # only do a REST call in case there is no query given because only in that case it is likely that there
            # is a problem with the connection to Stash and we would like to prevent doing slow calls in here
            if query or (not query and try_stash_connection(show_success=False)):
                workflow().add_item("No matching {} found.".format(self.entity_name), icon=icons.ERROR)
        else:
            for e in data:
                self._add_to_result_list(e)

        self._add_item_after_last_result()
Exemple #29
0
def _fetch_stash_data_if_necessary(stash_facade):
    # cached_data can only take a bare callable (no args),
    # so we need to wrap callables needing arguments in a function
    # that needs none.
    def wrapper_projects():
        return _find_all_projects(stash_facade)

    projects = workflow().cached_data(PROJECTS_CACHE_KEY, wrapper_projects, max_age=UPDATE_INTERVAL_PROJECTS)
    workflow().logger.debug('{} projects cached'.format(len(projects)))

    def wrapper_repositories():
        return _find_all_repositories(stash_facade)

    repos = workflow().cached_data(REPOS_CACHE_KEY, wrapper_repositories, max_age=UPDATE_INTERVAL_REPOS)
    workflow().logger.debug('{} repositories cached'.format(len(repos)))

    def wrapper_pull_requests_to_review():
        return _my_pull_requests_to_review(stash_facade)

    pull_requests_to_review = workflow().cached_data(PULL_REQUESTS_REVIEW_CACHE_KEY,
                                                     wrapper_pull_requests_to_review,
                                                     max_age=UPDATE_INTERVAL_MY_PULL_REQUESTS)
    workflow().logger.debug('{} pull requests to review cached'.format(len(pull_requests_to_review)))

    def wrapper_created_pull_requests():
        return _my_created_pull_requests(stash_facade)

    pull_requests_created = workflow().cached_data(PULL_REQUESTS_CREATED_CACHE_KEY,
                                                   wrapper_created_pull_requests,
                                                   max_age=UPDATE_INTERVAL_MY_PULL_REQUESTS)
    workflow().logger.debug('{} pull requests created cached'.format(len(pull_requests_created)))

    def wrapper_open_pull_requests():
        return _open_pull_requests(stash_facade)

    open_pull_requests = workflow().cached_data(PULL_REQUESTS_OPEN_CACHE_KEY,
                                                wrapper_open_pull_requests,
                                                max_age=UPDATE_INTERVAL_OPEN_PULL_REQUESTS)
    workflow().logger.debug('{} open pull requests cached'.format(len(open_pull_requests)))
Exemple #30
0
def _find_all_repositories(stash_facade):
    workflow().logger.debug('Starting to fetch repositories...')
    repositories = stash_facade.all_repositories()
    _fetch_user_avatars(repositories, stash_facade)
    workflow().logger.debug('Found {} repositories '.format(len(repositories)))
    return repositories