Skip to content

[WIP] CycloneDX v2.0 Specification #100

[WIP] CycloneDX v2.0 Specification

[WIP] CycloneDX v2.0 Specification #100

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"