Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
293 changes: 293 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
name: Publish Docker Image

on:
pull_request:
types: [closed]
branches:
- main
workflow_dispatch:
inputs:
bump_level:
description: 'Select the version bump level'
required: true
type: choice
options:
- patch
- minor
- major
default: 'patch'

permissions:
contents: write # Needed to push version bump commits and tags
packages: write # Needed to push docker image to GHCR

jobs:
publish_from_pr:
name: Publish Docker Image from PR
# Only run if PR was merged and has exactly one version label
if: |-
github.event.pull_request.merged == true &&
github.event_name == 'pull_request' &&
(contains(github.event.pull_request.labels.*.name, 'major') ||
contains(github.event.pull_request.labels.*.name, 'minor') ||
contains(github.event.pull_request.labels.*.name, 'patch'))
runs-on: ubuntu-latest
steps:
- name: Checkout repository based on PR merge commit
uses: actions/checkout@v4
with:
# Fetch all history and tags for version bumping/tagging
fetch-depth: 0
# Checkout the merge commit
ref: ${{ github.event.pull_request.merge_commit_sha }}

- name: Determine Version Bump Type from PR Label
id: version_bump
run: |
PR_NUMBER=${{ github.event.pull_request.number }}
REPO="${{ github.repository }}"
API_URL="/repos/$REPO/pulls/$PR_NUMBER"

echo "Fetching label counts for PR #$PR_NUMBER in repo $REPO"

# Use gh api with jq to count exact matches for each label type
major=$(gh api --jq '[.labels.[].name] | map(select(. == "major")) | length' "$API_URL")
minor=$(gh api --jq '[.labels.[].name] | map(select(. == "minor")) | length' "$API_URL")
patch=$(gh api --jq '[.labels.[].name] | map(select(. == "patch")) | length' "$API_URL")

echo "Counts - Major: $major, Minor: $minor, Patch: $patch"

total=$((major + minor + patch))

if [ "$total" -ne 1 ]; then
echo "Error: PR must have exactly one version label (major, minor, or patch). Found $total matching version labels (Major: $major, Minor: $minor, Patch: $patch)."
exit 1
fi

if [ "$major" -eq 1 ]; then
echo "bump_type=major"
elif [ "$minor" -eq 1 ]; then
echo "bump_type=minor"
elif [ "$patch" -eq 1 ]; then
echo "bump_type=patch"
# This else should theoretically not be reached due to the total check
else
echo "Error: Logic error determining bump type despite total count being 1."
exit 1
fi >> $GITHUB_OUTPUT
env:
# GITHUB_TOKEN is needed for gh api calls
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Calculate Next Version
id: calc_version
run: |
BUMP_TYPE=${{ steps.version_bump.outputs.bump_type }}
CURRENT_VERSION=$(cat VERSION)
echo "Current version: $CURRENT_VERSION"
echo "Bump type: $BUMP_TYPE"

# Use awk for shell-based SemVer bumping
current_major=$(echo $CURRENT_VERSION | awk -F. '{print $1}')
current_minor=$(echo $CURRENT_VERSION | awk -F. '{print $2}')
current_patch=$(echo $CURRENT_VERSION | awk -F. '{print $3}')

if [ "$BUMP_TYPE" == "major" ]; then
new_major=$((current_major + 1))
new_minor=0
new_patch=0
elif [ "$BUMP_TYPE" == "minor" ]; then
new_major=$current_major
new_minor=$((current_minor + 1))
new_patch=0
else # patch
new_major=$current_major
new_minor=$current_minor
new_patch=$((current_patch + 1))
fi

NEW_VERSION="${new_major}.${new_minor}.${new_patch}"
echo "New version: $NEW_VERSION"
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT

- name: Update VERSION file
run: |
echo "${{ steps.calc_version.outputs.new_version }}" > VERSION
cat VERSION

- name: Update docker-compose.yml with new version
run: |
echo "Updating docker-compose.yml with version ${{ steps.calc_version.outputs.new_version }}"
sed -i -E "s|(image: ghcr.io/stedrow/vulnspot:)[^[:space:]]+|\\1${{ steps.calc_version.outputs.new_version }}|g" docker-compose.yml
echo "Contents of docker-compose.yml after update:"
cat docker-compose.yml

- name: Commit and Push Version Update from PR
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add VERSION docker-compose.yml
# Use merge commit sha in message for traceability
COMMIT_SHA=${{ github.event.pull_request.merge_commit_sha }}
git commit -m "chore: Bump version to ${{ steps.calc_version.outputs.new_version }} for merge $COMMIT_SHA"
# Push directly to the base branch
BASE_REF=${{ github.event.pull_request.base.ref }}
echo "Pushing version update to $BASE_REF"
git push origin HEAD:$BASE_REF

- name: Create Git Tag
run: |
VERSION="v${{ steps.calc_version.outputs.new_version }}"
echo "Creating tag $VERSION"
git tag $VERSION
git push origin $VERSION

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}},value=v${{ steps.calc_version.outputs.new_version }}
type=semver,pattern={{major}}.{{minor}},value=v${{ steps.calc_version.outputs.new_version }}
type=semver,pattern={{major}},value=v${{ steps.calc_version.outputs.new_version }}
type=sha
# Add latest tag only for default branch (main)
type=raw,value=latest,enable=${{ github.event.pull_request.base.ref == 'main' }}

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

publish_manually:
name: Publish Docker Image Manually
runs-on: ubuntu-latest
# Only run on workflow_dispatch trigger
if: github.event_name == 'workflow_dispatch'
steps:
- name: Checkout main branch
uses: actions/checkout@v4
with:
# Fetch all history and tags for version bumping/tagging
fetch-depth: 0
ref: 'main'

# Version Bump Type is determined by the workflow_dispatch input
- name: Calculate Next Version
id: calc_version
run: |
CURRENT_VERSION=$(cat VERSION)
BUMP_TYPE=${{ github.inputs.bump_level }}
echo "Current version: $CURRENT_VERSION"
echo "Manual bump type: $BUMP_TYPE"

# Use awk for shell-based SemVer bumping
current_major=$(echo $CURRENT_VERSION | awk -F. '{print $1}')
current_minor=$(echo $CURRENT_VERSION | awk -F. '{print $2}')
current_patch=$(echo $CURRENT_VERSION | awk -F. '{print $3}')

if [ "$BUMP_TYPE" == "major" ]; then
new_major=$((current_major + 1))
new_minor=0
new_patch=0
elif [ "$BUMP_TYPE" == "minor" ]; then
new_major=$current_major
new_minor=$((current_minor + 1))
new_patch=0
else # patch
new_major=$current_major
new_minor=$current_minor
new_patch=$((current_patch + 1))
fi

NEW_VERSION="${new_major}.${new_minor}.${new_patch}"
echo "New version: $NEW_VERSION"
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT

- name: Update VERSION file
run: |
echo "${{ steps.calc_version.outputs.new_version }}" > VERSION
cat VERSION

- name: Update docker-compose.yml with new version
run: |
echo "Updating docker-compose.yml with version ${{ steps.calc_version.outputs.new_version }}"
sed -i -E "s|(image: ghcr.io/stedrow/vulnspot:)[^[:space:]]+|\\1${{ steps.calc_version.outputs.new_version }}|g" docker-compose.yml
echo "Contents of docker-compose.yml after update:"
cat docker-compose.yml

- name: Commit and Push Version Update from Manual Trigger
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add VERSION docker-compose.yml
git commit -m "chore(release): Bump version to ${{ steps.calc_version.outputs.new_version }} (manual trigger)"
# Push directly to main branch
echo "Pushing version update to main"
git push origin HEAD:refs/heads/main

- name: Create Git Tag
run: |
VERSION="v${{ steps.calc_version.outputs.new_version }}"
echo "Creating tag $VERSION"
git tag $VERSION
git push origin $VERSION

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}},value=v${{ steps.calc_version.outputs.new_version }}
type=semver,pattern={{major}}.{{minor}},value=v${{ steps.calc_version.outputs.new_version }}
type=semver,pattern={{major}},value=v${{ steps.calc_version.outputs.new_version }}
type=sha
# Add latest tag since this is running on main
type=raw,value=latest,enable=true

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ run: run-dev
run-dev:
@echo "Starting $(APP_NAME) development server with docker-compose (reload enabled)..."
@echo "Access at http://localhost:8000"
docker-compose up --build
docker-compose -f docker-compose.dev.yml up --build

# Stop the docker-compose services
stop:
Expand Down
35 changes: 35 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
services:
docker-socket-proxy:
image: 11notes/socket-proxy:2.1.2
container_name: docker-socket-proxy
read_only: true
user: "0:0" # make sure to use the same UID/GID as the owner of your docker socket!
restart: always
environment:
- SOCKET_PROXY_UID=65532 # nonroot, used by sherpa-dns image
- SOCKET_PROXY_GID=65532 # nonroot, used by sherpa-dns image
volumes:
- /run/docker.sock:/run/docker.sock:ro
- socket-proxy:/run/proxy

vulnspot:
container_name: vulnspot
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
volumes:
- socket-proxy:/var/run
- ./data:/app/data
- ./app:/app # Mount app directory for development
- ./templates:/app/templates # Mount templates directory for development
environment:
- DATABASE_URL=sqlite:////app/data/vuln_scanner.db # Set for Docker to use path inside container
restart: unless-stopped
depends_on:
docker-socket-proxy:
condition: service_healthy

volumes:
socket-proxy:
4 changes: 1 addition & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ services:
- socket-proxy:/run/proxy

vulnspot:
image: ghcr.io/stedrow/vulnspot:latest
container_name: vulnspot
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
volumes:
Expand Down
Loading