refactor(tooling): merge discourse-cli into tooling-cli/discourse
Consolidate the discourse-cli source into tooling-cli/discourse/
(CLI source, Dockerfile, README, AGENTS.md, validate.sh) with all
documentation rewritten to invoke the container via raw docker run
and credentials from the centralized ~/.creds/discourse.env store.
No bin/ wrapper, no system-dependent paths in the docs.
Removes the old discourse-cli/ subdirectory and updates the
KNELCredsManager consumer table to reference the container directly.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
@@ -28,7 +28,7 @@ All credential consumers source from `~/.creds/`:
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Redmine | `~/daytoday/redmine/bin/redmine` | `--env-file ~/.creds/redmine.env` |
|
| Redmine | `~/daytoday/redmine/bin/redmine` | `--env-file ~/.creds/redmine.env` |
|
||||||
| Redmine MCP | `mcp-redmine-wrapper.sh` | `set -a; . ~/.creds/redmine.env; set +a` |
|
| Redmine MCP | `mcp-redmine-wrapper.sh` | `set -a; . ~/.creds/redmine.env; set +a` |
|
||||||
| Discourse | `~/daytoday/discourse/bin/discourse` | `--env-file ~/.creds/discourse.env` |
|
| Discourse | `discourse-cli` container (`tooling-cli/discourse/`) | `docker run --env-file ~/.creds/discourse.env` |
|
||||||
| Discourse MCP | `mcp-discourse-wrapper.sh` | `set -a; . ~/.creds/discourse.env; set +a` |
|
| Discourse MCP | `mcp-discourse-wrapper.sh` | `set -a; . ~/.creds/discourse.env; set +a` |
|
||||||
| Beszel MCP | `mcp-beszel-wrapper.sh` | `set -a; . ~/.creds/beszel.env; set +a` |
|
| Beszel MCP | `mcp-beszel-wrapper.sh` | `set -a; . ~/.creds/beszel.env; set +a` |
|
||||||
| Uptime Kuma | (no active consumer yet) | Direct env reference |
|
| Uptime Kuma | (no active consumer yet) | Direct env reference |
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Secrets - never commit
|
||||||
|
.env
|
||||||
|
|
||||||
|
# OS / editor cruft
|
||||||
|
.DS_Store
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
@@ -0,0 +1,346 @@
|
|||||||
|
# AGENTS.md — Discourse CLI tooling
|
||||||
|
|
||||||
|
This directory holds the `discourse-cli` Docker container — a thin Python CLI
|
||||||
|
over the Discourse REST API. Every Crush session that works on or with this
|
||||||
|
tool should read this file first: how to invoke it, what commands exist, what
|
||||||
|
gotchas to avoid, and the patterns to follow for common forum operations.
|
||||||
|
|
||||||
|
## Connection & invocation
|
||||||
|
|
||||||
|
Invoke the real container with `docker run`. Credentials come from the
|
||||||
|
centralized credential store (see `tooling-cli/KNELCredsManager`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm --env-file ~/.creds/discourse.env \
|
||||||
|
git.knownelement.com/reachableceo/discourse-cli:latest <command>
|
||||||
|
```
|
||||||
|
|
||||||
|
There is intentionally **no `bin/` wrapper** — invoke the real container, as
|
||||||
|
the project-wide rules require. For readability in an interactive session you
|
||||||
|
may define a throwaway shell alias (do not commit one):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
alias discourse='docker run --rm --env-file ~/.creds/discourse.env \
|
||||||
|
git.knownelement.com/reachableceo/discourse-cli:latest'
|
||||||
|
```
|
||||||
|
|
||||||
|
The examples below write `discourse <cmd>` for brevity; expand it to the full
|
||||||
|
`docker run` line (or set the alias) before running.
|
||||||
|
|
||||||
|
| Item | Value |
|
||||||
|
|------|-------|
|
||||||
|
| Instance | `https://community.turnsys.com` |
|
||||||
|
| API user | Charles N Wyble (id 2, username `reachableceo`, trust level 4) |
|
||||||
|
| Admin? | **No** — trust 4 (Leader) but not staff/admin. Admin-only endpoints will 403. |
|
||||||
|
| Credentials | `~/.creds/discourse.env` (`DISCOURSE_URL`, `DISCOURSE_API_KEY`, `DISCOURSE_API_USERNAME`) |
|
||||||
|
| CLI image | `git.knownelement.com/reachableceo/discourse-cli:latest` |
|
||||||
|
| Source | `src/discourse_cli.py` in this directory |
|
||||||
|
|
||||||
|
To use a locally built image instead of the registry image, either set
|
||||||
|
`DISCOURSE_CLI_IMAGE` or swap the tag for `kneldevstack-aimiddleware-discourse-cli:latest`.
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Connection sanity check (run this first in any session):
|
||||||
|
discourse whoami
|
||||||
|
|
||||||
|
# List categories:
|
||||||
|
discourse categories
|
||||||
|
|
||||||
|
# Latest topics (or in a category):
|
||||||
|
discourse ls
|
||||||
|
discourse ls -c general
|
||||||
|
|
||||||
|
# Look at a topic:
|
||||||
|
discourse show 42
|
||||||
|
```
|
||||||
|
|
||||||
|
## Command reference
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `whoami` | Authenticated user + connection test. |
|
||||||
|
| `categories` | List categories (id, slug, topic count, name). |
|
||||||
|
| `cat-info <id\|slug>` | Show details of a single category. |
|
||||||
|
| `topics` (`ls`) | List topics (latest, or filtered). Filters below. |
|
||||||
|
| `show <topic_id>` | Full topic detail incl. all posts (HTML stripped to text). |
|
||||||
|
| `create` | Create a new topic (first post). |
|
||||||
|
| `reply <topic_id>` | Reply to a topic. |
|
||||||
|
| `update <post_id>` | Edit an existing post's body. |
|
||||||
|
| `delete <post_id>` | Delete a post. |
|
||||||
|
| `search <query>` | Search the forum. |
|
||||||
|
| `notifications` | List your notifications. |
|
||||||
|
|
||||||
|
### `topics` / `ls` filters
|
||||||
|
- `-c, --category <id|slug>` — only topics in a category
|
||||||
|
- `-n, --new` — only new (to you) topics
|
||||||
|
- `-u, --unread` — only unread topics
|
||||||
|
- `-p, --page <n>` — page number (default 0)
|
||||||
|
|
||||||
|
### `create` options
|
||||||
|
- `-t, --title` **required**
|
||||||
|
- `-b, --body` **required** (raw markdown/text — this becomes the first post)
|
||||||
|
- `-c, --category <id|slug>`
|
||||||
|
- `--tags` — comma-separated tag list
|
||||||
|
|
||||||
|
### `reply` options
|
||||||
|
- `topic_id` (positional, **required**) — the topic to reply in
|
||||||
|
- `-b, --body` **required** (raw markdown/text)
|
||||||
|
- `-r, --reply-to <post_number>` — reply to a specific **post number** (not id)
|
||||||
|
|
||||||
|
### `update` / `delete`
|
||||||
|
- Both operate on a **post id** (positional, required), not a topic id or
|
||||||
|
post number. Find the post id via `show` or the raw API (see gotcha below).
|
||||||
|
- `update -b, --body` **required** (new raw body).
|
||||||
|
|
||||||
|
### `search`
|
||||||
|
- `<query>` (positional, required)
|
||||||
|
- `-p, --page <n>` — page number (default 1)
|
||||||
|
|
||||||
|
### `notifications`
|
||||||
|
- `-l, --limit <n>` — max results (default 20)
|
||||||
|
|
||||||
|
## Key categories
|
||||||
|
|
||||||
|
| ID | Slug | Name | Notes |
|
||||||
|
|----|------|------|-------|
|
||||||
|
| 4 | `general` | General | Open discussion |
|
||||||
|
| 23 | `reachableceo` | ReachableCEO | Personal |
|
||||||
|
| 6 | `chiefoperationsandfinanceofficer` | ChiefOperationsOfficer | COO seat — parent for VP subcategories |
|
||||||
|
| 3 | `staff` | Staff | May be restricted |
|
||||||
|
| 74 | `vp-techops` | VP TechOps | ChiefOperationsOfficer (6) — 11 wiki topics migrated from PFVCluster |
|
||||||
|
| 75 | `vp-compliance` | VP Compliance | ChiefOperationsOfficer (6) — awaiting content |
|
||||||
|
| 76 | `board` | Board | (top-level, future) |
|
||||||
|
|
||||||
|
Run `discourse categories` for the full, current list.
|
||||||
|
|
||||||
|
### Key topics (VP TechOps — category 74)
|
||||||
|
|
||||||
|
Eleven wiki topics migrated from PFVCluster (Discourse is source of truth):
|
||||||
|
|
||||||
|
| Topic | Title | Pattern |
|
||||||
|
|-------|-------|---------|
|
||||||
|
| 296 | PFVCluster Project Overview | Pinned wiki |
|
||||||
|
| 297 | Operations Status | Pinned wiki (updated in place) |
|
||||||
|
| 298 | Infrastructure Audit Log | Wiki index + dated replies |
|
||||||
|
| 299 | Network Topology | Wiki |
|
||||||
|
| 300 | Storage Architecture | Wiki |
|
||||||
|
| 301 | Data Center Infrastructure | Wiki |
|
||||||
|
| 302 | Automation and Provisioning | Wiki |
|
||||||
|
| 303 | Security Architecture and Hardening | Wiki |
|
||||||
|
| 304 | Proxmox Fleet Reference | Wiki + replies |
|
||||||
|
| 305 | Kubernetes Platform | Wiki + replies |
|
||||||
|
| 306 | DNS and DHCP Services | Wiki + replies |
|
||||||
|
|
||||||
|
## Gotcha: post id vs post number
|
||||||
|
|
||||||
|
Discourse distinguishes a **post number** (1, 2, 3... within a topic — the
|
||||||
|
first post is always #1) from a **post id** (a globally unique integer).
|
||||||
|
- `reply -r` takes a **post number**.
|
||||||
|
- `update` / `delete` take a **post id**.
|
||||||
|
|
||||||
|
`show` prints headers like `--- #3 [3] author ...` where the value in brackets
|
||||||
|
is the post number (here they often coincide for simple topics, but they are
|
||||||
|
**not** the same thing). To reliably get a post's **id**, either read the raw
|
||||||
|
JSON via the escape hatch below, or note that `create`/`reply` print the id
|
||||||
|
of the post they just made (`Posted reply #<num> (id=<id>)`).
|
||||||
|
|
||||||
|
## Gotcha: not an admin (user key)
|
||||||
|
|
||||||
|
The default API user is trust level 4 (Leader) but **not** an admin/staff
|
||||||
|
member. With the user-level key:
|
||||||
|
- **Cannot** create categories, set wiki posts, configure site settings, or
|
||||||
|
access `/admin/...` endpoints (all 403).
|
||||||
|
- **Can** create topics, reply, edit own posts, search, list notifications.
|
||||||
|
|
||||||
|
For admin operations (category creation, wiki flagging, docs plugin config),
|
||||||
|
an **admin-scoped API key** is needed. Set it via `DISCOURSE_ADMIN_KEY` in the
|
||||||
|
credential file and pass it explicitly in raw `requests` calls.
|
||||||
|
|
||||||
|
Things that will fail or be restricted without admin:
|
||||||
|
- Deleting other users' posts.
|
||||||
|
- Creating topics in staff-only or restricted categories.
|
||||||
|
- Moving/merging/recategorizing topics.
|
||||||
|
- Setting the wiki flag on posts.
|
||||||
|
|
||||||
|
If an operation 403s, that's expected — surface it to the user rather than
|
||||||
|
retrying.
|
||||||
|
|
||||||
|
## Gotcha: bulk operations & raw API
|
||||||
|
|
||||||
|
For anything beyond a single `create`/`reply`/`update`, or to read fields the
|
||||||
|
CLI doesn't print (e.g. exact post ids, category permissions, user lists),
|
||||||
|
run Python directly inside the container with `requests`. The CLI uses raw
|
||||||
|
`requests` against the Discourse REST API (no heavyweight SDK).
|
||||||
|
|
||||||
|
Pattern (mount a script and run it in the same image):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > /tmp/script.py <<'PY'
|
||||||
|
import os, requests
|
||||||
|
URL = os.environ["DISCOURSE_URL"].rstrip("/")
|
||||||
|
H = {
|
||||||
|
"Api-Key": os.environ["DISCOURSE_API_KEY"],
|
||||||
|
"Api-Username": os.environ["DISCOURSE_API_USERNAME"],
|
||||||
|
"Accept": "application/json",
|
||||||
|
}
|
||||||
|
# Example: get post ids for topic 42
|
||||||
|
r = requests.get(f"{URL}/t/42.json", headers=H, timeout=30)
|
||||||
|
r.raise_for_status()
|
||||||
|
for p in r.json()["post_stream"]["posts"]:
|
||||||
|
print(p["post_number"], "id=", p["id"], "by", p["username"])
|
||||||
|
PY
|
||||||
|
|
||||||
|
docker run --rm --env-file ~/.creds/discourse.env \
|
||||||
|
--entrypoint python \
|
||||||
|
-v /tmp/script.py:/tmp/script.py \
|
||||||
|
git.knownelement.com/reachableceo/discourse-cli:latest \
|
||||||
|
/tmp/script.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful raw endpoints:
|
||||||
|
- `GET /t/<topic_id>.json` — full topic incl. `post_stream.posts[]` (each has
|
||||||
|
`id`, `post_number`, `username`, `cooked`, `raw`).
|
||||||
|
- `GET /categories.json` — all categories.
|
||||||
|
- `GET /c/<cat_id>.json` — topics in a category.
|
||||||
|
- `POST /posts.json` — create topic (`title`+`raw`+`category`) or reply
|
||||||
|
(`topic_id`+`raw`).
|
||||||
|
- `PUT /posts/<post_id>.json` — edit (`{"post":{"raw":"..."}}`).
|
||||||
|
- `DELETE /posts/<post_id>.json` — delete.
|
||||||
|
- `GET /search.json?q=<query>` — search.
|
||||||
|
- `GET /notifications.json` — your notifications.
|
||||||
|
|
||||||
|
## Patterns
|
||||||
|
|
||||||
|
### Pattern: review & respond to a topic
|
||||||
|
1. `discourse show <topic_id>` — read the topic and all replies.
|
||||||
|
2. Identify the post you're responding to; note its **post number** (for
|
||||||
|
`-r`) and the overall context.
|
||||||
|
3. Draft a reply. `create`/`reply` take **raw markdown** — links, lists,
|
||||||
|
code fences all work.
|
||||||
|
4. `discourse reply <topic_id> -b "your markdown" [-r <post_number>]`.
|
||||||
|
5. Verify with `discourse show <topic_id>`.
|
||||||
|
|
||||||
|
### Pattern: before you act
|
||||||
|
- Always `discourse show <topic_id>` before replying/editing — confirm
|
||||||
|
the current state so you don't duplicate or contradict prior posts.
|
||||||
|
- **Never delete a post** unless the user explicitly asks.
|
||||||
|
- Prefer replying over editing someone else's post (and editing others'
|
||||||
|
posts will likely 403 anyway as a non-admin).
|
||||||
|
- When unsure which category to post in, `discourse categories` and pick
|
||||||
|
the closest match, or ask the user.
|
||||||
|
|
||||||
|
### Pattern: posting conventions
|
||||||
|
- Bodies are **raw markdown** — Discourse renders them. Use fenced code
|
||||||
|
blocks for commands/output, headings, tables, and bullet lists freely.
|
||||||
|
- Keep titles concise and descriptive.
|
||||||
|
- Use tags where the category supports them (`--tags a,b,c`).
|
||||||
|
|
||||||
|
### Pattern: wiki topics (living documents)
|
||||||
|
Wiki topics are the core anti-sprawl primitive. A wiki topic's **first post
|
||||||
|
is editable by anyone** with permission (not just the original author), and
|
||||||
|
Discourse preserves the full edit history automatically.
|
||||||
|
|
||||||
|
When to use a wiki topic:
|
||||||
|
- **Living references** — inventories, host lists, network topology, storage
|
||||||
|
maps. Updated in place as facts change.
|
||||||
|
- **Collaborative documents** — policies, design docs, specs where multiple
|
||||||
|
agents/humans contribute. First post = the document; replies = discussion.
|
||||||
|
- **Status/index pages** — operations status, documentation indexes.
|
||||||
|
|
||||||
|
When NOT to use a wiki topic:
|
||||||
|
- **Discussion threads** — regular topics where each reply is a distinct
|
||||||
|
voice. Wiki-editing the first post would destroy the conversation.
|
||||||
|
- **Point-in-time reports** — these go as dated replies inside a wiki
|
||||||
|
"log" topic (see audit pattern below).
|
||||||
|
|
||||||
|
To mark a post as wiki via the API (requires admin/moderator):
|
||||||
|
```python
|
||||||
|
requests.put(f"{URL}/posts/{post_id}.json", headers=H, json={"wiki": True})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern: post lifecycle (comment vs replace vs new)
|
||||||
|
The golden rule: **never create a new topic for an update to existing
|
||||||
|
knowledge.** One wiki topic per subsystem, updated in place.
|
||||||
|
|
||||||
|
| Change type | Action | Why |
|
||||||
|
|-------------|--------|-----|
|
||||||
|
| Fact update (new VM, IP changed) | Edit the wiki post in place | Edit history preserves old state |
|
||||||
|
| New snapshot (audit, drift report) | New reply in the existing log topic | One topic accumulates history |
|
||||||
|
| Question/discussion about content | Reply to the relevant post | Threaded, doesn't mutate the doc |
|
||||||
|
| Major restructure | Edit wiki + reply noting why | Edit log = what; reply = why |
|
||||||
|
|
||||||
|
### Pattern: audit/snapshot logs
|
||||||
|
Point-in-time reports (audits, drift reports, status snapshots) do NOT each
|
||||||
|
get their own topic. Instead, create ONE wiki "log" topic per domain and
|
||||||
|
append each snapshot as a dated reply:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Wiki post #1] Index table (date | scope | link | superseded-by)
|
||||||
|
+ latest snapshot summary
|
||||||
|
[Reply #2] Audit 2026-07-29 — full content
|
||||||
|
[Reply #3] Audit 2026-07-30 — full content (supersedes #2)
|
||||||
|
[Reply #4] Audit 2026-08-05 — full content (current)
|
||||||
|
```
|
||||||
|
|
||||||
|
When a new snapshot arrives: add a reply with full content, then edit the
|
||||||
|
wiki first post to point at the latest reply as "current."
|
||||||
|
|
||||||
|
### Pattern: tag taxonomy
|
||||||
|
Tags are cross-cutting classifiers that prevent category multiplication:
|
||||||
|
|
||||||
|
| Tag | Meaning |
|
||||||
|
|-----|---------|
|
||||||
|
| `reference` | Living reference doc (inventories, topology, host lists) |
|
||||||
|
| `runbook` | Operational procedure (deploy, recover, configure) |
|
||||||
|
| `architecture` | Design doc, system architecture, capacity model |
|
||||||
|
| `policy` | Naming conventions, security policies, standards |
|
||||||
|
| `decision` | Resolved decision (ADR, distro choice, analysis outcome) |
|
||||||
|
| `audit` | Point-in-time snapshot |
|
||||||
|
| `security` | Security/hardening topic |
|
||||||
|
|
||||||
|
### Pattern: category taxonomy
|
||||||
|
Org-based hierarchy mirroring Known Element's VP seats:
|
||||||
|
|
||||||
|
```
|
||||||
|
ChiefOperationsOfficer (id 6)
|
||||||
|
├── vp-techops — infrastructure, network, compute, security ops
|
||||||
|
└── vp-compliance — frameworks, evidence, audit response
|
||||||
|
|
||||||
|
Board (future)
|
||||||
|
```
|
||||||
|
|
||||||
|
Security operations (SecOps) lives under vp-techops (security is operational).
|
||||||
|
Compliance covers frameworks (CMMC/STIG), evidence, and audit response.
|
||||||
|
|
||||||
|
## Git workflow
|
||||||
|
|
||||||
|
### Atomic commits
|
||||||
|
Each commit is **one logical change** — one feature, one fix, one doc update.
|
||||||
|
If you're tempted to write "and also..." in a commit message, that's a sign
|
||||||
|
to split it into two commits. Stage precisely (`git add <file>`, not
|
||||||
|
`git add -A`) so unrelated changes don't get bundled.
|
||||||
|
|
||||||
|
### Conventional commit messages
|
||||||
|
Use the [Conventional Commits](https://www.conventionalcommits.org/) format:
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<optional scope>): <imperative subject>
|
||||||
|
|
||||||
|
<optional body — why, not what>
|
||||||
|
```
|
||||||
|
|
||||||
|
Types used in this repo: `feat`, `fix`, `docs`, `refactor`, `chore`, `style`.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- Subject line **under 72 chars**, lowercase, imperative mood.
|
||||||
|
- No period at end of subject.
|
||||||
|
- Body wrapped at 72 chars, explains **why** the change exists.
|
||||||
|
|
||||||
|
### Commit cadence
|
||||||
|
- **Commit early and often.** Don't accumulate a pile of unrelated changes.
|
||||||
|
- Commit **without asking** — if you made a coherent change, commit it.
|
||||||
|
- Every task (topic review, reply, script addition, doc update) ends with
|
||||||
|
the relevant files committed.
|
||||||
|
- Run `git status` before committing to stage only what belongs together.
|
||||||
@@ -13,20 +13,22 @@ the Discourse REST API. A small Python CLI built on `requests`.
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Copy the env template and fill in real values (the `.env` is gitignored):
|
Credentials live in the **centralized credential store** at
|
||||||
|
`~/.creds/discourse.env` (see `tooling-cli/KNELCredsManager`). It holds:
|
||||||
|
|
||||||
```bash
|
|
||||||
cp .env.example .env
|
|
||||||
# then edit .env:
|
|
||||||
# DISCOURSE_URL=https://discourse.example.com
|
|
||||||
# DISCOURSE_API_KEY=abc123...
|
|
||||||
# DISCOURSE_API_USERNAME=your-username
|
|
||||||
```
|
```
|
||||||
|
DISCOURSE_URL=https://discourse.example.com
|
||||||
|
DISCOURSE_API_KEY=abc123...
|
||||||
|
DISCOURSE_API_USERNAME=your-username
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `.env.example` in this directory as a template if you need to create one.
|
||||||
|
Permissions: directory `700`, the env file `600` (owner read/write only).
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker build -t kneldevstack-aimiddleware-discourse-cli .
|
docker build -t kneldevstack-aimiddleware-discourse-cli:latest .
|
||||||
```
|
```
|
||||||
|
|
||||||
A prebuilt image is also available in the Gitea registry:
|
A prebuilt image is also available in the Gitea registry:
|
||||||
@@ -37,16 +39,29 @@ git.knownelement.com/reachableceo/discourse-cli:latest
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Via docker directly (from this directory)
|
Invoke the container directly with `docker run`. Pass credentials from the
|
||||||
|
centralized store via `--env-file`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --rm --env-file .env kneldevstack-aimiddleware-discourse-cli whoami
|
docker run --rm --env-file ~/.creds/discourse.env \
|
||||||
docker run --rm --env-file .env kneldevstack-aimiddleware-discourse-cli ls
|
git.knownelement.com/reachableceo/discourse-cli:latest whoami
|
||||||
docker run --rm --env-file .env kneldevstack-aimiddleware-discourse-cli show 42
|
|
||||||
docker run --rm --env-file .env kneldevstack-aimiddleware-discourse-cli create -c 5 -t "Hello" -b "Body text"
|
docker run --rm --env-file ~/.creds/discourse.env \
|
||||||
docker run --rm --env-file .env kneldevstack-aimiddleware-discourse-cli reply 42 -b "Nice point"
|
git.knownelement.com/reachableceo/discourse-cli:latest ls
|
||||||
|
|
||||||
|
docker run --rm --env-file ~/.creds/discourse.env \
|
||||||
|
git.knownelement.com/reachableceo/discourse-cli:latest show 42
|
||||||
|
|
||||||
|
docker run --rm --env-file ~/.creds/discourse.env \
|
||||||
|
git.knownelement.com/reachableceo/discourse-cli:latest create -c 5 -t "Hello" -b "Body text"
|
||||||
|
|
||||||
|
docker run --rm --env-file ~/.creds/discourse.env \
|
||||||
|
git.knownelement.com/reachableceo/discourse-cli:latest reply 42 -b "Nice point"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To use a locally built image instead of the registry image, either set
|
||||||
|
`DISCOURSE_CLI_IMAGE` or swap the image tag for `kneldevstack-aimiddleware-discourse-cli:latest`.
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
@@ -59,7 +74,7 @@ docker run --rm --env-file .env kneldevstack-aimiddleware-discourse-cli reply 42
|
|||||||
| `create` | Create a topic (`--category`, `--title`, `--body`, `--tags`). |
|
| `create` | Create a topic (`--category`, `--title`, `--body`, `--tags`). |
|
||||||
| `reply <topic_id>` | Reply to a topic (`--body`, optional `--reply-to`). |
|
| `reply <topic_id>` | Reply to a topic (`--body`, optional `--reply-to`). |
|
||||||
| `update <post_id>` | Update a post (`--body`). |
|
| `update <post_id>` | Update a post (`--body`). |
|
||||||
| `delete <post_id>` | Delete a post. |
|
| `delete <post_id>` | Delete a post. |
|
||||||
| `search <query>` | Search the forum. |
|
| `search <query>` | Search the forum. |
|
||||||
| `notifications` | List your notifications. |
|
| `notifications` | List your notifications. |
|
||||||
|
|
||||||
@@ -89,3 +104,14 @@ docker run --rm --env-file .env kneldevstack-aimiddleware-discourse-cli reply 42
|
|||||||
| `DISCOURSE_URL` | yes | Base URL of the Discourse instance. |
|
| `DISCOURSE_URL` | yes | Base URL of the Discourse instance. |
|
||||||
| `DISCOURSE_API_KEY` | yes | API key ("All users" or "Single user"). |
|
| `DISCOURSE_API_KEY` | yes | API key ("All users" or "Single user"). |
|
||||||
| `DISCOURSE_API_USERNAME`| yes | Username the API key acts as. |
|
| `DISCOURSE_API_USERNAME`| yes | Username the API key acts as. |
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./validate.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Runs a full live read/write cycle against the configured instance (credential
|
||||||
|
check, whoami, categories, topics, show, search, create+reply+update+delete
|
||||||
|
cleanup). Credentials are read from `~/.creds/discourse.env` by default;
|
||||||
|
override with `DISCOURSE_ENV_FILE`.
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
# discourse-cli validation script.
|
# discourse-cli validation script.
|
||||||
# Runs everything inside containers so no host tooling (curl, etc.) is needed.
|
# Runs everything inside containers so no host tooling (curl, etc.) is needed.
|
||||||
#
|
#
|
||||||
# Usage: ./validate.sh (reads .env from the discourse-cli directory)
|
# Usage: ./validate.sh
|
||||||
#
|
#
|
||||||
# Exit codes: 0 = all checks passed, 1 = one or more checks failed
|
# Exit codes: 0 = all checks passed, 1 = one or more checks failed
|
||||||
set -eu
|
set -eu
|
||||||
@@ -10,15 +10,18 @@ set -eu
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
cd "$SCRIPT_DIR"
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
ENV_FILE="${DISCOURSE_ENV_FILE:-/home/reachableceo/.creds/discourse.env}"
|
# Credentials live in the centralized ~/.creds/ store (see KNELCredsManager).
|
||||||
|
# Override with DISCOURSE_ENV_FILE if you need a different location.
|
||||||
|
ENV_FILE="${DISCOURSE_ENV_FILE:-$HOME/.creds/discourse.env}"
|
||||||
|
|
||||||
# Ensure env file exists
|
# Ensure env file exists
|
||||||
if [ ! -f "$ENV_FILE" ]; then
|
if [ ! -f "$ENV_FILE" ]; then
|
||||||
echo "FAIL: env file not found: $ENV_FILE"
|
echo "FAIL: env file not found: $ENV_FILE"
|
||||||
|
echo " Set DISCOURSE_ENV_FILE or create it in ~/.creds/discourse.env"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
IMAGE="${DISCOURSE_CLI_IMAGE:-kneldevstack-aimiddleware-discourse-cli}"
|
IMAGE="${DISCOURSE_CLI_IMAGE:-git.knownelement.com/reachableceo/discourse-cli:latest}"
|
||||||
PASS=0
|
PASS=0
|
||||||
FAIL=0
|
FAIL=0
|
||||||
TOTAL=0
|
TOTAL=0
|
||||||
@@ -37,7 +40,7 @@ echo ""
|
|||||||
# ---------------------------------------------------------------- #
|
# ---------------------------------------------------------------- #
|
||||||
echo "--- [0] Credential check (raw HTTP via containerized curl) ---"
|
echo "--- [0] Credential check (raw HTTP via containerized curl) ---"
|
||||||
|
|
||||||
# Load env vars from .env for the raw test
|
# Load env vars from the creds file for the raw test
|
||||||
DISCOURSE_URL=$(grep -E '^DISCOURSE_URL=' "$ENV_FILE" | cut -d= -f2-)
|
DISCOURSE_URL=$(grep -E '^DISCOURSE_URL=' "$ENV_FILE" | cut -d= -f2-)
|
||||||
DISCOURSE_API_KEY=$(grep -E '^DISCOURSE_API_KEY=' "$ENV_FILE" | cut -d= -f2-)
|
DISCOURSE_API_KEY=$(grep -E '^DISCOURSE_API_KEY=' "$ENV_FILE" | cut -d= -f2-)
|
||||||
DISCOURSE_API_USERNAME=$(grep -E '^DISCOURSE_API_USERNAME=' "$ENV_FILE" | cut -d= -f2-)
|
DISCOURSE_API_USERNAME=$(grep -E '^DISCOURSE_API_USERNAME=' "$ENV_FILE" | cut -d= -f2-)
|
||||||
@@ -199,13 +202,9 @@ fi
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ---------------------------------------------------------------- #
|
# ---------------------------------------------------------------- #
|
||||||
# Summary
|
# Results
|
||||||
# ---------------------------------------------------------------- #
|
# ---------------------------------------------------------------- #
|
||||||
echo "============================================"
|
echo "============================================"
|
||||||
echo " RESULTS: ${PASS}/${TOTAL} passed, ${FAIL} failed"
|
echo " RESULTS: ${PASS}/${TOTAL} passed, ${FAIL} failed"
|
||||||
echo "============================================"
|
echo "============================================"
|
||||||
|
[ "$FAIL" -eq 0 ]
|
||||||
if [ "$FAIL" -gt 0 ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
exit 0
|
|
||||||
Reference in New Issue
Block a user