diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..3e367ad --- /dev/null +++ b/.github/workflows/publish.yml @@ -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 \ No newline at end of file diff --git a/Makefile b/Makefile index c749dc6..86419d8 100644 --- a/Makefile +++ b/Makefile @@ -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: diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..28cd875 --- /dev/null +++ b/docker-compose.dev.yml @@ -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: \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 981e43f..769376e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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: