Skip to content

Commit 4a3780b

Browse files
authored
Merge pull request #59 from LibreCodeCoop/feat/ncdd-cli-runtime
feat(ncdd): add thin command interface over the existing stack
2 parents 9c34182 + a7ff897 commit 4a3780b

7 files changed

Lines changed: 398 additions & 1 deletion

File tree

‎.github/workflows/ncdd-cli.yml‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: NCDD CLI
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'bin/ncdd'
7+
- 'tests/ncdd.bats'
8+
- 'docs/ncdd.md'
9+
- 'AGENTS.md'
10+
- 'Makefile'
11+
- '.github/workflows/ncdd-cli.yml'
12+
push:
13+
branches:
14+
- main
15+
paths:
16+
- 'bin/ncdd'
17+
- 'tests/ncdd.bats'
18+
- 'docs/ncdd.md'
19+
- 'AGENTS.md'
20+
- 'Makefile'
21+
- '.github/workflows/ncdd-cli.yml'
22+
23+
permissions:
24+
contents: read
25+
26+
jobs:
27+
test:
28+
runs-on: ubuntu-latest
29+
steps:
30+
- name: Checkout
31+
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
32+
33+
- name: Setup Bats
34+
uses: bats-core/bats-action@77d6fb60505b4d0d1d73e48bd035b55074bbfb43 # 4.0.0
35+
with:
36+
support-install: false
37+
assert-install: false
38+
detik-install: false
39+
file-install: false
40+
41+
- name: ShellCheck
42+
run: shellcheck bin/ncdd
43+
44+
- name: Test
45+
run: bats tests/ncdd.bats

‎AGENTS.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# NCDD agent contract
2+
3+
Use `bin/ncdd` for stack operations. Do not reproduce Docker Compose commands in agent instructions unless debugging NCDD itself.
4+
5+
Keep Git checkout and authentication outside the containers. App worktrees belong under `volumes/nextcloud/apps-extra/`.
6+
7+
Use `bin/ncdd doctor --json` for machine-readable environment diagnostics. See `docs/ncdd.md` for the command contract.

‎Makefile‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
COMPOSE ?= docker compose
22
GARAGES3_COMPOSE_FILE ?= docker-compose-garages3.yml
33

4-
.PHONY: up-garages3 down-garages3 bootstrap-garages3 garage-status-garages3 start-garages3 wait-nextcloud-garages3 setup-garages3 test-hooks test-scan-images scan-images
4+
.PHONY: up-garages3 down-garages3 bootstrap-garages3 garage-status-garages3 start-garages3 wait-nextcloud-garages3 setup-garages3 test-hooks test-scan-images test-ncdd scan-images
55

66
up-garages3:
77
$(COMPOSE) -f $(GARAGES3_COMPOSE_FILE) up -d garage
@@ -32,6 +32,9 @@ test-hooks:
3232
test-scan-images:
3333
bash tests/test-scan-images.sh
3434

35+
test-ncdd:
36+
bats tests/ncdd.bats
37+
3538
scan-images:
3639
@set -e; \
3740
version="$$(sed -n 's/^NEXTCLOUD_VERSION=//p' .env.example | head -n 1)"; \

‎README.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ Languages avaliable: [pt-BR](docs/README_ptBR.md)
1717
- [Logs](#logs)
1818
- [Nextcloud Talk](#talk)
1919

20+
## NCDD command interface
21+
22+
For development automation, use the repository's `bin/ncdd` wrapper instead of reproducing Docker Compose details in CI, agents, or local instructions. It operates the existing `docker-compose.yml` and `docker-compose-postgres.yml`; it does not define a second stack.
23+
24+
```bash
25+
bin/ncdd up
26+
bin/ncdd doctor
27+
bin/ncdd shell
28+
bin/ncdd down
29+
```
30+
31+
See [docs/ncdd.md](docs/ncdd.md) for the command contract and testing policy.
32+
2033
## Setup of docker
2134

2235
You need to have, on your server, the installed docker. The installation can be done with an official script, following the following steps:

‎bin/ncdd‎

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
NCDD_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
5+
APP_SERVICE=${NCDD_APP_SERVICE:-app}
6+
RUNTIME_USER=${NCDD_RUNTIME_USER:-www-data}
7+
8+
usage() {
9+
cat <<'EOF'
10+
Usage: ncdd <command> [options]
11+
12+
Commands:
13+
up Start the existing NCDD stack
14+
down Stop the existing NCDD stack
15+
status Show stack status
16+
env Show resolved NCDD environment
17+
doctor [--json] Check Docker, Compose, app service, and Nextcloud
18+
shell [--as USER] Open a shell in the app container
19+
exec [--as USER] -- COMMAND...
20+
Execute a command in the app container
21+
help [ai] Show help or the compact agent contract
22+
EOF
23+
}
24+
25+
agent_help() {
26+
cat <<'EOF'
27+
NCDD agent contract
28+
- Use bin/ncdd instead of calling docker compose directly.
29+
- The CLI operates docker-compose.yml plus docker-compose-postgres.yml.
30+
- Git checkout/authentication stays on the host or CI workspace.
31+
- App worktrees belong under volumes/nextcloud/apps-extra/.
32+
- Use --as runtime for commands that must run as the Nextcloud runtime user.
33+
EOF
34+
}
35+
36+
fail() {
37+
printf 'ncdd: %s\n' "$*" >&2
38+
exit 1
39+
}
40+
41+
compose() {
42+
docker compose \
43+
--project-directory "$NCDD_ROOT" \
44+
-f "$NCDD_ROOT/docker-compose.yml" \
45+
-f "$NCDD_ROOT/docker-compose-postgres.yml" \
46+
"$@"
47+
}
48+
49+
ensure_network() {
50+
local network=$1
51+
52+
if ! docker network inspect "$network" >/dev/null 2>&1; then
53+
docker network create "$network" >/dev/null
54+
fi
55+
}
56+
57+
resolve_user() {
58+
case "$1" in
59+
runtime) printf '%s\n' "$RUNTIME_USER" ;;
60+
root) printf 'root\n' ;;
61+
*) printf '%s\n' "$1" ;;
62+
esac
63+
}
64+
65+
nextcloud_version() {
66+
if [ -n "${NEXTCLOUD_VERSION:-}" ]; then
67+
printf '%s\n' "$NEXTCLOUD_VERSION"
68+
return
69+
fi
70+
71+
local file
72+
for file in "$NCDD_ROOT/.env" "$NCDD_ROOT/.env.example"; do
73+
if [ -f "$file" ]; then
74+
local value
75+
value=$(sed -n 's/^NEXTCLOUD_VERSION=//p' "$file" | head -n 1)
76+
if [ -n "$value" ]; then
77+
printf '%s\n' "$value"
78+
return
79+
fi
80+
fi
81+
done
82+
83+
printf 'unset\n'
84+
}
85+
86+
doctor() {
87+
local json=false
88+
if [ "${1:-}" = "--json" ]; then
89+
json=true
90+
shift
91+
fi
92+
[ "$#" -eq 0 ] || fail "doctor accepts only --json"
93+
94+
local docker_ok=false compose_ok=false app_running=false nextcloud_ready=false
95+
command -v docker >/dev/null 2>&1 && docker_ok=true
96+
if $docker_ok && docker compose version >/dev/null 2>&1; then
97+
compose_ok=true
98+
fi
99+
if $compose_ok && compose ps --status running --services 2>/dev/null | grep -qx "$APP_SERVICE"; then
100+
app_running=true
101+
fi
102+
if $app_running && compose exec -T --user "$RUNTIME_USER" "$APP_SERVICE" php occ status --output=json >/dev/null 2>&1; then
103+
nextcloud_ready=true
104+
fi
105+
106+
local ready=false
107+
if $docker_ok && $compose_ok && $app_running && $nextcloud_ready; then
108+
ready=true
109+
fi
110+
111+
if $json; then
112+
printf '{"ready":%s,"docker":%s,"compose":%s,"app_running":%s,"nextcloud_ready":%s,"nextcloud_version":"%s"}\n' \
113+
"$ready" "$docker_ok" "$compose_ok" "$app_running" "$nextcloud_ready" "$(nextcloud_version)"
114+
else
115+
printf '%-20s %s\n' "Docker" "$docker_ok"
116+
printf '%-20s %s\n' "Docker Compose" "$compose_ok"
117+
printf '%-20s %s\n' "App running" "$app_running"
118+
printf '%-20s %s\n' "Nextcloud ready" "$nextcloud_ready"
119+
printf '%-20s %s\n' "Nextcloud version" "$(nextcloud_version)"
120+
fi
121+
122+
$ready
123+
}
124+
125+
command=${1:-help}
126+
if [ "$#" -gt 0 ]; then
127+
shift
128+
fi
129+
130+
case "$command" in
131+
help)
132+
if [ "${1:-}" = "ai" ]; then
133+
agent_help
134+
else
135+
usage
136+
fi
137+
;;
138+
env)
139+
printf 'NCDD_ROOT=%s\n' "$NCDD_ROOT"
140+
printf 'NCDD_APP_SERVICE=%s\n' "$APP_SERVICE"
141+
printf 'NCDD_RUNTIME_USER=%s\n' "$RUNTIME_USER"
142+
printf 'NEXTCLOUD_VERSION=%s\n' "$(nextcloud_version)"
143+
;;
144+
up)
145+
[ "$#" -eq 0 ] || fail "up does not accept arguments"
146+
ensure_network reverse-proxy
147+
ensure_network postgres
148+
compose up -d redis postgres app web cron
149+
;;
150+
down)
151+
compose down "$@"
152+
;;
153+
status)
154+
compose ps "$@"
155+
;;
156+
shell)
157+
user=root
158+
if [ "${1:-}" = "--as" ]; then
159+
user=$(resolve_user "${2:?missing user}")
160+
shift 2
161+
fi
162+
[ "$#" -eq 0 ] || fail "unknown shell arguments: $*"
163+
compose exec --user "$user" "$APP_SERVICE" bash
164+
;;
165+
exec)
166+
user=root
167+
if [ "${1:-}" = "--as" ]; then
168+
user=$(resolve_user "${2:?missing user}")
169+
shift 2
170+
fi
171+
if [ "${1:-}" = "--" ]; then
172+
shift
173+
fi
174+
[ "$#" -gt 0 ] || fail "exec requires a command"
175+
tty=()
176+
if [ ! -t 0 ] || [ ! -t 1 ]; then
177+
tty=(-T)
178+
fi
179+
compose exec "${tty[@]}" --user "$user" "$APP_SERVICE" "$@"
180+
;;
181+
doctor)
182+
doctor "$@"
183+
;;
184+
*)
185+
fail "unknown command: $command"
186+
;;
187+
esac

‎docs/ncdd.md‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# NCDD command interface
2+
3+
NCDD should expose a stable command surface without creating a second development stack.
4+
5+
## Architecture
6+
7+
`bin/ncdd` is a thin orchestration layer over the files that already define this repository:
8+
9+
- `docker-compose.yml`
10+
- `docker-compose-postgres.yml`
11+
- `.env` / `.env.example`
12+
13+
It must not duplicate the stack, own a second version source, or redefine image versions.
14+
15+
The Makefile remains focused on repository maintenance tasks. It only exposes `test-ncdd` as a convenience for running the CLI test suite; runtime behavior belongs to `bin/ncdd`.
16+
17+
## Commands
18+
19+
```bash
20+
bin/ncdd up
21+
bin/ncdd status
22+
bin/ncdd doctor
23+
bin/ncdd doctor --json
24+
bin/ncdd shell
25+
bin/ncdd exec --as runtime -- php occ status
26+
bin/ncdd down
27+
```
28+
29+
`runtime` resolves to `www-data` by default and can be overridden with `NCDD_RUNTIME_USER`.
30+
31+
## Git and app source
32+
33+
NCDD does not fetch application repositories and does not receive GitHub credentials. Human users, CI, and agents perform the checkout outside the containers.
34+
35+
Application worktrees should be placed under the existing Nextcloud tree:
36+
37+
```text
38+
volumes/nextcloud/apps-extra/<app-id>
39+
```
40+
41+
That keeps public and private branches identical from NCDD's point of view and avoids container-specific Git authentication.
42+
43+
## Version source
44+
45+
There is no NCDD version constant in the CLI. The Nextcloud image version is read from the existing `NEXTCLOUD_VERSION` environment variable, then `.env`, then `.env.example`.
46+
47+
A separate NCDD release/version mechanism should only be introduced if the project actually starts publishing the CLI independently.
48+
49+
## Testing
50+
51+
The CLI is Bash, so its behavior is covered with Bats rather than ad-hoc shell assertions. CI also runs ShellCheck.
52+
53+
```bash
54+
make test-ncdd
55+
```
56+
57+
The GitHub workflow pins the Bats setup action to an immutable commit SHA and pins the Bats version explicitly.
58+
59+
## Configuration policy
60+
61+
PR #59 intentionally does not introduce `.ncdd.yml`. Environment-specific project contracts may become useful for test-suite definitions later, but adding another configuration format before there is a concrete consumer requirement would duplicate information already present in Compose and `.env`.

0 commit comments

Comments
 (0)