-
Notifications
You must be signed in to change notification settings - Fork 7
Code coverage support #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v1
Are you sure you want to change the base?
Changes from all commits
6bb4ebb
f6da824
39189e4
b733f71
a9d3206
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -64,6 +64,32 @@ jobs: | |
| uses: "actions/checkout@v6" | ||
| with: | ||
| path: "${{ inputs.plugin-key }}" | ||
| - name: "Detect coverage configuration" | ||
| id: "coverage-config" | ||
| # Use default `bash` shell with `github-actions-runner` user | ||
| shell: "bash" | ||
| working-directory: "${{ github.workspace }}/${{ inputs.plugin-key }}" | ||
| run: | | ||
| if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.base_ref }}" != "${{ github.event.repository.default_branch }}" ]]; then | ||
| echo "coverage-enabled=false" >> $GITHUB_OUTPUT | ||
| echo "ℹ️ Code coverage is disabled for pull requests targeting non-default branch (${{ github.base_ref }})." | ||
| exit 0 | ||
| fi | ||
|
|
||
| CONFIG_FILE=".glpi-coverage.json" | ||
| if [[ -f "$CONFIG_FILE" ]]; then | ||
| ENABLED=$(jq -r '.enabled // true' "$CONFIG_FILE") | ||
| if [[ "$ENABLED" != "true" ]]; then | ||
| echo "coverage-enabled=false" >> $GITHUB_OUTPUT | ||
| echo "ℹ️ Code coverage is disabled via $CONFIG_FILE" | ||
| exit 0 | ||
| fi | ||
| echo "coverage-enabled=true" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "coverage-enabled=false" >> $GITHUB_OUTPUT | ||
| echo "ℹ️ No $CONFIG_FILE found, code coverage is disabled." | ||
| exit 0 | ||
| fi | ||
| - name: "Execute init script" | ||
| if: ${{ inputs.init-script != '' }} | ||
| # Use default `bash` shell with `github-actions-runner` user | ||
|
|
@@ -262,18 +288,55 @@ jobs: | |
| shell: "bash" | ||
| run: | | ||
| sudo service apache2 start | ||
| - name: "Setup coverage driver" | ||
| if: ${{ !cancelled() && steps.coverage-config.outputs.coverage-enabled == 'true' }} | ||
| shell: "bash" | ||
| run: | | ||
| if ! php -m | grep -q -E 'xdebug|pcov'; then | ||
| echo -e "\033[0;33mInstalling PCOV driver...\033[0m" | ||
| sudo pecl install pcov || true | ||
| fi | ||
|
|
||
| - name: "PHPUnit" | ||
| if: ${{ !cancelled() && hashFiles(format('{0}/phpunit.xml', inputs.plugin-key)) != '' }} | ||
| env: | ||
| PCOV_ENABLED: "${{ steps.coverage-config.outputs.coverage-enabled == 'true' && '1' || '0' }}" | ||
| XDEBUG_MODE: "${{ steps.coverage-config.outputs.coverage-enabled == 'true' && 'coverage' || 'off' }}" | ||
| run: | | ||
| echo -e "\033[0;33mExecuting PHPUnit...\033[0m" | ||
| PHPUNIT_FLAGS="--colors=always" | ||
| PHP_CMD="php" | ||
|
|
||
| if [[ "${{ steps.coverage-config.outputs.coverage-enabled }}" == "true" ]]; then | ||
| PHPUNIT_FLAGS="$PHPUNIT_FLAGS --coverage-text --coverage-cobertura=cobertura.xml --coverage-clover=clover.xml" | ||
| # Explicitly load PCOV if needed | ||
| PHP_CMD="php -d extension=pcov.so" | ||
|
Comment on lines
+312
to
+313
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the extention should be always loaded, but maybe with a The only requirement here would the be to enable pcov through the |
||
| fi | ||
|
|
||
| if [[ -f "vendor/bin/phpunit" ]]; then | ||
| vendor/bin/phpunit --colors=always | ||
| $PHP_CMD vendor/bin/phpunit $PHPUNIT_FLAGS | ||
| elif [[ -f "../../vendor/bin/phpunit" ]]; then | ||
| ../../vendor/bin/phpunit --colors=always | ||
| $PHP_CMD ../../vendor/bin/phpunit $PHPUNIT_FLAGS | ||
| else | ||
| echo -e "\033[0;31mPHPUnit binary not found!\033[0m" | ||
| exit 1 | ||
| fi | ||
| - name: "Fix coverage paths for IDE import" | ||
| if: ${{ !cancelled() && steps.coverage-config.outputs.coverage-enabled == 'true' }} | ||
| run: | | ||
| echo "Sanitizing paths in clover.xml..." | ||
| sed -i 's|/var/www/glpi/plugins/${{ inputs.plugin-key }}/|plugins/${{ inputs.plugin-key }}/|g' clover.xml | ||
| - name: "Upload coverage report" | ||
| uses: "actions/upload-artifact@v6" | ||
| if: ${{ !cancelled() && steps.coverage-config.outputs.coverage-enabled == 'true' }} | ||
| with: | ||
| name: "coverage-report" | ||
| path: | | ||
| /var/www/glpi/plugins/${{ inputs.plugin-key }}/cobertura.xml | ||
| /var/www/glpi/plugins/${{ inputs.plugin-key }}/clover.xml | ||
| /var/www/glpi/plugins/${{ inputs.plugin-key }}/.glpi-coverage.json | ||
| include-hidden-files: true | ||
| overwrite: true | ||
| - name: "Jest" | ||
| if: ${{ !cancelled() && hashFiles(format('{0}/jest.config.js', inputs.plugin-key)) != '' }} | ||
| run: | | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| name: "Coverage refresh" | ||
|
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| plugin-key: | ||
| required: true | ||
| type: string | ||
| workflow-name: | ||
| description: "Name of the CI workflow to trigger for coverage refresh. Must match the 'name' field in the plugin's CI workflow file." | ||
| required: false | ||
| type: string | ||
| default: "Continuous integration" | ||
|
|
||
| jobs: | ||
| check-and-refresh: | ||
| name: "Check and refresh coverage artifact" | ||
| runs-on: "ubuntu-latest" | ||
| steps: | ||
| - name: "Checkout" | ||
| uses: "actions/checkout@v6" | ||
| with: | ||
| sparse-checkout: ".glpi-coverage.json" | ||
|
|
||
| - name: "Check coverage configuration" | ||
| id: "coverage-config" | ||
| run: | | ||
| CONFIG_FILE=".glpi-coverage.json" | ||
| if [[ ! -f "$CONFIG_FILE" ]]; then | ||
| echo "ℹ️ No $CONFIG_FILE found, skipping coverage refresh." | ||
| echo "skip=true" >> $GITHUB_OUTPUT | ||
| exit 0 | ||
| fi | ||
|
|
||
| ENABLED=$(jq -r '.enabled // true' "$CONFIG_FILE") | ||
| if [[ "$ENABLED" != "true" ]]; then | ||
| echo "ℹ️ Code coverage is disabled via $CONFIG_FILE, skipping refresh." | ||
| echo "skip=true" >> $GITHUB_OUTPUT | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "skip=false" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: "Check artifact expiry" | ||
| if: steps.coverage-config.outputs.skip != 'true' | ||
| id: "check-expiry" | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| echo "Checking for existing coverage artifacts..." | ||
|
|
||
| # The clearlyip action uses the naming pattern: coverage-{branch_name} | ||
| DEFAULT_BRANCH="${{ github.event.repository.default_branch }}" | ||
| ARTIFACT_NAME="coverage-${DEFAULT_BRANCH}" | ||
|
|
||
| # List artifacts matching the coverage pattern | ||
| ARTIFACTS=$(gh api \ | ||
| "/repos/${{ github.repository }}/actions/artifacts?name=${ARTIFACT_NAME}&per_page=1" \ | ||
| --jq '.artifacts[0]' 2>/dev/null || echo "null") | ||
|
|
||
| if [[ "$ARTIFACTS" == "null" || -z "$ARTIFACTS" ]]; then | ||
| echo "⚠️ No coverage artifact found. Refresh needed." | ||
| echo "needs-refresh=true" >> $GITHUB_OUTPUT | ||
| exit 0 | ||
| fi | ||
|
|
||
| EXPIRES_AT=$(echo "$ARTIFACTS" | jq -r '.expires_at // empty') | ||
| if [[ -z "$EXPIRES_AT" ]]; then | ||
| echo "⚠️ Could not determine artifact expiry. Refresh needed." | ||
| echo "needs-refresh=true" >> $GITHUB_OUTPUT | ||
| exit 0 | ||
| fi | ||
|
|
||
| EXPIRES_TS=$(date -d "$EXPIRES_AT" +%s) | ||
| TOMORROW_TS=$(date -d "+1 day" +%s) | ||
|
|
||
| if [[ "$EXPIRES_TS" -le "$TOMORROW_TS" ]]; then | ||
| echo "⏰ Coverage artifact expires at $EXPIRES_AT (within 1 day). Refresh needed." | ||
| echo "needs-refresh=true" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "✅ Coverage artifact is valid until $EXPIRES_AT. No refresh needed." | ||
| echo "needs-refresh=false" >> $GITHUB_OUTPUT | ||
| fi | ||
|
|
||
| - name: "Trigger CI workflow" | ||
| if: steps.check-expiry.outputs.needs-refresh == 'true' | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| echo "🔄 Triggering CI workflow on default branch to refresh coverage artifact..." | ||
| gh workflow run "${{ inputs.workflow-name }}" \ | ||
| --repo "${{ github.repository }}" \ | ||
| --ref "${{ github.event.repository.default_branch }}" | ||
| echo "✅ Workflow dispatch triggered." |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| name: "Coverage report" | ||
|
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| plugin-key: | ||
| required: true | ||
| type: string | ||
|
|
||
| permissions: | ||
| pull-requests: write | ||
| actions: read | ||
|
|
||
| jobs: | ||
| coverage-report: | ||
| runs-on: "ubuntu-latest" | ||
| name: "Coverage report" | ||
| steps: | ||
| - name: "Check target branch" | ||
| id: "check-branch" | ||
| run: | | ||
| if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.base_ref }}" != "${{ github.event.repository.default_branch }}" ]]; then | ||
| echo "ℹ️ Code coverage is disabled for pull requests targeting non-default branch (${{ github.base_ref }})." | ||
| echo "skip=true" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "skip=false" >> $GITHUB_OUTPUT | ||
| fi | ||
|
|
||
| - name: "Download coverage report" | ||
| if: steps.check-branch.outputs.skip != 'true' | ||
| uses: "actions/download-artifact@v7" | ||
| with: | ||
| name: "coverage-report" | ||
|
|
||
| - name: "Read coverage configuration" | ||
| id: "coverage-config" | ||
| run: | | ||
| if [[ "${{ steps.check-branch.outputs.skip }}" == "true" ]]; then | ||
| echo "skip=true" >> $GITHUB_OUTPUT | ||
| exit 0 | ||
| fi | ||
|
|
||
| CONFIG_FILE=".glpi-coverage.json" | ||
| if [[ ! -f "$CONFIG_FILE" ]]; then | ||
| echo "⚠️ No $CONFIG_FILE found, skipping coverage report." | ||
| echo "skip=true" >> $GITHUB_OUTPUT | ||
| exit 0 | ||
| fi | ||
|
|
||
| ENABLED=$(jq -r '.enabled // true' "$CONFIG_FILE") | ||
| if [[ "$ENABLED" != "true" ]]; then | ||
| echo "ℹ️ Code coverage is disabled via $CONFIG_FILE" | ||
| echo "skip=true" >> $GITHUB_OUTPUT | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "skip=false" >> $GITHUB_OUTPUT | ||
| echo "only-list-changed-files=$(jq -r '.only_list_changed_files // true' "$CONFIG_FILE")" >> $GITHUB_OUTPUT | ||
| echo "badge=$(jq -r '.badge // true' "$CONFIG_FILE")" >> $GITHUB_OUTPUT | ||
| echo "overall-coverage-fail-threshold=$(jq -r '.overall_coverage_fail_threshold // 0' "$CONFIG_FILE")" >> $GITHUB_OUTPUT | ||
| echo "file-coverage-error-min=$(jq -r '.file_coverage_error_min // 50' "$CONFIG_FILE")" >> $GITHUB_OUTPUT | ||
| echo "file-coverage-warning-max=$(jq -r '.file_coverage_warning_max // 75' "$CONFIG_FILE")" >> $GITHUB_OUTPUT | ||
| echo "fail-on-negative-difference=$(jq -r '.fail_on_negative_difference // false' "$CONFIG_FILE")" >> $GITHUB_OUTPUT | ||
| echo "retention-days=$(jq -r '.retention_days // 90' "$CONFIG_FILE")" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: "Generate coverage report" | ||
| if: steps.coverage-config.outputs.skip != 'true' | ||
| uses: "clearlyip/code-coverage-report-action@v6" | ||
| id: "coverage-report" | ||
| with: | ||
| filename: "cobertura.xml" | ||
| only_list_changed_files: ${{ steps.coverage-config.outputs.only-list-changed-files }} | ||
| badge: ${{ steps.coverage-config.outputs.badge }} | ||
| overall_coverage_fail_threshold: ${{ steps.coverage-config.outputs.overall-coverage-fail-threshold }} | ||
| file_coverage_error_min: ${{ steps.coverage-config.outputs.file-coverage-error-min }} | ||
| file_coverage_warning_max: ${{ steps.coverage-config.outputs.file-coverage-warning-max }} | ||
| fail_on_negative_difference: ${{ steps.coverage-config.outputs.fail-on-negative-difference }} | ||
| retention_days: ${{ steps.coverage-config.outputs.retention-days }} | ||
| artifact_download_workflow_names: "Continuous integration" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it should not be hardcoded. |
||
|
|
||
| - name: "Generating Markdown report" | ||
| if: github.event_name == 'pull_request' && steps.coverage-config.outputs.skip != 'true' && steps.coverage-report.outputs.file != '' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| run: | | ||
| COVERAGE="${{ steps.coverage-report.outputs.coverage }}" | ||
| REPORT_FILE="code-coverage-results.md" | ||
| ARTIFACT_LINK="📥 [Download coverage-report artifact](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) _(contains \`clover.xml\` for IDE import + config file)_" | ||
|
|
||
| # Split: keep header/badge visible, collapse the table inside <details> | ||
| FIRST_TABLE_LINE=$(grep -n "^|" "$REPORT_FILE" | head -1 | cut -d: -f1) | ||
|
|
||
| if [[ -z "$FIRST_TABLE_LINE" ]]; then | ||
| { | ||
| cat "$REPORT_FILE" | ||
| echo "" | ||
| echo "$ARTIFACT_LINK" | ||
| } > "${REPORT_FILE}.tmp" | ||
| else | ||
| { | ||
| head -n "$((FIRST_TABLE_LINE - 1))" "$REPORT_FILE" | ||
| echo "" | ||
| echo "<details>" | ||
| echo "<summary>📋 Details</summary>" | ||
| echo "" | ||
| tail -n "+${FIRST_TABLE_LINE}" "$REPORT_FILE" | ||
| echo "" | ||
| echo "$ARTIFACT_LINK" | ||
| echo "" | ||
| echo "</details>" | ||
| } > "${REPORT_FILE}.tmp" | ||
| fi | ||
|
|
||
| mv "${REPORT_FILE}.tmp" "$REPORT_FILE" | ||
|
|
||
| - name: "Add coverage PR comment" | ||
| if: github.event_name == 'pull_request' && steps.coverage-config.outputs.skip != 'true' && steps.coverage-report.outputs.file != '' | ||
| uses: "marocchino/sticky-pull-request-comment@v2" | ||
| with: | ||
| header: coverage | ||
| path: code-coverage-results.md | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add
pcovin thegithubactions-php-apacheimage (inherited bygithubactions-glpi-apache).