def test_get_github_api_for_repo__not_installed(self, create_token): create_token.return_value = "ATOKEN" responses.add( "GET", "https://api.github.com/repos/TestOwner/TestRepo/installation", status=404, ) with mock.patch.dict( os.environ, {"GITHUB_APP_KEY": "bogus", "GITHUB_APP_ID": "1234"} ): with pytest.raises(GithubException): get_github_api_for_repo(None, "TestOwner", "TestRepo")
def _init_task(self): self.github_config = self.project_config.keychain.get_service("github") self.github = get_github_api_for_repo( self.project_config.keychain, self.project_config.repo_owner, self.project_config.repo_name, )
def notify_of_release(owner, name): # Dispatch a heroku-release-phase event to the github repository gh = get_github_api_for_repo(None, owner, name) repo = gh.repository(owner, name) url = repo._build_url("dispatches", base_url=repo._api) data = {"event_type": "heroku-release-phase"} repo._post(url, data=data)
def test_get_github_api_for_repo(self, create_token): create_token.return_value = "ATOKEN" responses.add( "GET", "https://api.github.com/repos/TestOwner/TestRepo/installation", json={ "id": 1, "access_tokens_url": "", "account": "", "app_id": "", "created_at": "", "events": "", "html_url": "", "permissions": "", "repositories_url": "", "repository_selection": "", "single_file_name": "", "target_id": "", "target_type": "", "updated_at": "", }, ) responses.add( "POST", "https://api.github.com/app/installations/1/access_tokens", status=201, json={"token": "ITOKEN", "expires_at": datetime.now().isoformat()}, ) with mock.patch.dict( os.environ, {"GITHUB_APP_KEY": "bogus", "GITHUB_APP_ID": "1234"} ): gh = get_github_api_for_repo(None, "TestOwner", "TestRepo") assert isinstance(gh.session.auth, AppInstallationTokenAuth)
def __init__(self, project_config, spec): self.project_config = project_config self.spec = spec self.url = spec["github"] if self.url.endswith(".git"): self.url = self.url[:-4] repo_owner, repo_name = self.url.split("/")[-2:] self.repo_owner = repo_owner self.repo_name = repo_name self.gh = get_github_api_for_repo(project_config.keychain, repo_owner, repo_name) self.repo = self.gh.repository(self.repo_owner, self.repo_name) self.resolve()
def local_github_checkout(repo_owner, repo_name, commit_ish=None): with temporary_dir() as repo_root: # pretend it's a git clone to satisfy cci os.mkdir(".git") repo = get_github_api_for_repo(None, repo_owner, repo_name) if commit_ish is None: commit_ish = repo.repository(repo_owner, repo_name).default_branch zip_file = download_extract_github(repo, repo_owner, repo_name, ref=commit_ish) zip_file.extractall(repo_root) yield repo_root
def get_github_api(self, owner=None, repo=None): return get_github_api_for_repo(self.keychain, owner or self.repo_owner, repo or self.repo_name)
def get_github_api(self): gh = get_github_api_for_repo(GitHubSettingsKeychain(), self.owner, self.name) repo = gh.repository(self.owner, self.name) return repo
def test_get_github_api_for_repo__token(self, GitHub): with mock.patch.dict(os.environ, {"GITHUB_TOKEN": "token"}): gh = get_github_api_for_repo(None, "TestOwner", "TestRepo") gh.login.assert_called_once_with(token="token")
def run_flows(*, user, plan, skip_steps, organization_url, result_class, result_id): """ This operates with side effects; it changes things in a Salesforce org, and then records the results of those operations on to a `result`. Args: user (User): The User requesting this flow be run. plan (Plan): The Plan instance for the flow you're running. skip_steps (List[str]): The strings in the list should be valid step_num values for steps in this flow. organization_url (str): The URL of the organization, required by the OrgConfig. result_class (Union[Type[Job], Type[PreflightResult]]): The type of the instance onto which to record the results of running steps in the flow. Either a PreflightResult or a Job, as appropriate. result_id (int): the PK of the result instance to get. """ result = result_class.objects.get(pk=result_id) token, token_secret = user.token repo_url = plan.version.product.repo_url commit_ish = plan.commit_ish or plan.version.commit_ish with contextlib.ExitStack() as stack: stack.enter_context(finalize_result(result)) stack.enter_context(report_errors_to(user)) tmpdirname = stack.enter_context(temporary_dir()) # Get cwd into Python path, so that the tasks below can import # from the checked-out repo: stack.enter_context(prepend_python_path(os.path.abspath(tmpdirname))) # Let's clone the repo locally: user, repo_name = extract_user_and_repo(repo_url) gh = get_github_api_for_repo(None, user, repo_name) repo = gh.repository(user, repo_name) # Make sure we have the actual owner/repo name if we were redirected user = repo.owner.login repo_name = repo.name zip_file_name = "archive.zip" repo.archive("zipball", path=zip_file_name, ref=commit_ish) zip_file = zipfile.ZipFile(zip_file_name) if not zip_file_is_safe(zip_file): # This is very unlikely, as we get the zipfile from GitHub, # but must be considered: url = f"https://github.com/{user}/{repo_name}#{commit_ish}" logger.error(f"Malformed or malicious zip file from {url}.") return zip_file.extractall() # We know that the zipball contains a root directory named # something like this by GitHub's convention. If that ever # breaks, this will break: zipball_root = glob(f"{user}-{repo_name}-*")[0] # It's not unlikely that the zipball root contains a directory # with the same name, so we pre-emptively rename it to probably # avoid collisions: shutil.move(zipball_root, "zipball_root") for path in itertools.chain(glob("zipball_root/*"), glob("zipball_root/.*")): shutil.move(path, ".") shutil.rmtree("zipball_root") # There's a lot of setup to make configs and keychains, link # them properly, and then eventually pass them into a flow, # which we then run: ctx = MetaDeployCCI(repo_root=tmpdirname, plan=plan) current_org = "current_org" org_config = OrgConfig( { "access_token": token, "instance_url": organization_url, "refresh_token": token_secret, }, current_org, keychain=ctx.keychain, ) org_config.save() # Set up the connected_app: connected_app = ServiceConfig({ "client_secret": settings.CONNECTED_APP_CLIENT_SECRET, "callback_url": settings.CONNECTED_APP_CALLBACK_URL, "client_id": settings.CONNECTED_APP_CLIENT_ID, }) ctx.keychain.set_service("connected_app", connected_app, True) steps = [ step.to_spec(project_config=ctx.project_config, skip=step.step_num in skip_steps) for step in plan.steps.all() ] org = ctx.keychain.get_org(current_org) result.run(ctx, plan, steps, org)