Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
exclude: ^static/.*|assets/.*|/migrations/.*|\.min\.js$|\.min\.css$|\.css\.map$|\.min\.js$|\.js\.map$|\.svg$
default_language_version:
python: python3.11
python: python3.12
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.2.1
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ For full documentation go to https://docs.taskbadger.net/python/.
pip install taskbadger
```

To use the `taskbadger` command-line tool, install the `cli` extra:

```bash
pip install 'taskbadger[cli]'
```

### Client Usage

```python
Expand Down
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ dependencies = [
"httpx >=0.20.0",
"attrs >=21.3.0",
"python-dateutil >=2.8.0",
"typer[all] <0.10.0",
"tomlkit >=0.12.5",
"importlib-metadata >=1.0; python_version < '3.8'",
"typing-extensions >=4.7.1; python_version <= '3.9'",
Expand All @@ -42,6 +41,10 @@ include = [
celery = [
"celery>=4.0.0,<6.0.0",
]
cli = [
"typer >=0.12",
"rich >=13.0",
]

[tool.uv]
package = true
Expand All @@ -64,6 +67,7 @@ dev = [
"pytest-celery",
"redis",
"openapi-python-client",
"taskbadger[cli]",
]

[project.scripts]
Expand Down
5 changes: 3 additions & 2 deletions taskbadger/cli/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
from enum import Enum
from typing import Optional

import typer
from rich import print
Expand Down Expand Up @@ -28,9 +29,9 @@ def get_actions(action_def: tuple[str, str, str]) -> list[Action]:
return []


def merge_kv_json(metadata_kv: list[str], metadata_json: str) -> dict:
def merge_kv_json(metadata_kv: Optional[list[str]], metadata_json: str) -> dict:
metadata = {}
for kv in metadata_kv:
for kv in metadata_kv or []:
k, v = kv.strip().split("=", 1)
metadata[k] = v

Expand Down
202 changes: 105 additions & 97 deletions taskbadger/cli_main.py
Original file line number Diff line number Diff line change
@@ -1,99 +1,107 @@
import sys
from typing import Optional

import typer
from rich import print

from taskbadger import __version__
from taskbadger.cli import create, get, list_tasks_command, run, update
from taskbadger.config import get_config, write_config
from taskbadger.sdk import _parse_token

app = typer.Typer(
rich_markup_mode="rich",
context_settings={"help_option_names": ["-h", "--help"]},
)


app.command(context_settings={"allow_extra_args": True, "ignore_unknown_options": False})(run)
app.command(context_settings={"ignore_unknown_options": False})(get)
app.command(context_settings={"ignore_unknown_options": False})(create)
app.command(context_settings={"ignore_unknown_options": False})(update)
app.command("list", context_settings={"ignore_unknown_options": False})(list_tasks_command)


def version_callback(value: bool):
if value:
print(f"Task Badger CLI Version: {__version__}")
raise typer.Exit()


@app.command()
def configure(ctx: typer.Context):
"""Update CLI configuration."""
config = ctx.meta["tb_config"]
token = typer.prompt("API Key", default=config.token)
parsed = _parse_token(token)
if parsed:
org_slug, project_slug, api_key = parsed
print(f"Project key detected — organization: [green]{org_slug}[/green], project: [green]{project_slug}[/green]")
config.organization_slug = org_slug
config.project_slug = project_slug
config.token = token
else:
config.organization_slug = typer.prompt("Organization slug", default=config.organization_slug)
config.project_slug = typer.prompt("Project slug", default=config.project_slug)
config.token = token
path = write_config(config)
print(f"Config written to [green]{path}[/green]")


@app.command()
def docs():
"""Open Task Badger docs in a browser."""
typer.launch("https://docs.taskbadger.net")


@app.command()
def info(ctx: typer.Context):
"""Show CLI configuration."""
config = ctx.meta["tb_config"]
print(str(config))


@app.callback()
def main(
ctx: typer.Context,
org: Optional[str] = typer.Option(
None,
"--org",
"-o",
metavar="TASKBADGER_ORG",
show_default=False,
help="Organization Slug. This will override values from the config file and environment variables.",
),
project: Optional[str] = typer.Option(
None,
"--project",
"-p",
show_envvar=False,
metavar="TASKBADGER_PROJECT",
show_default=False,
help="Project Slug. This will override values from the config file and environment variables.",
),
version: Optional[bool] = typer.Option( # noqa
None,
"--version",
callback=version_callback,
is_eager=True,
help="Show CLI Version",
),
):
"""
Task Badger CLI
"""
config = get_config(org=org, project=project)
ctx.meta["tb_config"] = config


if __name__ == "__main__":
app()
try:
import typer
from rich import print
except ImportError as exc:
_missing = exc.name or "typer"

def app() -> None:
sys.stderr.write(
f"The Task Badger CLI requires the '{_missing}' package, which is not installed.\n"
"Install the CLI extras with:\n\n"
" pip install 'taskbadger[cli]'\n"
)
sys.exit(1)
else:
from taskbadger import __version__
from taskbadger.cli import create, get, list_tasks_command, run, update
from taskbadger.config import get_config, write_config
from taskbadger.sdk import _parse_token

app = typer.Typer(
rich_markup_mode="rich",
context_settings={"help_option_names": ["-h", "--help"]},
)

app.command(context_settings={"allow_extra_args": True, "ignore_unknown_options": False})(run)
app.command(context_settings={"ignore_unknown_options": False})(get)
app.command(context_settings={"ignore_unknown_options": False})(create)
app.command(context_settings={"ignore_unknown_options": False})(update)
app.command("list", context_settings={"ignore_unknown_options": False})(list_tasks_command)

def version_callback(value: bool):
if value:
print(f"Task Badger CLI Version: {__version__}")
raise typer.Exit()

@app.command()
def configure(ctx: typer.Context):
"""Update CLI configuration."""
config = ctx.meta["tb_config"]
token = typer.prompt("API Key", default=config.token)
parsed = _parse_token(token)
if parsed:
org_slug, project_slug, api_key = parsed
print(
f"Project key detected — organization: [green]{org_slug}[/green], "
f"project: [green]{project_slug}[/green]"
)
config.organization_slug = org_slug
config.project_slug = project_slug
config.token = token
else:
config.organization_slug = typer.prompt("Organization slug", default=config.organization_slug)
config.project_slug = typer.prompt("Project slug", default=config.project_slug)
config.token = token
path = write_config(config)
print(f"Config written to [green]{path}[/green]")

@app.command()
def docs():
"""Open Task Badger docs in a browser."""
typer.launch("https://docs.taskbadger.net")

@app.command()
def info(ctx: typer.Context):
"""Show CLI configuration."""
config = ctx.meta["tb_config"]
print(str(config))

@app.callback()
def main(
ctx: typer.Context,
org: Optional[str] = typer.Option(
None,
"--org",
"-o",
metavar="TASKBADGER_ORG",
show_default=False,
help="Organization Slug. This will override values from the config file and environment variables.",
),
project: Optional[str] = typer.Option(
None,
"--project",
"-p",
show_envvar=False,
metavar="TASKBADGER_PROJECT",
show_default=False,
help="Project Slug. This will override values from the config file and environment variables.",
),
version: Optional[bool] = typer.Option( # noqa
None,
"--version",
callback=version_callback,
is_eager=True,
help="Show CLI Version",
),
):
"""
Task Badger CLI
"""
config = get_config(org=org, project=project)
ctx.meta["tb_config"] = config

if __name__ == "__main__":
app()
27 changes: 14 additions & 13 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.