def get_pull_requests_from_bitbucket_api(workspace, repository, commit, username, password): is_compliant = False pull_requests_evidence = [] url = f"https://api.bitbucket.org/2.0/repositories/{workspace}/{repository}/commit/{commit}/pullrequests" print("Getting pull requests from " + url) response = requests.get(url, auth=(username, password)) if response.status_code == 200: is_compliant = parse_response(commit, password, pull_requests_evidence, response, username) elif response.status_code == 202: message = "Repository pull requests are still being indexed, please retry." raise ChangeError(message) elif response.status_code == 404: message = " ".join([ "Repository does not exists or pull requests are not indexed.", "Please make sure Pull Request Commit Links app is installed" ]) raise ChangeError(message) else: message = " ".join([ "Exception occurred in fetching pull requests.", f"Http return code is {response.status_code}" ]) message += f"\n {response.text}" raise ChangeError(message) return is_compliant, pull_requests_evidence
def value(self): """ The OS env-var for name if present and non empty. Raises if not present or empty. """ if self.is_unset: raise ChangeError(f"{self.name} environment-variable is not set.") elif self.is_empty: raise ChangeError( f"{self.name} environment-variable is empty string.") else: return self.string
def load_json(self, filename): try: with open(filename) as file: return json.load(file) except FileNotFoundError: raise ChangeError(f"{filename} file not found.") except IsADirectoryError: # Note: If you do this... # --volume ${MERKELYPIPE}:/data/Merkelypipe.json # And ${MERKELYPIPE} does not exist (on the client) # then it is created as an empty directory on the # client and inside the container. raise ChangeError(f"{filename} is a directory.") except json.decoder.JSONDecodeError as exc: raise ChangeError(f"{filename} invalid json - {str(exc)}")
def raise_unless_success(response): # Eg https://github.com/merkely-development/change/runs/1961998055?check_suite_focus=true status_code = response.status_code if status_code in [200, 201]: print(response.text) else: message = f"HTTP status=={status_code}\n{response.text}" raise ChangeError(message)
def __validated(self, string): assert self.handles_protocol(string) after_protocol = string[len(PROTOCOL):] match = self.__REGEX.match(after_protocol) if match is None: raise ChangeError( f"Invalid {PROTOCOL} fingerprint: {after_protocol}") names = ('sha', 'artifact_name') args = (match.group('sha'), match.group('artifact_name')) return namedtuple('Both', names)(*args)
def ls_test_results(root_directory): import os if not os.path.isdir(root_directory): raise ChangeError(f"no directory {root_directory}") import glob files = sorted(glob.glob(root_directory + "/*.xml")) excluded_files = ["failsafe-summary.xml"] for exclude in excluded_files: test_files = [file for file in files if not file.endswith(exclude)] return test_files
def __call__(self): url = ApiSchema.url_for_artifact_approvals(self.host.value, self.merkelypipe, self.fingerprint.sha) approvals = http_get_json(url, self.api_token.value) is_approved = control_deployment_approved(approvals) if not is_approved: raise ChangeError( f"Artifact with sha {self.fingerprint.sha} is not approved.") return 'Getting', url, approvals
def run(external): name = external.env.get("MERKELY_COMMAND", None) if name is None: raise ChangeError("MERKELY_COMMAND environment-variable is not set.") print(f"MERKELY_COMMAND={name}") klass = Command.named(name) command = klass(external) for env_var in command.merkely_env_vars: env_var.value return command()
def fingerprinter_for(self, string): d = self.__docker_fingerprinter f = self.__file_fingerprinter s = self.__sha256_fingerprinter if d.handles_protocol(string): return d elif f.handles_protocol(string): return f elif s.handles_protocol(string): return s else: raise ChangeError(f"Unknown protocol: {string}")
def __call__(self): is_compliant, pull_requests = get_pull_request_for_current_commit(self.env) payload = { "evidence_type": self.evidence_type.value, "contents": { "is_compliant": is_compliant, "description": self.description.value, "url": self.ci_build_url.value, "source": pull_requests, } } url = ApiSchema.url_for_artifact(self.host.value, self.merkelypipe, self.fingerprint.sha) http_put_payload(url, payload, self.api_token.value) if not is_compliant: raise ChangeError(f"Artifact with sha {self.fingerprint.sha} is not compliant") return 'Putting', url, payload
def sha(self, string): assert self.handles_protocol(string) image_name = self.artifact_name(string) # Mocked in /tests/unit/utils/mock_docker_fingerprinter.py # docker is a Python package installed in requirements.txt try: client = docker.from_env() image = client.images.get(image_name) return image.attrs["RepoDigests"][0].split(":")[1] except (docker.errors.ImageNotFound, requests.exceptions.HTTPError): # For example, see # https://github.com/merkely-development/loan-calculator/runs/1903030144?check_suite_focus=true message = " ".join([ f"Cannot determine digest for image: {string}.", "Check the image name is correct.", "Check the image has been pushed to a registry." ]) raise ChangeError(message)
def merkelypipe(self): pipe_path = self.env.get('MERKELY_PIPE_PATH', '/data/Merkelypipe.json') if os.path.exists(pipe_path): return self.load_json(pipe_path) else: raise ChangeError(f"{pipe_path} file not found.")
def _expanded(self, part): value = part.value(self._env) if value is None: message = f"environment-variable {self.name} defaults to `{self.string}` but `{part.string}` is not set." raise ChangeError(message) return value
def artifact_name(self, string): assert self.handles_protocol(string) result = string[len(PROTOCOL):] if result == "": raise ChangeError(f"Empty {PROTOCOL} fingerprint") return result
def named(_cls, string): name = "".join(list(s.capitalize() for s in string.split('_'))) try: return Command.__classes[name] except KeyError: raise ChangeError(f"Unknown command: {string}")
def build_fingerprinter(name): klass = FINGERPRINTERS.get(name) if klass is None: raise ChangeError(f"Unknown fingerprinter: {name}") else: return klass()
def repo_at(root): try: repo = Repository(root + '/.git') except _pygit2.GitError as err: raise ChangeError(f"Error: {str(err)}") return repo
def _cannot_expand_error(self, ending): return ChangeError( f"{self.name} env-var is not set " "and cannot be default-expanded as the CI system " f"cannot be determined ({ending})." )