diff --git a/.forgejo/workflows/security-scan.yml b/.forgejo/workflows/security-scan.yml new file mode 100644 index 00000000..7544d94a --- /dev/null +++ b/.forgejo/workflows/security-scan.yml @@ -0,0 +1,50 @@ +# Sovereign security scanning — no cloud dependencies +# Replaces: GitHub Dependabot, CodeQL, Advanced Security +# PCI DSS: Req 6.3.2 (code review), Req 11.3 (vulnerability scanning) + +name: Security Scan + +on: + push: + branches: [main, dev, 'feat/*'] + pull_request: + branches: [main] + +jobs: + govulncheck: + name: Go Vulnerability Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1.25' + - name: Install govulncheck + run: go install golang.org/x/vuln/cmd/govulncheck@latest + - name: Run govulncheck + run: govulncheck ./... + + gitleaks: + name: Secret Detection + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install gitleaks + run: | + GITLEAKS_VERSION=$(curl -s https://api.github.com/repos/gitleaks/gitleaks/releases/latest | jq -r '.tag_name' | tr -d 'v') + curl -sL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" | tar xz -C /usr/local/bin gitleaks + - name: Scan for secrets + run: gitleaks detect --source . --no-banner + + trivy: + name: Dependency & Config Scan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Trivy + run: | + curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin + - name: Filesystem scan + run: trivy fs --scanners vuln,secret,misconfig --severity HIGH,CRITICAL --exit-code 1 . diff --git a/.gitignore b/.gitignore index fdb55209..8c1cd7f5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,8 @@ bin/ dist/ tasks /core - +/i18n-validate +cmd/bugseti/bugseti patch_cov.* go.work.sum diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 00000000..893d7184 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,10 @@ +# Gitleaks configuration for host-uk/core +# Test fixtures contain private keys for cryptographic testing — not real secrets. + +[allowlist] + description = "Test fixture allowlist" + paths = [ + '''pkg/crypt/pgp/pgp_test\.go''', + '''pkg/crypt/rsa/rsa_test\.go''', + '''pkg/crypt/openpgp/test_util\.go''', + ] diff --git a/.woodpecker/bugseti.yml b/.woodpecker/bugseti.yml new file mode 100644 index 00000000..5e9387c9 --- /dev/null +++ b/.woodpecker/bugseti.yml @@ -0,0 +1,52 @@ +when: + - event: tag + ref: "refs/tags/bugseti-v*" + - event: push + branch: main + path: "cmd/bugseti/**" + +steps: + - name: frontend + image: node:22-bookworm + commands: + - cd cmd/bugseti/frontend + - npm ci --prefer-offline + - npm run build + + - name: build-linux + image: golang:1.25-bookworm + environment: + CGO_ENABLED: "1" + GOOS: linux + GOARCH: amd64 + commands: + - apt-get update -qq && apt-get install -y -qq libgtk-3-dev libwebkit2gtk-4.1-dev > /dev/null 2>&1 + - cd cmd/bugseti + - go build -tags production -trimpath -buildvcs=false -ldflags="-w -s" -o ../../bin/bugseti + depends_on: [frontend] + + - name: package + image: alpine:3.21 + commands: + - cd bin + - tar czf bugseti-linux-amd64.tar.gz bugseti + - sha256sum bugseti-linux-amd64.tar.gz > bugseti-linux-amd64.tar.gz.sha256 + - echo "=== Package ===" + - ls -lh bugseti-linux-amd64.* + - cat bugseti-linux-amd64.tar.gz.sha256 + depends_on: [build-linux] + + - name: release + image: plugins/gitea-release + settings: + api_key: + from_secret: forgejo_token + base_url: https://forge.lthn.ai + files: + - bin/bugseti-linux-amd64.tar.gz + - bin/bugseti-linux-amd64.tar.gz.sha256 + title: ${CI_COMMIT_TAG} + note: "BugSETI ${CI_COMMIT_TAG} — Linux amd64 build" + when: + - event: tag + depends_on: [package] diff --git a/.woodpecker/core.yml b/.woodpecker/core.yml new file mode 100644 index 00000000..7e1e7b29 --- /dev/null +++ b/.woodpecker/core.yml @@ -0,0 +1,21 @@ +when: + - event: [push, pull_request, manual] + +steps: + - name: build + image: golang:1.25-bookworm + commands: + - go version + - go mod download + - >- + go build + -ldflags "-X github.com/host-uk/core/pkg/cli.AppVersion=ci + -X github.com/host-uk/core/pkg/cli.BuildCommit=${CI_COMMIT_SHA:0:7} + -X github.com/host-uk/core/pkg/cli.BuildDate=$(date -u +%Y%m%d)" + -o ./bin/core . + - ./bin/core --version + + - name: test + image: golang:1.25-bookworm + commands: + - go test -short -count=1 -timeout 120s ./... diff --git a/Taskfile.yml b/Taskfile.yml index 1e267461..22403187 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,14 +1,33 @@ version: '3' vars: - VERSION: - sh: git describe --tags --exact-match 2>/dev/null || echo "dev" - # Base ldflags for version injection - LDFLAGS_BASE: "-X github.com/host-uk/core/pkg/cli.AppVersion={{.VERSION}}" + # SemVer 2.0.0 build variables + SEMVER_TAG: + sh: git describe --tags --abbrev=0 2>/dev/null || echo "0.0.0" + SEMVER_VERSION: + sh: echo "{{.SEMVER_TAG}}" | sed 's/^v//' + SEMVER_COMMITS: + sh: git rev-list {{.SEMVER_TAG}}..HEAD --count 2>/dev/null || echo "0" + SEMVER_COMMIT: + sh: git rev-parse --short HEAD 2>/dev/null || echo "unknown" + SEMVER_DATE: + sh: date -u +%Y%m%d + SEMVER_PRERELEASE: + sh: '[ "{{.SEMVER_COMMITS}}" = "0" ] && echo "" || echo "dev.{{.SEMVER_COMMITS}}"' + # ldflags + PKG: "github.com/host-uk/core/pkg/cli" + LDFLAGS_BASE: >- + -X {{.PKG}}.AppVersion={{.SEMVER_VERSION}} + -X {{.PKG}}.BuildCommit={{.SEMVER_COMMIT}} + -X {{.PKG}}.BuildDate={{.SEMVER_DATE}} + -X {{.PKG}}.BuildPreRelease={{.SEMVER_PRERELEASE}} # Development build: includes debug info LDFLAGS: "{{.LDFLAGS_BASE}}" # Release build: strips debug info and symbol table for smaller binary LDFLAGS_RELEASE: "-s -w {{.LDFLAGS_BASE}}" + # Compat alias + VERSION: + sh: git describe --tags --exact-match 2>/dev/null || echo "dev" tasks: # --- CLI Management --- @@ -140,6 +159,67 @@ tasks: cmds: - go run ./internal/tools/i18n-validate ./... + # --- Core IDE (Wails v3) --- + ide:dev: + desc: "Run Core IDE in Wails dev mode" + dir: cmd/core-ide + cmds: + - cd frontend && npm install && npm run build + - wails3 dev + + ide:build: + desc: "Build Core IDE production binary" + dir: cmd/core-ide + cmds: + - cd frontend && npm install && npm run build + - wails3 build + + ide:frontend: + desc: "Build Core IDE frontend only" + dir: cmd/core-ide/frontend + cmds: + - npm install + - npm run build + + # --- Core App (FrankenPHP + Wails v3) --- + app:setup: + desc: "Install PHP-ZTS build dependency for Core App" + cmds: + - brew tap shivammathur/php 2>/dev/null || true + - brew install shivammathur/php/php@8.4-zts + + app:composer: + desc: "Install Laravel dependencies for Core App" + dir: cmd/core-app/laravel + cmds: + - composer install --no-dev --optimize-autoloader --no-interaction + + app:build: + desc: "Build Core App (FrankenPHP + Laravel desktop binary)" + dir: cmd/core-app + env: + CGO_ENABLED: "1" + CGO_CFLAGS: + sh: /opt/homebrew/opt/php@8.4-zts/bin/php-config --includes + CGO_LDFLAGS: + sh: "echo -L/opt/homebrew/opt/php@8.4-zts/lib $(/opt/homebrew/opt/php@8.4-zts/bin/php-config --ldflags) $(/opt/homebrew/opt/php@8.4-zts/bin/php-config --libs)" + cmds: + - go build -tags nowatcher -o ../../bin/core-app . + + app:dev: + desc: "Build and run Core App" + dir: cmd/core-app + env: + CGO_ENABLED: "1" + CGO_CFLAGS: + sh: /opt/homebrew/opt/php@8.4-zts/bin/php-config --includes + CGO_LDFLAGS: + sh: "echo -L/opt/homebrew/opt/php@8.4-zts/lib $(/opt/homebrew/opt/php@8.4-zts/bin/php-config --ldflags) $(/opt/homebrew/opt/php@8.4-zts/bin/php-config --libs)" + DYLD_LIBRARY_PATH: "/opt/homebrew/opt/php@8.4-zts/lib" + cmds: + - go build -tags nowatcher -o ../../bin/core-app . + - ../../bin/core-app + # --- Multi-repo (when in workspace) --- dev:health: desc: "Check health of all repos" diff --git a/cmd/bugseti/frontend/src/app/app.routes.ts b/cmd/bugseti/frontend/src/app/app.routes.ts index 8367d07a..76725edb 100644 --- a/cmd/bugseti/frontend/src/app/app.routes.ts +++ b/cmd/bugseti/frontend/src/app/app.routes.ts @@ -21,5 +21,9 @@ export const routes: Routes = [ { path: 'onboarding', loadComponent: () => import('./onboarding/onboarding.component').then(m => m.OnboardingComponent) + }, + { + path: 'jellyfin', + loadComponent: () => import('./jellyfin/jellyfin.component').then(m => m.JellyfinComponent) } ]; diff --git a/cmd/bugseti/frontend/src/app/jellyfin/jellyfin.component.ts b/cmd/bugseti/frontend/src/app/jellyfin/jellyfin.component.ts new file mode 100644 index 00000000..0f7c8382 --- /dev/null +++ b/cmd/bugseti/frontend/src/app/jellyfin/jellyfin.component.ts @@ -0,0 +1,187 @@ +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; + +type Mode = 'web' | 'stream'; + +@Component({ + selector: 'app-jellyfin', + standalone: true, + imports: [CommonModule, FormsModule], + template: ` +
Quick embed for media.lthn.ai or any Jellyfin host.
+Set Item ID and API key to build stream URL.
+