diff --git a/.github/workflows/bandit.yml b/.github/workflows/bandit.yml new file mode 100644 index 00000000..c8579fa7 --- /dev/null +++ b/.github/workflows/bandit.yml @@ -0,0 +1,29 @@ +name: bandit + +on: + push: + branches: + - develop + - main + pull_request: + branches: + - develop + - main + +permissions: + contents: read + +jobs: + bandit-scan: + name: Bandit Security Scan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 + with: + version: "0.8.6" + - name: Sync Python dependencies + run: uv sync --project services/analysis-engine --group dev --frozen + - name: Run Bandit + working-directory: services/analysis-engine + run: uv run bandit -c pyproject.toml -r src diff --git a/.github/workflows/build-baseline.yml b/.github/workflows/build-baseline.yml index fd6be89f..4c63e572 100644 --- a/.github/workflows/build-baseline.yml +++ b/.github/workflows/build-baseline.yml @@ -11,9 +11,6 @@ on: - main tags: - "v*" - release: - types: - - published permissions: contents: read @@ -87,17 +84,14 @@ jobs: - name: Build frontend run: npm run build --workspace @bandscope/desktop - name: Build native shell - run: cargo +stable build --manifest-path apps/desktop/src-tauri/Cargo.toml --release --locked --target $env:BANDSCOPE_TARGET_TRIPLE + run: npm exec --workspace @bandscope/desktop -- tauri build --target $env:BANDSCOPE_TARGET_TRIPLE --bundles nsis - name: Package Windows amd64 artifact run: python scripts/release/package_desktop_artifact.py - name: Upload Windows amd64 artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: bandscope-windows-amd64-${{ github.sha }} - path: | - artifacts/*.zip - artifacts/*.sha256 - artifacts/*.manifest.txt + path: artifacts/* build-windows-arm64: name: build / windows / arm64 @@ -168,17 +162,14 @@ jobs: - name: Build frontend run: npm run build --workspace @bandscope/desktop - name: Build native shell - run: cargo +stable build --manifest-path apps/desktop/src-tauri/Cargo.toml --release --locked --target $env:BANDSCOPE_TARGET_TRIPLE + run: npm exec --workspace @bandscope/desktop -- tauri build --target $env:BANDSCOPE_TARGET_TRIPLE --bundles nsis - name: Package Windows arm64 artifact run: python scripts/release/package_desktop_artifact.py - name: Upload Windows arm64 artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: bandscope-windows-arm64-${{ github.sha }} - path: | - artifacts/*.zip - artifacts/*.sha256 - artifacts/*.manifest.txt + path: artifacts/* gate-windows: name: gate / build / windows @@ -226,17 +217,14 @@ jobs: - name: Build frontend run: npm run build --workspace @bandscope/desktop - name: Build native shell - run: cargo +stable build --manifest-path apps/desktop/src-tauri/Cargo.toml --release --locked --target "$BANDSCOPE_TARGET_TRIPLE" + run: npm exec --workspace @bandscope/desktop -- tauri build --target "$BANDSCOPE_TARGET_TRIPLE" --bundles dmg - name: Package macOS amd64 artifact run: python3 scripts/release/package_desktop_artifact.py - name: Upload macOS amd64 artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: bandscope-macos-amd64-${{ github.sha }} - path: | - artifacts/*.zip - artifacts/*.sha256 - artifacts/*.manifest.txt + path: artifacts/* build-macos-arm64: name: build / macos / arm64 @@ -274,17 +262,14 @@ jobs: - name: Build frontend run: npm run build --workspace @bandscope/desktop - name: Build native shell - run: cargo +stable build --manifest-path apps/desktop/src-tauri/Cargo.toml --release --locked --target "$BANDSCOPE_TARGET_TRIPLE" + run: npm exec --workspace @bandscope/desktop -- tauri build --target "$BANDSCOPE_TARGET_TRIPLE" --bundles dmg - name: Package macOS arm64 artifact run: python3 scripts/release/package_desktop_artifact.py - name: Upload macOS arm64 artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: bandscope-macos-arm64-${{ github.sha }} - path: | - artifacts/*.zip - artifacts/*.sha256 - artifacts/*.manifest.txt + path: artifacts/* gate-macos: name: gate / build / macos @@ -296,44 +281,58 @@ jobs: - name: Confirm both macOS architectures built run: true - attach-windows-release-artifact: - name: release-artifact / windows - if: github.event_name == 'release' + publish-immutable-release: + name: release-artifact / publish + if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest needs: - - build-windows-native - - build-windows-arm64 + - gate-windows + - gate-macos permissions: contents: write steps: - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - pattern: bandscope-windows-*-${{ github.sha }} - path: artifacts - merge-multiple: true - - name: Attach Windows artifacts to release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - RELEASE_TAG: ${{ github.event.release.tag_name }} - run: gh release upload "$RELEASE_TAG" artifacts/*.zip artifacts/*.sha256 artifacts/*.manifest.txt --clobber - - attach-macos-release-artifact: - name: release-artifact / macos - if: github.event_name == 'release' - runs-on: ubuntu-latest - needs: - - build-macos-native - - build-macos-arm64 - permissions: - contents: write - steps: + persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - pattern: bandscope-macos-*-${{ github.sha }} + pattern: bandscope-*-${{ github.sha }} path: artifacts merge-multiple: true - - name: Attach macOS artifacts to release + - name: Generate release CycloneDX SBOM + uses: anchore/sbom-action@57aae528053a48a3f6235f2d9461b05fbcb7366d # v0.23.1 + with: + path: . + format: cyclonedx-json + output-file: bandscope-sbom.cdx.json + upload-artifact: false + upload-release-assets: false + - name: Upload release SBOM artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: bandscope-release-sbom-${{ github.sha }} + path: | + bandscope-sbom.cdx.json + supply-chain/supplemental-component-inventory.json + - name: Validate release asset set + run: python3 scripts/release/select_release_assets.py --output release-assets.txt + - name: Create draft release with complete assets, then publish env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - RELEASE_TAG: ${{ github.event.release.tag_name }} - run: gh release upload "$RELEASE_TAG" artifacts/*.zip artifacts/*.sha256 artifacts/*.manifest.txt --clobber + RELEASE_TAG: ${{ github.ref_name }} + run: | + set -euo pipefail + if gh release view "$RELEASE_TAG" --repo "${{ github.repository }}" >/dev/null 2>&1; then + echo "Release $RELEASE_TAG already exists; immutable release assets must be attached before publication." + exit 1 + fi + mapfile -t release_assets < release-assets.txt + (( ${#release_assets[@]} > 0 )) + gh release create "$RELEASE_TAG" \ + "${release_assets[@]}" \ + --draft \ + --generate-notes \ + --title "BandScope ${RELEASE_TAG#v}" \ + --verify-tag \ + --repo "${{ github.repository }}" + gh release edit "$RELEASE_TAG" --draft=false --repo "${{ github.repository }}" diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 5f8e2c64..267a96fb 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -22,15 +22,21 @@ jobs: with: persist-credentials: false - uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) with: results_file: results.sarif results_format: sarif - publish_results: ${{ github.ref == 'refs/heads/develop' }} + publish_results: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) with: name: ossf-scorecard-results path: results.sarif retention-days: 5 - uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 + if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) with: sarif_file: results.sarif + - name: Skip OSSF Scorecard on non-default branch + if: github.ref != format('refs/heads/{0}', github.event.repository.default_branch) + run: echo "OSSF Scorecard only supports the default branch; skipped for ${GITHUB_REF}." diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index 7b90f577..3de92100 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -58,29 +58,3 @@ jobs: with: name: bandscope-supply-chain-inventory path: supply-chain/supplemental-component-inventory.json - - release-sbom: - name: attach-sbom-to-release - if: github.event_name == 'release' - runs-on: ubuntu-latest - needs: - - sbom - permissions: - contents: write - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: bandscope-sbom - - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: bandscope-supply-chain-inventory - path: supply-chain - - - name: Attach SBOM to GitHub Release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - RELEASE_TAG: ${{ github.event.release.tag_name }} - run: gh release upload "$RELEASE_TAG" bandscope-sbom.cdx.json supply-chain/supplemental-component-inventory.json --clobber diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e42c1c2..071a641d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,45 @@ # Changelog +## [Unreleased] + +## [0.1.3] - 2026-04-29 + +### Fixed + +- Published release assets through a tag-driven draft release flow so immutable GitHub Releases include desktop installers, checksums, SBOM, and supplemental inventory before publication. +- Added a supply-chain regression guard that rejects post-publication release asset uploads. + +## [0.1.2] - 2026-04-29 + +### Changed + +- Aligned the packaged desktop app version with the release package metadata. + +### Fixed + +- Stabilized YouTube import fallback behavior in browser and desktop dev paths. +- Guarded OSSF Scorecard execution so release-branch pushes skip unsupported non-default branch runs cleanly. + +## [0.1.1] - 2026-04-28 + +### Added + +- Implemented rehearsal workspace design (Issue #107) +- Add capo and tuning detection heuristics (Issue #103) +- Add bandit security scan workflow + +### Fixed + +- Upgrade pytest to 9.0.3 to fix GHSA-6w46-j5rx-g56g +- Resolve npm audit vulnerabilities +- Fix ruff import sorting and formatting errors +- Add missing docstrings to tests +- Fix test configuration and typing issues + ## [0.1.0] - 2026-03-27 ### Added + - Issue #29: Defined core `song -> section -> role` rehearsal domain contracts - Issue #38: Added cross-architecture build support (Windows/macOS arm64+amd64) - Issue #40: Enforced 100% Python docstring and test coverage diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..b1e80bb2 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.3 diff --git a/apps/desktop/components.json b/apps/desktop/components.json new file mode 100644 index 00000000..15addee8 --- /dev/null +++ b/apps/desktop/components.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "base-nova", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "menuColor": "default", + "menuAccent": "subtle", + "registries": {} +} diff --git a/apps/desktop/index.html b/apps/desktop/index.html index 8a5b0e35..647c7df1 100644 --- a/apps/desktop/index.html +++ b/apps/desktop/index.html @@ -3,10 +3,11 @@
+