def get(self, id): raise GitlabAuthenticationError('Unauthorized', 401)
def update_project(project: Project, local_file_path: str, gitlab_file_path: str, name: str, chart_name: str, chart_ref: str, old_version: str, new_version: str, remove_source_branch: bool = False, squash: bool = False, automerge: bool = False, merge_major: bool = False, assignee_ids: List[int] = [], labels: List[str] = []) -> ProjectMergeRequest: """Main function for handling branches, merge requests and version in file. - create/update a branch - create/update a merge request - replace the version in a file and updates the content to a Gitlab repo Args: project (gitlab.v4.objects.Project): Gitlab project object local_file_path (str): path to the local file gitlab_file_path (str): path to file on Gitlab name (str): name of chart chart_name (str): name of chart repository chart_ref (str): reference of chart old_version (str): current version of chart new_version (str): new version of chart remove_source_branch (bool, optional):. remove brunch after merge. Defaults to 'False'. squash (bool, optional):. squash commits after merge. Defaults to 'False'. automerge (bool, optional):. merge request automatically merge_major (bool, optional):. merge also major updates assignee_ids (List[int], optional): list of assignee id's to assign mr. Defaults to []. labels (List[str], optional): list of labels to set. Defaults to []. Raises: TypeError: parameter 'project' is not of type 'gitlab.v4.objects.Project' LookupError: branch could not be created GitlabUpdateError: unable to update merge request GitlabCreateError: unable to create branch GitlabCreateError: unable to create merge request GitlabUploadError: unable to upload new file content Returns: gitlab.v4.objects.ProjectMergeRequest: Gitlab merge request object """ if not isinstance(project, gitlab.v4.objects.Project): raise TypeError( f"parameter 'project' must be of type 'gitlab.v4.objects.Project', got '{type(project)}'" ) mergerequest_title = templates.merge_request_title.format( CHART_NAME=chart_name, NEW_VERSION=new_version) try: merge_request = eval_merge_requests(project=project, title=mergerequest_title, chart_name=chart_name) except Exception as e: raise LookupError(f"unable check existing merge requests. {str(e)}") if merge_request.closed: return if merge_request.exists: pass # go on, maybe a file update is needed is_major = (semver.VersionInfo.parse(new_version.lstrip("v")).major > semver.VersionInfo.parse(old_version.lstrip("v")).major) if not automerge: config = "🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\n\n" if automerge and is_major and merge_major: config = "🚦 **Automerge**: Enabled by config. Merge request will merge automatically.\n\n" if automerge and is_major and not merge_major: config = ( "🚦 **Automerge**: Enabled by config, but disabled for major updates. " "Please merge this manually once you are satisfied.\n\n") config += "🔕 **Ignore**: Close this MR and you won't be reminded about this update again." description = templates.description.format(NAME=name, CHART_REF=chart_ref, OLD_VERSION=old_version, NEW_VERSION=new_version, CONFIG=config) branch_name = templates.branch_name.format(CHART_NAME=chart_name) mr = None if merge_request.update: try: mr = get_merge_request_by_title( project=project, title=pattern.mr_title.format(CHART_NAME=chart_name), state="opened", sort="desc") if not mr: raise LookupError(f"merge request '{chart_name}' not found!") mr = mr[0] # get newest merge request if labels: mr.labels = labels mr.title = mergerequest_title mr.description = description if remove_source_branch is not None: mr.remove_source_branch = str( remove_source_branch).lower() == "true" if squash is not None: mr.squash = str(squash).lower() == "true" mr.save() except Exception as e: raise GitlabUpdateError(f"cannot update merge request. {str(e)}") if merge_request.missing: try: create_branch(project=project, branch_name=branch_name) except GitlabCreateError as e: logging.debug( f"cannot create branch '{branch_name}'. {str(e.error_message)}" ) except Exception as e: raise GitlabCreateError( f"cannot create branch '{branch_name}'. {str(e)}") try: mr = create_merge_request( project=project, branch_name=branch_name, description=description, title=mergerequest_title, remove_source_branch=remove_source_branch, squash=squash, assignee_ids=assignee_ids, labels=labels) except Exception as e: raise GitlabCreateError( f"unable to create merge request. {str(e)}") try: old_chart_version = re.compile( pattern=templates.chart_version.format(VERSION=old_version), flags=re.IGNORECASE) new_chart_version = templates.chart_version.format(VERSION=new_version) with open(file=local_file_path, mode="r+") as f: old_content = f.read() new_content = re.sub(pattern=old_chart_version, repl=new_chart_version, string=old_content) update_file(project=project, branch_name=branch_name, commit_msg=mergerequest_title, content=new_content, path_to_file=gitlab_file_path) except Exception as e: raise GitlabUploadError(f"unable to upload file. {str(e)}") try: if automerge: mr.merge(merge_when_pipeline_succeeds=True) except GitlabAuthenticationError as e: raise GitlabAuthenticationError( "Authentication not set correctly. 'Helminator' User must have the role 'Maintainer'" ) except Exception as e: raise Exception(f"cannot merge MR. {e}") return mr
def delete(self, hook_id): raise GitlabAuthenticationError('Unauthorized', 401)
def get(self, name, lazy): raise GitlabAuthenticationError('Unauthorized', 401)
def create(self, data): raise GitlabAuthenticationError('Unauthorized', 401)
async def http_request(self, verb, path, query_data=None, post_data=None, streamed=False, files=None, **kwargs): query_data = query_data or {} url = self._build_url(path) params = {} utils.copy_dict(params, query_data) # Deal with kwargs: by default a user uses kwargs to send data to the # gitlab server, but this generates problems (python keyword conflicts # and python-gitlab/gitlab conflicts). # So we provide a `query_parameters` key: if it's there we use its dict # value as arguments for the gitlab server, and ignore the other # arguments, except pagination ones (per_page and page) if "query_parameters" in kwargs: utils.copy_dict(params, kwargs["query_parameters"]) for arg in ("per_page", "page"): if arg in kwargs: params[arg] = kwargs[arg] else: utils.copy_dict(params, kwargs) opts = {"headers": self._create_headers("application/json")} # If timeout was passed into kwargs, allow it to override the default timeout = kwargs.get("timeout") # We need to deal with json vs. data when uploading files if files: data = post_data json = None del opts["headers"]["Content-type"] else: json = post_data data = None req = httpx.Request(verb, url, json=json, data=data, params=params, files=files, **opts) # obey the rate limit by default obey_rate_limit = kwargs.get("obey_rate_limit", True) # do not retry transient errors by default retry_transient_errors = kwargs.get("retry_transient_errors", False) # set max_retries to 10 by default, disable by setting it to -1 max_retries = kwargs.get("max_retries", 10) cur_retries = 0 while True: result = await self.client.send(req, stream=streamed, timeout=timeout) self._check_redirects(result) if 200 <= result.status_code < 300: return result if (429 == result.status_code and obey_rate_limit) or ( result.status_code in [500, 502, 503, 504] and retry_transient_errors): if max_retries == -1 or cur_retries < max_retries: wait_time = 2**cur_retries * 0.1 if "Retry-After" in result.headers: wait_time = int(result.headers["Retry-After"]) cur_retries += 1 await asyncio.sleep(wait_time) continue error_message = result.content try: error_json = result.json() for k in ("message", "error"): if k in error_json: error_message = error_json[k] except (KeyError, ValueError, TypeError): pass if result.status_code == 401: raise GitlabAuthenticationError( response_code=result.status_code, error_message=error_message, response_body=result.content, ) raise GitlabHttpError( response_code=result.status_code, error_message=error_message, response_body=result.content, )
async def http_request( self, verb, path, query_data=None, post_data=None, streamed=False, files=None, **kwargs ): """Make an HTTP request to the Gitlab server. Args: verb (str): The HTTP method to call ('get', 'post', 'put', 'delete') path (str): Path or full URL to query ('/projects' or 'http://whatever/v4/api/projecs') query_data (dict): Data to send as query parameters post_data (dict): Data to send in the body (will be converted to json) streamed (bool): Whether the data should be streamed files (dict): The files to send to the server **kwargs: Extra options to send to the server (e.g. sudo) Returns: A requests result object. Raises: GitlabHttpError: When the return code is not 2xx """ query_data = query_data or {} url = self._build_url(path) params = {} utils.copy_dict(params, query_data) # Deal with kwargs: by default a user uses kwargs to send data to the # gitlab server, but this generates problems (python keyword conflicts # and python-gitlab/gitlab conflicts). # So we provide a `query_parameters` key: if it's there we use its dict # value as arguments for the gitlab server, and ignore the other # arguments, except pagination ones (per_page and page) if "query_parameters" in kwargs: utils.copy_dict(params, kwargs["query_parameters"]) for arg in ("per_page", "page"): if arg in kwargs: params[arg] = kwargs[arg] else: utils.copy_dict(params, kwargs) opts = self._get_session_opts(content_type="application/json") verify = opts.pop("verify") timeout = opts.pop("timeout") # If timeout was passed into kwargs, allow it to override the default timeout = kwargs.get("timeout", timeout) # We need to deal with json vs. data when uploading files if files: data = post_data json = None del opts["headers"]["Content-type"] else: json = post_data data = None # Requests assumes that `.` should not be encoded as %2E and will make # changes to urls using this encoding. Using a prepped request we can # get the desired behavior. # The Requests behavior is right but it seems that web servers don't # always agree with this decision (this is the case with a default # gitlab installation) req = httpx.Request( verb, url, json=json, data=data, params=params, files=files, **opts ) prepped = self.client.prepare_request(req) prepped.url = utils.sanitized_url(prepped.url) settings = self.client.merge_environment_settings( prepped.url, {}, streamed, verify, None ) # obey the rate limit by default obey_rate_limit = kwargs.get("obey_rate_limit", True) # do not retry transient errors by default retry_transient_errors = kwargs.get("retry_transient_errors", False) # set max_retries to 10 by default, disable by setting it to -1 max_retries = kwargs.get("max_retries", 10) cur_retries = 0 while True: result = await self.client.send(prepped, timeout=timeout, **settings) self._check_redirects(result) if 200 <= result.status_code < 300: return result if (429 == result.status_code and obey_rate_limit) or ( result.status_code in [500, 502, 503, 504] and retry_transient_errors ): if max_retries == -1 or cur_retries < max_retries: wait_time = 2 ** cur_retries * 0.1 if "Retry-After" in result.headers: wait_time = int(result.headers["Retry-After"]) cur_retries += 1 time.sleep(wait_time) continue error_message = result.content try: error_json = result.json() for k in ("message", "error"): if k in error_json: error_message = error_json[k] except (KeyError, ValueError, TypeError): pass if result.status_code == 401: raise GitlabAuthenticationError( response_code=result.status_code, error_message=error_message, response_body=result.content, ) raise GitlabHttpError( response_code=result.status_code, error_message=error_message, response_body=result.content, )