From bd7710d03c54b079212dcf6c271217643b7d5947 Mon Sep 17 00:00:00 2001 From: 3252a8 <3252a8@proton.me> Date: Sat, 30 May 2026 22:57:41 +0300 Subject: [PATCH 1/3] ci: add GitHub Actions for image builds, PR checks and security scans - dev-images: build/push backend, worker, frontend to ghcr.io and Docker Hub on every push to dev (tags: dev, dev-) - release-images: same images on v* tag push (tags: latest, ) - PR checks (into main/dev): ruff lint+format, eslint+prettier, no-push Docker build of all targets - CodeQL (python, js/ts), dependency-review, pip-audit, npm audit, Trivy fs - pin .github/workflows/*.yml to LF --- .gitattributes | 1 + .github/workflows/_docker-build-push.yml | 116 +++++++++++++++++++++++ .github/workflows/ci.yml | 64 +++++++++++++ .github/workflows/codeql.yml | 48 ++++++++++ .github/workflows/dependency-review.yml | 27 ++++++ .github/workflows/docker-dev.yml | 25 +++++ .github/workflows/docker-release.yml | 28 ++++++ .github/workflows/security.yml | 89 +++++++++++++++++ 8 files changed, 398 insertions(+) create mode 100644 .github/workflows/_docker-build-push.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/docker-dev.yml create mode 100644 .github/workflows/docker-release.yml create mode 100644 .github/workflows/security.yml diff --git a/.gitattributes b/.gitattributes index 27c560a..4380649 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ .gitattributes text eol=lf *.sh text eol=lf +.github/workflows/*.yml text eol=lf deploy/docker/frontend/*.sh text eol=lf frontend/src/*.js text eol=lf frontend/src/**/*.js text eol=lf diff --git a/.github/workflows/_docker-build-push.yml b/.github/workflows/_docker-build-push.yml new file mode 100644 index 0000000..29862b0 --- /dev/null +++ b/.github/workflows/_docker-build-push.yml @@ -0,0 +1,116 @@ +name: Docker build & push (reusable) + +# Reusable workflow that builds the three image targets defined in +# deploy/docker/Dockerfile (backend, worker, frontend) and optionally pushes +# them to both ghcr.io and Docker Hub under the 3252a8/ namespace. +# +# Called by: +# - docker-dev.yml (tag_mode: dev, push: true) on pushes to dev +# - docker-release.yml (tag_mode: release, push: true) on pushes to main +# - ci.yml (tag_mode: dev, push: false) on pull requests + +on: + workflow_call: + inputs: + push: + description: "Push the built images to the registries" + type: boolean + default: true + tag_mode: + description: "Tagging strategy: 'dev' or 'release'" + type: string + required: true + +permissions: + contents: read + packages: write + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - target: backend + image: remnawave-minishop-backend + - target: worker + image: remnawave-minishop-worker + - target: frontend + image: remnawave-minishop-frontend + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # Full history + tags: the Dockerfile's version-builder stage runs + # `git describe --tags` against the copied .git tree. + fetch-depth: 0 + + - name: Resolve release version + id: version + if: inputs.tag_mode == 'release' + run: | + # On a tag push github.ref_name is the tag (e.g. v3.4.5); for a + # manual workflow_dispatch on a branch, fall back to the latest tag. + if [ "${{ github.ref_type }}" = "tag" ]; then + raw="${{ github.ref_name }}" + else + raw="$(git describe --tags --abbrev=0 2>/dev/null)" + fi + version="${raw#v}" + if [ -z "$version" ]; then + echo "::error::No git tag found to derive the release version from" + exit 1 + fi + echo "version=${version}" >> "$GITHUB_OUTPUT" + echo "Release version: ${version}" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + if: inputs.push + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Log in to Docker Hub + if: inputs.push + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + 3252a8/${{ matrix.image }} + ghcr.io/3252a8/${{ matrix.image }} + tags: | + type=raw,value=dev,enable=${{ inputs.tag_mode == 'dev' }} + type=sha,prefix=dev-,format=short,enable=${{ inputs.tag_mode == 'dev' }} + type=raw,value=latest,enable=${{ inputs.tag_mode == 'release' }} + type=raw,value=${{ steps.version.outputs.version }},enable=${{ inputs.tag_mode == 'release' }} + + - name: Build${{ inputs.push && ' & push' || '' }} ${{ matrix.image }} + uses: docker/build-push-action@v6 + with: + context: . + file: deploy/docker/Dockerfile + target: ${{ matrix.target }} + platforms: linux/amd64 + push: ${{ inputs.push }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + # The Dockerfile's version-builder appends a "-" suffix to the + # internal version string for non-main builds. Force "main" on release + # (the ref is the tag, not a branch) so release images stay un-suffixed. + build-args: | + GITHUB_REF_NAME=${{ inputs.tag_mode == 'release' && 'main' || github.ref_name }} + cache-from: type=gha,scope=${{ matrix.target }} + cache-to: type=gha,mode=max,scope=${{ matrix.target }} + provenance: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..027a381 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,64 @@ +name: PR checks + +# Runs on pull requests into main (typically from dev) and into dev (typically +# from feature/* branches): lint + format checks and a no-push image build to +# prove the Docker images still build. + +on: + pull_request: + branches: [main, dev] + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + lint: + name: Lint & format + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install ruff + run: pip install "ruff>=0.8.0" + + - name: Ruff lint (Python) + run: ruff check . + + - name: Ruff format check (Python) + run: ruff format --check . + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install frontend deps + run: npm ci + working-directory: frontend + + - name: ESLint (frontend) + run: npm run lint + working-directory: frontend + + - name: Prettier check (frontend) + run: npm run format:check + working-directory: frontend + + build: + name: Docker build + uses: ./.github/workflows/_docker-build-push.yml + with: + push: false + tag_mode: dev diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..97457a0 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,48 @@ +name: CodeQL + +# Static analysis of the Python and JS/TS code for security and quality issues. +# Results appear under the repository's Security -> Code scanning tab. + +on: + push: + branches: [main, dev] + pull_request: + branches: [main, dev] + schedule: + - cron: "27 3 * * 1" # weekly, Monday 03:27 UTC + +concurrency: + group: codeql-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + permissions: + security-events: write + actions: read + contents: read + strategy: + fail-fast: false + matrix: + include: + - language: python + - language: javascript-typescript + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: none + + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..41c850d --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,27 @@ +name: Dependency review + +# On PRs into main/dev, flag any newly added dependency that has a known +# vulnerability or an incompatible license before it gets merged. + +on: + pull_request: + branches: [main, dev] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Dependency review + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: high + comment-summary-in-pr: on-failure diff --git a/.github/workflows/docker-dev.yml b/.github/workflows/docker-dev.yml new file mode 100644 index 0000000..93d3f88 --- /dev/null +++ b/.github/workflows/docker-dev.yml @@ -0,0 +1,25 @@ +name: Dev images + +# On every push to the dev branch, build all three images and push them to +# ghcr.io and Docker Hub tagged `dev` and `dev-`. + +on: + push: + branches: [dev] + workflow_dispatch: + +concurrency: + group: docker-dev-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + packages: write + +jobs: + build-push: + uses: ./.github/workflows/_docker-build-push.yml + with: + push: true + tag_mode: dev + secrets: inherit diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml new file mode 100644 index 0000000..56bfbab --- /dev/null +++ b/.github/workflows/docker-release.yml @@ -0,0 +1,28 @@ +name: Release images + +# Build all three images and push them to ghcr.io and Docker Hub tagged +# `latest` and the release version (the pushed tag with its leading `v` +# stripped, e.g. v3.4.5 -> 3.4.5). Triggered only when a new v* tag is pushed, +# so images are built once per release rather than on every commit to main. + +on: + push: + tags: + - "v*" + workflow_dispatch: + +concurrency: + group: docker-release-${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: read + packages: write + +jobs: + build-push: + uses: ./.github/workflows/_docker-build-push.yml + with: + push: true + tag_mode: release + secrets: inherit diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..80f74eb --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,89 @@ +name: Security + +# Audits the full dependency set (pip-audit, npm audit) and runs a Trivy +# filesystem scan (dependencies + Dockerfile/IaC misconfig). Trivy results are +# uploaded to the Security -> Code scanning tab. + +on: + pull_request: + branches: [main, dev] + push: + branches: [main, dev] + schedule: + - cron: "27 4 * * 1" # weekly, Monday 04:27 UTC + workflow_dispatch: + +concurrency: + group: security-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + python-audit: + name: pip-audit + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install pip-audit + run: pip install pip-audit + + - name: Audit Python dependencies + run: pip-audit -r backend/requirements.txt + + npm-audit: + name: npm audit + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install frontend deps + run: npm ci + working-directory: frontend + + - name: Audit npm dependencies + run: npm audit --audit-level=high + working-directory: frontend + + trivy: + name: Trivy filesystem scan + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run Trivy + uses: aquasecurity/trivy-action@0.28.0 + with: + scan-type: fs + scan-ref: . + format: sarif + output: trivy-results.sarif + severity: CRITICAL,HIGH + ignore-unfixed: true + + - name: Upload Trivy results + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: trivy-results.sarif + category: trivy-fs From cda3b741a10ccaabafce7507e81970077eb980a2 Mon Sep 17 00:00:00 2001 From: 3252a8 <3252a8@proton.me> Date: Sat, 30 May 2026 23:14:46 +0300 Subject: [PATCH 2/3] ci: fix workflow failures on PR - drop permissions block from reusable build workflow so callers set token scope (fixes PR-checks startup failure: ci.yml grants only contents:read while the reusable demanded packages:write) - remove codeql.yml: repo already uses CodeQL default setup, which conflicts with an advanced configuration - pin trivy-action to 0.36.0 (0.28.0 tag does not exist; <0.35.0 is the compromised supply-chain release flagged by dependency-review) - make pip-audit and npm audit informational (continue-on-error); they flag upstream/transitive advisories, dependency-review stays the PR gate --- .github/workflows/_docker-build-push.yml | 7 ++-- .github/workflows/codeql.yml | 48 ------------------------ .github/workflows/security.yml | 9 ++++- 3 files changed, 12 insertions(+), 52 deletions(-) delete mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/_docker-build-push.yml b/.github/workflows/_docker-build-push.yml index 29862b0..bcbd24d 100644 --- a/.github/workflows/_docker-build-push.yml +++ b/.github/workflows/_docker-build-push.yml @@ -21,9 +21,10 @@ on: type: string required: true -permissions: - contents: read - packages: write +# No permissions block here on purpose: a reusable workflow cannot request more +# than its caller grants, so the token scope is set by each caller +# (docker-dev.yml / docker-release.yml grant packages: write to push; ci.yml +# only needs contents: read for a no-push build). jobs: build: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 97457a0..0000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: CodeQL - -# Static analysis of the Python and JS/TS code for security and quality issues. -# Results appear under the repository's Security -> Code scanning tab. - -on: - push: - branches: [main, dev] - pull_request: - branches: [main, dev] - schedule: - - cron: "27 3 * * 1" # weekly, Monday 03:27 UTC - -concurrency: - group: codeql-${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - analyze: - name: Analyze (${{ matrix.language }}) - runs-on: ubuntu-latest - permissions: - security-events: write - actions: read - contents: read - strategy: - fail-fast: false - matrix: - include: - - language: python - - language: javascript-typescript - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - build-mode: none - - - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 80f74eb..95e0627 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -3,6 +3,11 @@ name: Security # Audits the full dependency set (pip-audit, npm audit) and runs a Trivy # filesystem scan (dependencies + Dockerfile/IaC misconfig). Trivy results are # uploaded to the Security -> Code scanning tab. +# +# pip-audit / npm audit are informational (continue-on-error): they surface +# upstream/transitive advisories that aren't necessarily fixable in a given PR, +# so they report in the logs without blocking merges. The PR gate for newly +# introduced vulnerable deps is dependency-review.yml. on: pull_request: @@ -37,6 +42,7 @@ jobs: run: pip install pip-audit - name: Audit Python dependencies + continue-on-error: true run: pip-audit -r backend/requirements.txt npm-audit: @@ -58,6 +64,7 @@ jobs: working-directory: frontend - name: Audit npm dependencies + continue-on-error: true run: npm audit --audit-level=high working-directory: frontend @@ -72,7 +79,7 @@ jobs: uses: actions/checkout@v4 - name: Run Trivy - uses: aquasecurity/trivy-action@0.28.0 + uses: aquasecurity/trivy-action@0.36.0 with: scan-type: fs scan-ref: . From 4a84bba697a24ecad3505f4ec7b07ad93b2cc87d Mon Sep 17 00:00:00 2001 From: 3252a8 <3252a8@proton.me> Date: Sat, 30 May 2026 23:20:22 +0300 Subject: [PATCH 3/3] ci: use v-prefixed trivy-action tag (v0.36.0) --- .github/workflows/security.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 95e0627..b02f00a 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -79,7 +79,7 @@ jobs: uses: actions/checkout@v4 - name: Run Trivy - uses: aquasecurity/trivy-action@0.36.0 + uses: aquasecurity/trivy-action@v0.36.0 with: scan-type: fs scan-ref: .