[WIP] CycloneDX v2.0 Specification #100
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CT CDX-2.x Lint | |
| on: | |
| push: | |
| branches: ['master', 'main'] | |
| pull_request: | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token | |
| permissions: {} | |
| env: | |
| NODE_VERSION: '24.x' | |
| jobs: | |
| discover-schema: | |
| timeout-minutes: 5 | |
| runs-on: ubuntu-latest | |
| outputs: | |
| versions: ${{ steps.discover.outputs.versions }} | |
| steps: | |
| - name: Checkout repository | |
| # see https://github.com/actions/checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Discover schema versions | |
| id: discover | |
| run: | | |
| mapfile -d '' -t dirs < <( | |
| find schema \ | |
| -mindepth 1 -maxdepth 1 \ | |
| -type d \ | |
| -name '2.*' \ | |
| -printf '%f\0' \ | |
| | sort -z | |
| ) | |
| if [ ${#dirs[@]} -eq 0 ]; then | |
| echo 'No schema/2.* directories found' >&2 | |
| exit 1 | |
| fi | |
| printf 'versions=' >> "$GITHUB_OUTPUT" | |
| printf '%s\n' "${dirs[@]}" | jq -R . | jq -c -s . >> "$GITHUB_OUTPUT" | |
| discover-linter: | |
| timeout-minutes: 5 | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tests: ${{ steps.discover.outputs.tests }} | |
| steps: | |
| - name: Checkout repository | |
| # see https://github.com/actions/checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Setup Node.js | |
| # see https://github.com/actions/setup-node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| package-manager-cache: false | |
| - name: Install linter | |
| working-directory: tools/src/main/js/linter | |
| run: npm install | |
| - name: Discover linter tests | |
| id: discover | |
| run: | | |
| printf 'tests=' >> "$GITHUB_OUTPUT" | |
| node tools/src/main/js/linter/cli.js -l -f json | jq -c 'keys' >> "$GITHUB_OUTPUT" | |
| lint-schemas: | |
| needs: | |
| - discover-schema | |
| - discover-linter | |
| timeout-minutes: 10 | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| schema_version: ${{ fromJSON(needs.discover-schema.outputs.versions) }} | |
| linter_test: ${{ fromJSON(needs.discover-linter.outputs.tests) }} | |
| # you could exclude some combinations via matrix.exclude | |
| # docs: https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idstrategymatrixexclude | |
| # exclude: | |
| # - schema_version: '2.1' | |
| # linter_test: 'no-todos' | |
| # - schema_version: '2.1' | |
| # linter_test: 'no-deprecated' | |
| name: lint ${{ matrix.schema_version }} ${{ matrix.linter_test }} | |
| env: | |
| REPORT_FILE: tools/src/main/js/linter/reports/${{ matrix.schema_version }}_${{ matrix.linter_test }}.json | |
| steps: | |
| - name: Checkout repository | |
| # see https://github.com/actions/checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Install aspell | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends aspell aspell-en | |
| - name: Setup Node.js | |
| # see https://github.com/actions/setup-node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| package-manager-cache: false | |
| - name: Install linter | |
| working-directory: tools/src/main/js/linter | |
| run: npm install | |
| - name: Lint schemas | |
| env: | |
| SCHEMA_VERSION: ${{ matrix.schema_version }} | |
| LINTER_TEST: ${{ matrix.linter_test }} | |
| run: | | |
| set -eu | |
| mapfile -d '' -t schemas < <( | |
| find "schema/${SCHEMA_VERSION}" \ | |
| -type f \ | |
| -name '*.schema.json' \ | |
| -not -name '*-bundled*.schema.json' \ | |
| -print0 | |
| ) | |
| if [ ${#schemas[@]} -eq 0 ]; then | |
| echo "No schemas found in schema/${SCHEMA_VERSION}" >&2 | |
| exit 1 | |
| fi | |
| mkdir -p "$(dirname "$REPORT_FILE")" | |
| node tools/src/main/js/linter/cli.js \ | |
| --include "$LINTER_TEST" \ | |
| --format json \ | |
| "${schemas[@]}" \ | |
| > "$REPORT_FILE" | |
| - name: Make report relative | |
| if: ${{ !cancelled() }} | |
| run: sed -i "s|${GITHUB_WORKSPACE}/||g" "$REPORT_FILE" | |
| - name: Print report | |
| if: ${{ !cancelled() }} | |
| run: cat "$REPORT_FILE" | |
| - name: Artifact report | |
| if: ${{ !cancelled() }} | |
| # https://github.com/actions/upload-artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: report_${{ matrix.schema_version }}_${{ matrix.linter_test }} | |
| path: ${{ env.REPORT_FILE }} | |
| if-no-files-found: error | |
| retention-days: 1 | |
| lint-bundled: | |
| if: >- | |
| ${{ github.ref == 'refs/heads/master' | |
| || github.ref == 'refs/heads/main' | |
| || github.ref == 'refs/heads/2.0-dev' | |
| || ( github.event_name == 'pull_request' | |
| && github.base_ref == 'master' | |
| ) | |
| }} | |
| needs: | |
| - discover-schema | |
| timeout-minutes: 10 | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| schema_version: ${{ fromJSON(needs.discover-schema.outputs.versions) }} | |
| name: lint ${{ matrix.schema_version }}-bundled | |
| env: | |
| REPORT_FILE: tools/src/main/js/linter/reports/${{ matrix.schema_version }}-bundled.json | |
| steps: | |
| - name: Checkout repository | |
| # see https://github.com/actions/checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Setup Node.js | |
| # see https://github.com/actions/setup-node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| package-manager-cache: false | |
| - name: Install linter | |
| working-directory: tools/src/main/js/linter | |
| run: npm install | |
| - name: Lint schemas | |
| env: | |
| SCHEMA_VERSION: ${{ matrix.schema_version }} | |
| run: | | |
| set -eu | |
| mapfile -d '' -t schemas < <( | |
| find "schema/${SCHEMA_VERSION}" \ | |
| -type f \ | |
| -name '*-bundled*.schema.json' \ | |
| -print0 | |
| ) | |
| if [ ${#schemas[@]} -eq 0 ]; then | |
| echo "No schemas found in schema/${SCHEMA_VERSION}" >&2 | |
| exit 1 | |
| fi | |
| mkdir -p "$(dirname "$REPORT_FILE")" | |
| # only run checks not carried over from source files | |
| node tools/src/main/js/linter/cli.js \ | |
| --include schema-draft \ | |
| --include schema-id-pattern \ | |
| --include schema-id-filepath \ | |
| --include schema-comment \ | |
| --format json \ | |
| "${schemas[@]}" \ | |
| > "$REPORT_FILE" | |
| - name: Make report relative | |
| if: ${{ !cancelled() }} | |
| run: sed -i "s|${GITHUB_WORKSPACE}/||g" "$REPORT_FILE" | |
| - name: Print report | |
| if: '!cancelled()' | |
| run: cat "$REPORT_FILE" | |
| - name: Artifact report | |
| if: ${{ !cancelled() }} | |
| # https://github.com/actions/upload-artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: report_${{ matrix.schema_version }}-bundled | |
| path: ${{ env.REPORT_FILE }} | |
| if-no-files-found: error | |
| retention-days: 1 | |
| summarize-version: | |
| if: ${{ !cancelled() }} | |
| needs: | |
| - discover-schema | |
| - lint-schemas | |
| - lint-bundled | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| schema_version: ${{ fromJSON(needs.discover-schema.outputs.versions) }} | |
| name: summarize ${{ matrix.schema_version }} | |
| env: | |
| BUNDLED_RAN: ${{ needs.lint-bundled.result != 'skipped' }} | |
| steps: | |
| - name: Fetch Reports | |
| # https://github.com/actions/download-artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| path: reports | |
| pattern: report_${{ matrix.schema_version }}[_-]* | |
| merge-multiple: true | |
| - name: Make summary | |
| working-directory: reports | |
| shell: python | |
| run: | | |
| import json, os | |
| from collections import Counter, defaultdict | |
| from itertools import chain | |
| from pathlib import Path | |
| SEVERITY = ('error', 'warning', 'info') | |
| _ICONS = {'error': '❌', 'warning': '⚠️', 'info': 'ℹ️'} | |
| def md_icon(counter): | |
| return next((_ICONS[s] for s in SEVERITY if counter[s] > 0), '✅') | |
| def md_table(title, header, counters): | |
| yield f'## {title}' | |
| yield f'| | {header} | {" | ".join(SEVERITY)} |' | |
| yield f'|:-:|---|{"--:|" * len(SEVERITY)}' | |
| for k in sorted(counters): | |
| c = counters[k] | |
| yield f'| {md_icon(c)} | `{k}` | {" | ".join(str(c[s]) for s in SEVERITY)} |' | |
| yield '' | |
| by_check, by_file = defaultdict(Counter), defaultdict(Counter) | |
| for f in Path('.').glob('*.json'): | |
| report = json.loads(f.read_text()) | |
| for c in report['enabledChecks']: | |
| by_check[c] | |
| for r in report['results']: | |
| file_counter = by_file[r['filePath']] | |
| for i in r['issues']: | |
| by_check[i['checkId']][i['severity']] += 1 | |
| file_counter[i['severity']] += 1 | |
| Path('summary.md').write_text('\n'.join(chain( | |
| md_table('By CheckID', 'checkId', by_check), | |
| md_table('By File', 'filePath', by_file), | |
| ( '*) bundled files were checked for specific issues only; see raw file section "enabledChecks".' | |
| if os.environ.get('BUNDLED_RAN') == 'true' | |
| else '', ), | |
| ))) | |
| - name: Artifact reports | |
| id: artifact-reports | |
| # https://github.com/actions/upload-artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: reports_${{ matrix.schema_version }} | |
| path: reports/ | |
| if-no-files-found: error | |
| - name: Print summary | |
| env: | |
| SCHEMA_VERSION: ${{ matrix.schema_version }} | |
| REPORTS_URL: ${{ steps.artifact-reports.outputs.artifact-url }} | |
| run: | | |
| cat reports/summary.md | |
| echo -e "# Summary for ${SCHEMA_VERSION}\n" > "$GITHUB_STEP_SUMMARY" | |
| echo -e "Reports: <${REPORTS_URL}>\n\n" >> "$GITHUB_STEP_SUMMARY" | |
| cat reports/summary.md >> "$GITHUB_STEP_SUMMARY" |