def _get_launchpad_client(): cred_location = os.path.expanduser('~/.lp_creds') credential_store = UnencryptedFileCredentialStore(cred_location) return Launchpad.login_with('cpc', 'production', version='devel', credential_store=credential_store)
def connect_launchpad(): """Use the launchpad module connect to launchpad. Will connect you to the Launchpad website the first time you run this to authorize your system to connect. """ cred_location = os.path.expanduser('~/.lp_creds') credential_store = UnencryptedFileCredentialStore(cred_location) return Launchpad.login_with('ustriage', 'production', version='devel', credential_store=credential_store)
def get_launchpad(launchpadlib_dir=None, lp_credentials_store=None): """ return a launchpad API class. In case launchpadlib_dir is specified used that directory to store launchpadlib cache instead of the default """ if not lp_credentials_store: creds_prefix = os.environ.get('SNAP_USER_COMMON', os.path.expanduser('~')) store = UnencryptedFileCredentialStore( os.path.join(creds_prefix, '.launchpad.credentials')) else: store = UnencryptedFileCredentialStore(lp_credentials_store) lp_app = 'review-gator' lp_env = 'production' lp_version = 'devel' authorization_engine = AuthorizeRequestTokenWithConsole(lp_env, lp_app) return Launchpad.login_with(lp_app, lp_env, credential_store=store, authorization_engine=authorization_engine, launchpadlib_dir=launchpadlib_dir, version=lp_version)
def get_launchpad(launchpadlib_dir=None): """ return a launchpad API class. In case launchpadlib_dir is specified used that directory to store launchpadlib cache instead of the default """ store = UnencryptedFileCredentialStore( os.path.join(os.path.expanduser('~'), '.launchpad.credentials')) lp_app = 'landscape-review-aggregator' lp_env = 'production' lp_version = 'devel' authorization_engine = AuthorizeRequestTokenWithConsole(lp_env, lp_app) return Launchpad.login_with(lp_app, lp_env, credential_store=store, authorization_engine=authorization_engine, launchpadlib_dir=launchpadlib_dir, version=lp_version)
class TestUnencryptedFileCredentialStore(CredentialStoreTestCase): """Tests for the UnencryptedFileCredentialStore class.""" def setUp(self): ignore, self.filename = tempfile.mkstemp() self.store = UnencryptedFileCredentialStore(self.filename) def tearDown(self): if os.path.exists(self.filename): os.remove(self.filename) def test_save_and_load(self): # Make sure you can save and load credentials to a file. credential = self.make_credential("consumer key") self.store.save(credential, "unique key") credential2 = self.store.load("unique key") self.assertEquals(credential.consumer.key, credential2.consumer.key) def test_unique_id_doesnt_matter(self): # If a file contains a credential, that credential will be # accessed no matter what unique ID you specify. credential = self.make_credential("consumer key") self.store.save(credential, "some key") credential2 = self.store.load("some other key") self.assertEquals(credential.consumer.key, credential2.consumer.key) def test_file_only_contains_one_credential(self): # A credential file may contain only one credential. If you # write two credentials with different unique IDs to the same # file, the first credential will be overwritten with the # second. credential1 = self.make_credential("consumer key") credential2 = self.make_credential("consumer key2") self.store.save(credential1, "unique key 1") self.store.save(credential1, "unique key 2") loaded = self.store.load("unique key 1") self.assertEquals(loaded.consumer.key, credential2.consumer.key)
def _launchpad_connect(self, anon=False): """Use the launchpad module connect to launchpad. Will connect you to the Launchpad website the first time you run this to authorize your system to connect unless anonymous login is specified. """ if anon: self._log.debug('logging into Launchpad anonymously') return Launchpad.login_anonymously('ubuntu-bug-triage', 'production', version='devel') self._log.debug('logging into Launchpad') credential_store = UnencryptedFileCredentialStore( os.path.expanduser('~/.lp_creds')) return Launchpad.login_with('ubuntu-bug-triage', 'production', version='devel', credential_store=credential_store)
def credential_store_factory(cls, credential_save_failed): return UnencryptedFileCredentialStore(tempfile.mkstemp()[1], credential_save_failed)
if __name__ == "__main__": check_notices = os.path.isfile(CHECK_NOTICES_PATH) and os.access( CHECK_NOTICES_PATH, os.X_OK) and CHECK_NOTICES_PATH if not check_notices: logger.info("`review-tools` not found, skipping USN checks…") sys.exit(0) try: lp = Launchpad.login_with( APPLICATION, LAUNCHPAD, version="devel", authorization_engine=RequestTokenAuthorizationEngine( LAUNCHPAD, APPLICATION), credential_store=UnencryptedFileCredentialStore( os.path.expanduser(sys.argv[1])), ) except NotImplementedError: raise RuntimeError("Invalid credentials.") ubuntu = lp.distributions["ubuntu"] logger.debug("Got ubuntu: %s", ubuntu) team = lp.people[TEAM] logger.debug("Got team: %s", team) errors = [] for snap, channels in SNAPS.items(): for channel, snap_map in channels.items(): logger.info("Processing channel %s for snap %s…", channel, snap)
def main(args=None): global jira_server opt_parser = argparse.ArgumentParser( description="Report the status of all active LaunchPad bug " "imported into a JIRA project with lp-to-jira", formatter_class=argparse.RawTextHelpFormatter, epilog=textwrap.dedent('''\ Examples: lp-to-jira-report FR lp-to-jira-report --csv results.csv FR lp-to-jira-report --json results.json FR lp-to-jira-report --html results.html FR ''') ) opt_parser.add_argument( 'project', type=str, help="The JIRA project string key") opt_parser.add_argument( '--csv', dest='csv', help='export the results of the report into FILE in csv format', ) opt_parser.add_argument( '--html', dest='html', help='export the results of the report into FILE in html format', ) opt_parser.add_argument( '--json', dest='json', help='export the results of the report into FILE in json format', ) opt_parser.add_argument( '--sync', dest='sync', action='store_true', help='Sync JIRA items with corresponding bugs in LP', ) opts = opt_parser.parse_args(args) jira_project = opts.project # 1. Initialize JIRA API api = jira_api() jira_server = api.server jira = JIRA(api.server, basic_auth=(api.login, api.token)) # TODO: catch exception if the Launchpad API isn't open # 2. Initialize Launchpad API # Connect to Launchpad API # TODO: catch exception if the Launchpad API isn't open snap_home = os.getenv("SNAP_USER_COMMON") if snap_home: credential_store = UnencryptedFileCredentialStore( "{}/.lp_creds".format(snap_home)) else: credential_store = UnencryptedFileCredentialStore( os.path.expanduser("~/.lp_creds")) lp = Launchpad.login_with( 'foundations', 'production', version='devel', credential_store=credential_store) print( "Searching for JIRA issues in project %s imported with lp-to-jira..." % opts.project, flush=True ) # Create a Database of all JIRA issues imported by lp-to-jira jira_lp_db = find_issues_in_project(jira, jira_project) print("Found %s issues" % len(jira_lp_db)) # For each issue retrieve latest lp data and sync if required merge_lp_data_with_jira_issues(jira, lp, jira_lp_db, opts.sync) # Create a table version of the database jira_lp_db_table = [] if jira_lp_db: jira_lp_db_table.append(list(jira_lp_db[0].keys())) jira_lp_db_table += [list(x.values()) for x in jira_lp_db] else: return 1 # Display the content of jira_lp_db based on the output option if opts.json: with open(opts.json, 'w') as fp: json.dump(jira_lp_db, fp, indent=2) print("JSON report saved as %s" % opts.json) if opts.html: print_html_report(jira_lp_db, opts.html) print("HTML report saved as %s" % opts.html) if opts.csv: print_table(jira_lp_db_table, sep=";", limit=1024, align=False, draw_title=False, file=opts.csv) print("CSV report saved as %s" % opts.csv) if not opts.csv and not opts.html and not opts.json: print_table(jira_lp_db_table, sep=" | ", limit=60, align=True, draw_title=True, file="/dev/stdout") return 0
def setUp(self): ignore, self.filename = tempfile.mkstemp() self.store = UnencryptedFileCredentialStore(self.filename)
except subprocess.CalledProcessError as e: logger.error("Failed to check notices:\n%s", e.output) else: notices = json.loads(notices) return notices if __name__ == '__main__': try: lp = Launchpad.login_with( APPLICATION, LAUNCHPAD, version="devel", authorization_engine=RequestTokenAuthorizationEngine( LAUNCHPAD, APPLICATION), credential_store=UnencryptedFileCredentialStore( os.path.expanduser("~/.launchpadlib/credentials"))) except NotImplementedError: raise RuntimeError("Invalid credentials.") check_notices = (os.path.isfile(CHECK_NOTICES_PATH) and os.access(CHECK_NOTICES_PATH, os.X_OK) and CHECK_NOTICES_PATH) if not check_notices: logger.info("`review-tools` not found, skipping USN checks…") ubuntu = lp.distributions["ubuntu"] logger.debug("Got ubuntu: %s", ubuntu) series = ubuntu.getSeries(name_or_version=RELEASE) logger.debug("Got series: %s", series)
def login_with(cls, application_name=None, service_root=uris.STAGING_SERVICE_ROOT, launchpadlib_dir=None, timeout=None, proxy_info=None, authorization_engine=None, allow_access_levels=None, max_failed_attempts=None, credentials_file=None, version=DEFAULT_VERSION, consumer_name=None, credential_save_failed=None, credential_store=None): """Log in to Launchpad, possibly acquiring and storing credentials. Use this method to get a `Launchpad` object. If the end-user has no cached Launchpad credential, their browser will open and they'll be asked to log in and authorize a desktop integration. The authorized Launchpad credential will be stored securely: in the GNOME keyring, the KDE Wallet, or in an encrypted file on disk. The next time your program (or any other program run by that user on the same computer) invokes this method, the end-user will be prompted to unlock their keyring (or equivalent), and the credential will be retrieved from local storage and reused. You can customize this behavior in three ways: 1. Pass in a filename to `credentials_file`. The end-user's credential will be written to that file, and on subsequent runs read from that file. 2. Subclass `CredentialStore` and pass in an instance of the subclass as `credential_store`. This lets you change how the end-user's credential is stored and retrieved locally. 3. Subclass `RequestTokenAuthorizationEngine` and pass in an instance of the subclass as `authorization_engine`. This lets you change change what happens when the end-user needs to authorize the Launchpad credential. :param application_name: The application name. This is *not* the OAuth consumer name. Unless a consumer_name is also provided, the OAuth consumer will be a system-wide consumer representing the end-user's computer as a whole. :type application_name: string :param service_root: The URL to the root of the web service. :type service_root: string. Can either be the full URL to a service or one of the short service names. :param launchpadlib_dir: The directory used to store cached data obtained from Launchpad. The cache is shared by all consumers, and each Launchpad service root has its own cache. :type launchpadlib_dir: string :param authorization_engine: A strategy for getting the end-user to authorize an OAuth request token, for exchanging the request token for an access token, and for storing the access token locally so that it can be reused. By default, launchpadlib will open the end-user's web browser to have them authorize the request token. :type authorization_engine: `RequestTokenAuthorizationEngine` :param allow_access_levels: The acceptable access levels for this application. This argument is used to construct the default `authorization_engine`, so if you pass in your own `authorization_engine` any value for this argument will be ignored. This argument will also be ignored unless you also specify `consumer_name`. :type allow_access_levels: list of strings :param max_failed_attempts: Ignored; only present for backwards compatibility. :param credentials_file: The path to a file in which to store this user's OAuth access token. :param version: The version of the Launchpad web service to use. :param consumer_name: The consumer name, as appropriate for the `Consumer` constructor. You probably don't want to provide this, since providing it will prevent you from taking advantage of desktop-wide integration. :type consumer_name: string :param credential_save_failed: a callback that is called upon a failure to save the credentials locally. This argument is used to construct the default `credential_store`, so if you pass in your own `credential_store` any value for this argument will be ignored. :type credential_save_failed: A callable :param credential_store: A strategy for storing an OAuth access token locally. By default, tokens are stored in the GNOME keyring (or equivalent). If `credentials_file` is provided, then tokens are stored unencrypted in that file. :type credential_store: `CredentialStore` :return: A web service root authorized as the end-user. :rtype: `Launchpad` """ (service_root, launchpadlib_dir, cache_path, service_root_dir) = cls._get_paths(service_root, launchpadlib_dir) if (application_name is None and consumer_name is None and authorization_engine is None): raise ValueError( "At least one of application_name, consumer_name, or " "authorization_engine must be provided.") if credentials_file is not None and credential_store is not None: raise ValueError( "At most one of credentials_file and credential_store " "must be provided.") if credential_store is None: if credentials_file is not None: # The end-user wants credentials stored in an # unencrypted file. credential_store = UnencryptedFileCredentialStore( credentials_file, credential_save_failed) else: credential_store = cls.credential_store_factory( credential_save_failed) else: # A credential store was passed in, so we won't be using # any provided value for credential_save_failed. But at # least make sure we weren't given a conflicting value, # since that makes the calling code look confusing. cls._assert_login_argument_consistency( 'credential_save_failed', credential_save_failed, credential_store.credential_save_failed, "credential_store") credential_store = credential_store if authorization_engine is None: authorization_engine = cls.authorization_engine_factory( service_root, application_name, consumer_name, allow_access_levels) else: # An authorization engine was passed in, so we won't be # using any provided values for application_name, # consumer_name, or allow_access_levels. But at least make # sure we weren't given conflicting values, since that # makes the calling code look confusing. cls._assert_login_argument_consistency( "application_name", application_name, authorization_engine.application_name) cls._assert_login_argument_consistency( "consumer_name", consumer_name, authorization_engine.consumer.key) cls._assert_login_argument_consistency( "allow_access_levels", allow_access_levels, authorization_engine.allow_access_levels) return cls._authorize_token_and_login( authorization_engine.consumer, service_root, cache_path, timeout, proxy_info, authorization_engine, allow_access_levels, credential_store, credential_save_failed, version)
def main(): usage = """\ usage: lp-to-jira [options] bug-id project-id Create JIRA entry for a given Launchpad bug ID options: -e, --exists" Look if the Launchpad Bug has alreaady been imported print the JIRA issue ID if found -l, --label LABEL Add LABEL to the JIRA issue after creation -s SYNC_PROJECT_BUGS, --sync_project_bugs=SYNC_PROJECT_BUGS The name of the LP Project. This will bring in every bug from your project if you do not also specify days -d DAYS, --days=DAYS Only look for LP Bugs in the past n days --no-lp-tag Examples: lp-to-jira 3215487 FR lp-to-jira -e 3215487 FR lp-to-jira -l ubuntu-meeting 3215487 PR lp-to-jira -s ubuntu -d 3 IQA """ opt_parser = OptionParser(usage) opt_parser.add_option( '-l', '--label', dest='label', ) opt_parser.add_option('-e', '--exists', dest='exists', action='store_true') opt_parser.add_option( '-s', '--sync_project_bugs', dest='sync_project_bugs', action='store', type=str, help='Adds all bugs from a specified LP Project to specified Jira board' ' if they are not already on the Jira board.' ' Use --days to narrow down bugs') opt_parser.add_option('-d', '--days', dest='days', action='store', type=int, help='Only look for LP Bugs in the past n days') opt_parser.add_option('--no-lp-tag', dest='no_lp_tag', action='store_true', help='Do not add tag to LP Bug') opts, args = opt_parser.parse_args() # Connect to Launchpad API # TODO: catch exception if the Launchpad API isn't open credential_store = UnencryptedFileCredentialStore( os.path.expanduser("~/.lp_creds")) lp = Launchpad.login_with('foundations', 'production', version='devel', credential_store=credential_store) # Connect to the JIRA API api = jira_api() jira = JIRA(api.server, basic_auth=(api.login, api.token)) if opts.sync_project_bugs: bugs_list = get_all_lp_project_bugs(lp, opts.sync_project_bugs, opts.days) for bug in bugs_list: bug_number = bug.bug_link.split('/')[-1] bug = get_lp_bug(lp, bug_number) lp_to_jira_bug(lp, jira, bug, args[0], opts) return 0 # Make sure there's 2 arguments if len(args) < 2: opt_parser.print_usage() return 1 bug_number = args[0] project_id = args[1] bug = get_lp_bug(lp, bug_number) if opts.exists: # We are simply testing if the bug was already in JIRA if is_bug_in_jira(jira, bug, project_id): return 0 print("Launchpad Issue {} is not in JIRA project {}".format( bug.id, project_id)) return 1 # Create the Jira Issue lp_to_jira_bug(lp, jira, bug, project_id, opts) return 0
def main(args=None): opt_parser = argparse.ArgumentParser( description="A script create JIRA issue from Launchpad bugs", formatter_class=argparse.RawTextHelpFormatter, epilog=textwrap.dedent('''\ Examples: lp-to-jira 3215487 FR lp-to-jira -e 3215487 FR lp-to-jira -l ubuntu-meeting 3215487 PR lp-to-jira -s ubuntu -d 3 IQA ''') ) opt_parser.add_argument( 'bug', type=int, # Somewhat hacky way to allow -s option to not require bug id # -s (sync) is an optional parameter that doesn't require bug id default=0, nargs='?', help="The Launchpad numeric bug ID") opt_parser.add_argument( 'project', type=str, help="The JIRA project string key") opt_parser.add_argument( '-l', '--label', dest='label', help='Add LABEL to the JIRA issue after creation') opt_parser.add_argument( '-c', '--component', dest='component', help='Specify COMPONENT to assign the issue to') opt_parser.add_argument( '-E', '--epic', dest='epic', help='Specify EPIC to link this new issue to') opt_parser.add_argument( '-e', '--exists', dest='exists', action='store_true', help=textwrap.dedent(''' Look if the Launchpad Bug has already been imported print the JIRA issue ID if found ''') ) opt_parser.add_argument( '-s', '--sync_project_bugs', dest='sync_project_bugs', action='store', type=str, help=textwrap.dedent(''' Adds all bugs from a specified LP Project to specified Jira board if they are not already on the Jira board. Use --days to narrow down bugs ''') ) opt_parser.add_argument( '-d', '--days', dest='days', action='store', type=int, help='Only look for LP Bugs in the past n days' ) opt_parser.add_argument( '--no-lp-tag', dest='no_lp_tag', action='store_true', help='Do not add tag to LP Bug' ) opts = opt_parser.parse_args(args) if (opts.bug == 0 and not opts.sync_project_bugs): opt_parser.print_usage() print('lp-to-jira: error: the follow argument is required: bug') return 1 # Connect to Launchpad API # TODO: catch exception if the Launchpad API isn't open snap_home = os.getenv("SNAP_USER_COMMON") if snap_home: credential_store = UnencryptedFileCredentialStore( "{}/.lp_creds".format(snap_home)) else: credential_store = UnencryptedFileCredentialStore( os.path.expanduser("~/.lp_creds")) lp = Launchpad.login_with( 'foundations', 'production', version='devel', credential_store=credential_store) # Connect to the JIRA API try: api = jira_api() except ValueError: return "ERROR: Cannot initialize JIRA API." jira = JIRA(api.server, basic_auth=(api.login, api.token)) if opts.sync_project_bugs: tasks_list = get_all_lp_project_bug_tasks( lp, opts.sync_project_bugs, opts.days) if tasks_list is None: return 1 for bug_task in tasks_list: bug = bug_task.bug lp_to_jira_bug(lp, jira, bug, opts.project, opts) return 0 bug_number = opts.bug project_id = opts.project bug = get_lp_bug(lp, bug_number) if bug is None: return 1 if opts.exists: # We are simply testing if the bug was already in JIRA if is_bug_in_jira(jira, bug, project_id): return 0 print("Launchpad Issue {} is not in JIRA project {}".format( bug.id, project_id)) return 1 # Create the Jira Issue lp_to_jira_bug(lp, jira, bug, project_id, opts) return 0