semacli
A CLI tool to manage Semaphore UI (ansible-semaphore) via its HTTP REST API.
Designed for LLM/agent and automation use — deterministic commands, JSON output, exit codes.
Features
- List projects, templates, inventories, environments
- Templates: create / update / audit / sync, with the per-run overrides
(
--limit,--tags, …) declared explicitly — see Templates - Launch and monitor tasks
- Read task output
- Schedules: cron or one-shot
--run-attriggers, carrying the same ansible overrides as a run (--limit/--tags/--skip-tags/--inventory/--cli-args) so a planned run can target a subset of hosts without a dedicated template — seesem sched create --help - JSON output support
- Bearer-token authentication (User Settings → API Tokens)
- VS Code extension: running / finished tasks in the sidebar, task detail
with its output, stop — reads the same
semacli.ini(see VS Code extension)
Installation
# From PyPI
pip install semacli
# From source
pip install git+https://github.com/lduchosal/semacli.git
# Development
git clone https://github.com/lduchosal/semacli.git
cd semacli
pdm install
VS Code extension
Each GitHub release
attaches semacli-vscode-<version>.vsix (no Marketplace):
gh release download semacli-<version> -R lduchosal/semacli -p '*.vsix'
code --install-extension semacli-vscode-<version>.vsix
It finds semacli.ini from the workspace folder upwards (then
~/.semacli.ini, /usr/local/etc/semacli.ini) and honours [auth],
load_dotenv and the TLS settings like the CLI. Details:
vscode/README.md.
Quick Start
Configuration
Create semacli.ini in the current directory or ~/.semacli.ini:
[semaphore]
url = https://monitor.example.com/semaphore
project = 1
[auth]
method = bearer_token
bearer_token = your-api-token-here
[settings]
timeout = 30
verify_ssl = true
Get a bearer token from the Semaphore UI: User Settings → API Tokens → Create.
Basic Usage
# Ping the API
sem ping
# List projects
sem projects
# (more commands wired in as the CLI grows)
Output Options
# JSON output
sem projects --json
# Verbose debugging
sem projects -v
sem projects -vv
sem projects -vvv
Configuration Options
Authentication Methods
Bearer token (recommended)
[semaphore]
url = https://monitor.example.com/semaphore
[auth]
method = bearer_token
bearer_token = your-api-token
Bearer token from environment variable
[semaphore]
url = https://monitor.example.com/semaphore
[auth]
method = env_var
env_var = SEMAPHORE_TOKEN
TLS / system certificate store
requests ships with the certifi CA
bundle and ignores the OS trust store by default. On corporate Windows
machines, root CAs installed via Group Policy are therefore not
trusted and you'll see SSLError: unable to get local issuer certificate against an internal Semaphore. Same trap on macOS if your
corp CA only lives in Keychain.
semacli defaults to use_system_ca = auto — on (Windows) / off (macOS,
Linux). Force it either way if needed:
[settings]
# use_system_ca = auto # default: true on Windows, false elsewhere
# use_system_ca = true # force-use OS trust store (e.g. macOS Keychain)
# use_system_ca = false # force-use certifi bundle (e.g. cross-platform CI)
Implementation: when on, semacli calls truststore.inject_into_ssl()
(via the truststore library,
maintained by the urllib3 author). TLS verification stays on — only the
source of trust anchors changes.
Auto-load .env (opt-in)
When method = env_var, the token must be in your shell before you run
sem. Two ways to make that happen:
direnv(recommended for active dev) —brew install direnv+eval "$(direnv hook zsh)", then drop a.envrc(or.envwithdotenvdirective) in the project. Auto-loads oncd.[settings] load_dotenv = true(no extra tool) — semacli reads.envnext to yoursemacli.iniat startup. Existing shell vars always win, so the file only fills gaps.
[settings]
load_dotenv = true
# load_dotenv_file = .env # optional; relative paths resolve against
# the semacli.ini directory.
# .env (gitignored, chmod 600)
SEMAPHORE_TOKEN=ninjwlgclse7_...
The file is parsed by python-dotenv,
so quoting, comments, and ${VAR} interpolation all work as expected.
A warning is printed if .env is group/world-readable — chmod 600 .env
to silence it.
Shell hooks around sem run
Declare [hook] keys in semacli.ini to fire shell commands before/after
a template run. Useful for syncing a remote repo, sending notifications,
or paging on failure. Relative paths resolve against the .ini directory.
[hook]
# Aborts the run with exit 6 on non-zero. Receives env vars:
# SEMACLI_TEMPLATE, SEMACLI_LIMIT, SEMACLI_TAGS, SEMACLI_PROJECT,
# SEMACLI_TEMPLATE_ID, SEMACLI_CONFIG, SEMACLI_EVENT
task_run_prehook = scripts/sync-svn.sh
# Fires after watch completes (any status). Failures = warnings only.
# Also receives SEMACLI_TASK_ID and SEMACLI_STATUS.
task_run_posthook = scripts/notify.sh
# Same envelope as posthook, fires only when status != success.
task_run_failhook = scripts/page-oncall.sh
# Default 60s, applies per-hook.
timeout = 30
Pass --no-hooks on sem run to bypass them (debug / replay).
Templates
A template decides which per-run overrides a task may pass. Semaphore
does not reject a forbidden override — it drops it and runs anyway, so a
--limit that is not allowed runs the playbook on the whole
inventory. semacli refuses that run instead, which makes the template's
task_params worth getting right.
# Create: every override allowed unless you narrow it
sem template create --name deploy --playbook deploy.yml \
--repository ansible --inventory prod --environment secrets --view DEPLOY
# A template nobody may re-target at run time
sem template create --name reboot-all --playbook reboot.yml \
--repository ansible --inventory prod --allow-override none
# Update: read-modify-write, only the flags you pass are touched
sem template update deploy --allow-override limit,tags
# Which templates would silently ignore a --limit? (exit 1 if any)
sem template audit
sem template audit --require limit,tags
sem template --json audit # shared flags live on the group
--arguments only accepts a JSON array of static flags ('["--diff"]').
Jinja placeholders are refused: Semaphore stores the string verbatim and
ansible reads {{ limit }} as a literal host pattern.
Declarative sync
sem template sync reconciles a YAML manifest with the project: one
template per declared playbook, idempotent, and never a delete (a
template owns its task history). Run it with --dry-run first — it
prints, field by field, what would change.
# templates.yml
defaults:
repository: ansible # name or id
inventory: prod
environment: secrets
description: "Run ansible playbook {playbook}"
allow_override: [limit, tags, skip-tags, inventory, debug]
playbooks: ansible # scan ansible/*.yml, relative to this file
ignore: [requirements.yml, site.yml]
views: # template name -> board view
mtree: BSD
templates: # explicit entries, merged over the scan
- name: book_base
playbook: book_base.yml
view: BOOK
sem template sync --manifest templates.yml --dry-run # review
sem template sync --manifest templates.yml # create what is missing
sem template sync --manifest templates.yml --update # + patch what drifted
Existing templates are skipped unless --update is passed, and with it
only the ones that actually differ are written back. Templates present on
the server but absent from the manifest are reported, never removed.
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Configuration error |
| 3 | Authentication error |
| 4 | API error |
| 5 | Not found |
| 6 | Hook aborted the command (pre-hook returned non-zero or timed out) |
sem template audit uses exit 1 for "findings" (at least one template
forbids a required override) so it can be wired as a periodic check;
sem template sync uses it when a create/update failed.
Development
# Clone and setup
git clone https://github.com/lduchosal/semacli.git
cd semacli
pdm install -G dev
# Run tests
pdm test
# Lint and format
pdm lint
pdm format
# Type check
pdm typecheck
# Build
pdm build
Architecture
semacli/
├── cli/ # Click CLI interface
│ ├── commands/ # Individual commands
│ ├── decorators.py # Common CLI options
│ └── handlers.py # Error handlers
├── core/ # Core business logic
│ ├── client/ # Semaphore HTTP client (per-resource mixins)
│ ├── config.py # Configuration
│ ├── exceptions.py # Custom exceptions
│ ├── guards.py # Pre-flight refusals (overrides, arguments)
│ ├── manifest.py # `template sync` manifest parsing
│ ├── models.py # Data models
│ ├── overrides.py # task_params vocabulary
│ ├── resolve.py # name-or-id resolution
│ └── sync.py # `template sync` planner (pure)
└── services/ # Business services
See ARCHITECTURE.md for the wiki classification map used by ken wiki groom.
License
MIT License - see LICENSE for details.
Related Projects
- nagioscli - sibling CLI for Nagios Core (model project)
Metadata
Release files for semacli 0.5.29
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| semacli-0.5.29.tar.gz | 162.8 kB | Without provenance | Details for semacli-0.5.29.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| semacli-0.5.29-py3-none-any.whl | Python 3 | none | any | Details for semacli-0.5.29-py3-none-any.whl |
Total release size: 266.1 kB
Release files / semacli-0.5.29.tar.gz
| Download URL | semacli-0.5.29.tar.gz |
|---|---|
| Size | 162.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
59d270bb8143f67ffb71fe64287c9106c9b216b2b1608071567e1d8bb5c98a8e
|
|
BLAKE2b-256 checksum How to use checksums |
139a9d84b7e92fa14fda03e99dd211a864d93f2f8863c9a403367e7dc44a3d3d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
pdm/2.29.2 CPython/3.12.3 Darwin/25.6.0
|
Release files / semacli-0.5.29-py3-none-any.whl
| Download URL | semacli-0.5.29-py3-none-any.whl |
|---|---|
| Size | 103.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
54820361b7a074bbd966060ee0aac6b7fc89b7b5e1557ae311c5612e7e579d07
|
|
BLAKE2b-256 checksum How to use checksums |
ed979fd2657e68b9f9e0f38ed91cb8413b904196be52a0478b2a85d2522bb5bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
pdm/2.29.2 CPython/3.12.3 Darwin/25.6.0
|
Security for semacli 0.5.29
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.