Skip to content
Open
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
28 changes: 28 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Project Guidelines

## Project Context

XTS Core is a Python CLI for running declarative command workflows defined in `.xts` YAML files.
Prioritize the alias-based workflow described in [README.md](../README.md) over direct file execution.
Target Linux environments and Python 3.10+.

## Architecture

- Keep changes aligned with the existing split between CLI entrypoints, alias management, argument parsing, and plugins under `src/xts_core/`.
- Treat `.xts` files as the user-facing workflow definition format and Python modules as the execution/runtime layer.
- Keep plugin work isolated to `src/xts_core/plugins/` and follow the existing `BaseXTSPlugin` abstraction.
- Prefer small, local changes over broad refactors unless the task explicitly requires restructuring.

## Build And Test

- Set up a local environment with `python -m venv .venv`, activate it, then run `pip install -e .`.
- Install runtime dependencies from the project metadata rather than duplicating dependency definitions.
- Run the focused test slice first when possible, then `pytest test/` for broader validation.
- If a change affects CLI behavior, validate it with a targeted `pytest` run before expanding scope.

## Conventions

- Preserve the existing Apache 2.0 header format in Python source files.
- Use helpers from `xts_core.utils` for user-facing terminal output instead of adding ad hoc `print()` calls.
- Keep YAML examples and `.xts` fixtures readable, minimal, and consistent with [examples/hello_world.xts](/home/ubuntu/TEST/test-automation/xts_core/worktrees/bash_completion_AI/examples/hello_world.xts).
- Link contributors to [CONTRIBUTING.md](/home/ubuntu/TEST/test-automation/xts_core/worktrees/bash_completion_AI/CONTRIBUTING.md) for branch naming, issue linkage, and pull request workflow.
14 changes: 14 additions & 0 deletions .github/instructions/python-conventions.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
description: "Use when editing Python source in src/xts_core, adding CLI behavior, extending alias management, or implementing plugins. Covers project-specific Python conventions and library usage."
applyTo: "src/**/*.py"
---

# Python Conventions

- Preserve the existing Apache 2.0 file header when editing or creating Python source files.
- Add a module docstring for new modules and keep public function or class docstrings concise and concrete.
- Prefer project helpers from `xts_core.utils` for user-facing output instead of raw `print()`.
- Follow the existing CLI style: keep argument parsing explicit, keep validation close to the command entrypoint, and avoid hidden side effects.
- Keep plugin implementations under `src/xts_core/plugins/` and subclass `BaseXTSPlugin` when adding new plugins.
- Reuse the project's current libraries and patterns before introducing new dependencies. In particular, stay consistent with `argparse`, `yaml`, `requests`, and `rich` usage already present in the codebase.
- Keep functions focused and local. Prefer straightforward control flow over indirection.
13 changes: 13 additions & 0 deletions .github/instructions/test-conventions.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
description: "Use when adding or updating pytest coverage, CLI tests, alias tests, or plugin tests in the test suite. Covers fixtures, mocking, and repository test patterns."
applyTo: "test/**/*.py"
---

# Test Conventions

- Write tests with `pytest` and the existing fixture style used in the `test/` directory.
- Use `tmp_path` for temporary files and directories instead of writing into real user locations.
- Use `monkeypatch` to isolate environment-dependent behavior such as `HOME` and XTS cache paths.
- Mock external systems and network calls with `unittest.mock.patch` or pytest fixtures so tests stay deterministic.
- Keep tests narrow and behavior-focused. Name them for the observable behavior being validated.
- When adding coverage for a module, prefer extending the nearest existing test file unless a new file gives a clearer split.
14 changes: 14 additions & 0 deletions .github/instructions/xts-yaml-format.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
description: "Use when creating or editing .xts workflow files, examples, or YAML command definitions for XTS. Covers command structure, nesting, passthrough parameters, and formatting expectations."
applyTo: "**/*.xts"
---

# XTS YAML Format

- Follow the command layout shown in [examples/hello_world.xts](../../examples/hello_world.xts).
- Use top-level keys as command groups and nested keys as executable commands or subcommands.
- Provide a `description` for each runnable command so CLI help stays useful.
- Use `command` as either a single shell command string or a list of commands to run in sequence.
- Use `params.passthrough: true` only when the command is intended to forward extra CLI arguments.
- Keep YAML indentation to two spaces and do not use tabs.
- Ignore unrelated data structures unless the task explicitly requires documenting or validating ignored sections.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ To see available commands for an alias:
xts myalias --help
```

### Validate an .xts File

Use the built-in validator to check YAML syntax for an `.xts` file without running it:

```sh
xts validate /path/to/file.xts
```

This command reports syntax errors clearly and exits with code `0` for valid files or `1` for invalid files.

## Example .xts File

```yaml
Expand Down
26 changes: 23 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
[build-system]
requires = ["setuptools>=64", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "xts"
version = "2.0.0"
dependencies = [
"rich>=13.7.1",
"yaml_runner @ git+https://github.com/rdkcentral/yaml_runner.git@2.1.0",
"pyinstaller>=6.12.0",
"requests>=2.32.3",
"rich-argparse>=1.7.2"
"rich-argparse>=1.7.2",
"importlib_resources>=7.1.0",
"yaml_runner @ git+https://github.com/rdkcentral/yaml_runner.git@2.2.0",
"argparse_completion @ git+https://github.com/rdkcentral/argparse_completion.git@1.0.0"
]
[tool.setuptools.packages.find]
include = ["xts_core", "xts_core.plugins"]
include = ["xts_core*"]
where = ["src"]
namespaces = false

# --- Include non-Python files ---
[tool.setuptools.package-data]
# If your data folder is *inside* xts_core, use this:
xts_core = ["data/**/*"]

# --- Include data outside packages ---
[tool.setuptools]
include-package-data = true

# This ensures data/ is included in sdist/wheel
[tool.setuptools.data-files]
# Installs data into a shared location in site-packages
"xts_core_data" = ["data/**/*"]

[project.scripts]
xts-install = "xts_core.install:main"
xts = "xts_core.xts:main"
13 changes: 13 additions & 0 deletions src/xts_core/data/xts_bash_completion.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

function _xts_runner_completion()
{
local IFS='
'
COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \
COMP_CWORD=$COMP_CWORD \
_XTS_COMPLETE=complete_bash $1 ) )
return 0
}

complete -o default -F _xts_runner_completion xts
50 changes: 43 additions & 7 deletions src/xts_core/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,26 @@
#* ******************************************************************************

import datetime
import importlib_resources
import os
from pathlib import Path
import platform
import subprocess
import tempfile
import xts_core

from .utils import info, error, warning

SCRIPT_PATH = Path(__file__).absolute()
SCRIPT_DIR = Path(os.path.dirname(SCRIPT_PATH))

def main():
if 'Linux' in platform.platform():
_linux_install()
else:
error('Unfortunately XTS does not currently support your OS')

def _linux_install():
script_path = Path(__file__).absolute()
script_dir = Path(os.path.dirname(script_path))
user_home = Path('.').home()
xts_dir = user_home.joinpath(Path('.xts'))
bin_dir = xts_dir.joinpath(Path('bin'))
Expand All @@ -50,7 +53,7 @@ def _linux_install():
os.makedirs(log_dir, exist_ok=True)
result = subprocess.run(['pyinstaller',
'--onefile',
f'{script_dir}/xts.py',
f'{SCRIPT_DIR}/xts.py',
'--name',
'xts',
'--distpath',
Expand Down Expand Up @@ -79,10 +82,43 @@ def _linux_install():
'Raise an issue on xts_core for more help:\n'+
'https://github.com/rdkcentral/xts_core/issues/new?template=01-bugs.yml')
else:
info('XTS has been installed successfully.\n'+
'Please run the following commands:\n' +
f'\texport PATH="{bin_dir}:$PATH"\n' +
'To permanently install this, add the export command as a line in your ~/.bashrc file')
shell = os.environ.get('SHELL','')
match shell:
case '/bin/bash':
_install_bash(user_home)
info('XTS has been installed successfully.\n')
case _:
warning('XTS has been installed successfully, but we could not automatically add xts to your PATH.\n' +
'Please add the following line to your shell config file:\n' +
f'export PATH="{user_home.joinpath(Path(".xts/bin"))}:$PATH"\n')

def _install_bash(user_home: Path):
if (bashrc:=user_home.joinpath(Path('.bashrc'))).exists():
with open(user_home.joinpath(Path('.bashrc')), 'a') as f:
f.write('\n# XTS PATH\n')
f.write(f'export PATH="{user_home.joinpath(Path(".xts/bin"))}:$PATH"\n')
_install_bash_completion(bashrc)
elif (bash_profile:=user_home.joinpath(Path('.bash_profile'))).exists():
with open(user_home.joinpath(Path('.bash_profile')), 'a') as f:
f.write('\n# XTS PATH\n')
f.write(f'export PATH="{user_home.joinpath(Path(".xts/bin"))}:$PATH"\n')
_install_bash_completion(bash_profile)
else:
warning('Could not find .bashrc or .bash_profile to add xts to PATH. Please add the following line to your shell config file:\n' +
f'export PATH="{user_home.joinpath(Path(".xts/bin"))}:$PATH"\n')

def _install_bash_completion(rc_file: Path):
user_home = rc_file.parent
bash_completion_dir = user_home.joinpath(Path('.bash_completion.d'))
if not bash_completion_dir.exists():
os.makedirs(bash_completion_dir)
bash_completion_script = bash_completion_dir.joinpath(Path('xts_bash_completion.sh'))
with importlib_resources.open_text(xts_core,'data/xts_bash_completion.sh', encoding='utf-8') as src, \
open(bash_completion_script, 'w') as dst:
dst.write(src.read())
with open(rc_file, 'a') as f:
f.write('\n# XTS bash completion\n')
f.write(f'source {bash_completion_dir}/xts_bash_completion.sh\n')

if __name__ == '__main__':
main()
7 changes: 6 additions & 1 deletion src/xts_core/plugins/xts_allocator_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ def _send_request(method, url, data=None) -> dict:
except requests.exceptions.RequestException as e:
plugin_utils.error(f'Error during request: {e}')

@staticmethod
def send_request(method, url, data=None) -> dict:
"""Compatibility wrapper expected by tests: delegates to _send_request."""
return XTSAllocatorClient._send_request(method, url, data)


@staticmethod
def _format_slots_list_to_table(response:list[dict]) -> Table:
Expand Down Expand Up @@ -506,7 +511,7 @@ def _update_slot(self,
state,
owner_email]):
plugin_utils.error('At least one field must be provided for update.')
sys.exit(1)
raise SystemExit(1)
# Required fields
payload = {
'slot_id': slot_id
Expand Down
Loading
Loading