Skip to content
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
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.git
.github
*.md
121 changes: 121 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: Continuous Integration Pipeline

on:
release:
types:
- published
# - released
# - created

workflow_dispatch:
push:
branches: [ main ]
# tags:
# - 'v[0-9]+.[0-9]+.[0-9]+'

pull_request:
branches: [ main ]

env:
GO_VERSION: '1.24'
MAIN_FILE: './cmd/server/hostinfo.go'

jobs:

# Verification:
# if: github.ref_name != 'main'
# name: Verification of all checks
# runs-on: ubuntu-latest

# steps:
# - name: Checkout code
# uses: actions/checkout@v4

# - name: Set up Go ${{ env.GO_VERSION }}
# uses: actions/setup-go@v5
# with:
# go-version: ${{ env.GO_VERSION }}

# # - name: Cache Go modules
# # uses: actions/cache@v3
# # with:
# # path: |
# # ~/go/pkg/mod
# # ~/.cache/go-build
# # key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
# # restore-keys: |
# # ${{ runner.os }}-go-


# - name: Run Lint
# run: |
# echo "Running lint..."
# fmt_out=$(gofmt -l .)
# if [ -n "$fmt_out" ]; then
# echo "Go code is not formatted:"
# echo "$fmt_out"
# gofmt -d .
# exit 1
# fi
# go vet ./...

# - name: Run tests
# run: |
# echo "Running tests..."
# go mod download
# go test -v -race ./...
# go build -v ${{ env.MAIN_FILE }}

# - name: Run Security
# run: |
# echo "Running security checks..."

# # Hardcoded credentials check
# # Exclude common directories and files
# EXCLUDES=(".git" "frontend" "node_modules" "build" "docs")
# # Exclude file patterns
# FILE_EXCLUDES=("*.yml" "*.yaml" "*.md" "*.js" "*.jsx" "*.json" "*_test.go")
# # Build grep exclude params
# EXCLUDE_PARAMS=()
# for dir in "${EXCLUDES[@]}"; do EXCLUDE_PARAMS+=(--exclude-dir="$dir"); done
# for file in "${FILE_EXCLUDES[@]}"; do EXCLUDE_PARAMS+=(--exclude="$file"); done
# # Run grep safely
# if grep -rI "password.*=" . "${EXCLUDE_PARAMS[@]}" | \
# grep -v "^[[:space:]]*//" | \
# grep -v 'json:' | \
# grep -v '`json:' | \
# grep -v "Password.*string" | \
# grep -v "r.BasicAuth()" | \
# grep -v "subtle.ConstantTimeCompare"; then
# echo "Potential hardcoded credentials found"
# exit 1
# fi

# build-and-publish-docker:
# if: github.ref_name != 'main'
# needs: [Verification]
# uses: ./.github/workflows/template-docker.yml
# with:
# dockerfile_path: "./docker/Dockerfile"
# docker_push: true
# image_name: "hostinfo"
# version: ${{ github.ref_name }}
# registry: "docker.io"
# organization: "maximleus"
# platforms: "linux/amd64,linux/arm64"
# secrets:
# DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
# DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}

semantic-release:
if: >
github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: write # to be able to publish a GitHub release
uses: ./.github/workflows/template-semantic-release.yml
with:
dry_run: false
semantic_version: 20
secrets:
MY_GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}

221 changes: 221 additions & 0 deletions .github/workflows/template-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
name: Docker Template Workflow for GitHub Actions

on:
workflow_call:
inputs:
dockerfile_path:
description: "Path to the Dockerfile"
required: false
default: "./Dockerfile"
type: string
docker_image_overwrite:
description: "Overwrite existing Docker image if it exists"
required: false
type: boolean
default: false
docker_push:
description: "If true, the Docker image will be built and pushed"
required: false
type: boolean
default: false
image_name:
description: "Docker image name (without registry)"
required: true
type: string
version:
description: "Tag version (ex: 1.0.0, latest, dev)"
required: true
type: string
registry:
description: "Registry (docker.io, ghcr.io, etc)"
required: false
default: "docker.io"
type: string
organization:
description: "Docker Hub or GHCR organization/user"
required: true
type: string
platforms:
description: "Platforms to build (comma separated)"
required: false
default: "linux/amd64,linux/arm64"
type: string
secrets:
DOCKER_USERNAME:
required: true
DOCKER_PASSWORD:
required: true

permissions:
contents: read
# packages: write
# security-events: write

jobs:
docker-build-push:
if: "!contains(github.event.head_commit.message, '[docker skip]')"
name: Build & Publish Docker Image
runs-on: ubuntu-latest

steps:
- name: Checkout source
uses: actions/checkout@v4

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

- name: Log in to Docker Hub
if: ${{ inputs.registry == 'docker.io' }}
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Log in to GHCR
if: ${{ inputs.registry == 'ghcr.io' }}
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set dynamic version
id: set_version
run: |
# detect PR source branch or fallback to ref_name
VERSION="${{ github.event_name == 'pull_request' && github.head_ref || inputs.version }}"

# Sanitize for Docker tags: replace / , with -
VERSION="${VERSION//\//-}"
VERSION="${VERSION//,/ -}"

echo "Dynamic version: $VERSION"
echo "version=$VERSION" >> $GITHUB_OUTPUT

- name: Verification if Dockerfile exists
if: ${{ inputs.dockerfile_path != '' }}
run: |
if [ ! -f "${{ inputs.dockerfile_path }}" ]; then
echo "Dockerfile not found at path: ${{ inputs.dockerfile_path }}"
exit 1
fi

- name: Verification if Docker image already exists
id: check_image
run: |
IMAGE="${{ inputs.organization }}/${{ inputs.image_name }}:${{ steps.set_version.outputs.version }}"
OVERWRITE="${{ inputs.docker_image_overwrite }}"
EXISTS=false

echo "Checking if image exists: $IMAGE"

if docker manifest inspect "$IMAGE" > /dev/null 2>&1; then
echo "Image exists: $IMAGE"
if [ "$OVERWRITE" = "true" ]; then
echo "Overwrite enabled → will rebuild"
EXISTS=false
else
EXISTS=true
fi
else
echo "Image does not exist → will build"
fi

echo "exists=$EXISTS" >> $GITHUB_OUTPUT
echo "overwrite=$OVERWRITE" >> $GITHUB_OUTPUT

- name: Set docker_push for feature branches
id: check_feature_branch
run: |
# Determine ref name
REF_NAME="${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }}"

# Decide docker_push
if [[ "$REF_NAME" == feature/* ]]; then
echo "Feature branch detected, setting docker_push to false"
echo "docker_push=false" >> $GITHUB_OUTPUT
else
echo "Not a feature branch, using input docker_push value"
echo "docker_push=${{ inputs.docker_push }}" >> $GITHUB_OUTPUT
fi

- name: Set Docker tags
id: docker_tags
run: |
VERSION_TAG="${{ inputs.organization }}/${{ inputs.image_name }}:${{ steps.set_version.outputs.version }}"
echo "tags=$VERSION_TAG" >> "$GITHUB_OUTPUT"

if [ "${{ github.event_name }}" = "release" ] && [ "${{ github.event.action }}" = "published" ]; then
LATEST_TAG="${{ inputs.organization }}/${{ inputs.image_name }}:latest"
echo "tags=${VERSION_TAG},${LATEST_TAG}" >> "$GITHUB_OUTPUT"
fi

# - name: Extract metadata for Docker
# id: meta
# uses: docker/metadata-action@v5
# with:
# images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# tags: |
# # Branch name
# type=ref,event=branch
# # Tag name
# type=ref,event=tag
# # Short SHA
# type=sha,prefix=sha-
# # Latest tag for main branch
# type=raw,value=latest,enable={{is_default_branch}}
# # Semver tags
# type=semver,pattern={{version}}
# type=semver,pattern={{major}}.{{minor}}
# type=semver,pattern={{major}}

- name: Build and push Docker image
if: steps.check_image.outputs.exists == 'false' || steps.check_image.outputs.overwrite == 'true'
uses: docker/build-push-action@v6
with:
context: .
file: ${{ inputs.dockerfile_path }}
push: ${{ steps.check_feature_branch.outputs.docker_push }}
platforms: ${{ inputs.platforms }}
tags: ${{ steps.docker_tags.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Summary
run: |
if [ "${{ inputs.docker_push }}" = "false" ]; then
echo "Dry run mode: image was built but not pushed."
else
echo "Image published to:"
echo "${{ inputs.registry }}/${{ inputs.organization }}/${{ inputs.image_name }}:${{ steps.set_version.outputs.version }}"
fi

# security-scan:
# name: Security Scan
# runs-on: ubuntu-latest
# needs: build
# if: github.event_name != 'pull_request'

# steps:
# - name: Checkout code
# uses: actions/checkout@v4

# - name: Log in to Docker Hub
# uses: docker/login-action@v3
# with:
# username: ${{ secrets.DOCKERHUB_USERNAME }}
# password: ${{ secrets.DOCKERHUB_TOKEN }}

# - name: Run Trivy vulnerability scanner
# uses: aquasecurity/trivy-action@master
# with:
# image-ref: '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}'
# format: 'sarif'
# output: 'trivy-results.sarif'
# severity: 'CRITICAL,HIGH'

# - name: Upload Trivy scan results
# uses: github/codeql-action/upload-sarif@v4
# if: always()
# with:
# sarif_file: 'trivy-results.sarif'
Loading