Skip to content

harden(portal): Pi-hole-Portal footgun-Review-Befunde (v5-Isolation +… #444

harden(portal): Pi-hole-Portal footgun-Review-Befunde (v5-Isolation +…

harden(portal): Pi-hole-Portal footgun-Review-Befunde (v5-Isolation +… #444

Workflow file for this run

name: Build & Release
on:
push:
branches: [master]
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
env:
REGISTRY: ghcr.io
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: write
packages: write
jobs:
security-gate:
name: Security Gate
runs-on: ubuntu-latest
if: "!startsWith(github.event.head_commit.message, 'chore: bump version')"
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 20
registry-url: https://npm.pkg.github.com/
scope: '@callmetechie'
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.GH_PACKAGES_TOKEN }}
- name: npm audit (high/critical)
run: |
CRITICAL=$(npm audit --json 2>/dev/null | node -e "try{const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));console.log((d.metadata?.vulnerabilities?.critical||0))}catch{console.log(0)}")
if [ "$CRITICAL" -gt 0 ]; then echo "::error::Critical vulnerabilities found"; exit 1; fi
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 20
registry-url: https://npm.pkg.github.com/
scope: '@callmetechie'
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.GH_PACKAGES_TOKEN }}
- run: npm test
env:
NODE_ENV: test
release:
needs: [test, security-gate]
runs-on: ubuntu-latest
# Skip version-bump commits to prevent infinite loop
if: "!startsWith(github.event.head_commit.message, 'chore: bump version')"
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-node@v6
with:
node-version: 20
- name: Determine version bump
id: bump
env:
COMMIT_MSG: ${{ github.event.head_commit.message }}
run: |
CURRENT=$(node -p "require('./package.json').version")
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
FIRST_LINE=$(echo "$COMMIT_MSG" | head -1)
if echo "$FIRST_LINE" | grep -qE "^feat(\(.+\))?:"; then
MINOR=$((MINOR + 1))
PATCH=0
BUMP="minor"
else
PATCH=$((PATCH + 1))
BUMP="patch"
fi
NEW="${MAJOR}.${MINOR}.${PATCH}"
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
echo "new=$NEW" >> "$GITHUB_OUTPUT"
echo "bump=$BUMP" >> "$GITHUB_OUTPUT"
echo "tag=v$NEW" >> "$GITHUB_OUTPUT"
echo "Version: $CURRENT → $NEW ($BUMP)"
- name: Check if tag already exists
id: check_tag
env:
TAG: ${{ steps.bump.outputs.tag }}
run: |
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi
- name: Update version in package.json
if: steps.check_tag.outputs.exists == 'false'
env:
NEW_VERSION: ${{ steps.bump.outputs.new }}
run: npm version "$NEW_VERSION" --no-git-tag-version
- name: Update CHANGELOG.md
if: steps.check_tag.outputs.exists == 'false'
env:
NEW_VERSION: ${{ steps.bump.outputs.new }}
BUMP_TYPE: ${{ steps.bump.outputs.bump }}
COMMIT_MSG: ${{ github.event.head_commit.message }}
run: |
FIRST_LINE=$(echo "$COMMIT_MSG" | head -1)
DATE=$(date +%Y-%m-%d)
if echo "$FIRST_LINE" | grep -qE "^feat"; then
SECTION="Features"
ENTRY=$(echo "$FIRST_LINE" | sed 's/^feat[^:]*: //')
elif echo "$FIRST_LINE" | grep -qE "^fix"; then
SECTION="Fixes"
ENTRY=$(echo "$FIRST_LINE" | sed 's/^fix[^:]*: //')
elif echo "$FIRST_LINE" | grep -qE "^docs"; then
SECTION="Dokumentation"
ENTRY=$(echo "$FIRST_LINE" | sed 's/^docs[^:]*: //')
else
SECTION="Änderungen"
ENTRY=$(echo "$FIRST_LINE" | sed 's/^[a-z]*[^:]*: //')
fi
NEW_BLOCK="## [$NEW_VERSION] — $DATE
### $SECTION
- $ENTRY"
# Remove leading whitespace from heredoc
NEW_BLOCK=$(echo "$NEW_BLOCK" | sed 's/^ //')
if [ -f CHANGELOG.md ]; then
TEMP=$(mktemp)
awk -v block="$NEW_BLOCK" '
/^# Changelog/ { print; print ""; print block; print ""; print "---"; next }
{ print }
' CHANGELOG.md > "$TEMP"
mv "$TEMP" CHANGELOG.md
fi
- name: Commit version bump
if: steps.check_tag.outputs.exists == 'false'
env:
TAG: ${{ steps.bump.outputs.tag }}
NEW_VERSION: ${{ steps.bump.outputs.new }}
run: |
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git add package.json package-lock.json CHANGELOG.md 2>/dev/null || true
git add package.json
git commit -m "chore: bump version to $NEW_VERSION"
git tag "$TAG"
git push origin master --tags
# ─── GHCR: Build & Push ───────────────────────────
- name: Set image name
if: steps.check_tag.outputs.exists == 'false'
run: echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"
- uses: docker/login-action@v4
if: steps.check_tag.outputs.exists == 'false'
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v7
if: steps.check_tag.outputs.exists == 'false'
with:
context: .
push: true
build-args: |
NODE_AUTH_TOKEN=${{ secrets.GH_PACKAGES_TOKEN }}
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.bump.outputs.new }},${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
# ─── Release: Offline-Paket ───────────────────────
- name: Build image for offline package
if: steps.check_tag.outputs.exists == 'false'
env:
NODE_AUTH_TOKEN: ${{ secrets.GH_PACKAGES_TOKEN }}
run: docker build --build-arg "NODE_AUTH_TOKEN=$NODE_AUTH_TOKEN" -t gatecontrol:latest .
- name: Export image
if: steps.check_tag.outputs.exists == 'false'
run: docker save gatecontrol:latest | gzip > gatecontrol-image.tar.gz
- name: Create GitHub Release
if: steps.check_tag.outputs.exists == 'false'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.bump.outputs.tag }}
name: GateControl ${{ steps.bump.outputs.tag }}
generate_release_notes: true
files: |
gatecontrol-image.tar.gz
deploy/docker-compose.yml
deploy/setup.sh
.env.example