blob: 3de44ddd31860d1b1bbc1602c23b418aa7e1f0e2 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Command line interface for magpie-fossil."""
from __future__ import annotations
import argparse
import json
import sys
from collections.abc import Sequence
from pathlib import Path
from typing import Any
from magpie_fossil import forum, ticket, wiki
from magpie_fossil.client import FossilError, find_repo_db
def _print_json(data: dict | list) -> None:
"""Helper to pretty print JSON data to stdout."""
print(json.dumps(data, indent=2))
def _build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="magpie-fossil",
description="Fossil SCM forge and tracker bridge capability wrapper.",
)
parser.add_argument(
"-C", "--cwd", default=".", help="Checkout directory to search for repository database."
)
parser.add_argument("-R", "--repository", help="Direct path to the Fossil repository database file.")
subparsers = parser.add_subparsers(dest="subcommand", required=True)
# tickets
p_ticket = subparsers.add_parser("ticket", help="Interact with Fossil tickets.")
t_sub = p_ticket.add_subparsers(dest="action", required=True)
t_sub.add_parser("list", help="List all tickets.")
p_t_get = t_sub.add_parser("get", help="Get details of a ticket.")
p_t_get.add_argument("uuid", help="Ticket UUID (or prefix).")
p_t_create = t_sub.add_parser("create", help="Create a ticket.")
p_t_create.add_argument("--title", required=True, help="Ticket title.")
p_t_create.add_argument("--body", required=True, help="Ticket description/body.")
p_t_create.add_argument(
"--field", action="append", nargs=2, metavar=("NAME", "VALUE"), help="Extra ticket field value pairs."
)
p_t_edit = t_sub.add_parser("edit", help="Update ticket fields.")
p_t_edit.add_argument("uuid", help="Ticket UUID (or prefix).")
p_t_edit.add_argument(
"--field",
action="append",
nargs=2,
required=True,
metavar=("NAME", "VALUE"),
help="Ticket field value pairs to update.",
)
p_t_comment = t_sub.add_parser("comment", help="Comment on a ticket.")
p_t_comment.add_argument("uuid", help="Ticket UUID (or prefix).")
p_t_comment.add_argument("--body", required=True, help="Comment body.")
# wiki
p_wiki = subparsers.add_parser("wiki", help="Interact with Fossil wiki.")
w_sub = p_wiki.add_subparsers(dest="action", required=True)
w_sub.add_parser("list", help="List all wiki page names.")
p_w_read = w_sub.add_parser("read", help="Read a wiki page.")
p_w_read.add_argument("name", help="Wiki page name.")
# forum
p_forum = subparsers.add_parser("forum", help="Interact with Fossil forums.")
f_sub = p_forum.add_subparsers(dest="action", required=True)
f_sub.add_parser("list", help="List all forum threads.")
p_f_read = f_sub.add_parser("read", help="Read all posts in a forum thread.")
p_f_read.add_argument("thread_uuid", help="Thread root UUID.")
return parser
def main(argv: Sequence[str] | None = None) -> int:
parser = _build_parser()
ns = parser.parse_args(argv)
# Resolve repository file path
repo_path: Path | None = None
if ns.repository:
repo_path = Path(ns.repository).resolve()
else:
start_dir = Path(ns.cwd).resolve()
repo_path = find_repo_db(start_dir)
if not repo_path or not repo_path.exists():
print(
"Error: Fossil repository database file not found. "
"Ensure you are running inside a Fossil checkout or specify the database path with -R/--repository.",
file=sys.stderr,
)
return 1
res: Any = None
try:
if ns.subcommand == "ticket":
if ns.action == "list":
res = ticket.list_tickets(repo_path)
_print_json(res)
elif ns.action == "get":
res = ticket.get_ticket(repo_path, ns.uuid)
_print_json(res)
elif ns.action == "create":
fields = dict(ns.field) if ns.field else {}
uuid = ticket.submit_ticket(repo_path, ns.title, ns.body, fields)
_print_json({"tkt_uuid": uuid})
elif ns.action == "edit":
fields = dict(ns.field)
uuid = ticket.update_ticket_fields(repo_path, ns.uuid, fields)
_print_json({"tkt_uuid": uuid})
elif ns.action == "comment":
uuid = ticket.submit_comment(repo_path, ns.uuid, ns.body)
_print_json({"tkt_uuid": uuid})
elif ns.subcommand == "wiki":
if ns.action == "list":
res = wiki.list_wiki(repo_path)
_print_json(res)
elif ns.action == "read":
res = wiki.read_wiki(repo_path, ns.name)
# Output page content as plain string
sys.stdout.write(res)
elif ns.subcommand == "forum":
if ns.action == "list":
res = forum.list_forum_threads(repo_path)
_print_json(res)
elif ns.action == "read":
res = forum.read_forum_thread(repo_path, ns.thread_uuid)
_print_json(res)
except FossilError as exc:
print(f"Error: {exc}", file=sys.stderr)
return 2
return 0