diff --git a/.github/workflows/ncdd-cli.yml b/.github/workflows/ncdd-cli.yml index 5f835d5..10bfac4 100644 --- a/.github/workflows/ncdd-cli.yml +++ b/.github/workflows/ncdd-cli.yml @@ -10,6 +10,10 @@ on: - 'AGENTS.md' - 'Makefile' - '.github/workflows/ncdd-cli.yml' + - '.github/workflows/test-nextcloud-app.yml' + - 'templates/workflows/ncdd-tests.yml' + - '.github/workflows/test-nextcloud-app.yml' + - 'templates/workflows/ncdd-tests.yml' push: branches: - main diff --git a/.github/workflows/test-nextcloud-app.yml b/.github/workflows/test-nextcloud-app.yml new file mode 100644 index 0000000..5929b8c --- /dev/null +++ b/.github/workflows/test-nextcloud-app.yml @@ -0,0 +1,172 @@ +name: Test Nextcloud app with NCDD + +on: + workflow_call: + inputs: + suite: + description: 'Test suite to run: phpunit or behat' + required: false + default: phpunit + type: string + target: + description: Optional single test target passed to the selected runner + required: false + default: '' + type: string + ref: + description: Optional app ref to test; defaults to the caller SHA + required: false + default: '' + type: string + app_id: + description: Nextcloud app id; defaults to the caller repository name + required: false + default: '' + type: string + ncdd_ref: + description: nextcloud-docker ref containing the NCDD runtime + required: false + default: main + type: string + composer_install: + description: Install Composer dependencies before starting tests + required: false + default: true + type: boolean + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout NCDD + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: LibreCodeCoop/nextcloud-docker + ref: ${{ inputs.ncdd_ref }} + path: .ncdd-runtime + persist-credentials: false + + - name: Resolve test contract + id: contract + shell: bash + env: + INPUT_APP_ID: ${{ inputs.app_id }} + INPUT_SUITE: ${{ inputs.suite }} + run: | + set -euo pipefail + + app_id="${INPUT_APP_ID:-${GITHUB_REPOSITORY#*/}}" + if [[ ! "${app_id}" =~ ^[A-Za-z0-9._-]+$ ]]; then + echo "Invalid Nextcloud app id: ${app_id}" >&2 + exit 2 + fi + + case "${INPUT_SUITE}" in + phpunit|behat) ;; + *) + echo "Unsupported NCDD suite: ${INPUT_SUITE}" >&2 + exit 2 + ;; + esac + + echo "app_id=${app_id}" >> "${GITHUB_OUTPUT}" + echo "app_path=.ncdd-runtime/volumes/nextcloud/apps-extra/${app_id}" >> "${GITHUB_OUTPUT}" + + - name: Checkout app + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: ${{ github.repository }} + ref: ${{ inputs.ref != '' && inputs.ref || github.sha }} + path: ${{ steps.contract.outputs.app_path }} + persist-credentials: false + + - name: Prepare NCDD CI environment + shell: bash + working-directory: .ncdd-runtime + run: | + set -euo pipefail + cp .env.example .env + sed -i \ + -e 's/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=nextcloud/' \ + -e 's/^NEXTCLOUD_ADMIN_USER=.*/NEXTCLOUD_ADMIN_USER=admin/' \ + -e 's/^NEXTCLOUD_ADMIN_PASSWORD=.*/NEXTCLOUD_ADMIN_PASSWORD=admin/' \ + -e 's/^NEXTCLOUD_TRUSTED_DOMAINS=.*/NEXTCLOUD_TRUSTED_DOMAINS=web localhost/' \ + -e 's/^TZ=.*/TZ=UTC/' \ + .env + { + echo 'POSTGRES_DB=nextcloud' + echo 'POSTGRES_USER=nextcloud' + } >> .env + + - name: Install Composer dependencies + if: ${{ inputs.composer_install }} + shell: bash + working-directory: ${{ steps.contract.outputs.app_path }} + env: + SUITE: ${{ inputs.suite }} + run: | + set -euo pipefail + command -v composer >/dev/null 2>&1 || { + echo 'Composer is required when composer_install is enabled.' >&2 + exit 2 + } + + composer install --no-interaction --prefer-dist + + if [ "${SUITE}" = behat ] && [ -f tests/integration/composer.json ]; then + composer --working-dir=tests/integration install --no-interaction --prefer-dist + fi + + - name: Start NCDD + shell: bash + run: bash .ncdd-runtime/bin/ncdd up + + - name: Wait for Nextcloud + shell: bash + run: | + set -euo pipefail + for attempt in $(seq 1 90); do + if bash .ncdd-runtime/bin/ncdd doctor --json; then + exit 0 + fi + sleep 2 + done + bash .ncdd-runtime/bin/ncdd doctor --json || true + exit 1 + + - name: Enable app + shell: bash + env: + APP_ID: ${{ steps.contract.outputs.app_id }} + run: bash .ncdd-runtime/bin/ncdd exec --as runtime -- php occ app:enable "${APP_ID}" + + - name: Run app tests + shell: bash + env: + APP_ID: ${{ steps.contract.outputs.app_id }} + SUITE: ${{ inputs.suite }} + TARGET: ${{ inputs.target }} + run: | + set -euo pipefail + args=(test "${SUITE}" --app "${APP_ID}") + if [ -n "${TARGET}" ]; then + args+=(-- "${TARGET}") + fi + bash .ncdd-runtime/bin/ncdd "${args[@]}" + + - name: Show NCDD diagnostics + if: failure() + shell: bash + run: | + bash .ncdd-runtime/bin/ncdd doctor --json || true + bash .ncdd-runtime/bin/ncdd logs app postgres || true + + - name: Stop NCDD + if: always() + shell: bash + run: bash .ncdd-runtime/bin/ncdd down -v || true \ No newline at end of file diff --git a/bin/ncdd b/bin/ncdd index 5604a6f..7a279b4 100644 --- a/bin/ncdd +++ b/bin/ncdd @@ -13,6 +13,7 @@ Commands: up Start the existing NCDD stack down Stop the existing NCDD stack status Show stack status + logs [SERVICE...] Show stack logs without ANSI colors env Show resolved NCDD environment doctor [--json] Check Docker, Compose, app service, and Nextcloud shell [--as USER] Open a shell in the app container @@ -161,6 +162,9 @@ case "$command" in status) compose ps "$@" ;; + logs) + compose logs --no-color "$@" + ;; shell) user=root if [ "${1:-}" = "--as" ]; then diff --git a/docs/ncdd.md b/docs/ncdd.md index 1b2c3db..8d7374e 100644 --- a/docs/ncdd.md +++ b/docs/ncdd.md @@ -19,6 +19,7 @@ The Makefile remains focused on repository maintenance tasks. It only exposes `t ```bash bin/ncdd up bin/ncdd status +bin/ncdd logs app bin/ncdd doctor bin/ncdd doctor --json bin/ncdd shell @@ -28,6 +29,8 @@ bin/ncdd down `runtime` resolves to `www-data` by default and can be overridden with `NCDD_RUNTIME_USER`. +Use `bin/ncdd logs` for diagnostics instead of rebuilding the underlying Compose command. Optional service names are passed through to Compose. + ## Git and app source NCDD does not fetch application repositories and does not receive GitHub credentials. Human users, CI, and agents perform the checkout outside the containers. @@ -74,4 +77,36 @@ The initial API intentionally supports only PHPUnit and Behat. Frontend runners, ## Configuration policy -NCDD still does not introduce `.ncdd.yml`. Test behavior uses explicit CLI arguments and narrowly scoped environment variables so the repository does not acquire a second configuration model before it is necessary. \ No newline at end of file +NCDD still does not introduce `.ncdd.yml`. Test behavior uses explicit CLI arguments and narrowly scoped environment variables so the repository does not acquire a second configuration model before it is necessary. + +## GitHub Actions consumer workflow + +The reusable workflow `.github/workflows/test-nextcloud-app.yml` lets an app repository run the same NCDD test contract in GitHub Actions. + +It intentionally follows the current NCDD scope: + +- PHPUnit and Behat only; +- the app is checked out under `volumes/nextcloud/apps-extra/`; +- no `.ncdd.yml`; +- no alternate Compose stack; +- no scenario or frontend mini-framework. + +A consumer can call it with: + +```yaml +jobs: + ncdd: + uses: LibreCodeCoop/nextcloud-docker/.github/workflows/test-nextcloud-app.yml@main + with: + suite: phpunit +``` + +`app_id` defaults to the caller repository name and can be overridden when the repository name differs from the Nextcloud app id. `target` is a single runner argument and is passed without shell evaluation. + +The workflow can install Composer dependencies before starting NCDD. This bootstrap runs in the GitHub-hosted workspace, while Nextcloud and the selected test runner execute through NCDD. Set `composer_install: false` when the caller provides dependencies by another mechanism. + +The workflow uses the Nextcloud version already defined by this repository. It does not introduce a second Nextcloud version input. + +A ready-to-copy caller example is available at `templates/workflows/ncdd-tests.yml`. + +Dev Containers and Codespaces are deliberately outside this increment. They should only be added after the reusable CI contract has been exercised by a real app repository. diff --git a/templates/workflows/ncdd-tests.yml b/templates/workflows/ncdd-tests.yml new file mode 100644 index 0000000..d9009f3 --- /dev/null +++ b/templates/workflows/ncdd-tests.yml @@ -0,0 +1,28 @@ +name: NCDD app tests + +on: + pull_request: + workflow_dispatch: + inputs: + suite: + description: Test suite + required: true + default: phpunit + type: choice + options: + - phpunit + - behat + target: + description: Optional single test target + required: false + type: string + +permissions: + contents: read + +jobs: + ncdd: + uses: LibreCodeCoop/nextcloud-docker/.github/workflows/test-nextcloud-app.yml@main + with: + suite: ${{ inputs.suite || 'phpunit' }} + target: ${{ inputs.target || '' }} diff --git a/tests/ncdd.bats b/tests/ncdd.bats index 5150a53..b3801d9 100644 --- a/tests/ncdd.bats +++ b/tests/ncdd.bats @@ -69,6 +69,12 @@ teardown() { grep -q 'up -d redis postgres app web cron' "$NCDD_TEST_LOG" } +@test "logs uses the existing compose stack" { + run bash "$REPO_ROOT/bin/ncdd" logs app + [ "$status" -eq 0 ] + grep -q -- 'logs --no-color app' "$NCDD_TEST_LOG" +} + @test "agent help keeps Docker Compose as an implementation detail" { run bash "$REPO_ROOT/bin/ncdd" help ai [ "$status" -eq 0 ] @@ -108,4 +114,4 @@ teardown() { run bash -n "$REPO_ROOT/lib/ncdd-test.sh" [ "$status" -eq 0 ] -} +} \ No newline at end of file