# SPDX-FileCopyrightText: 2026 Nextcloud cookbook contributors # # SPDX-License-Identifier: AGPL-3.0-only OR AGPL-3.0-or-later name: CI on: pull_request: workflow_dispatch: defaults: run: shell: bash jobs: code-lint: name: Run PHP Linter and Code Style checker runs-on: ubuntu-latest steps: - name: Checkout the project uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: persist-credentials: false - name: Read package.json node and npm engines version uses: skjnldsv/read-package-engines-version-actions@06d6db44aa62f4609ba070f556ec24bc99fb85cb # v4 id: versions # Continue if no package.json continue-on-error: true with: path: . fallbackNode: '^24' fallbackNpm: '^11.3' - name: Set up node ${{ steps.versions.outputs.nodeVersion }} # Skip if no package.json if: ${{ steps.versions.outputs.nodeVersion }} uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ steps.versions.outputs.nodeVersion }} - name: Set up npm ${{ steps.versions.outputs.npmVersion }} # Skip if no package.json if: ${{ steps.versions.outputs.npmVersion }} run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - name: Get the date id: date run: echo "date=$(date +%Y-%m-%d)" >> $GITHUB_OUTPUT - name: Cache NPM cache uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 with: path: | ~/.npm node_modules key: ${{ runner.os }}-node-${{ steps.date.outputs.date }}-${{ hashFiles('package-lock.json') }} restore-keys: | ${{ runner.os }}-node-${{ steps.date.outputs.date }}- ${{ runner.os }}-node- - name: Cache composer files uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 with: path: | vendor key: ${{ runner.os }}-composer-${{ steps.date.outputs.date }}-${{ hashFiles('composer.lock') }} restore-keys: | ${{ runner.os }}-composer-${{ steps.date.outputs.date }}- ${{ runner.os }}-composer- - name: Install PHP uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 with: php-version: '8.4' tools: composer - name: Get PHP version shell: bash run: php -v - name: Install PHP packages shell: bash run: composer install - name: Install Node packages shell: bash run: npm -q ci || { cat ~/.npm/eresolve-report.txt ; exit 1; } - name: Run PHP linter shell: bash run: composer lint:lint - name: Run PHP code style checker shell: bash run: composer cs:check - name: Run PHP type checker Psalm shell: bash run: composer psalm - name: Check for formatting issues with vue files (prettier) shell: bash run: >- npm run prettier || { echo '::error ::The prettier style checker failed. Please run `npm run prettier-fix` in order to correct this.'; exit 1; } - name: Check for issues with vue and js files (eslint) shell: bash run: >- npm run eslint -- --max-warnings=0 || { echo '::error ::The eslint style checker failed. Please run `npm run eslint-fix` in order to correct this.'; exit 1; } - name: Check types in vue and js files shell: bash run: >- npm run type-check || { echo '::error ::The vue and js type checker failed. Please run `npm run type-check` to identify the type errors.'; exit 1; } - name: Check for formatting issues with CSS files (stylelint) shell: bash run: >- npm run stylelint || { echo '::error ::The stylelint checker failed. Please run `npm run stylelint-fix` to help solving these issues if possible.'; exit 1; } unit-tests: name: Run the unittests runs-on: ubuntu-latest strategy: fail-fast: false matrix: database: - mysql coreVersion: - 35 phpVersion: - "8.5" httpServer: - "apache" include: - database: sqlite coreVersion: 33 phpVersion: "8.3" httpServer: "apache" - database: pgsql coreVersion: 34 phpVersion: "8.4" httpServer: "nginx" steps: - name: Checkout the app uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: fetch-depth: 2 persist-credentials: false - name: Ensure the appinfo is built shell: bash run: make appinfo/info.xml #- name: Docker Layer Caching #uses: satackey/action-docker-layer-caching@v0.0.8 - name: Install helper program shell: bash run: |- pip install yq echo "$HOME/.local/bin" >> "$GITHUB_PATH" - name: Get the min and max compatible server versions from manifest XML file shell: bash id: ncVersionRequirements run: |- minVersion="$(cat appinfo/info.xml | xq '.info.dependencies.nextcloud."@min-version"' -r)" maxVersion="$(cat appinfo/info.xml | xq '.info.dependencies.nextcloud."@max-version"' -r)" echo "minVersion=$minVersion" >> $GITHUB_OUTPUT echo "maxVersion=$maxVersion" >> $GITHUB_OUTPUT echo "Compatible NC server versions: [$minVersion, $maxVersion]" if [ "${{ matrix.coreVersion }}" -ge "$minVersion" -a "${{ matrix.coreVersion }}" -le "$maxVersion" ]; then mv="true" else mv="false" echo "Skipping the main tests as this version is marked incompatible with the installed NC version." >> $GITHUB_STEP_SUMMARY fi echo "matchingVersion=$mv" >> $GITHUB_OUTPUT echo "The app is compatible with the current NV core v${{ matrix.coreVersion }}: $mv" - name: Prepare folder for installation of composer dependencies run: mkdir -p vendor - name: Install Python dependencies for test helper run: cd .github/actions/run-tests && pip install -r requirements.txt - name: Pull and build the test images run: >- cd .github/actions/run-tests && ./run-locally.py --pull --create-images --input-db ${{ matrix.database }} --http-server ${{ matrix.httpServer }} --php-version ${{ matrix.phpVersion }} - name: Compute the fixture cache key id: fixtureKey env: FILES_HASH: ${{ hashFiles('lib/Migration/**', 'appinfo/info.xml', '.github/actions/run-tests/*.py', '.github/actions/run-tests/docker-compose.yml', '.github/actions/run-tests/mysql/**', '.github/actions/run-tests/postgres/**', '.github/actions/run-tests/test_runner/*.py', '.github/actions/run-tests/scripts/**', '.github/actions/run-tests/tests/**') }} run: >- sha="$(git ls-remote https://github.com/nextcloud/server refs/heads/stable${{ matrix.coreVersion }} | cut -f1)" && echo "key=fixture-tar-${{ matrix.database }}-${{ matrix.phpVersion }}-$sha-$FILES_HASH" >> "$GITHUB_OUTPUT" - name: Restore the cached fixtures id: fixtureCache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 with: path: ${{ runner.temp }}/fixture.tar key: ${{ steps.fixtureKey.outputs.key }} - name: Extract the cached fixtures if: steps.fixtureCache.outputs.cache-hit == 'true' run: >- mkdir -p .github/actions/run-tests/volumes/dumps && sudo tar -xpf "$RUNNER_TEMP/fixture.tar" -C .github/actions/run-tests/volumes/dumps - name: Create the fixtures for the tests id: fixtures run: >- if [ "${{matrix.database}}" = 'pgsql' ]; then PARAM=''; else PARAM='--use-db-dump'; fi && cd .github/actions/run-tests && if [ "${{ steps.fixtureCache.outputs.cache-hit }}" = 'true' ]; then mkdir -p volumes/nextcloud/custom_apps/cookbook volumes/nextcloud/data && PARAM='--start-helpers'; else PARAM="$PARAM --create-fixture default stable${{ matrix.coreVersion }}"; fi && ./run-locally.py $PARAM --activate-fixture default --input-db ${{ matrix.database }} --http-server ${{ matrix.httpServer }} --php-version ${{ matrix.phpVersion }} - name: Archive the fixtures if: steps.fixtureCache.outputs.cache-hit != 'true' run: >- sudo tar -cf "$RUNNER_TEMP/fixture.tar" -C .github/actions/run-tests/volumes/dumps version.json fixtures/default && sudo chown "$USER" "$RUNNER_TEMP/fixture.tar" - name: Cache the fixtures if: steps.fixtureCache.outputs.cache-hit != 'true' uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 with: path: ${{ runner.temp }}/fixture.tar key: ${{ steps.fixtureKey.outputs.key }} - name: Run the unit tests if: ${{ !cancelled() && steps.fixtures.outcome == 'success' }} run: >- cd .github/actions/run-tests && ./run-locally.py --run-unit-tests --extract-code-coverage --keep-code-coverage --install-composer-deps - name: Run the integration tests if: ${{ !cancelled() && steps.fixtures.outcome == 'success' }} run: >- cd .github/actions/run-tests && ./run-locally.py --run-integration-tests --extract-code-coverage --keep-code-coverage --install-composer-deps - name: Run the migration tests if: ${{ !cancelled() && steps.fixtures.outcome == 'success' }} run: >- cd .github/actions/run-tests && ./run-locally.py --run-migration-tests --extract-code-coverage --keep-code-coverage --install-composer-deps - name: Export the annotations of unit tests to github shell: bash run: >- if [ -r .github/actions/run-tests/volumes/coverage/teamcity.log ]; then python .github/actions/run-tests/extract-annotations.py .github/actions/run-tests/volumes/coverage/teamcity.log ; fi - name: Export the annotations of integration tests to github shell: bash run: >- if [ -r .github/actions/run-tests/volumes/coverage/teamcity.integration.log ]; then python .github/actions/run-tests/extract-annotations.py .github/actions/run-tests/volumes/coverage/teamcity.integration.log ; fi - name: Export the annotations of migration tests to github shell: bash run: >- if [ -r .github/actions/run-tests/volumes/coverage/teamcity.migration.log ]; then python .github/actions/run-tests/extract-annotations.py .github/actions/run-tests/volumes/coverage/teamcity.migration.log ; fi # - name: Upload the code coverage files as artifacts # if: always() # uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # with: # name: Code coverage files (${{matrix.database}}, stable${{matrix.coreVersion}}, ${{matrix.httpServer}}, ${{matrix.phpVersion}}) # path: ".github/actions/run-tests/volumes/coverage" - name: Upload the log file as artifact if: steps.ncVersionRequirements.outputs.matchingVersion == 'true' && always() uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 with: name: Nextcloud-logs (${{matrix.database}}, stable${{matrix.coreVersion}}, ${{matrix.httpServer}}, ${{matrix.phpVersion}}) path: .github/actions/run-tests/volumes/data/nextcloud.log - name: Copy coverage-reports into non-link folder if: steps.ncVersionRequirements.outputs.matchingVersion == 'true' shell: bash run: >- mkdir /tmp/coverage && cp .github/actions/run-tests/volumes/coverage/run-*/coverage.*.xml /tmp/coverage - name: Upload the code coverage report for codecov as artifacts if: steps.ncVersionRequirements.outputs.matchingVersion == 'true' uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 with: name: Code coverage (XML) (${{matrix.database}}, stable${{matrix.coreVersion}}, ${{matrix.httpServer}}, ${{matrix.phpVersion}}) path: "/tmp/coverage/coverage.*.xml" # - name: Upload the code coverage report (unit tests) as artifacts # uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # with: # name: Code coverage (HTML) (${{matrix.database}}, stable${{matrix.coreVersion}}, ${{matrix.httpServer}}, ${{matrix.phpVersion}}) # path: "/tmp/coverage" - name: Upload the junit log file artifact as artifact uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 with: name: Junit test log files (${{matrix.database}}, stable${{matrix.coreVersion}}, ${{matrix.httpServer}}, ${{matrix.phpVersion}}) path: .github/actions/run-tests/volumes/coverage/junit*.xml if: steps.ncVersionRequirements.outputs.matchingVersion == 'true' && always() unit-tests-master: name: Run the tests against the master branch of the NC server runs-on: ubuntu-latest steps: - name: Checkout the app uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: fetch-depth: 2 persist-credentials: false - name: Ensure the appinfo is built shell: bash run: make appinfo/info.xml #- name: Docker Layer Caching #uses: satackey/action-docker-layer-caching@v0.0.8 - name: Prepare folder for installation of composer dependencies run: mkdir -p vendor - name: Install Python dependencies for test helper run: cd .github/actions/run-tests && pip install -r requirements.txt - name: Pull and build the test images run: >- cd .github/actions/run-tests && ./run-locally.py --pull --create-images --input-db mysql --http-server nginx --php-version 8.5 - name: Create the fixtures for the tests id: fixtures run: >- cd .github/actions/run-tests && ./run-locally.py --create-fixture default master --activate-fixture default --input-db mysql --http-server nginx --php-version latest --install-untested - name: Run the unit tests if: ${{ !cancelled() && steps.fixtures.outcome == 'success' }} run: >- cd .github/actions/run-tests && ./run-locally.py --run-unit-tests --extract-code-coverage --keep-code-coverage --install-composer-deps - name: Run the integration tests if: ${{ !cancelled() && steps.fixtures.outcome == 'success' }} run: >- cd .github/actions/run-tests && ./run-locally.py --run-integration-tests --extract-code-coverage --keep-code-coverage --install-composer-deps - name: Run the migration tests if: ${{ !cancelled() && steps.fixtures.outcome == 'success' }} run: >- cd .github/actions/run-tests && ./run-locally.py --run-migration-tests --extract-code-coverage --keep-code-coverage --install-composer-deps - name: Export the annotations of unit tests to github shell: bash run: >- if [ -r .github/actions/run-tests/volumes/coverage/teamcity.log ]; then python .github/actions/run-tests/extract-annotations.py .github/actions/run-tests/volumes/coverage/teamcity.log ; fi - name: Export the annotations of integration tests to github shell: bash run: >- if [ -r .github/actions/run-tests/volumes/coverage/teamcity.integration.log ]; then python .github/actions/run-tests/extract-annotations.py .github/actions/run-tests/volumes/coverage/teamcity.integration.log ; fi - name: Export the annotations of migration tests to github shell: bash run: >- if [ -r .github/actions/run-tests/volumes/coverage/teamcity.migration.log ]; then python .github/actions/run-tests/extract-annotations.py .github/actions/run-tests/volumes/coverage/teamcity.migration.log ; fi # - name: Upload the coverage files as artifact # uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # with: # name: Coverage files (mysql, master, apache) # path: .github/actions/run-tests/volumes/coverage # if: always() - name: Upload the log file as artifact uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 if: always() with: name: Nextcloud-logs (mysql, master, apache) path: .github/actions/run-tests/volumes/data/nextcloud.log - name: Copy coverage-reports into non-link folder shell: bash run: >- mkdir /tmp/coverage && cp .github/actions/run-tests/volumes/coverage/run-*/coverage.*.xml /tmp/coverage - name: Upload the code coverage report for codecov as artifacts uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 with: name: Code coverage (XML) (mysql, master, apache) path: "/tmp/coverage/coverage.*.xml" - name: Upload the junit log file artifact as artifact uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 with: name: Junit test log files (mysql, master, apache) path: .github/actions/run-tests/volumes/coverage/junit*.xml if: always() source-package: name: Create source code artifacts runs-on: ubuntu-latest steps: - name: Checkout the app uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: persist-credentials: false - name: Read package.json node and npm engines version uses: skjnldsv/read-package-engines-version-actions@06d6db44aa62f4609ba070f556ec24bc99fb85cb # v4 id: versions # Continue if no package.json continue-on-error: true with: path: . fallbackNode: '^24' fallbackNpm: '^11.3' - name: Set up node ${{ steps.versions.outputs.nodeVersion }} # Skip if no package.json if: ${{ steps.versions.outputs.nodeVersion }} uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ steps.versions.outputs.nodeVersion }} - name: Set up npm ${{ steps.versions.outputs.npmVersion }} # Skip if no package.json if: ${{ steps.versions.outputs.npmVersion }} run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - name: Get the date id: date run: echo "date=$(date +%Y-%m-%d)" >> $GITHUB_OUTPUT - name: Use cache for NPM uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 with: path: | ~/.npm node_modules key: ${{ runner.os }}-node-${{ steps.date.outputs.date }}-${{ hashFiles('package-lock.json') }} restore-keys: | ${{ runner.os }}-node-${{ steps.date.outputs.date }}- ${{ runner.os }}-node- - name: Use cache for Composer uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 with: path: | vendor key: ${{ runner.os }}-composer-${{ steps.date.outputs.date }}-${{ hashFiles('composer.lock') }} restore-keys: | ${{ runner.os }}-composer-${{ steps.date.outputs.date }}- ${{ runner.os }}-composer- - name: Install the NPM dependencies shell: bash run: >- npm ci - name: Install the dependencies for composer shell: bash run: >- make composer_dist - name: Build node app shell: bash run: >- make npm - name: Create the app store package shell: bash run: >- make appstore_package_name=/tmp/appstore appstore - name: Publish the app store package as artifact uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 with: name: cookbook-appstore path: /tmp/appstore.tar.gz event_file: name: "Upload Event File" runs-on: ubuntu-latest-low steps: - name: Upload uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 with: name: Event File path: ${{ github.event_path }} retention-days: 1 lint-php: name: "Lint PHP files to be compatible" runs-on: ubuntu-latest defaults: run: shell: bash steps: - name: Checkout of the app uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: persist-credentials: false - name: build docker container image run: docker build -t php-lint-tester .github/actions/php-linter - name: Check the code base run: docker run --rm -v $(pwd):/work:ro php-lint-tester run-js-tests: name: "Run JS tests" runs-on: ubuntu-latest defaults: run: shell: bash steps: - name: Checkout of the app uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: persist-credentials: false - name: Read package.json node and npm engines version uses: skjnldsv/read-package-engines-version-actions@06d6db44aa62f4609ba070f556ec24bc99fb85cb # v4 id: versions # Continue if no package.json continue-on-error: true with: path: . fallbackNode: '^24' fallbackNpm: '^11.3' - name: Set up node ${{ steps.versions.outputs.nodeVersion }} # Skip if no package.json if: ${{ steps.versions.outputs.nodeVersion }} uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ steps.versions.outputs.nodeVersion }} - name: Set up npm ${{ steps.versions.outputs.npmVersion }} # Skip if no package.json if: ${{ steps.versions.outputs.npmVersion }} run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - name: Get the date id: date run: echo "date=$(date +%Y-%m-%d)" >> $GITHUB_OUTPUT - name: Cache NPM cache uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 with: path: | ~/.npm node_modules key: ${{ runner.os }}-node-${{ steps.date.outputs.date }}-${{ hashFiles('package-lock.json') }} restore-keys: | ${{ runner.os }}-node-${{ steps.date.outputs.date }}- ${{ runner.os }}-node- - name: Install the NPM dependencies run: npm ci - name: Run the tests run: npm run ci-test successful_run: name: The tests have successfully run runs-on: ubuntu-latest-low needs: - unit-tests - code-lint - source-package - lint-php - event_file - run-js-tests steps: - name: Run successfully shell: bash run: echo "The tests have been running successfully"