示例#1
0
 def _missing_(cls, value: object) -> None:
     """
     Called if a given value does not exist.
     Supported in all version of Python since 3.6.
     """
     Utils.log(LogType.WARNING, f"Invalid status '{str(value)}'")
     sys.exit(1)
示例#2
0
文件: __init__.py 项目: KDE/git-lab
def main() -> None:
    """
    Entry point
    """
    parser: Parser = Parser()

    try:
        parser.parse()
    except GitCommandError as git_error:
        Utils.log(LogType.ERROR, str(git_error))
    except SystemExit:
        pass
    except KeyboardInterrupt:
        pass
    except:  # noqa: E722
        print()
        Utils.log(LogType.ERROR, "git-lab crashed. This should not happen.")
        print(
            "Please help us to fix it by opening an issue on",
            "https://invent.kde.org/sdk/git-lab/-/issues.",
            "Make sure to include the information below:",
            "\n```\n",
            traceback.format_exc(),
            "```",
        )
示例#3
0
文件: issues.py 项目: KDE/git-lab
 def __init__(self, issue_id: int):
     RepositoryConnection.__init__(self)
     try:
         self.issue: ProjectIssue = self._remote_project.issues.get(issue_id, lazy=False)
     except GitlabGetError:
         Utils.log(LogType.WARNING, f"No issue with ID {issue_id}")
         sys.exit(1)
示例#4
0
    def __upload_assets(self, text: str) -> str:
        """
        Scans the text for local file paths, uploads the files and returns
        the text modified to load the files from the uploaded urls
        """
        find_expr = re.compile(r"!\[[^\[\(]*\]\([^\[\(]*\)")
        extract_expr = re.compile(r"(?<=\().+?(?=\))")

        matches: List[Any] = find_expr.findall(text)

        output_text: str = text

        for match in matches:
            image = extract_expr.findall(match)[0]

            if not image.startswith("http"):
                Utils.log(LogType.INFO, "Uploading", image)

                filename: str = os.path.basename(image)
                try:
                    uploaded_file = self._remote_project.upload(filename,
                                                                filepath=image)
                    output_text = output_text.replace(image,
                                                      uploaded_file["url"])
                except FileNotFoundError:
                    Utils.log(LogType.WARNING, "Failed to upload image", image)
                    print("The file does not exist.")

        return output_text
示例#5
0
 def __init__(self, pipeline_id: int) -> None:
     RepositoryConnection.__init__(self)
     try:
         self.pipeline: ProjectPipeline = self._remote_project.pipelines.get(
             pipeline_id, lazy=False)
     except GitlabGetError:
         Utils.log(LogType.WARNING, f"No pipeline with ID {pipeline_id}")
         sys.exit(1)
示例#6
0
def run(args: argparse.Namespace) -> None:
    """
    :param args: parsed arguments
    """
    repo: Repo = Utils.get_cwd_repo()
    remote_url = repo.git.remote("get-url", args.remote)
    ssh_url = Utils.ssh_url_from_http(Utils.normalize_url(remote_url))
    repo.git.remote("set-url", args.remote, ssh_url)
示例#7
0
 def __login(self, hostname: str, token: str) -> None:
     try:
         connection: Gitlab = Gitlab(hostname, private_token=token)
         connection.auth()
         self._connections.append(connection)
     except GitlabAuthenticationError:
         Utils.log(LogType.ERROR, "Could not log into GitLab")
         sys.exit(1)
示例#8
0
 def __login(self, hostname: str, token: str) -> None:
     try:
         self._connection: Gitlab = Gitlab(hostname, private_token=token)
         self._connection.auth()
     except (GitlabAuthenticationError, GitlabGetError):
         Utils.log(LogType.ERROR,
                   "Could not log into GitLab: {}".format(hostname))
         sys.exit(1)
示例#9
0
文件: issues.py 项目: KDE/git-lab
 def open_web(self) -> None:
     """
     Open issue with xdg-open
     """
     if self._remote_project.issues_enabled:
         Utils.xdg_open(f"{self._remote_project.web_url}/-/issues")
     else:
         Utils.log(LogType.ERROR, "Issue are disabled for this project")
示例#10
0
    def test_normalize_url(self):
        scp_url: str = "[email protected]:KDE/kaidan.git"
        url = Utils.normalize_url(scp_url)
        self.assertEqual(url, "ssh://[email protected]/KDE/kaidan.git")

        http_url: str = "https://invent.kde.org/KDE/kaidan.git"
        url = Utils.normalize_url(http_url)
        self.assertEqual(url, "https://invent.kde.org/KDE/kaidan.git")
示例#11
0
    def __fulltext_valid(self) -> bool:
        lines = self.__fulltext.splitlines()

        if not lines or not lines[0]:
            Utils.log(LogType.ERROR, "The first line (title) can't be empty")
            return False

        return True
示例#12
0
    def update_estimated(self, time_str: str) -> None:
        """Updates the estimated time for the issue. Overrides the old value."""
        if not is_valid_time_str(time_str):
            Utils.log(LogType.WARNING,
                      f"{time_str} is an invalid time string.")
            sys.exit(1)

        self.issue.time_estimate(time_str)
        self.issue.save()
        print(TextFormatting.green(f"Set estimate to {time_str}"))
示例#13
0
    def update_spent(self, time_str: str) -> None:
        """Adds time spent to the already existing time spent."""
        if not is_valid_time_str(time_str):
            Utils.log(LogType.WARNING,
                      f"{time_str} is an invalid time string.")
            sys.exit(1)

        self.issue.add_spent_time(time_str)
        self.issue.save()
        print(TextFormatting.green(f"Added time entry of {time_str}"))
示例#14
0
 def __init__(self, issue_id: int):
     """
     Creates a new issue connection. Requires a valid issue ID for the current project.
     """
     RepositoryConnection.__init__(self)
     try:
         self.issue: ProjectIssue = self._remote_project.issues.get(
             issue_id, lazy=False)
     except GitlabGetError:
         Utils.log(LogType.WARNING, f"No issue with ID {issue_id}")
         sys.exit(1)
示例#15
0
    def test_str_id_for_url(self):
        url: str = "ssh://[email protected]/KDE/kaidan.git"

        str_id: str = Utils.str_id_for_url(url)

        self.assertEqual(str_id, "KDE/kaidan")

        # This is not a valid repository name for KDE invent, though
        url = "ssh://[email protected]/KDE/kaidan%.git"
        str_id = Utils.str_id_for_url(url)
        self.assertEqual(str_id, "KDE/kaidan%")
示例#16
0
    def __init__(self,
                 status: Optional[PipelineStatus] = None,
                 ref: Optional[str] = None) -> None:
        RepositoryConnection.__init__(self)

        self.status: Optional[PipelineStatus] = status
        self.ref: Optional[str] = ref

        if ref is not None and ref not in self._local_repo.refs:
            # Print a warning, if the ref is not found LOCALLY
            # The remote may contain refs, that do not exists inside the local copy,
            # therefore only a warning is printed.
            Utils.log(LogType.WARNING, f"Ref '{ref}' is not found locally.")
示例#17
0
    def __init__(self,
                 extra_text: str = "",
                 placeholder_title: str = "",
                 placeholder_body: str = "") -> None:
        self.__input(extra_text, placeholder_title, placeholder_body)
        self.__fulltext_remove_comments()
        if not self.__fulltext_valid():
            Utils.log(LogType.ERROR, "Text not valid, aborting")
            sys.exit(1)

        lines: List[str] = self.__fulltext.splitlines()

        self.title = lines[0]
        self.body = "\n".join(lines[1:])
示例#18
0
    def create_mr(self) -> None:
        """
        Creates a merge request with the changes from the current branch
        """

        mrs: List[
            ProjectMergeRequest] = self._remote_project.mergerequests.list(
                source_branch=self._local_repo.active_branch.name,
                target_branch=self.__target_branch,
                target_project_id=self._remote_project.id,
            )

        if len(mrs) > 0:
            merge_request = mrs[0]
            Utils.log(
                LogType.INFO,
                'Updating existing merge request "{}" at: {}'.format(
                    merge_request.title, merge_request.web_url),
            )
            return

        e_input = EditorInput(
            placeholder_title=self._local_repo.head.commit.summary,
            placeholder_body=self._local_repo.head.commit.message.split(
                "\n", 1)[1].strip(),
            extra_text="The markdown syntax for embedding images " +
            "![description](/path/to/file) can be used to upload images.",
        )

        project: Project = self.__remote_fork if self.__fork else self._remote_project

        merge_request = project.mergerequests.create({
            "source_branch":
            self._local_repo.active_branch.name,
            "target_branch":
            self.__target_branch,
            "title":
            e_input.title,
            "description":
            self.__upload_assets(e_input.body),
            "target_project_id":
            self._remote_project.id,
            "allow_maintainer_to_push":
            True,
            "remove_source_branch":
            True,
        })
        Utils.log(LogType.INFO, "Created merge request at",
                  merge_request.web_url)
示例#19
0
文件: config.py 项目: KDE/git-lab
    def __migrate_to_version_1(self) -> None:
        if "version" not in self.__config:
            Utils.log(LogType.INFO,
                      "Migrating configuration file to version 1")

            new_config: Dict[str, Any] = {"version": 1, "instances": {}}

            for hostname in self.__config.keys():
                new_config["instances"][hostname] = {
                    "auth_type": "token",
                    "token": self.__config[hostname],
                }

            self.__config = new_config
            self.save()
示例#20
0
文件: config.py 项目: KDE/git-lab
    def __init__(self) -> None:
        repository_path: Optional[str] = Utils.find_dotgit(os.getcwd())
        if repository_path:
            self.config_path = repository_path + os.path.sep + ".git" + os.path.sep + "gitlabconfig"
        else:
            Utils.log(LogType.ERROR,
                      "Current directory is not a git repository")
            sys.exit(1)

        if not os.path.isfile(self.config_path):
            with open(self.config_path, "w+") as file:
                json.dump({}, file)
                file.close()

        self.__file = open(self.config_path, "r+")
        self.__config = json.load(self.__file)
示例#21
0
文件: snippet.py 项目: KDE/git-lab
 def paste(self, file: TextIO, title: Optional[str]) -> None:
     """
     paste the contents of a TextIO object
     """
     snippet: Snippet = self._connection.snippets.create({
         "title":
         title,
         "file_name":
         file.name,
         "content":
         file.read(),
         "visibility":
         "public"
     })
     Utils.log(LogType.INFO, "Created snippet at", snippet.web_url)
     print("You can access it raw at", snippet.raw_url)
示例#22
0
    def print_formatted_list(self) -> None:
        """
        Print the list of pipelines to terminal formatted as a table
        """
        table = Table()

        # Compute args that are sent to GitLab
        args: Dict[str, str] = {}
        if self.status is not None:
            # Only yield pipelines that have the specific status
            # If empty all pipelines will be returned by GitLab
            args["status"] = str(self.status)

        if self.ref is not None:
            # Only yield pipeline that match a given reference
            args["ref"] = self.ref

        # Build the printable table
        pipelines: List[ProjectPipeline] = self._remote_project.pipelines.list(
            **args)
        for pipeline in pipelines:
            row: List[str] = [
                TextFormatting.BOLD + "#" + str(pipeline.id) +
                TextFormatting.END,
                pipeline.ref,
                Utils.pretty_date(pipeline.created_at),
                PipelineStatus.format(pipeline.status),
            ]
            table.add_row(row)

        table.print()
示例#23
0
    def test_gitlab_instance_url(self):
        # https
        repos = (
            "[email protected]:KDE/kaidan.git",
            "ssh://[email protected]/KDE/kaidan.git"
            "https://invent.kde.org/KDE/kaidan",
            "git://invent.kde.org/KDE/kaidan",
        )

        for repo in repos:
            url = Utils.gitlab_instance_url(repo)
            self.assertEqual(url, "https://invent.kde.org")

        # http
        url = Utils.gitlab_instance_url("http://invent.kde.org/KDE/kaidan")
        self.assertEqual(url, "http://invent.kde.org")
示例#24
0
文件: snippet.py 项目: KDE/git-lab
def run(args: argparse.Namespace) -> None:
    """
    run snippet creation commands
    :param args: parsed arguments
    """
    snippets = Snippets()
    file: TextIO
    if args.filename:
        try:
            file = open(args.filename, "r")
        except FileNotFoundError:
            Utils.log(LogType.ERROR, "Failed to open file", args.filename)
            sys.exit(1)
    else:
        file = sys.stdin

    snippets.paste(file, title=args.title)
示例#25
0
    def test_timezone_awareness_of_pretty_date(self) -> None:
        ref_date = datetime(2020, 1, 1, tzinfo=timezone.utc)
        fmt = "%Y-%m-%dT%H:%M:%S.%fZ"

        iso_date_1 = ref_date + timedelta(seconds=9)
        self.assertEqual(
            Utils.pretty_date(
                ref_date.replace(tzinfo=None).strftime(fmt), iso_date_1),
            "just now")
示例#26
0
    def fork(self) -> None:
        """
        Try to create a fork of the remote repository.
        If the fork already exists, no new fork will be created.
        """

        if "fork" in self._local_repo.remotes:
            # Fork already exists
            fork_str_id: str = Utils.str_id_for_url(
                self._local_repo.remotes.fork.url)

            # Try to retrieve the remote project object, if it doesn't exist on the server,
            # go on with the logic to create a new fork.
            try:
                self.__remote_fork = self._connection.projects.get(fork_str_id)
                return
            except GitlabGetError:
                pass

        try:
            self.__remote_fork = self._remote_project.forks.create({})

            # WORKAROUND: the return of create() is unreliable,
            # and sometimes doesn't allow to create merge requests,
            # so request a fresh project object.
            self.__remote_fork = self._connection.projects.get(
                self.__remote_fork.id)

            self._local_repo.create_remote(
                "fork", url=self.__remote_fork.ssh_url_to_repo)
        except GitlabCreateError:
            Utils.log(
                LogType.INFO,
                "Fork exists, but no fork remote exists locally, trying to guess the url",
            )
            # Detect ssh url
            url = Utils.ssh_url_from_http(self._connection.user.web_url + "/" +
                                          self._remote_project.path)

            self._local_repo.create_remote("fork", url=url)

            str_id: str = Utils.str_id_for_url(
                self._local_repo.remotes.fork.url)
            self.__remote_fork = self._connection.projects.get(str_id)
示例#27
0
    def commit(self) -> None:
        """
        Determine whether there are uncommitted changes, and ask the user what to do about them
        """

        index: IndexFile = self._local_repo.index
        if len(index.diff("HEAD")) > 0:
            Utils.log(LogType.INFO, "You have staged but uncommited changes.")
            create_commit: bool = Utils.ask_bool(
                "do you want to create a new commit?")

            if create_commit:
                # We can't use self.local_repo().git.commit() here, as it would
                # start the editor in the background
                try:
                    subprocess.check_call(["git", "commit"])
                except subprocess.CalledProcessError:
                    Utils.log(LogType.ERROR, "git exited with an error code")
                    sys.exit(1)
示例#28
0
    def checkout(self, merge_request_id: int) -> None:
        """
        Checks out the merge request with the specified id in the local worktree
        """
        self.__mr = self._remote_project.mergerequests.get(merge_request_id, lazy=False)
        print('Checking out merge request "{}"...'.format(self.__mr.title))
        print("  branch:", self.__mr.source_branch)

        fetch_info = self._local_repo.remotes.origin.fetch(
            "merge-requests/{}/head".format(merge_request_id)
        )[0]
        if self.__mr.source_branch in self._local_repo.refs:
            # Make sure not to overwrite local changes
            overwrite = Utils.ask_bool(
                'Branch "{}" already exists locally, do you want to overwrite it?'.format(
                    self.__mr.source_branch
                )
            )

            if not overwrite:
                print("Aborting")
                sys.exit(1)

            # If the branch that we want to overwrite is currently checked out,
            # that will of course not work, so try to switch to another branch in the meantime.
            if self.__mr.source_branch == self._local_repo.head.reference.name:
                if "main" in self._local_repo.refs:
                    self._local_repo.refs.main.checkout()
                elif "master" in self._local_repo.refs:
                    self._local_repo.refs.master.checkout()
                else:
                    Utils.log(
                        LogType.ERROR,
                        "The branch that you want to overwrite is currently checked out \
                        and no other branch to temporarily switch to could be found. Please check out \
                        a different branch and try again.",
                    )
                    sys.exit(1)

            self._local_repo.delete_head(self.__mr.source_branch, "-f")

        head = self._local_repo.create_head(self.__mr.source_branch, fetch_info.ref)
        head.checkout()
示例#29
0
    def test_pretty_time_delta(self) -> None:
        delta = timedelta(days=1, hours=12, minutes=10, seconds=20)
        tot_sec = int(delta.total_seconds())
        self.assertEqual(Utils.pretty_time_delta(tot_sec), "1d 12h 10m 20s")

        delta = timedelta(hours=23, minutes=57, seconds=0)
        tot_sec = int(delta.total_seconds())
        self.assertEqual(Utils.pretty_time_delta(tot_sec), "23h 57m 0s")

        delta = timedelta(minutes=2, seconds=59)
        tot_sec = int(delta.total_seconds())
        self.assertEqual(Utils.pretty_time_delta(tot_sec), "2m 59s")

        delta = timedelta(seconds=3)
        tot_sec = int(delta.total_seconds())
        self.assertEqual(Utils.pretty_time_delta(tot_sec), "3s")

        # Also test sign
        delta = timedelta(seconds=3)
        tot_sec = -int(delta.total_seconds())
        self.assertEqual(Utils.pretty_time_delta(tot_sec), "3s")
示例#30
0
    def test_pretty_date(self) -> None:
        ref_date = datetime(2020, 1, 1)
        fmt = "%Y-%m-%dT%H:%M:%S.%fZ"

        iso_date_1 = ref_date + timedelta(seconds=9)
        self.assertEqual(Utils.pretty_date(ref_date.strftime(fmt), iso_date_1),
                         "just now")

        iso_date_1 = ref_date + timedelta(seconds=59)
        self.assertEqual(Utils.pretty_date(ref_date.strftime(fmt), iso_date_1),
                         "59 seconds ago")

        iso_date_1 = ref_date + timedelta(seconds=5 * 60)
        self.assertEqual(Utils.pretty_date(ref_date.strftime(fmt), iso_date_1),
                         "5 minutes ago")

        iso_date_1 = ref_date + timedelta(seconds=60 * 60)
        self.assertEqual(Utils.pretty_date(ref_date.strftime(fmt), iso_date_1),
                         "an hour ago")

        iso_date_1 = ref_date + timedelta(seconds=24 * 60 * 60)
        self.assertEqual(Utils.pretty_date(ref_date.strftime(fmt), iso_date_1),
                         "Yesterday")

        iso_date_1 = ref_date + timedelta(seconds=14 * 24 * 60 * 60)
        self.assertEqual(Utils.pretty_date(ref_date.strftime(fmt), iso_date_1),
                         "2 weeks ago")