diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4f8f035 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: CI +on: [push, pull_request] +permissions: {contents: read} +concurrency: {group: ci, cancel-in-progress: true} +jobs: + verify: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: deps-${{ github.sha }} + - name: Detect stack and verify + shell: bash + run: | + set -euo pipefail + if find . -name '*.py' -print -quit | grep -q .; then + python -m pip install --upgrade pip + for f in requirements.txt requirements-dev.txt requirements/dev.txt; do [ -f "$f" ] && python -m pip install -r "$f"; done + python -m pip install pytest pytest-cov ruff black + python -m compileall -q . + if [ -d tests ]; then python -m pytest --cov=. --cov-report=xml; fi + if [ -f pyproject.toml ] && grep -qE 'ruff|black' pyproject.toml; then python -m ruff check .; python -m ruff format --check .; fi + elif [ -f package.json ]; then [ -f package-lock.json ] && npm ci || npm install; npm run lint --if-present; npm test --if-present; npm run build --if-present + elif [ -f go.mod ]; then gofmt -l . | tee /tmp/gofmt.out; test ! -s /tmp/gofmt.out; go test ./... + elif [ -f Cargo.toml ]; then cargo fmt --all -- --check; cargo clippy --all-targets --all-features -- -D warnings; cargo test --all + elif [ -f pom.xml ]; then (./mvnw -B verify || mvn -B verify) + elif [ -f CMakeLists.txt ]; then cmake -S . -B build; cmake --build build --parallel; ctest --test-dir build --output-on-failure + else echo 'No supported build manifest detected.'; fi + - uses: actions/upload-artifact@v4 + if: always() + with: {name: coverage, path: coverage.xml, if-no-files-found: ignore} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..f5f690d --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,45 @@ +name: CodeQL +on: + push: + pull_request: + schedule: + - cron: '23 3 * * 1' +permissions: + contents: read + security-events: write +jobs: + detect: + runs-on: ubuntu-latest + outputs: + languages: ${{ steps.lang.outputs.languages }} + steps: + - uses: actions/checkout@v4 + - id: lang + shell: bash + run: | + set -euo pipefail + langs='' + [ -n "$(find . -name '*.py' -print -quit)" ] && langs="$langs python" + [ -n "$(find . -name '*.js' -print -quit)" ] || [ -n "$(find . -name '*.ts' -print -quit)" ] && langs="$langs javascript-typescript" + [ -f go.mod ] && langs="$langs go" + [ -f Cargo.toml ] && langs="$langs rust" + [ -f pom.xml ] || [ -f build.gradle ] && langs="$langs java-kotlin" + [ -n "$(find . -name '*.c' -print -quit)" ] || [ -n "$(find . -name '*.cpp' -print -quit)" ] && langs="$langs c-cpp" + [ -n "$langs" ] || langs=' python' + json="$(jq -cn --arg l "$langs" '$l | split(" ") | map(select(length > 0))')" + echo "languages=$json" >> "$GITHUB_OUTPUT" + analyze: + needs: detect + strategy: + fail-fast: false + matrix: + language: ${{ fromJSON(needs.detect.outputs.languages) }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: none + + - uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..52a13f9 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,9 @@ +name: Dependency Review +on: {pull_request:} +permissions: {contents: read, pull-requests: read} +jobs: + review: + runs-on: ubuntu-latest + steps: + - uses: actions/dependency-review-action@v4 + with: {fail-on-severity: high} diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..a05394f --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,18 @@ +name: Security +on: + push: + pull_request: + schedule: [{cron: '41 4 * * 1'}] +permissions: {contents: read, security-events: write} +jobs: + scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: {fetch-depth: 0} + - uses: gitleaks/gitleaks-action@v2 + - uses: aquasecurity/trivy-action@master + with: {scan-type: fs, scan-ref: ., format: sarif, output: trivy-results.sarif, ignore-unfixed: true, severity: 'HIGH,CRITICAL', exit-code: '1'} + - uses: github/codeql-action/upload-sarif@v3 + if: always() + with: {sarif_file: trivy-results.sarif}