# +-------------------------------------------------------------------------+ # | Copyright (C) 2004-2026 The Cacti Group | # | | # | This program is free software; you can redistribute it and/or | # | modify it under the terms of the GNU General Public License | # | as published by the Free Software Foundation; either version 2 | # | of the License, or (at your option) any later version. | # | | # | This program is distributed in the hope that it will be useful, | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | # | GNU General Public License for more details. | # +-------------------------------------------------------------------------+ # | Cacti: The Complete RRDtool-based Graphing Solution | # +-------------------------------------------------------------------------+ # | This code is designed, written, and maintained by the Cacti Group. See | # | about.php and/or the AUTHORS file for specific developer information. | # +-------------------------------------------------------------------------+ # | http://www.cacti.net/ | # +-------------------------------------------------------------------------+ name: Plugin Integration Tests on: push: branches: - main - develop pull_request: branches: - main - develop permissions: contents: read env: CACTI: 1.2.x concurrency: group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: integration-test: timeout-minutes: 45 runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: php: ['8.2', '8.3', '8.4'] os: [ubuntu-latest] services: mariadb: image: mariadb:11.8@sha256:6422478cb8e159f080fb1d8ccf65101e26fe51385787fde7d16c3b165a331f15 env: MYSQL_ROOT_PASSWORD: cactiroot MYSQL_DATABASE: cacti MYSQL_USER: cactiuser MYSQL_PASSWORD: cactiuser ports: - 3306:3306 options: >- --health-cmd="mariadb-admin ping" --health-interval=10s --health-timeout=5s --health-retries=3 name: PHP ${{ matrix.php }} Integration Test on ${{ matrix.os }} steps: - name: Checkout Cacti uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false repository: Cacti/cacti ref: ${{ env.CACTI }} path: cacti - name: Checkout Monitor Plugin uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false path: cacti/plugins/monitor # patch-coverage.php diffs against the base branch below. fetch-depth: 0 # Monitor declares `requires = thold:1.2.1` in its INFO file, so a Cacti # that now reports install failures refuses to install Monitor without it. - name: Checkout Thold Plugin uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false repository: Cacti/plugin_thold ref: develop path: cacti/plugins/thold - name: Install PHP ${{ matrix.php }} uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # 2.40.0 with: php-version: ${{ matrix.php }} tools: composer:2.10.3 extensions: intl, mysql, gd, ldap, gmp, xml, curl, json, mbstring, snmp coverage: xdebug ini-values: "post_max_size=256M, max_execution_time=60, date.timezone=America/New_York" - name: Check PHP version run: php -v - name: Validate plugin manifest (expected-file drift) run: php cacti/plugins/monitor/tests/bin/validate-manifest.php - name: Run apt-get update run: sudo apt-get update - name: Install System Dependencies run: sudo apt-get install -y snmp snmpd rrdtool fping gettext - name: Start SNMPD Agent and Test run: | sudo systemctl start snmpd sudo snmpwalk -c public -v2c -On localhost .1.3.6.1.2.1.1 - name: Setup Permissions run: | # Composer runs as the runner user in a later step and calls touch() # while generating the fixture autoloader. utime() only succeeds for the # file owner, so the runner user must own the tree here; www-data keeps # group access. Ownership is handed back to www-data before polling. sudo chown -R runner:www-data ${{ github.workspace }}/cacti sudo find ${{ github.workspace }}/cacti -type d -exec chmod 775 {} \; sudo find ${{ github.workspace }}/cacti -type f -exec chmod 664 {} \; sudo chmod +x ${{ github.workspace }}/cacti/cmd.php sudo chmod +x ${{ github.workspace }}/cacti/poller.php - name: Create MySQL Config run: | echo "::add-mask::cactiroot" echo -e "[client]\nuser = root\npassword = cactiroot\nhost = 127.0.0.1\n" > ~/.my.cnf chmod 600 ~/.my.cnf - name: Initialize Cacti Database run: | MYSQL_AUTH_USR="--defaults-file=$HOME/.my.cnf" mysql "$MYSQL_AUTH_USR" -e 'CREATE DATABASE IF NOT EXISTS cacti;' mysql "$MYSQL_AUTH_USR" -e "CREATE USER IF NOT EXISTS 'cactiuser'@'localhost' IDENTIFIED BY 'cactiuser';" mysql "$MYSQL_AUTH_USR" -e "CREATE USER IF NOT EXISTS 'cactiuser'@'127.0.0.1' IDENTIFIED BY 'cactiuser';" mysql "$MYSQL_AUTH_USR" -e "GRANT ALL PRIVILEGES ON cacti.* TO 'cactiuser'@'localhost';" mysql "$MYSQL_AUTH_USR" -e "GRANT ALL PRIVILEGES ON cacti.* TO 'cactiuser'@'127.0.0.1';" mysql "$MYSQL_AUTH_USR" -e "GRANT SELECT ON mysql.time_zone_name TO 'cactiuser'@'localhost';" mysql "$MYSQL_AUTH_USR" -e "GRANT SELECT ON mysql.time_zone_name TO 'cactiuser'@'127.0.0.1';" mysql "$MYSQL_AUTH_USR" -e "FLUSH PRIVILEGES;" mysql "$MYSQL_AUTH_USR" cacti < ${{ github.workspace }}/cacti/cacti.sql mysql "$MYSQL_AUTH_USR" -e "INSERT INTO settings (name, value) VALUES ('path_php_binary', '/usr/bin/php')" cacti # Core's release archives exclude .github and tests. Fetch these paths # through Git at the immutable helper revision instead of an Action archive. - name: Checkout shared CI tooling uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: Cacti/cacti ref: f3ff777992875740e4665fedfe2eff8335f5b894 persist-credentials: false path: ci-tools sparse-checkout: | .github/actions/setup-plugin-dependencies tests/tools/plugin-harness - name: Install locked Cacti and plugin test dependencies uses: ./ci-tools/.github/actions/setup-plugin-dependencies with: cacti-directory: cacti install-core-dev: 'false' - name: Create Cacti config.php run: | cat ${{ github.workspace }}/cacti/include/config.php.dist | \ sed -r "s/localhost/127.0.0.1/g" | \ sed -r "s/'cacti'/'cacti'/g" | \ sed -r "s/'cactiuser'/'cactiuser'/g" | \ sed -r "s/'cactiuser'/'cactiuser'/g" > ${{ github.workspace }}/cacti/include/config.php sudo chmod 664 ${{ github.workspace }}/cacti/include/config.php - name: Install Cacti via CLI run: | cd ${{ github.workspace }}/cacti sudo php cli/install_cacti.php --accept-eula --install --force - name: Install Thold Plugin run: | cd ${{ github.workspace }}/cacti sudo php cli/plugin_manage.php --plugin=thold --install --enable - name: Install Monitor Plugin run: | cd ${{ github.workspace }}/cacti sudo php cli/plugin_manage.php --plugin=monitor --install --enable - name: Check PHP Syntax for Plugin run: | cd ${{ github.workspace }}/cacti/plugins/monitor find . -path './vendor' -prune -o -type f -name '*.php' -print0 | xargs -0 -r -n1 php -l - name: Verify translation template is up to date if: github.event_name == 'pull_request' env: BASE_REF: ${{ github.event.pull_request.base.sha }} run: | cd ${{ github.workspace }}/cacti/plugins/monitor git config --global --add safe.directory ${{ github.workspace }}/cacti/plugins/monitor php tests/bin/check-i18n-pot.php "$BASE_REF" - name: Set expected Cacti version for unit tests run: echo -n "${{ env.CACTI }}" | sudo tee ${{ github.workspace }}/cacti/plugins/monitor/tests/.cacti-version > /dev/null - name: Run Pest Unit Tests run: | cd ${{ github.workspace }}/cacti "$CACTI_PEST_BIN" --configuration=plugins/monitor/phpunit.xml \ --coverage-clover=plugins/monitor/coverage/clover.xml # Whole-file coverage is meaningless here: most of the plugin only runs # inside a live Cacti. What is enforceable is that a change covers the # lines it adds. - name: Enforce coverage of changed lines if: github.event_name == 'pull_request' env: BASE_REF: ${{ github.event.pull_request.base.sha }} run: | cd ${{ github.workspace }}/cacti/plugins/monitor git config --global --add safe.directory ${{ github.workspace }}/cacti/plugins/monitor php tests/bin/patch-coverage.php coverage/clover.xml "$BASE_REF" 100 - name: Upload coverage report if: always() uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: coverage-php${{ matrix.php }} path: ${{ github.workspace }}/cacti/plugins/monitor/coverage/ if-no-files-found: warn - name: Remove the plugins directory exclusion from the .phpstan.neon if: ${{ env.CACTI != '1.2.x' }} run: sed '/plugins/d' -i .phpstan.neon working-directory: ${{ github.workspace }}/cacti - name: Mark composer scripts executable if: ${{ env.CACTI != '1.2.x' }} run: sudo chmod +x ${{ github.workspace }}/cacti/include/vendor/bin/* - name: Run Linter on base code if: ${{ env.CACTI != '1.2.x' }} run: composer run-script lint ${{ github.workspace }}/cacti/plugins/monitor working-directory: ${{ github.workspace }}/cacti - name: Checking coding standards on base code if: ${{ env.CACTI != '1.2.x' }} run: composer run-script phpcsfixer ${{ github.workspace }}/cacti/plugins/monitor working-directory: ${{ github.workspace }}/cacti - name: Run PHPStan at Level 6 on base code outside of Composer due to technical issues if: ${{ env.CACTI != '1.2.x' }} run: ./include/vendor/bin/phpstan analyze --level 6 ${{ github.workspace }}/cacti/plugins/monitor working-directory: ${{ github.workspace }}/cacti - name: Re-apply web user ownership before polling run: | # Ancestor directories above the checkout (e.g. /home/runner, .../work) # are typically 750 owned by the runner user, which blocks a non-root # www-data process from traversing down to the checkout at all. p="${{ github.workspace }}" while [ "$p" != "/" ]; do sudo chmod o+x "$p" || true p=$(dirname "$p") done sudo chown -R www-data:runner ${{ github.workspace }}/cacti sudo find ${{ github.workspace }}/cacti -type d -exec chmod 775 {} \; sudo find ${{ github.workspace }}/cacti -type f -exec chmod 664 {} \; sudo chmod +x ${{ github.workspace }}/cacti/cmd.php sudo chmod +x ${{ github.workspace }}/cacti/poller.php - name: Run Cacti Poller run: | cd ${{ github.workspace }}/cacti sudo -u www-data php poller.php --poller=1 --force --debug if ! grep -q "SYSTEM STATS" log/cacti.log; then echo "Cacti poller did not finish successfully" cat log/cacti.log exit 1 fi - name: View Cacti Logs if: always() run: | if [ -f ${{ github.workspace }}/cacti/log/cacti.log ]; then echo "=== Cacti Log ===" sudo cat ${{ github.workspace }}/cacti/log/cacti.log fi - name: Check Cacti Logs for Errors if: always() run: | if [ -f ${{ github.workspace }}/cacti/log/cacti.log ]; then if grep -E "FATAL:|ERROR:|WARNING:" ${{ github.workspace }}/cacti/log/cacti.log; then echo "Found FATAL/ERROR/WARNING entries in Cacti log" exit 1 fi fi