Skip to content

Commit 127ed60

Browse files
authored
Merge pull request #3 from ni-kismet/users/fvisser/python-cli-add-notebook-management
SystemLink CLI - Initial Implementation
2 parents b25ffe3 + 1cac037 commit 127ed60

23 files changed

+4531
-0
lines changed

.github/copilot-instructions.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Copilot Project Guidelines for slcli
2+
3+
## General Best Practices
4+
5+
- All code must be PEP8-compliant and pass linting (black, ni-python-styleguide).
6+
- All new and modified code must include appropriate docstrings and type hints where possible.
7+
- All public functions and classes must have docstrings.
8+
- Use environment variables and keyring for all credentials and sensitive data.
9+
- All CLI commands must provide clear help text and validation.
10+
- Use Click for all CLI interfaces (not Typer).
11+
- All API interactions must handle errors gracefully and provide user-friendly messages.
12+
- All new features and bugfixes must include or update unit tests.
13+
- All code must be cross-platform (Windows, macOS, Linux) unless otherwise specified.
14+
- All generated, build, and cache files must be excluded via `.gitignore`.
15+
16+
## CLI Best Practices (Based on https://clig.dev)
17+
18+
### Output & Formatting
19+
- All list commands must support `--output/-o` option with `table` (default) and `json` formats
20+
- Use consistent visual indicators: `` for success messages, `` for error messages
21+
- Send all error messages to stderr using `click.echo(..., err=True)`
22+
- For JSON output, display all data at once (no pagination); for table output, retain pagination
23+
- Handle empty results gracefully: `[]` for JSON, descriptive message for table format
24+
25+
### Error Handling & Exit Codes
26+
- Use standardized exit codes from the `ExitCodes` class:
27+
- `SUCCESS = 0`: Successful operation
28+
- `GENERAL_ERROR = 1`: Generic error
29+
- `INVALID_INPUT = 2`: Invalid user input or parameters
30+
- `NOT_FOUND = 3`: Resource not found
31+
- `PERMISSION_DENIED = 4`: Permission/authorization error
32+
- `NETWORK_ERROR = 5`: Network connectivity error
33+
- Use `handle_api_error(exc)` function for consistent API error handling
34+
- Use `format_success(message, data)` function for consistent success messages
35+
- Always exit with appropriate codes using `sys.exit(ExitCodes.*)` rather than raising ClickException
36+
37+
### Command Structure
38+
- All commands must validate required parameters and provide helpful error messages
39+
- Use consistent parameter names across similar commands (e.g., `--workspace`, `--output`)
40+
- Provide sensible defaults and show them in help text with `show_default=True`
41+
- Support both ID and name-based lookups where applicable (e.g., `--id` or `--name`)
42+
43+
### User Experience
44+
- Show progress for long-running operations when possible
45+
- Use confirmation prompts for destructive operations
46+
- Provide clear, actionable error messages that guide users toward solutions
47+
- Support shell completion where feasible
48+
49+
## Required Actions After Any Change
50+
51+
- Run `poetry run ni-python-styleguide lint` to check for linting and style issues.
52+
- Run `poetry run black .` to auto-format code to the configured line length (100).
53+
- Run `poetry run pytest` to ensure all tests pass.
54+
- If adding or modifying CLI commands, update the `README.md` usage examples if needed.
55+
- If adding dependencies, update `pyproject.toml` and run `poetry lock`.
56+
- If changing packaging or build scripts, verify `poetry run build-pyinstaller` works and produces a binary in `dist/`.
57+
- Verify all new CLI commands follow the CLI best practices outlined above.
58+
59+
## Pull Request/Commit Requirements
60+
61+
- All code must pass CI (lint, test, build) before merging.
62+
- All new features must be documented in `README.md`.
63+
- All code must be reviewed by at least one other developer.
64+
- All new CLI commands must include JSON output support via `--output/-o` option.
65+
- All error handling must use standardized exit codes and consistent formatting.
66+
67+
## Copilot-Specific Instructions
68+
69+
- After making any code change, always:
70+
1. Run linting and auto-formatting.
71+
2. Run all unit tests.
72+
3. Report any failures or issues to the user.
73+
- If you add a new CLI command, ensure it:
74+
- Is covered by a unit test in `tests/unit/`
75+
- Supports `--output/-o` option with `table` and `json` formats
76+
- Uses consistent error handling with appropriate exit codes
77+
- Follows the success/error message formatting standards
78+
- If you update the CLI interface, update the `README.md` accordingly.
79+
- Never commit or suggest committing files listed in `.gitignore`.
80+
- When implementing list commands, ensure JSON output shows all results (no pagination).
81+
- Use `handle_api_error()` for all API error handling instead of generic Click exceptions.
82+
- Use `format_success()` for all success messages to maintain consistency.
83+
84+
---
85+
86+
These guidelines ensure code quality, maintainability, and a smooth developer experience for all contributors and Copilot.

.github/workflows/ci.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
build-test-lint:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout code
14+
uses: actions/checkout@v4
15+
16+
- name: Set up Python
17+
uses: actions/setup-python@v5
18+
with:
19+
python-version: '3.11'
20+
21+
- name: Install Poetry
22+
run: pip install poetry
23+
24+
- name: Install dependencies
25+
run: poetry install
26+
27+
- name: Lint with ni-python-styleguide
28+
run: poetry run ni-python-styleguide lint
29+
30+
- name: Lint with black
31+
run: poetry run black --check .
32+
33+
- name: Run tests
34+
run: poetry run pytest
35+
36+
- name: Build Homebrew tarball and update formula
37+
run: poetry run python scripts/build_homebrew.py
38+
39+
- name: Upload Homebrew formula artifact
40+
uses: actions/upload-artifact@v4
41+
with:
42+
name: homebrew-slcli-formula
43+
path: dist/homebrew-slcli.rb
44+
45+
- name: Upload Homebrew tarball artifact
46+
uses: actions/upload-artifact@v4
47+
with:
48+
name: slcli-tarball
49+
path: dist/slcli.tar.gz

.github/workflows/release.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
jobs:
9+
release:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: write
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: '3.11'
21+
22+
- name: Install Poetry
23+
run: pip install poetry
24+
25+
- name: Install dependencies
26+
run: poetry install
27+
28+
- name: Run tests
29+
run: poetry run pytest
30+
31+
- name: Build Homebrew tarball and formula
32+
run: poetry run python scripts/build_homebrew.py
33+
34+
- name: Create Release
35+
uses: softprops/action-gh-release@v2
36+
with:
37+
files: |
38+
dist/slcli.tar.gz
39+
dist/homebrew-slcli.rb
40+
generate_release_notes: true
41+
env:
42+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43+
44+
update-homebrew-tap:
45+
needs: release
46+
runs-on: ubuntu-latest
47+
steps:
48+
- name: Update Homebrew tap
49+
uses: mislav/bump-homebrew-formula-action@v3
50+
with:
51+
formula-name: slcli
52+
homebrew-tap: ni-kismet/homebrew-ni
53+
download-url: https://github.com/ni-kismet/systemlink-cli/releases/download/${{ github.ref_name }}/slcli.tar.gz
54+
commit-message: |
55+
{{formulaName}} {{version}}
56+
57+
Created by https://github.com/mislav/bump-homebrew-formula-action
58+
env:
59+
COMMITTER_TOKEN: ${{ secrets.HOMEBREW_GITHUB_TOKEN }}

.github/workflows/scoop.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Scoop Windows Build
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches: [ main ]
7+
pull_request:
8+
branches: [ main ]
9+
10+
jobs:
11+
build-scoop:
12+
runs-on: windows-latest
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: '3.11'
21+
22+
- name: Install Poetry
23+
run: pip install poetry
24+
25+
- name: Install dependencies
26+
run: poetry install
27+
28+
- name: Build PyInstaller binary (Windows)
29+
run: poetry run python scripts/build_pyinstaller.py
30+
31+
- name: Build Scoop manifest
32+
run: poetry run python scripts/build_scoop.py
33+
34+
- name: Upload Scoop manifest artifact
35+
uses: actions/upload-artifact@v4
36+
with:
37+
name: scoop-slcli-manifest
38+
path: dist/scoop-slcli.json
39+
40+
- name: Upload slcli.zip artifact
41+
uses: actions/upload-artifact@v4
42+
with:
43+
name: slcli-zip
44+
path: dist/slcli.zip

.gitignore

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
env/
12+
build/
13+
develop-eggs/
14+
dist/
15+
downloads/
16+
eggs/
17+
.eggs/
18+
lib/
19+
lib64/
20+
parts/
21+
sdist/
22+
var/
23+
*.egg-info/
24+
.installed.cfg
25+
*.egg
26+
27+
# Installer logs
28+
debug.log
29+
pip-log.txt
30+
pip-delete-this-directory.txt
31+
32+
# Unit test / coverage reports
33+
htmlcov/
34+
.tox/
35+
.nox/
36+
.coverage
37+
.coverage.*
38+
.cache
39+
nosetests.xml
40+
coverage.xml
41+
*.cover
42+
.hypothesis/
43+
.pytest_cache/
44+
45+
# PyInstaller
46+
# Usually these files are written by a python script from a template
47+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
48+
build/
49+
dist/
50+
*.spec
51+
*.manifest
52+
*.spec
53+
*.pkg
54+
*.toc
55+
*.pyz
56+
*.zip
57+
*.html
58+
warn-*.txt
59+
xref-*.html
60+
localpycs/
61+
62+
# VS Code
63+
.vscode/
64+
65+
# Jupyter Notebook
66+
.ipynb_checkpoints/
67+
68+
# System
69+
.DS_Store
70+
Thumbs.db
71+
72+
# Logs
73+
*.log
74+
75+
# Custom: slcli generated
76+
slcli/__pycache__/
77+
scripts/__pycache__/
78+
docs/__pycache__/
79+
tests/unit/__pycache__/
80+
81+
# End of .gitignore

README.md

7.72 KB

systemlink-cliSystemLink CLI

SystemLink CLI (slcli) is a cross-platform Python CLI for SystemLink integrators, providing comprehensive management of SystemLink resources via REST APIs.

Features

  • Secure Authentication: Credential storage using keyring with login/logout commands
  • Test Plan Templates: Complete management (list, export, import, delete) with JSON and table output formats
  • Jupyter Notebooks: Full lifecycle management (list, download, create, update, delete) with workspace filtering
  • Cross-Platform: Windows, macOS, and Linux support with standalone binaries
  • Professional CLI: Consistent error handling, colored output, and comprehensive help system
  • Output Formats: JSON and table output options for programmatic integration and human-readable display
  • Extensible Architecture: Designed for easy addition of new SystemLink resource types
  • Quality Assurance: Full test suite with CI/CD, linting, and automated packaging

Installation

Homebrew (macOS/Linux)

Install SystemLink CLI using Homebrew from our official tap:

# Add the NI developer tools tap
brew tap ni-kismet/homebrew-ni

# Install slcli
brew install slcli

From Source

  1. Install dependencies:

    poetry install

Quick Start

  1. Login to SystemLink:

    slcli login
  2. List available resources:

    # View test plan templates
    slcli templates list
    
    # View notebooks
    slcli notebook list
  3. Get help for any command:

    slcli --help
    slcli templates --help
    slcli notebook --help

Development Setup

  1. Install Poetry (if not already installed):

    pip install poetry
  2. Install dependencies:

    poetry install

Running

  • Run the CLI directly:

    poetry run slcli
  • Or as a Python module:

    poetry run python -m slcli

Build a Standalone Binary (Cross-Platform)

macOS/Linux (Homebrew/PyInstaller)

To build a single-file executable and Homebrew formula:

poetry run python scripts/build_homebrew.py
  • This will:
    • Build the PyInstaller binary in dist/slcli/
    • Create a tarball dist/slcli.tar.gz
    • Generate a Homebrew formula dist/homebrew-slcli.rb with the correct SHA256

You can then install locally with:

brew install ./dist/homebrew-slcli.rb

Windows (Scoop/PyInstaller)

To build a Windows executable and Scoop manifest:

poetry run python scripts/build_pyinstaller.py
poetry run python scripts/build_scoop.py
  • This will:
    • Build dist/slcli.exe
    • Generate a Scoop manifest dist/scoop-slcli.json with the correct SHA256

You can use the manifest in your own Scoop bucket for easy installation.

CI/CD Automation

  • All builds, tests, and packaging are automated via GitHub Actions for both Homebrew and Scoop.
  • Artifacts (slcli.tar.gz, homebrew-slcli.rb, slcli.exe, scoop-slcli.json) are uploaded for each build.

Release Process

Creating a Release

  1. Update the version in pyproject.toml:

    [tool.poetry]
    version = "0.2.0"  # Update to new version
  2. Create and push a git tag:

    git tag v0.2.0
    git push origin v0.2.0
  3. Automated release workflow will:

    • Run all tests and linting
    • Build PyInstaller binaries for all platforms
    • Generate Homebrew formula and Scoop manifest
    • Create GitHub release with all artifacts
    • Automatically update the Homebrew tap (ni-kismet/homebrew-ni)

Release Artifacts

Each release automatically generates:

  • slcli.tar.gz - Source tarball for Homebrew
  • homebrew-slcli.rb - Homebrew formula
  • slcli.exe - Windows executable
  • scoop-slcli.json - Scoop manifest for Windows

Homebrew Publishing

The release workflow automatically:

  • Updates the formula in ni-kismet/homebrew-ni tap
  • Calculates SHA256 checksums
  • Updates version and download URLs
  • Commits changes to the tap repository

Users can then install the latest version with:

brew update
brew upgrade slcli

Testing & Linting

  • Run tests:
    poetry run pytest
  • Lint code:
    poetry run ni-python-styleguide lint

Contributing

See the NI Python development wiki for contribution guidelines.

Authentication

Before using SystemLink CLI commands, you need to authenticate with your SystemLink server:

Login to SystemLink

slcli login

This will securely prompt for your API key and SystemLink URL, then store them using your system's keyring.

Logout (remove stored credentials)

slcli logout

Test Plan Template Management

The templates command group allows you to manage test plan templates in SystemLink.

List all test plan templates

# Table format (default)
slcli templates list

# JSON format for programmatic use
slcli templates list --output json

Export a template to a local JSON file

slcli templates export --id <template_id> --output template.json

Import a template from a local JSON file

slcli templates import --file template.json

Delete a template

slcli templates delete --id <template_id>

Notebook Management

The notebook command group allows you to manage Jupyter notebooks in SystemLink.

List all notebooks in a workspace

# List all notebooks (table format - default)
slcli notebook list

# List notebooks in specific workspace
slcli notebook list --workspace MyWorkspace

# JSON format for programmatic use
slcli notebook list --output json

# Control pagination (table format only)
slcli notebook list --take 50

Download notebook content and/or metadata

# Download notebook content (.ipynb) by ID:
slcli notebook download --id <notebook_id> --output mynotebook.ipynb

# Download notebook content by name:
slcli notebook download --name MyNotebook --output mynotebook.ipynb

# Download notebook metadata as JSON:
slcli notebook download --id <notebook_id> --type metadata --output metadata.json

# Download both content and metadata:
slcli notebook download --id <notebook_id> --type both --output mynotebook.ipynb

Create a new notebook

# Create from existing .ipynb file:
slcli notebook create --file mynotebook.ipynb --name MyNotebook
slcli notebook create --file mynotebook.ipynb --workspace MyWorkspace --name MyNotebook

# Create an empty notebook:
slcli notebook create --name MyNotebook
slcli notebook create --workspace MyWorkspace --name MyNotebook

Update notebook metadata and/or content

# Update metadata only:
slcli notebook update --id <notebook_id> --metadata metadata.json

# Update content only:
slcli notebook update --id <notebook_id> --content mynotebook.ipynb

# Update both metadata and content:
slcli notebook update --id <notebook_id> --metadata metadata.json --content mynotebook.ipynb

Delete a notebook

slcli notebook delete --id <notebook_id>

Output Formats

SystemLink CLI supports both human-readable table output and machine-readable JSON output for list commands:

Table Output (Default)

  • Colored, formatted tables using GitHub-style formatting
  • Pagination support for large result sets
  • Truncated text with ellipsis (…) for better readability
  • Visual success (✓) and error (✗) indicators

JSON Output

  • Complete data export without pagination
  • Perfect for scripting and automation
  • Consistent structure across all list commands
# Human-readable table
slcli templates list
slcli notebook list

# Machine-readable JSON
slcli templates list --output json
slcli notebook list --output json

0 commit comments

Comments
 (0)