lazy-github 0.3.0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lazy_github-0.3.2/.github/workflows/publish.yaml +61 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/PKG-INFO +1 -1
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/bindings.py +4 -3
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/config.py +8 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/constants.py +1 -1
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/context.py +23 -1
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/github/backends/cli.py +10 -1
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/github/backends/hishel.py +9 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/github/backends/protocol.py +7 -0
- lazy_github-0.3.2/lazy_github/lib/github/checks.py +13 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/github/client.py +3 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/github/pull_requests.py +18 -5
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/logging.py +0 -6
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/models/github.py +28 -0
- lazy_github-0.3.2/lazy_github/ui/screens/lookup_pull_request.py +88 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/screens/lookup_repository.py +3 -3
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/screens/new_issue.py +4 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/screens/new_pull_request.py +10 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/screens/primary.py +1 -1
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/widgets/pull_requests.py +105 -5
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/widgets/repositories.py +0 -1
- lazy_github-0.3.2/lazy_github/version.py +1 -0
- lazy_github-0.3.0/.github/workflows/publish.yaml +0 -44
- lazy_github-0.3.0/lazy_github/version.py +0 -1
- {lazy_github-0.3.0 → lazy_github-0.3.2}/.devcontainer/devcontainer.json +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/.github/workflows/code-checks.yaml +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/.gitignore +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/.pre-commit-config.yaml +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/LICENSE +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/README.md +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/images/lazy-github-conversation-ui.svg +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/images/lazy-github-settings-ui.png +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/__main__.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/cli.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/git_cli.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/github/auth.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/github/branches.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/github/issues.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/github/notifications.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/github/repositories.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/github/workflows.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/messages.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/lib/utils.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/app.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/screens/auth.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/screens/edit_issue.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/screens/new_comment.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/screens/notifications.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/screens/settings.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/screens/trigger_workflow.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/widgets/command_log.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/widgets/common.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/widgets/conversations.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/widgets/info.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/widgets/issues.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lazy_github/ui/widgets/workflows.py +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/lint.sh +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/pyproject.toml +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/start.sh +0 -0
- {lazy_github-0.3.0 → lazy_github-0.3.2}/uv.lock +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: Publish the package to pypi
|
|
2
|
+
|
|
3
|
+
on: workflow_dispatch
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
pypi-publish:
|
|
7
|
+
name: Upload release to PyPI
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
environment:
|
|
10
|
+
name: pypi
|
|
11
|
+
url: https://pypi.org/p/lazy-github
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write
|
|
14
|
+
outputs:
|
|
15
|
+
new-version: ${{ steps.calculate-version.outputs.version }}
|
|
16
|
+
steps:
|
|
17
|
+
# Perform a bunch of setup
|
|
18
|
+
- uses: actions/checkout@v3
|
|
19
|
+
- uses: actions/setup-python@v4
|
|
20
|
+
- uses: yezz123/setup-uv@v4
|
|
21
|
+
|
|
22
|
+
- name: Calculate version
|
|
23
|
+
id: calculate-version
|
|
24
|
+
run: |
|
|
25
|
+
new_version=$(uvx hatch version)
|
|
26
|
+
echo "version=${new_version}" >> $GITHUB_OUTPUT
|
|
27
|
+
|
|
28
|
+
# Build the distribution
|
|
29
|
+
- name: Build lazy-github distribution
|
|
30
|
+
run: uvx hatch build
|
|
31
|
+
|
|
32
|
+
- name: Publish package distributions to PyPI
|
|
33
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
34
|
+
|
|
35
|
+
create-tag:
|
|
36
|
+
name: Create tag
|
|
37
|
+
needs: pypi-publish
|
|
38
|
+
permissions: write-all
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/github-script@v7
|
|
42
|
+
with:
|
|
43
|
+
script: |
|
|
44
|
+
github.rest.git.createRef({
|
|
45
|
+
owner: context.repo.owner,
|
|
46
|
+
repo: context.repo.repo,
|
|
47
|
+
ref: 'refs/tags/v${{ needs.pypi-publish.outputs.new-version }}',
|
|
48
|
+
sha: context.sha
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
notify-discord:
|
|
52
|
+
name: Notify when this workflow completes (regardless of success or failure)
|
|
53
|
+
needs: pypi-publish
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
steps:
|
|
56
|
+
- uses: nobrayner/discord-webhook@v1
|
|
57
|
+
with:
|
|
58
|
+
title: "Version ${{ needs.pypi-publish.outputs.new-version }} published to PyPi"
|
|
59
|
+
description: "Check out the new version [here](https://pypi.org/project/lazy-github/${{ needs.pypi-publish.outputs.new-version }}/)"
|
|
60
|
+
github-token: ${{ secrets.github_token }}
|
|
61
|
+
discord-webhook: ${{ secrets.DISCORD_WEBHOOK }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lazy-github
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: A terminal UI for interacting with Github
|
|
5
5
|
Author-email: "Chris (Gizmo)" <gizmo385@users.noreply.github.com>
|
|
6
6
|
Maintainer-email: "Chris (Gizmo)" <gizmo385@users.noreply.github.com>
|
|
@@ -8,18 +8,19 @@ class LazyGithubBindings:
|
|
|
8
8
|
# Global App Bindings
|
|
9
9
|
QUIT_APP = Binding("q", "quit", "Quit", id="app.quit")
|
|
10
10
|
OPEN_COMMAND_PALLETE = Binding("ctrl+p", "command_palette", "Commands", id="app.command_palette")
|
|
11
|
-
MAXIMIZE_WIDGET = Binding("
|
|
11
|
+
MAXIMIZE_WIDGET = Binding("M", "maximize", "Maximize", id="app.maximize_widget")
|
|
12
12
|
|
|
13
13
|
# Triggering creation flows
|
|
14
14
|
OPEN_ISSUE = Binding("I", "open_issue", "New Issue", id="issue.new")
|
|
15
15
|
EDIT_ISSUE = Binding("E", "edit_issue", "Edit Issue", id="issue.edit")
|
|
16
|
-
OPEN_PULL_REQUEST = Binding("P", "open_pull_request", "New PR", id="pull_request.new")
|
|
17
16
|
NEW_COMMENT = Binding("n", "new_comment", "New Comment", id="conversation.comment.new")
|
|
18
17
|
REPLY_TO_REVIEW = Binding("r", "reply_to_review", "Reply to Review", id="conversation.review.reply")
|
|
19
18
|
REPLY_TO_COMMENT = Binding("r", "reply_to_individual_comment", "Reply to Comment", id="conversation.comment.reply")
|
|
20
19
|
|
|
21
20
|
# Pull request actions
|
|
22
|
-
|
|
21
|
+
OPEN_PULL_REQUEST = Binding("P", "open_pull_request", "New PR", id="pull_request.new")
|
|
22
|
+
MERGE_PULL_REQUEST = Binding("ctrl+m", "merge_pull_request", "Merge PR", id="pull_request.merge")
|
|
23
|
+
LOOKUP_PULL_REQUEST = Binding("O", "lookup_pull_request", "Lookup Pull Request", id="pull_request.lookup")
|
|
23
24
|
|
|
24
25
|
# Repository actions
|
|
25
26
|
TOGGLE_FAVORITE_REPO = Binding("ctrl+f", "toggle_favorite_repo", "Toggle Favorite", id="repositories.favorite")
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from contextlib import contextmanager
|
|
3
3
|
from datetime import timedelta
|
|
4
|
+
from enum import StrEnum
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import Any, Generator, Literal, Optional
|
|
6
7
|
|
|
@@ -71,11 +72,18 @@ class RepositorySettings(BaseModel):
|
|
|
71
72
|
favorites: list[str] = []
|
|
72
73
|
|
|
73
74
|
|
|
75
|
+
class MergeMethod(StrEnum):
|
|
76
|
+
MERGE = "merge"
|
|
77
|
+
SQUASH = "squash"
|
|
78
|
+
REBASE = "rebase"
|
|
79
|
+
|
|
80
|
+
|
|
74
81
|
class PullRequestSettings(BaseModel):
|
|
75
82
|
"""Changes how pull requests are retrieved from the Github API"""
|
|
76
83
|
|
|
77
84
|
state_filter: IssueStateFilter = IssueStateFilter.ALL
|
|
78
85
|
owner_filter: IssueOwnerFilter = IssueOwnerFilter.ALL
|
|
86
|
+
preferred_merge_method: MergeMethod = MergeMethod.SQUASH
|
|
79
87
|
|
|
80
88
|
|
|
81
89
|
class IssueSettings(BaseModel):
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from typing import Optional
|
|
2
3
|
|
|
3
4
|
from lazy_github.lib.config import Config
|
|
4
5
|
from lazy_github.lib.constants import JSON_CONTENT_ACCEPT_TYPE
|
|
5
|
-
from lazy_github.lib.git_cli import current_local_repo_full_name
|
|
6
|
+
from lazy_github.lib.git_cli import current_local_branch_name, current_local_repo_full_name
|
|
6
7
|
from lazy_github.lib.github.backends.protocol import BackendType
|
|
7
8
|
from lazy_github.lib.github.client import GithubClient
|
|
9
|
+
from lazy_github.lib.logging import LazyGithubLogFormatter, lg
|
|
8
10
|
from lazy_github.lib.utils import classproperty
|
|
9
11
|
from lazy_github.models.github import Repository
|
|
10
12
|
|
|
@@ -16,14 +18,27 @@ class LazyGithubContext:
|
|
|
16
18
|
_config: Config | None = None
|
|
17
19
|
_client: GithubClient | None = None
|
|
18
20
|
_current_directory_repo: str | None = None
|
|
21
|
+
_current_directory_branch: str | None = None
|
|
19
22
|
|
|
20
23
|
# Directly assigned attributes
|
|
21
24
|
current_repo: Repository | None = None
|
|
22
25
|
|
|
26
|
+
@classmethod
|
|
27
|
+
def _setup_logging_handler(cls, config: Config) -> None:
|
|
28
|
+
"""Setup the file logger for LazyGithub"""
|
|
29
|
+
try:
|
|
30
|
+
config.core.logfile_path.parent.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
lg_file_handler = logging.FileHandler(filename=config.core.logfile_path)
|
|
32
|
+
lg_file_handler.setFormatter(LazyGithubLogFormatter())
|
|
33
|
+
lg.addHandler(lg_file_handler)
|
|
34
|
+
except Exception:
|
|
35
|
+
lg.exception("Failed to setup file logger for LazyGithub")
|
|
36
|
+
|
|
23
37
|
@classproperty
|
|
24
38
|
def config(cls) -> Config:
|
|
25
39
|
if cls._config is None:
|
|
26
40
|
cls._config = Config.load_config()
|
|
41
|
+
cls._setup_logging_handler(cls._config)
|
|
27
42
|
return cls._config
|
|
28
43
|
|
|
29
44
|
@classproperty
|
|
@@ -52,6 +67,13 @@ class LazyGithubContext:
|
|
|
52
67
|
cls._current_directory_repo = current_local_repo_full_name()
|
|
53
68
|
return cls._current_directory_repo
|
|
54
69
|
|
|
70
|
+
@classproperty
|
|
71
|
+
def current_directory_branch(cls) -> str | None:
|
|
72
|
+
"""The owner/name of the repo associated with the current working directory (if one exists)"""
|
|
73
|
+
if not cls._current_directory_branch:
|
|
74
|
+
cls._current_directory_branch = current_local_branch_name()
|
|
75
|
+
return cls._current_directory_branch
|
|
76
|
+
|
|
55
77
|
|
|
56
78
|
def github_headers(accept: str = JSON_CONTENT_ACCEPT_TYPE, cache_duration: Optional[int] = None) -> dict[str, str]:
|
|
57
79
|
"""Helper function to build headers for Github API requests"""
|
|
@@ -9,6 +9,7 @@ from typing import Any
|
|
|
9
9
|
from lazy_github.lib.config import Config
|
|
10
10
|
from lazy_github.lib.constants import CONFIG_FOLDER, JSON_CONTENT_ACCEPT_TYPE
|
|
11
11
|
from lazy_github.lib.github.backends.protocol import GithubApiBackend, GithubApiRequestFailed, GithubApiResponse
|
|
12
|
+
from lazy_github.lib.logging import lg
|
|
12
13
|
from lazy_github.models.github import User
|
|
13
14
|
|
|
14
15
|
_HEADER_RE = re.compile(r"^([a-zA-Z-]+)\:(.+)$")
|
|
@@ -66,7 +67,6 @@ def _clear_temporary_bodies() -> None:
|
|
|
66
67
|
|
|
67
68
|
async def run_gh_cli_command(command: list[str]) -> CliApiResponse:
|
|
68
69
|
"""Simple wrapper around running a Github CLI command"""
|
|
69
|
-
from lazy_github.lib.logging import lg
|
|
70
70
|
|
|
71
71
|
proc = await asyncio.create_subprocess_exec(
|
|
72
72
|
"gh", *command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
|
@@ -158,6 +158,15 @@ class GithubCliBackend(GithubApiBackend):
|
|
|
158
158
|
command = _build_command(url, headers=headers, body=json, method="PATCH")
|
|
159
159
|
return await run_gh_cli_command(command)
|
|
160
160
|
|
|
161
|
+
async def put(
|
|
162
|
+
self,
|
|
163
|
+
url: str,
|
|
164
|
+
headers: dict[str, str] | None = None,
|
|
165
|
+
json: dict[str, str] | None = None,
|
|
166
|
+
) -> Any:
|
|
167
|
+
command = _build_command(url, headers=headers, body=json, method="PUT")
|
|
168
|
+
return await run_gh_cli_command(command)
|
|
169
|
+
|
|
161
170
|
async def get_user(self) -> User:
|
|
162
171
|
response = await self.get("/user")
|
|
163
172
|
return User(**response.json())
|
|
@@ -69,6 +69,15 @@ class HishelGithubApiBackend(GithubApiBackend):
|
|
|
69
69
|
response = await self.api_client.patch(url, headers=headers, json=json)
|
|
70
70
|
return HishelApiResponse(response)
|
|
71
71
|
|
|
72
|
+
async def put(
|
|
73
|
+
self,
|
|
74
|
+
url: str,
|
|
75
|
+
headers: dict[str, str] | None = None,
|
|
76
|
+
json: dict[str, str] | None = None,
|
|
77
|
+
) -> HishelApiResponse:
|
|
78
|
+
response = await self.api_client.put(url, headers=headers, json=json)
|
|
79
|
+
return HishelApiResponse(response)
|
|
80
|
+
|
|
72
81
|
def github_headers(
|
|
73
82
|
self, accept: str = JSON_CONTENT_ACCEPT_TYPE, cache_duration: int | None = None
|
|
74
83
|
) -> dict[str, str]:
|
|
@@ -48,6 +48,13 @@ class GithubApiBackend(Protocol):
|
|
|
48
48
|
json: dict[str, str] | None = None,
|
|
49
49
|
) -> GithubApiResponse: ...
|
|
50
50
|
|
|
51
|
+
async def put(
|
|
52
|
+
self,
|
|
53
|
+
url: str,
|
|
54
|
+
headers: dict[str, str] | None = None,
|
|
55
|
+
json: dict[str, str] | None = None,
|
|
56
|
+
) -> GithubApiResponse: ...
|
|
57
|
+
|
|
51
58
|
async def get_user(self) -> User: ...
|
|
52
59
|
|
|
53
60
|
def github_headers(
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from lazy_github.lib.context import LazyGithubContext, github_headers
|
|
2
|
+
from lazy_github.models.github import CombinedCheckStatus, Repository
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
async def combined_check_status_for_ref(
|
|
6
|
+
repo: Repository, ref: str, per_page: int = 100, page: int = 1
|
|
7
|
+
) -> CombinedCheckStatus:
|
|
8
|
+
query_params = {"page": page, "per_page": per_page}
|
|
9
|
+
response = await LazyGithubContext.client.get(
|
|
10
|
+
f"/repos/{repo.owner.login}/{repo.name}/commits/{ref}/status", headers=github_headers(), params=query_params
|
|
11
|
+
)
|
|
12
|
+
response.raise_for_status()
|
|
13
|
+
return CombinedCheckStatus(**response.json())
|
|
@@ -45,5 +45,8 @@ class GithubClient(GithubApiBackend):
|
|
|
45
45
|
async def patch(self, url: str, headers: dict[str, str] | None = None, json: dict[str, str] | None = None) -> Any:
|
|
46
46
|
return await self.backend.patch(url, headers, json)
|
|
47
47
|
|
|
48
|
+
async def put(self, url: str, headers: dict[str, str] | None = None, json: dict[str, str] | None = None) -> Any:
|
|
49
|
+
return await self.backend.put(url, headers, json)
|
|
50
|
+
|
|
48
51
|
async def get_user(self) -> User:
|
|
49
52
|
return await self.backend.get_user()
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from lazy_github.lib.config import MergeMethod
|
|
1
2
|
from lazy_github.lib.constants import DIFF_CONTENT_ACCEPT_TYPE
|
|
2
3
|
from lazy_github.lib.context import LazyGithubContext, github_headers
|
|
3
4
|
from lazy_github.lib.github.backends.cli import run_gh_cli_command
|
|
@@ -7,6 +8,7 @@ from lazy_github.models.github import (
|
|
|
7
8
|
FullPullRequest,
|
|
8
9
|
Issue,
|
|
9
10
|
PartialPullRequest,
|
|
11
|
+
PullRequestMergeResult,
|
|
10
12
|
Repository,
|
|
11
13
|
Review,
|
|
12
14
|
ReviewComment,
|
|
@@ -24,13 +26,13 @@ async def list_for_repo(repo: Repository) -> list[PartialPullRequest]:
|
|
|
24
26
|
async def create_pull_request(
|
|
25
27
|
repo: Repository, title: str, body: str, base_ref: str, head_ref: str, draft: bool = False
|
|
26
28
|
) -> FullPullRequest:
|
|
27
|
-
user = await LazyGithubContext.client.user()
|
|
28
29
|
url = f"/repos/{repo.owner.login}/{repo.name}/pulls"
|
|
29
30
|
request_body = {
|
|
30
31
|
"title": title,
|
|
31
32
|
"draft": draft,
|
|
32
33
|
"base": base_ref,
|
|
33
|
-
|
|
34
|
+
# TODO: This prevents it from working with forks, but means it'll work for same-repo PRs. Issue
|
|
35
|
+
"head": f"{repo.owner.login}:{head_ref}",
|
|
34
36
|
}
|
|
35
37
|
if body:
|
|
36
38
|
request_body["body"] = body
|
|
@@ -39,12 +41,12 @@ async def create_pull_request(
|
|
|
39
41
|
return FullPullRequest(**response.json(), repo=repo)
|
|
40
42
|
|
|
41
43
|
|
|
42
|
-
async def get_full_pull_request(
|
|
44
|
+
async def get_full_pull_request(repo: Repository, pr_number: int) -> FullPullRequest:
|
|
43
45
|
"""Converts a partial pull request into a full pull request"""
|
|
44
|
-
url = f"/repos/{
|
|
46
|
+
url = f"/repos/{repo.owner.login}/{repo.name}/pulls/{pr_number}"
|
|
45
47
|
response = await LazyGithubContext.client.get(url, headers=github_headers())
|
|
46
48
|
response.raise_for_status()
|
|
47
|
-
return FullPullRequest(**response.json(), repo=
|
|
49
|
+
return FullPullRequest(**response.json(), repo=repo)
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
async def get_diff(pr: FullPullRequest) -> str:
|
|
@@ -62,6 +64,17 @@ async def get_diff(pr: FullPullRequest) -> str:
|
|
|
62
64
|
return response.text
|
|
63
65
|
|
|
64
66
|
|
|
67
|
+
async def merge_pull_request(pr: FullPullRequest, merge_method: MergeMethod) -> PullRequestMergeResult:
|
|
68
|
+
"""
|
|
69
|
+
Attempts to merge the PR via the Github API. The head sha of the PR must match for the merge to be successful.
|
|
70
|
+
"""
|
|
71
|
+
url = f"/repos/{pr.repo.owner.login}/{pr.repo.name}/pulls/{pr.number}/merge"
|
|
72
|
+
body = {"merge_method": merge_method, "sha": pr.head.sha}
|
|
73
|
+
response = await LazyGithubContext.client.put(url, headers=github_headers(), json=body)
|
|
74
|
+
response.raise_for_status()
|
|
75
|
+
return PullRequestMergeResult(**response.json())
|
|
76
|
+
|
|
77
|
+
|
|
65
78
|
async def get_review_comments(pr: FullPullRequest, review: Review) -> list[ReviewComment]:
|
|
66
79
|
url = f"/repos/{pr.repo.owner.login}/{pr.repo.name}/pulls/{pr.number}/reviews/{review.id}/comments"
|
|
67
80
|
response = await LazyGithubContext.client.get(url, headers=github_headers())
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
-
from lazy_github.lib.context import LazyGithubContext
|
|
4
|
-
|
|
5
3
|
|
|
6
4
|
# A universal logging format that we can use
|
|
7
5
|
class LazyGithubLogFormatter(logging.Formatter):
|
|
@@ -16,11 +14,7 @@ class LazyGithubLogFormatter(logging.Formatter):
|
|
|
16
14
|
return super().format(record)
|
|
17
15
|
|
|
18
16
|
|
|
19
|
-
LazyGithubContext.config.core.logfile_path.parent.mkdir(parents=True, exist_ok=True)
|
|
20
17
|
lg = logging.Logger("lazy_github", level=logging.DEBUG)
|
|
21
|
-
_lg_file_handler = logging.FileHandler(filename=LazyGithubContext.config.core.logfile_path)
|
|
22
|
-
_lg_file_handler.setFormatter(LazyGithubLogFormatter())
|
|
23
|
-
lg.addHandler(_lg_file_handler)
|
|
24
18
|
|
|
25
19
|
|
|
26
20
|
# Override the logging level for a bunch of noisy library loggers
|
|
@@ -61,6 +61,7 @@ class Issue(BaseModel):
|
|
|
61
61
|
class Ref(BaseModel):
|
|
62
62
|
user: User
|
|
63
63
|
ref: str
|
|
64
|
+
sha: str
|
|
64
65
|
|
|
65
66
|
|
|
66
67
|
class PartialPullRequest(Issue):
|
|
@@ -84,6 +85,12 @@ class FullPullRequest(PartialPullRequest):
|
|
|
84
85
|
diff_url: str
|
|
85
86
|
|
|
86
87
|
|
|
88
|
+
class PullRequestMergeResult(BaseModel):
|
|
89
|
+
sha: str
|
|
90
|
+
merged: bool
|
|
91
|
+
message: str
|
|
92
|
+
|
|
93
|
+
|
|
87
94
|
class AuthorAssociation(StrEnum):
|
|
88
95
|
COLLABORATOR = "COLLABORATOR"
|
|
89
96
|
CONTRIBUTOR = "CONTRIBUTOR"
|
|
@@ -189,3 +196,24 @@ class Notification(BaseModel):
|
|
|
189
196
|
unread: bool
|
|
190
197
|
updated_at: datetime
|
|
191
198
|
last_read_at: datetime | None
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class CheckStatusState(StrEnum):
|
|
202
|
+
SUCCESS = "success"
|
|
203
|
+
PENDING = "pending"
|
|
204
|
+
ERROR = "error"
|
|
205
|
+
FAILURE = "failure"
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class CheckStatus(BaseModel):
|
|
209
|
+
description: str
|
|
210
|
+
context: str
|
|
211
|
+
state: CheckStatusState
|
|
212
|
+
target_url: str | None
|
|
213
|
+
updated_at: datetime | None
|
|
214
|
+
created_at: datetime | None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class CombinedCheckStatus(BaseModel):
|
|
218
|
+
state: CheckStatusState
|
|
219
|
+
statuses: list[CheckStatus]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from textual import on
|
|
2
|
+
from textual.app import ComposeResult
|
|
3
|
+
from textual.containers import Container, Horizontal
|
|
4
|
+
from textual.screen import ModalScreen
|
|
5
|
+
from textual.widgets import Button, Input, Label, Markdown, Rule
|
|
6
|
+
|
|
7
|
+
from lazy_github.lib.bindings import LazyGithubBindings
|
|
8
|
+
from lazy_github.lib.context import LazyGithubContext
|
|
9
|
+
from lazy_github.lib.github.backends.protocol import GithubApiRequestFailed
|
|
10
|
+
from lazy_github.lib.github.pull_requests import get_full_pull_request
|
|
11
|
+
from lazy_github.models.github import FullPullRequest
|
|
12
|
+
from lazy_github.ui.widgets.common import LazyGithubFooter
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LookupPullRequestButtons(Horizontal):
|
|
16
|
+
DEFAULT_CSS = """
|
|
17
|
+
LookupPullRequestButtons {
|
|
18
|
+
align: center middle;
|
|
19
|
+
height: auto;
|
|
20
|
+
width: 100%;
|
|
21
|
+
}
|
|
22
|
+
Button {
|
|
23
|
+
margin: 1;
|
|
24
|
+
}
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def compose(self) -> ComposeResult:
|
|
28
|
+
yield Button("Open", id="lookup", variant="success")
|
|
29
|
+
yield Button("Cancel", id="cancel", variant="error")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class LookupPullRequestContainer(Container):
|
|
33
|
+
DEFAULT_CSS = """
|
|
34
|
+
LookupPullRequestContainer {
|
|
35
|
+
align: center middle;
|
|
36
|
+
}
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def compose(self) -> ComposeResult:
|
|
40
|
+
yield Markdown("# Search for a pull request by number:")
|
|
41
|
+
yield Label("[bold]Pull Request Number:[/bold]")
|
|
42
|
+
yield Input(
|
|
43
|
+
id="pull_request_number",
|
|
44
|
+
placeholder="Pull request number",
|
|
45
|
+
type="number",
|
|
46
|
+
)
|
|
47
|
+
yield Rule()
|
|
48
|
+
yield LookupPullRequestButtons()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class LookupPullRequestModal(ModalScreen[FullPullRequest | None]):
|
|
52
|
+
DEFAULT_CSS = """
|
|
53
|
+
LookupPullRequestModal {
|
|
54
|
+
align: center middle;
|
|
55
|
+
content-align: center middle;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
LookupPullRequestContainer {
|
|
59
|
+
width: 60;
|
|
60
|
+
max-height: 25;
|
|
61
|
+
border: thick $background 80%;
|
|
62
|
+
background: $surface-lighten-3;
|
|
63
|
+
}
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
BINDINGS = [LazyGithubBindings.SUBMIT_DIALOG, LazyGithubBindings.CLOSE_DIALOG]
|
|
67
|
+
|
|
68
|
+
def compose(self) -> ComposeResult:
|
|
69
|
+
yield LookupPullRequestContainer()
|
|
70
|
+
yield LazyGithubFooter()
|
|
71
|
+
|
|
72
|
+
@on(Button.Pressed, "#lookup")
|
|
73
|
+
async def action_submit(self) -> None:
|
|
74
|
+
assert LazyGithubContext.current_repo is not None, "Current repo is missing!"
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
pr_number = int(self.query_one("#pull_request_number", Input).value)
|
|
78
|
+
pull_request = await get_full_pull_request(LazyGithubContext.current_repo, pr_number)
|
|
79
|
+
except ValueError:
|
|
80
|
+
self.notify("Must enter a valid pull request number!", title="Invalid PR Number", severity="error")
|
|
81
|
+
except GithubApiRequestFailed:
|
|
82
|
+
self.notify("Could not find pull request!", title="Unknown PR", severity="error")
|
|
83
|
+
else:
|
|
84
|
+
self.dismiss(pull_request)
|
|
85
|
+
|
|
86
|
+
@on(Button.Pressed, "#cancel")
|
|
87
|
+
async def action_close(self) -> None:
|
|
88
|
+
self.dismiss(None)
|
|
@@ -54,11 +54,11 @@ class LookupRepositoryContainer(Container):
|
|
|
54
54
|
class LookupRepositoryModal(ModalScreen[Repository | None]):
|
|
55
55
|
DEFAULT_CSS = """
|
|
56
56
|
LookupRepositoryModal {
|
|
57
|
-
|
|
57
|
+
align: center middle;
|
|
58
|
+
content-align: center middle;
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
LookupRepositoryContainer {
|
|
61
|
-
dock: top;
|
|
62
62
|
width: 60;
|
|
63
63
|
max-height: 25;
|
|
64
64
|
border: thick $background 80%;
|
|
@@ -78,7 +78,7 @@ class LookupRepositoryModal(ModalScreen[Repository | None]):
|
|
|
78
78
|
# If we haven't tracked this repo already, we will do so
|
|
79
79
|
config.repositories.additional_repos_to_track.append(repo_name)
|
|
80
80
|
|
|
81
|
-
@on(Button.Pressed, "#
|
|
81
|
+
@on(Button.Pressed, "#lookup")
|
|
82
82
|
async def action_submit(self) -> None:
|
|
83
83
|
repo_input = self.query_one("#repo_to_lookup", Input)
|
|
84
84
|
continue_tracking_input = self.query_one("#continue_tracking", Switch)
|
|
@@ -64,8 +64,18 @@ class BranchSelection(Horizontal):
|
|
|
64
64
|
def base_ref(self) -> str:
|
|
65
65
|
return self._base_ref_input.value
|
|
66
66
|
|
|
67
|
+
@work
|
|
68
|
+
async def set_default_branch_value(self) -> None:
|
|
69
|
+
if (
|
|
70
|
+
LazyGithubContext.current_directory_repo
|
|
71
|
+
and LazyGithubContext.current_repo
|
|
72
|
+
and LazyGithubContext.current_directory_repo == LazyGithubContext.current_repo.full_name
|
|
73
|
+
):
|
|
74
|
+
self.query_one("#head_ref", Input).value = LazyGithubContext.current_directory_branch
|
|
75
|
+
|
|
67
76
|
async def on_mount(self) -> None:
|
|
68
77
|
self.fetch_branches()
|
|
78
|
+
self.set_default_branch_value()
|
|
69
79
|
|
|
70
80
|
@on(BranchesLoaded)
|
|
71
81
|
def handle_loaded_branches(self, message: BranchesLoaded) -> None:
|
|
@@ -270,7 +270,7 @@ class MainViewPane(Container):
|
|
|
270
270
|
return self.query_one("#selection_details", SelectionDetailsContainer)
|
|
271
271
|
|
|
272
272
|
async def on_pull_request_selected(self, message: PullRequestSelected) -> None:
|
|
273
|
-
full_pr = await get_full_pull_request(message.pr)
|
|
273
|
+
full_pr = await get_full_pull_request(message.pr.repo, message.pr.number)
|
|
274
274
|
tabbed_content = self.query_one("#selection_detail_tabs", TabbedContent)
|
|
275
275
|
await tabbed_content.clear_panes()
|
|
276
276
|
await tabbed_content.add_pane(PrOverviewTabPane(full_pr))
|
|
@@ -3,20 +3,30 @@ from textual import on, work
|
|
|
3
3
|
from textual.app import ComposeResult
|
|
4
4
|
from textual.containers import ScrollableContainer, VerticalScroll
|
|
5
5
|
from textual.coordinate import Coordinate
|
|
6
|
-
from textual.widgets import DataTable, Label, Markdown, RichLog, Rule, TabPane
|
|
6
|
+
from textual.widgets import Collapsible, DataTable, Label, ListItem, ListView, Markdown, RichLog, Rule, TabPane
|
|
7
7
|
|
|
8
8
|
from lazy_github.lib.bindings import LazyGithubBindings
|
|
9
|
+
from lazy_github.lib.constants import CHECKMARK, X_MARK
|
|
9
10
|
from lazy_github.lib.context import LazyGithubContext
|
|
11
|
+
from lazy_github.lib.github.backends.protocol import GithubApiRequestFailed
|
|
12
|
+
from lazy_github.lib.github.checks import combined_check_status_for_ref
|
|
10
13
|
from lazy_github.lib.github.issues import get_comments, list_issues
|
|
11
14
|
from lazy_github.lib.github.pull_requests import (
|
|
12
15
|
get_diff,
|
|
13
16
|
get_reviews,
|
|
17
|
+
merge_pull_request,
|
|
14
18
|
reconstruct_review_conversation_hierarchy,
|
|
15
19
|
)
|
|
16
20
|
from lazy_github.lib.logging import lg
|
|
17
21
|
from lazy_github.lib.messages import IssuesAndPullRequestsFetched, PullRequestSelected
|
|
18
22
|
from lazy_github.lib.utils import bold, link, pluralize
|
|
19
|
-
from lazy_github.models.github import
|
|
23
|
+
from lazy_github.models.github import (
|
|
24
|
+
CheckStatus,
|
|
25
|
+
CheckStatusState,
|
|
26
|
+
FullPullRequest,
|
|
27
|
+
PartialPullRequest,
|
|
28
|
+
)
|
|
29
|
+
from lazy_github.ui.screens.lookup_pull_request import LookupPullRequestModal
|
|
20
30
|
from lazy_github.ui.screens.new_comment import NewCommentModal
|
|
21
31
|
from lazy_github.ui.widgets.common import LazilyLoadedDataTable, LazyGithubContainer
|
|
22
32
|
from lazy_github.ui.widgets.conversations import IssueCommentContainer, ReviewContainer
|
|
@@ -31,6 +41,8 @@ class PullRequestsContainer(LazyGithubContainer):
|
|
|
31
41
|
This container includes the primary datatable for viewing pull requests on the UI.
|
|
32
42
|
"""
|
|
33
43
|
|
|
44
|
+
BINDINGS = [LazyGithubBindings.LOOKUP_PULL_REQUEST]
|
|
45
|
+
|
|
34
46
|
def __init__(self, *args, **kwargs) -> None:
|
|
35
47
|
super().__init__(*args, **kwargs)
|
|
36
48
|
self.pull_requests: dict[int, PartialPullRequest] = {}
|
|
@@ -50,6 +62,16 @@ class PullRequestsContainer(LazyGithubContainer):
|
|
|
50
62
|
reverse_sort=True,
|
|
51
63
|
)
|
|
52
64
|
|
|
65
|
+
@work
|
|
66
|
+
async def action_lookup_pull_request(self) -> None:
|
|
67
|
+
if pr := await self.app.push_screen_wait(LookupPullRequestModal()):
|
|
68
|
+
if pr.number not in self.pull_requests:
|
|
69
|
+
self.pull_requests[pr.number] = pr
|
|
70
|
+
self.searchable_table.append_rows([pull_request_to_cell(pr)])
|
|
71
|
+
|
|
72
|
+
self.post_message(PullRequestSelected(pr))
|
|
73
|
+
lg.info(f"Looked up PR #{pr.number}")
|
|
74
|
+
|
|
53
75
|
async def fetch_more_pull_requests(self, batch_size: int, batch_to_fetch: int) -> list[tuple[str | int, ...]]:
|
|
54
76
|
if not LazyGithubContext.current_repo:
|
|
55
77
|
return []
|
|
@@ -117,12 +139,64 @@ class PrOverviewTabPane(TabPane):
|
|
|
117
139
|
PrOverviewTabPane {
|
|
118
140
|
overflow-y: auto;
|
|
119
141
|
}
|
|
142
|
+
|
|
143
|
+
Collapsible {
|
|
144
|
+
height: auto;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
ListView {
|
|
148
|
+
height: auto;
|
|
149
|
+
}
|
|
120
150
|
"""
|
|
121
151
|
|
|
152
|
+
BINDINGS = [LazyGithubBindings.MERGE_PULL_REQUEST]
|
|
153
|
+
|
|
122
154
|
def __init__(self, pr: FullPullRequest) -> None:
|
|
123
155
|
super().__init__("Overview", id="overview_pane")
|
|
124
156
|
self.pr = pr
|
|
125
157
|
|
|
158
|
+
def _status_check_to_label(self, status: CheckStatus) -> str:
|
|
159
|
+
match status.state:
|
|
160
|
+
case CheckStatusState.SUCCESS:
|
|
161
|
+
status_summary = f"[green]{CHECKMARK} Passed[/green]"
|
|
162
|
+
case CheckStatusState.PENDING:
|
|
163
|
+
status_summary = "[yellow]... Pending[/yellow]"
|
|
164
|
+
case CheckStatusState.FAILURE:
|
|
165
|
+
status_summary = f"[red]{X_MARK} Failed[/red]"
|
|
166
|
+
case CheckStatusState.ERROR:
|
|
167
|
+
status_summary = f"[red]{X_MARK} Errored[/red]"
|
|
168
|
+
|
|
169
|
+
return f"{status_summary} {status.context} - {status.description}"
|
|
170
|
+
|
|
171
|
+
async def action_merge_pull_request(self) -> None:
|
|
172
|
+
if self.pr.merged_at is not None:
|
|
173
|
+
self.notify("PR has already been merged!", title="Already Merged", severity="warning")
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
merge_result = await merge_pull_request(
|
|
178
|
+
self.pr, LazyGithubContext.config.pull_requests.preferred_merge_method
|
|
179
|
+
)
|
|
180
|
+
if merge_result.merged:
|
|
181
|
+
lg.info(f"Merged PR {self.pr.number} in repo {self.pr.repo.full_name}")
|
|
182
|
+
self.notify(
|
|
183
|
+
f"Pull request {self.pr.number} merged. Note some cached information on the UI may be out of date.",
|
|
184
|
+
title="PR Merged",
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# This will force refetch the updated information about the PR for the UI
|
|
188
|
+
self.post_message(PullRequestSelected(self.pr))
|
|
189
|
+
else:
|
|
190
|
+
lg.warning(f"Failed to merge PR {self.pr.number} in repo {self.pr.repo.full_name}")
|
|
191
|
+
self.notify(
|
|
192
|
+
f"Pull request {self.pr.number} could not be merged", title="Error Merging PR", severity="error"
|
|
193
|
+
)
|
|
194
|
+
except GithubApiRequestFailed:
|
|
195
|
+
lg.exception(f"Failed to merge PR {self.pr.number} in repo {self.pr.repo.full_name}")
|
|
196
|
+
self.notify(
|
|
197
|
+
f"Pull request {self.pr.number} could not be merged", title="Error Merging PR", severity="error"
|
|
198
|
+
)
|
|
199
|
+
|
|
126
200
|
def compose(self) -> ComposeResult:
|
|
127
201
|
pr_link = link(f"(#{self.pr.number})", self.pr.html_url)
|
|
128
202
|
user_link = link(self.pr.user.login, self.pr.user.html_url)
|
|
@@ -157,10 +231,36 @@ class PrOverviewTabPane(TabPane):
|
|
|
157
231
|
if self.pr.merged_at:
|
|
158
232
|
date = self.pr.merged_at.strftime("%c")
|
|
159
233
|
yield Label(f"\nMerged on {date}")
|
|
234
|
+
yield Rule()
|
|
235
|
+
|
|
236
|
+
# This is where we'll store information about the status checks being run on the PR
|
|
237
|
+
with Collapsible(title="Status Checks: ...", id="collapsible_status_checks") as c:
|
|
238
|
+
c.loading = True
|
|
239
|
+
# TODO: We should probably make this a table? That would allow follow-up actions to be performed as well
|
|
240
|
+
yield ListView(id="status_checks_list")
|
|
160
241
|
|
|
161
242
|
yield Rule()
|
|
162
243
|
yield Markdown(self.pr.body)
|
|
163
244
|
|
|
245
|
+
@work
|
|
246
|
+
async def load_checks(self) -> None:
|
|
247
|
+
# TODO: This should probably check normal check runs as well? Unsure if the combined check status includes all
|
|
248
|
+
# of those
|
|
249
|
+
combined_check_status = await combined_check_status_for_ref(self.pr.repo, self.pr.head.sha)
|
|
250
|
+
status_checks_list = self.query_one("#status_checks_list", ListView)
|
|
251
|
+
collapse_container = self.query_one("#collapsible_status_checks", Collapsible)
|
|
252
|
+
if statuses := combined_check_status.statuses:
|
|
253
|
+
status_labels = sorted(self._status_check_to_label(c) for c in statuses)
|
|
254
|
+
status_checks_list.extend(ListItem(Label(status_label)) for status_label in status_labels)
|
|
255
|
+
collapse_container.title = f"Status checks: {combined_check_status.state.value.title()}"
|
|
256
|
+
else:
|
|
257
|
+
collapse_container.title = "No status checks on PR"
|
|
258
|
+
|
|
259
|
+
collapse_container.loading = False
|
|
260
|
+
|
|
261
|
+
async def on_mount(self) -> None:
|
|
262
|
+
_ = self.load_checks()
|
|
263
|
+
|
|
164
264
|
|
|
165
265
|
class PrDiffTabPane(TabPane):
|
|
166
266
|
def __init__(self, pr: FullPullRequest) -> None:
|
|
@@ -172,7 +272,7 @@ class PrDiffTabPane(TabPane):
|
|
|
172
272
|
yield RichLog(id="diff_contents", highlight=True)
|
|
173
273
|
|
|
174
274
|
@work
|
|
175
|
-
async def fetch_diff(self):
|
|
275
|
+
async def fetch_diff(self) -> None:
|
|
176
276
|
diff_contents = self.query_one("#diff_contents", RichLog)
|
|
177
277
|
try:
|
|
178
278
|
diff = await get_diff(self.pr)
|
|
@@ -185,9 +285,9 @@ class PrDiffTabPane(TabPane):
|
|
|
185
285
|
diff_contents.write(diff)
|
|
186
286
|
self.loading = False
|
|
187
287
|
|
|
188
|
-
def on_mount(self) -> None:
|
|
288
|
+
async def on_mount(self) -> None:
|
|
189
289
|
self.loading = True
|
|
190
|
-
self.fetch_diff()
|
|
290
|
+
_ = self.fetch_diff()
|
|
191
291
|
|
|
192
292
|
|
|
193
293
|
class PrConversationTabPane(TabPane):
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "0.3.2"
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
name: Publish the package to pypi
|
|
2
|
-
|
|
3
|
-
on: workflow_dispatch
|
|
4
|
-
|
|
5
|
-
jobs:
|
|
6
|
-
pypi-publish:
|
|
7
|
-
name: Upload release to PyPI
|
|
8
|
-
runs-on: ubuntu-latest
|
|
9
|
-
environment:
|
|
10
|
-
name: pypi
|
|
11
|
-
url: https://pypi.org/p/lazy-github
|
|
12
|
-
permissions:
|
|
13
|
-
id-token: write
|
|
14
|
-
steps:
|
|
15
|
-
# Perform a bunch of setup
|
|
16
|
-
- uses: actions/checkout@v3
|
|
17
|
-
- uses: actions/setup-python@v4
|
|
18
|
-
- uses: yezz123/setup-uv@v4
|
|
19
|
-
|
|
20
|
-
- name: Calculate version
|
|
21
|
-
id: calculate-version
|
|
22
|
-
run: |
|
|
23
|
-
new_version=$((uvx hatch version))
|
|
24
|
-
echo "version=${new_version}" >> $GITHUB_OUTPUT
|
|
25
|
-
|
|
26
|
-
# Build the distribution
|
|
27
|
-
- name: Build lazy-github distribution
|
|
28
|
-
run: uvx hatch build
|
|
29
|
-
|
|
30
|
-
- name: Publish package distributions to PyPI
|
|
31
|
-
uses: pypa/gh-action-pypi-publish@release/v1
|
|
32
|
-
|
|
33
|
-
- name: Create tag
|
|
34
|
-
run: |
|
|
35
|
-
tag_name="v${{ steps.calculate-version.outputs.version }}"
|
|
36
|
-
git tag $tag_name && git push origin $tag_name
|
|
37
|
-
|
|
38
|
-
- name: Notify when this workflow completes (regardless of success or failure)
|
|
39
|
-
uses: nobrayner/discord-webhook@v1
|
|
40
|
-
with:
|
|
41
|
-
title: "Version ${{ steps.calculate-version.outputs.version }} published to PyPi"
|
|
42
|
-
description: "Check out the new version [here](https://pypi.org/project/lazy-github/${{ steps.calculate-version.outputs.version }}/)"
|
|
43
|
-
github-token: ${{ secrets.github_token }}
|
|
44
|
-
discord-webhook: ${{ secrets.DISCORD_WEBHOOK }}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VERSION = "0.3.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|