diff --git a/.github/tag-changelog-config.js b/.github/tag-changelog-config.js new file mode 100644 index 0000000..6d9b511 --- /dev/null +++ b/.github/tag-changelog-config.js @@ -0,0 +1,36 @@ +module.exports = { + types: [ + { types: ['feat', 'feature', 'Feat'], label: '๐ŸŽ‰ New Features' }, + { types: ['fix', 'bugfix', 'Fix'], label: '๐Ÿ› Bugfixes' }, + { types: ['improvements', 'enhancement'], label: '๐Ÿ”จ Improvements' }, + { types: ['perf'], label: '๐ŸŽ๏ธ Performance Improvements' }, + { types: ['build', 'ci'], label: '๐Ÿ—๏ธ Build System' }, + { types: ['refactor'], label: '๐Ÿชš Refactors' }, + { types: ['doc', 'docs'], label: '๐Ÿ“š Documentation Changes' }, + { types: ['config'], label: '๐Ÿช› Configuration Changes' }, + { types: ['test', 'tests'], label: '๐Ÿ” Tests' }, + { types: ['style', 'codestyle', 'lint'], label: '๐Ÿ’… Code Style Changes' }, + { types: ['chore', 'Chore', 'deps', 'Deps'], label: '๐Ÿงน Chores' }, + { types: ['other', 'Other'], label: 'Other Changes' }, + ], + + excludeTypes: [], + + renderTypeSection: function(label, commits) { + let text = `\n## ${label}\n\n` + + commits.forEach(commit => { + const scope = commit.scope ? `**${commit.scope}:** ` : '' + text += `- ${scope}${commit.subject}\n` + }) + + return text + }, + + renderChangelog: function(release, changes) { + const now = new Date() + const d = now.toISOString().substring(0, 10) + const header = `# ${release} - ${d}\n` + return header + changes + '\n\n' + }, +} diff --git a/.github/workflows/new-release.yml b/.github/workflows/new-release.yml new file mode 100644 index 0000000..f842456 --- /dev/null +++ b/.github/workflows/new-release.yml @@ -0,0 +1,46 @@ +--- +name: Release Daily State + +on: + workflow_dispatch: + schedule: + - cron: '0 21 * * *' # 00:00 at Europe/Helsinki + +permissions: read-all + +jobs: + new-daily-release: + runs-on: ubuntu-latest + + permissions: + contents: write + + outputs: + created: ${{ steps.daily-version.outputs.created }} + version: ${{ steps.daily-version.outputs.version }} + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - name: Create tag if necessary + uses: fregante/daily-version-action@fb1a60b7c4daf1410cd755e360ebec3901e58588 # v2 + id: daily-version + + - name: Create changelog text + if: steps.daily-version.outputs.created + id: changelog + uses: loopwerk/tag-changelog@941366edb8920e2071eae0449031830984b9f26e # v1.3.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + config_file: .github/tag-changelog-config.js + + - name: Create release + if: steps.daily-version.outputs.created + uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1.16.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag: ${{ steps.daily-version.outputs.version }} + name: Release ${{ steps.daily-version.outputs.version }} + body: ${{ steps.changelog.outputs.changes }} + allowUpdates: true diff --git a/.github/workflows/security-suite.yml b/.github/workflows/security-suite.yml index 7554405..3653d66 100644 --- a/.github/workflows/security-suite.yml +++ b/.github/workflows/security-suite.yml @@ -28,25 +28,18 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -env: - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - jobs: - security-checks: - name: Security Checks + check-secrets: + name: Check Required Secrets runs-on: ubuntu-latest - timeout-minutes: 30 - - permissions: - security-events: write - pull-requests: write - statuses: write - issues: write - id-token: write + outputs: + run_snyk: ${{ steps.check.outputs.run_snyk }} + run_slack: ${{ steps.check.outputs.run_slack }} + run_sonarcloud: ${{ steps.check.outputs.run_sonarcloud }} steps: - name: Check Required Secrets - id: check-secrets + id: check shell: bash run: | { @@ -55,7 +48,6 @@ jobs: echo "run_sonarcloud=false" } >> "$GITHUB_OUTPUT" - # Check secrets if [ -n "${{ secrets.SNYK_TOKEN }}" ]; then echo "run_snyk=true" >> "$GITHUB_OUTPUT" else @@ -74,11 +66,15 @@ jobs: echo "::warning::SONAR_TOKEN not set - SonarCloud analysis will be skipped" fi - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 + owasp: + name: OWASP Dependency Check + runs-on: ubuntu-latest + needs: check-secrets + permissions: + security-events: write - # OWASP Dependency Check + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Run OWASP Dependency Check uses: dependency-check/Dependency-Check_Action@3102a65fd5f36d0000297576acc56a475b0de98d # main with: @@ -90,53 +86,92 @@ jobs: --enableRetired --enableExperimental --failOnCVSS 7 - - name: Upload OWASP Results uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9 with: sarif_file: reports/dependency-check-report.sarif category: owasp-dependency-check + - name: Upload artifact + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: owasp-results + path: reports/dependency-check-report.sarif - # Snyk Analysis - - name: Setup Node.js - if: steps.check-secrets.outputs.run_snyk == 'true' - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + snyk: + name: Snyk Security Scan + runs-on: ubuntu-latest + needs: check-secrets + if: needs.check-secrets.outputs.run_snyk == 'true' + permissions: + security-events: write + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 with: node-version: 'lts/*' cache: 'npm' - - name: Run Snyk Scan - id: snyk - if: steps.check-secrets.outputs.run_snyk == 'true' uses: snyk/actions/node@cdb760004ba9ea4d525f2e043745dfe85bb9077e # master continue-on-error: true env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: args: --all-projects --sarif-file-output=snyk-results.sarif - - name: Upload Snyk Results - if: steps.check-secrets.outputs.run_snyk == 'true' uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9 with: sarif_file: snyk-results.sarif category: snyk + - name: Upload artifact + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: snyk-results + path: snyk-results.sarif - # OSSF Scorecard + scorecard: + name: OSSF Scorecard + runs-on: ubuntu-latest + needs: check-secrets + permissions: + security-events: write + id-token: write + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Run Scorecard uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 with: results_file: scorecard-results.sarif results_format: sarif publish_results: true - - name: Upload Scorecard Results uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9 with: sarif_file: scorecard-results.sarif category: scorecard + - name: Upload artifact + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: scorecard-results + path: scorecard-results.sarif + + analyze: + name: Analyze Results + runs-on: ubuntu-latest + needs: [check-secrets, owasp, scorecard, snyk] + if: always() + permissions: + issues: write + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Download scan results + uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.0 + with: + path: ./results - # Analysis and Metrics - name: Analyze Results id: analysis uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 @@ -194,16 +229,16 @@ jobs: // Analyze all SARIF files metrics.tools = { - owasp: analyzeSarif('reports/dependency-check-report.sarif', 'OWASP'), - snyk: ${{ steps.check-secrets.outputs.run_snyk == 'true' }} ? - analyzeSarif('snyk-results.sarif', 'Snyk') : null, - scorecard: analyzeSarif('scorecard-results.sarif', 'Scorecard') + owasp: analyzeSarif('./results/owasp-results/dependency-check-report.sarif', 'OWASP'), + snyk: ${{ needs.check-secrets.outputs.run_snyk == 'true' }} ? + analyzeSarif('./results/snyk-results/snyk-results.sarif', 'Snyk') : null, + scorecard: analyzeSarif('./results/scorecard-results/scorecard-results.sarif', 'Scorecard') }; - // Save results for other steps + // Save results fs.writeFileSync('security-results.json', JSON.stringify(metrics, null, 2)); - // Set outputs for other steps + // Set outputs core.setOutput('total_critical', metrics.vulnerabilities.critical); core.setOutput('total_high', metrics.vulnerabilities.high); @@ -313,7 +348,7 @@ jobs: retention-days: 30 - name: Notify on Failure - if: failure() && steps.check-secrets.outputs.run_slack == 'true' + if: failure() && needs.check-secrets.outputs.run_slack == 'true' run: | curl -X POST -H 'Content-type: application/json' \ --data '{"text":"โŒ Security checks failed! Check the logs for details."}' \