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-<sha>) - release-images: same images on v* tag push (tags: latest, <version>) - 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
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
.gitattributes text eol=lf
|
.gitattributes text eol=lf
|
||||||
*.sh text eol=lf
|
*.sh text eol=lf
|
||||||
|
.github/workflows/*.yml text eol=lf
|
||||||
deploy/docker/frontend/*.sh text eol=lf
|
deploy/docker/frontend/*.sh text eol=lf
|
||||||
frontend/src/*.js text eol=lf
|
frontend/src/*.js text eol=lf
|
||||||
frontend/src/**/*.js text eol=lf
|
frontend/src/**/*.js text eol=lf
|
||||||
|
|||||||
@@ -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 "-<branch>" 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
|
||||||
@@ -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
|
||||||
@@ -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 }}"
|
||||||
@@ -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
|
||||||
@@ -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-<short-sha>`.
|
||||||
|
|
||||||
|
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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user