Add universal dependency tracking workflow #3
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: Dependency Track SBOM Scan | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - master | |
| pull_request: | |
| branches: | |
| - main | |
| - master | |
| schedule: | |
| # Run daily at 9am UTC | |
| - cron: '0 9 * * *' | |
| workflow_dispatch: # Allow manual trigger | |
| jobs: | |
| dependency-scan: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js (if needed) | |
| if: hashFiles('**/package.json') != '' | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18' | |
| - name: Setup Python (if needed) | |
| if: hashFiles('**/requirements.txt', '**/pyproject.toml', '**/setup.py') != '' | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.x' | |
| - name: Setup Go (if needed) | |
| if: hashFiles('**/go.mod') != '' | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: 'stable' | |
| - name: Setup Java (if needed) | |
| if: hashFiles('**/pom.xml', '**/build.gradle', '**/build.gradle.kts') != '' | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '17' | |
| - name: Install dependencies (all languages, including monorepos) | |
| run: | | |
| echo "🔍 Detecting and installing dependencies..." | |
| # Install pnpm if any pnpm-lock.yaml exists (needed for mixed monorepos) | |
| if find . -name "pnpm-lock.yaml" -not -path "*/node_modules/*" | grep -q .; then | |
| echo "📦 Installing pnpm globally (detected pnpm projects)" | |
| npm install -g pnpm | |
| fi | |
| # Node.js: Check root first for workspace-level install | |
| if [ -f "pnpm-lock.yaml" ]; then | |
| echo "📦 Detected pnpm workspace at root" | |
| pnpm install --no-frozen-lockfile --ignore-scripts 2>/dev/null || echo "Note: pnpm install failed, continuing..." | |
| elif [ -f "yarn.lock" ]; then | |
| echo "📦 Detected yarn workspace at root" | |
| yarn install --ignore-scripts 2>/dev/null || echo "Note: yarn install failed, continuing..." | |
| elif [ -f "package-lock.json" ]; then | |
| echo "📦 Found npm project at root" | |
| npm ci 2>/dev/null || npm install 2>/dev/null || echo "Note: npm install failed, continuing..." | |
| elif [ -f "package.json" ]; then | |
| echo "📦 Found Node.js project at root (no lock file)" | |
| npm install --package-lock-only 2>/dev/null || echo "Note: npm install failed, continuing..." | |
| fi | |
| # Find all subdirectories and install based on their lock files (for mixed monorepos) | |
| find . -type d -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "." | while read -r dir; do | |
| if [ -f "$dir/pnpm-lock.yaml" ]; then | |
| echo "📦 Found pnpm project in: $dir" | |
| (cd "$dir" && pnpm install --no-frozen-lockfile --ignore-scripts 2>/dev/null || echo "Note: pnpm install failed in $dir") | |
| elif [ -f "$dir/yarn.lock" ]; then | |
| echo "📦 Found yarn project in: $dir" | |
| (cd "$dir" && yarn install --ignore-scripts 2>/dev/null || echo "Note: yarn install failed in $dir") | |
| elif [ -f "$dir/package-lock.json" ]; then | |
| echo "📦 Found npm project in: $dir" | |
| (cd "$dir" && (npm ci 2>/dev/null || npm install 2>/dev/null || echo "Note: npm install failed in $dir")) | |
| elif [ -f "$dir/package.json" ]; then | |
| echo "📦 Found Node.js project in: $dir (no lock file)" | |
| (cd "$dir" && npm install --package-lock-only 2>/dev/null || echo "Note: npm install failed in $dir") | |
| fi | |
| done | |
| # Python projects | |
| find . -name "requirements.txt" -not -path "*/.git/*" -not -path "*/venv/*" | while read -r req; do | |
| dir=$(dirname "$req") | |
| echo "🐍 Found Python project in: $dir" | |
| (cd "$dir" && pip install -r requirements.txt 2>/dev/null || echo "Note: pip install failed in $dir") | |
| done | |
| # Go projects | |
| find . -name "go.mod" -not -path "*/.git/*" | while read -r gomod; do | |
| dir=$(dirname "$gomod") | |
| echo "🔷 Found Go project in: $dir" | |
| (cd "$dir" && go mod download 2>/dev/null || echo "Note: go mod download failed in $dir") | |
| done | |
| # Java/Maven projects | |
| find . -name "pom.xml" -not -path "*/.git/*" | while read -r pom; do | |
| dir=$(dirname "$pom") | |
| echo "☕ Found Maven project in: $dir" | |
| (cd "$dir" && mvn dependency:resolve 2>/dev/null || echo "Note: mvn dependency:resolve failed in $dir") | |
| done | |
| # Java/Gradle projects | |
| find . -name "build.gradle*" -not -path "*/.git/*" | while read -r gradle; do | |
| dir=$(dirname "$gradle") | |
| echo "☕ Found Gradle project in: $dir" | |
| (cd "$dir" && (./gradlew dependencies 2>/dev/null || gradle dependencies 2>/dev/null || echo "Note: gradle dependencies failed in $dir")) | |
| done | |
| echo "✅ Dependency installation complete" | |
| continue-on-error: true | |
| - name: Install Trivy | |
| run: | | |
| curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin | |
| - name: Generate SBOM with Trivy | |
| run: | | |
| echo "🔍 Scanning repository for dependencies and licenses..." | |
| trivy fs --format cyclonedx --scanners license --license-full . -o bom-raw.json | |
| echo "🧹 Cleaning SBOM (removing lock files and false positives)..." | |
| cat bom-raw.json | jq ' | |
| .components |= map(select( | |
| .name != "package-lock.json" and | |
| .name != "yarn.lock" and | |
| .name != "pnpm-lock.yaml" and | |
| .name != "go.mod" and | |
| .name != "go.sum" and | |
| .name != "uv.lock" and | |
| .name != "poetry.lock" and | |
| .name != "Pipfile.lock" and | |
| .name != "Cargo.lock" and | |
| .name != "composer.lock" and | |
| (.name | test(".*lock\\.json$") | not) | |
| )) | |
| ' > bom.json | |
| echo "" | |
| echo "📊 SBOM Statistics:" | |
| cat bom.json | jq '{ | |
| totalComponents: (.components | length), | |
| componentsWithLicenses: ([.components[] | select(.licenses)] | length) | |
| }' | |
| - name: Upload BOM to Dependency-Track | |
| uses: DependencyTrack/gh-upload-sbom@v3 | |
| with: | |
| serverhostname: '44.194.0.211' | |
| port: '8091' | |
| protocol: 'http' | |
| apikey: ${{ secrets.DEPENDENCYTRACK_API_KEY }} | |
| projectname: ${{ github.event.repository.name }} | |
| projectversion: 'main' | |
| bomfilename: 'bom.json' | |
| autocreate: 'true' | |
| - name: Archive SBOM artifact | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: sbom-cyclonedx | |
| path: bom.json | |
| retention-days: 90 |