Prefer Forgejo as the self-hosted forge product. Keep Gitea only as the Gitea-compatible API identifier (module backends/gitea, type string gitea). FORGEJO_TOKEN is preferred; GITEA_* remains a deprecated alias. INTENT/SCOPE quote ACT-ADR-005: issue-core is not the fleet ops claim queue. Connector docs describe repo work record → hub index → optional Forgejo projection, not activity-core → issue-core → harness. Assistant: grok Assistant-Session: 01a09dc6-3f0d-7c93-8b11-8e83c0623d49
171 lines
No EOL
4.9 KiB
Python
171 lines
No EOL
4.9 KiB
Python
"""
|
|
Backend Management CLI Commands
|
|
|
|
Commands for configuring and managing issue tracking backends.
|
|
"""
|
|
|
|
import click
|
|
import os
|
|
from .utils import (
|
|
load_backend_configs, save_backend_configs, format_backend_list,
|
|
test_backend_connection, validate_backend_type, echo_success,
|
|
echo_error, echo_warning, echo_info, confirm_action
|
|
)
|
|
|
|
# Preferred FORGEJO_*; GITEA_* remain deprecated aliases (ISSUE-WP-0006).
|
|
_FORGE_TOKEN_ENV_NAMES = (
|
|
'FORGEJO_TOKEN',
|
|
'FORGEJO_API_TOKEN',
|
|
'GITEA_API_TOKEN',
|
|
'GITEA_TOKEN',
|
|
)
|
|
|
|
|
|
def _forge_token_from_env():
|
|
"""Return (env_name, token) for the first set Forgejo/legacy token var."""
|
|
for name in _FORGE_TOKEN_ENV_NAMES:
|
|
value = os.getenv(name)
|
|
if value:
|
|
return name, value
|
|
return None, None
|
|
|
|
|
|
@click.group()
|
|
def backend_group():
|
|
"""Backend configuration and management."""
|
|
pass
|
|
|
|
|
|
@backend_group.command('list')
|
|
def list_backends():
|
|
"""List configured backends."""
|
|
configs = load_backend_configs()
|
|
click.echo(format_backend_list(configs))
|
|
|
|
|
|
@backend_group.command('add')
|
|
@click.argument('name')
|
|
@click.argument('backend_type', type=click.Choice(['local', 'gitea']))
|
|
@click.pass_context
|
|
def add_backend(ctx, name, backend_type):
|
|
"""Add a new backend configuration."""
|
|
configs = load_backend_configs()
|
|
|
|
if name in configs:
|
|
if not confirm_action(f"Backend '{name}' already exists. Overwrite?"):
|
|
click.echo("Aborted")
|
|
return
|
|
|
|
if backend_type == 'local':
|
|
db_path = click.prompt('Database path', default=f'~/.config/issue-tracker/{name}.db')
|
|
config = {
|
|
'type': 'local',
|
|
'db_path': str(db_path)
|
|
}
|
|
elif backend_type == 'gitea':
|
|
base_url = click.prompt('Forgejo base URL (Gitea-compatible API)')
|
|
owner = click.prompt('Repository owner/organization')
|
|
repo = click.prompt('Repository name')
|
|
|
|
env_name, env_token = _forge_token_from_env()
|
|
if env_token:
|
|
if env_name in ('GITEA_API_TOKEN', 'GITEA_TOKEN'):
|
|
click.echo(
|
|
f"Using API token from {env_name} "
|
|
"(deprecated alias; prefer FORGEJO_TOKEN)"
|
|
)
|
|
else:
|
|
click.echo(f"Using API token from {env_name} environment variable")
|
|
token = env_token
|
|
else:
|
|
token = click.prompt('Access token', hide_input=True)
|
|
|
|
config = {
|
|
'type': 'gitea',
|
|
'base_url': base_url.rstrip('/'),
|
|
'owner': owner,
|
|
'repo': repo,
|
|
'token': token
|
|
}
|
|
|
|
# Test connection
|
|
click.echo("Testing connection...")
|
|
if test_backend_connection(config):
|
|
echo_success("Connection successful!")
|
|
else:
|
|
echo_warning("Connection test failed, but configuration will be saved anyway.")
|
|
|
|
# Save configuration
|
|
configs[name] = config
|
|
save_backend_configs(configs)
|
|
|
|
echo_success(f"Backend '{name}' added successfully")
|
|
|
|
# Set as default if it's the first one
|
|
if 'default' not in configs:
|
|
configs['default'] = name
|
|
save_backend_configs(configs)
|
|
echo_info(f"Set '{name}' as default backend")
|
|
|
|
|
|
@backend_group.command('remove')
|
|
@click.argument('name')
|
|
def remove_backend(name):
|
|
"""Remove a backend configuration."""
|
|
configs = load_backend_configs()
|
|
|
|
if name not in configs:
|
|
echo_error(f"Backend '{name}' not found")
|
|
return
|
|
|
|
if not confirm_action(f"Remove backend '{name}'?"):
|
|
click.echo("Aborted")
|
|
return
|
|
|
|
del configs[name]
|
|
|
|
# Update default if necessary
|
|
if configs.get('default') == name:
|
|
remaining_backends = [k for k in configs.keys() if k != 'default']
|
|
if remaining_backends:
|
|
configs['default'] = remaining_backends[0]
|
|
echo_info(f"Set '{configs['default']}' as new default backend")
|
|
else:
|
|
del configs['default']
|
|
|
|
save_backend_configs(configs)
|
|
echo_success(f"Backend '{name}' removed")
|
|
|
|
|
|
@backend_group.command('test')
|
|
@click.argument('name')
|
|
def test_backend(name):
|
|
"""Test backend connection."""
|
|
configs = load_backend_configs()
|
|
|
|
if name not in configs:
|
|
echo_error(f"Backend '{name}' not found")
|
|
return
|
|
|
|
config = configs[name]
|
|
click.echo(f"Testing connection to '{name}'...")
|
|
|
|
if test_backend_connection(config):
|
|
echo_success("Connection successful!")
|
|
else:
|
|
echo_error("Connection failed!")
|
|
|
|
|
|
@backend_group.command('set-default')
|
|
@click.argument('name')
|
|
def set_default_backend(name):
|
|
"""Set default backend."""
|
|
configs = load_backend_configs()
|
|
|
|
if name not in configs:
|
|
echo_error(f"Backend '{name}' not found")
|
|
return
|
|
|
|
configs['default'] = name
|
|
save_backend_configs(configs)
|
|
echo_success(f"Set '{name}' as default backend") |