Skip to content
Open
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
88 changes: 88 additions & 0 deletions .github/workflows/go-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: go-ci

on:
push:
branches: [ "main", "master", "lab*" ]
paths:
- "app_go/**"
- ".github/workflows/go-ci.yml"
pull_request:
paths:
- "app_go/**"
- ".github/workflows/go-ci.yml"

concurrency:
group: go-ci-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: app_go
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: app_go/go.mod
cache: true
cache-dependency-path: app_go/go.sum

- name: fmt check (print files)
run: |
echo "Go version:"
go version

files="$(gofmt -l .)"
if [ -n "$files" ]; then
echo "::error::gofmt wants to reformat these files:"
echo "$files"
exit 1
fi

- name: vet
run: go vet ./...

- name: test + coverage
run: go test ./... -coverprofile=coverage.out

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: app_go/coverage.out
flags: app_go

# Docker push if tests passed
docker:
needs: [test]
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/lab'))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: docker/setup-buildx-action@v3
id: buildx

- name: Set tags
run: |
echo "CALVER=$(date -u +'%Y.%m.%d')" >> $GITHUB_ENV
echo "SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV

- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}

- uses: docker/build-push-action@v6
with:
context: ./app_go
push: true
builder: ${{ steps.buildx.outputs.name }}
cache-from: type=gha
cache-to: type=gha,mode=max
tags: |
${{ secrets.DOCKER_USERNAME }}/app-go:${{ env.CALVER }}
${{ secrets.DOCKER_USERNAME }}/app-go:sha-${{ env.SHA_SHORT }}
${{ secrets.DOCKER_USERNAME }}/app-go:latest
140 changes: 140 additions & 0 deletions .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: Python CI

on:
push:
# Run CI only if something has changed in the application folder or in the workflow itself
paths:
- "app_python/**/*.py"
- "app_python/requirements*.txt"
- "app_python/Dockerfile"
- ".github/workflows/python-ci.yml"

pull_request:
paths:
- "app_python/**/*.py"
- "app_python/requirements*.txt"
- "app_python/Dockerfile"
- ".github/workflows/python-ci.yml"

# Prevents old launches from replaying when new pushes are made
concurrency:
group: python-ci-${{ github.ref }}
cancel-in-progress: true

# Minimum required privileges are provided
permissions:
contents: read

env:
# To avoid duplicating paths/versions throughout the file
APP_DIR: app_python
PYTHON_VERSION: "3.12"
# Image repository on Docker Hub (tags added separately)
IMAGE_NAME: ${{ secrets.DOCKER_USERNAME }}/app_python
# SNYK token
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
# We're grabbing the repository code for the runner
- name: Checkout
uses: actions/checkout@v4

# Prepare the required version of Python and enable the pip cache (to speed up the work during subsequent launches)
- name: Setup Python (pip cache)
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: "pip"
cache-dependency-path: |
app_python/requirements.txt
app_python/requirements-dev.txt

# Installing dependencies
- name: Install deps
working-directory: ${{ env.APP_DIR }}
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt

# Code Quality Check: Style + Typical Errors (Before Testing)
- name: Lint (flake8)
working-directory: ${{ env.APP_DIR }}
run: flake8 app.py tests

# Running unit tests
- name: Tests (pytest)
working-directory: ${{ env.APP_DIR }}
run: pytest -q

# Security scan
- name: Setup Snyk
# Run Snyk only on the push branches we need and only if SNYK_TOKEN is specified (otherwise skip this step)
if: ${{ github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || startsWith(github.ref_name, 'lab')) && env.SNYK_TOKEN != '' }}
uses: snyk/actions/setup@master

# Checking dependencies for vulnerabilities (build stops at high+)
- name: Snyk dependency scan (fail on high+)
if: ${{ github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || startsWith(github.ref_name, 'lab')) && env.SNYK_TOKEN != '' }}
working-directory: ${{ env.APP_DIR }}
env:
SNYK_TOKEN: ${{ env.SNYK_TOKEN }}
run: snyk test --severity-threshold=high

# Checking code for vulnerabilities (build stops at high+)
- name: Snyk code scan (SAST)
if: ${{ github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || startsWith(github.ref_name, 'lab')) && env.SNYK_TOKEN != '' }}
working-directory: ${{ env.APP_DIR }}
env:
SNYK_TOKEN: ${{ env.SNYK_TOKEN }}
run: snyk code test --severity-threshold=high

docker:
# Docker job is started only if tests/linter passed
needs: [test]
runs-on: ubuntu-latest
timeout-minutes: 15

# push image only on push
if: ${{ github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || startsWith(github.ref_name, 'lab')) }}

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

- name: Set up Docker Buildx (for GHA cache)
uses: docker/setup-buildx-action@v3
with:
driver: docker-container
install: true

# CalVer - version by date
# SHA — a unique tag for each commit
- name: Generate CalVer + SHA tag
run: |
echo "CALVER=$(date -u +'%Y.%m.%d')" >> $GITHUB_ENV
echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV

# Authorization in Docker Hub using a token from GitHub Secrets
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}

# Build and push the image (Layers are cached to make rebuilds faster)
- name: Build and push (with cache)
uses: docker/build-push-action@v6
with:
context: ./${{ env.APP_DIR }}
push: true
tags: |
${{ env.IMAGE_NAME }}:${{ env.CALVER }}
${{ env.IMAGE_NAME }}:sha-${{ env.SHORT_SHA }}
${{ env.IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
66 changes: 66 additions & 0 deletions .github/workflows/terraform-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Terraform CI

on:
pull_request:
paths:
- "terraform/**/*.tf"
- "terraform/**/*.hcl"
- "terraform/**/.terraform.lock.hcl"
- "terraform/**/.tflint.hcl"
- ".github/workflows/terraform-ci.yml"
- "!terraform/docs/**"

# Prevent old runs from continuing if new commits are pushed to the same PR
concurrency:
group: terraform-ci-${{ github.ref }}
cancel-in-progress: true

# Minimum required privileges
permissions:
contents: read

env:
TF_IN_AUTOMATION: "true"
TF_INPUT: "false"
TF_DIR: terraform

jobs:
validate:
runs-on: ubuntu-latest
timeout-minutes: 10

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

- name: Setup Terraform
uses: hashicorp/setup-terraform@v3

- name: Terraform fmt (check)
run: terraform fmt -check -recursive
working-directory: ${{ env.TF_DIR }}

- name: Terraform init (no backend)
run: terraform init -backend=false -input=false
working-directory: ${{ env.TF_DIR }}

- name: Terraform validate
run: terraform validate -no-color
working-directory: ${{ env.TF_DIR }}

- name: Setup TFLint
uses: terraform-linters/setup-tflint@v6
with:
tflint_version: latest
cache: false
tflint_config_path: terraform/.tflint.hcl

- name: TFLint init
run: tflint --init
working-directory: ${{ env.TF_DIR }}
env:
GITHUB_TOKEN: ${{ github.token }}

- name: TFLint
run: tflint -f compact
working-directory: ${{ env.TF_DIR }}
51 changes: 51 additions & 0 deletions app_go/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# VCS
.git
.gitignore

# IDE / editors
.vscode/
.idea/
*.swp

# OS junk
.DS_Store
Thumbs.db

# Docs (not needed for build/run)
docs/
*.md
README*
LICENSE*

# Secrets / env files (NEVER ship)
.env
.env.*

# Logs / reports
*.log
coverage*
*.out
*.prof
*.trace

# Go build artifacts
bin/
dist/
build/
out/
*.exe
*.dll
*.so
*.dylib
*.a
*.o

# Go test cache / tooling caches
**/*_test.go
.golangci.yml
.golangci-lint*
.gotools/
.tmp/
tmp/


8 changes: 8 additions & 0 deletions app_go/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Go build outputs
/devops-info-service
*.exe
*.out

# Go tooling
/bin/
/dist/
27 changes: 27 additions & 0 deletions app_go/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# ---------- Stage 1: Build ----------
# Use the full Go toolchain image only for compilation, which will take place in the /app directory
FROM golang:1.25.5-bookworm AS builder
WORKDIR /app

# Copy and install module metadata to use Docker layer caching (if go.sum appears later, this will speed up rebuilding).
COPY go.mod ./
RUN go mod download

# Copy the rest of the source code (excess code is filtered out by .dockerignore) and build the binary
COPY . .
RUN CGO_ENABLED=0 go build -o myapp .

# ---------- Stage 2: Runtime ----------
# Define the runtime environment (only necessary for running the binary).
FROM alpine:3.18

# Create an unprivileged user (does not run as root).
RUN adduser -D appuser

# Create an application directory inside the runtime container and copy the binary from the compilation environment into it (assigning the file to the appuser user)
WORKDIR /app
COPY --from=builder --chown=appuser:appuser /app/myapp .
USER appuser

EXPOSE 5000
CMD ["./myapp"]
Loading
Loading