Skip to content
Closed
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
99 changes: 99 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# https://github.com/gitattributes/gitattributes/blob/fddc586cf0f10ec4485028d0d2dd6f73197a4258/Common.gitattributes
# Common settings that generally should always be used with your language specific settings

# Auto detect text files and perform LF normalization
* text=auto

#
# The above will handle all files NOT found below
#

# Documents
*.bibtex text diff=bibtex
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.md text diff=markdown
*.mdx text diff=markdown
*.tex text diff=tex
*.adoc text
*.textile text
*.mustache text
*.csv text eol=crlf
*.tab text
*.tsv text
*.txt text
*.sql text
*.epub diff=astextplain

# Graphics
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.tif binary
*.tiff binary
*.ico binary
# SVG treated as text by default.
*.svg text
# If you want to treat it as binary,
# use the following line instead.
# *.svg binary
*.eps binary

# Scripts
*.bash text eol=lf
*.fish text eol=lf
*.ksh text eol=lf
*.sh text eol=lf
*.zsh text eol=lf
# These are explicitly windows files and should use crlf
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf

# Serialisation
*.json text
*.toml text
*.xml text
*.yaml text
*.yml text

# Archives
*.7z binary
*.bz binary
*.bz2 binary
*.bzip2 binary
*.gz binary
*.lz binary
*.lzma binary
*.rar binary
*.tar binary
*.taz binary
*.tbz binary
*.tbz2 binary
*.tgz binary
*.tlz binary
*.txz binary
*.xz binary
*.Z binary
*.zip binary
*.zst binary

# Text files where line endings should be preserved
*.patch -text

#
# Exclude files from exporting
#

.gitattributes export-ignore
.gitignore export-ignore
.gitkeep export-ignore
57 changes: 57 additions & 0 deletions .github/actions/python-setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Python Poetry Setup
description: Set up Python + Poetry, cache dependencies, and install project deps

inputs:
python-version:
description: Python version to install
required: false
default: "3.14"
poetry-version:
description: Poetry version to install
required: false
default: "2.3.2"
working-directory:
description: Project directory containing pyproject.toml
required: false
default: "app_python"
lockfile-path:
description: Path to poetry.lock for cache key invalidation
required: false
default: "app_python/poetry.lock"
install-args:
description: Extra arguments passed to poetry install
required: false
default: "--with dev --no-interaction --no-ansi"

runs:
using: composite
steps:
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: ${{ inputs.python-version }}

- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: ${{ inputs.poetry-version }}

- name: Configure Poetry virtualenv location
shell: bash
working-directory: ${{ inputs.working-directory }}
run: poetry config virtualenvs.in-project true

- name: Cache Poetry dependencies
uses: actions/cache@v4
with:
path: |
~/.cache/pypoetry
${{ inputs.working-directory }}/.venv
key: ${{ runner.os }}-py${{ inputs.python-version }}-poetry${{ inputs.poetry-version }}-${{ hashFiles(inputs.lockfile-path) }}
restore-keys: |
${{ runner.os }}-py${{ inputs.python-version }}-poetry${{ inputs.poetry-version }}-

- name: Install dependencies
shell: bash
working-directory: ${{ inputs.working-directory }}
run: poetry install ${{ inputs.install-args }}
56 changes: 56 additions & 0 deletions .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Python CI

on:
push:
paths:
- app_python/**
- .github/actions/python-setup/**
- .github/workflows/python-ci.yml
pull_request:
branches:
- master
paths:
- app_python/**
- .github/actions/python-setup/**
- .github/workflows/python-ci.yml

jobs:
test:
strategy:
fail-fast: false
matrix:
python-version: [3.14]
poetry-version: [2.3.2]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: ./app_python
steps:
- uses: actions/checkout@v2
- name: Setup Python tooling and dependencies
uses: ./.github/actions/python-setup
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ matrix.poetry-version }}
working-directory: app_python
lockfile-path: app_python/poetry.lock
install-args: --with dev --no-interaction --no-ansi
- name: Lint with flake8
run: poetry run flake8 src tests
- name: Test using pytest with coverage report
run: |
mkdir -p test-results
poetry run pytest \
--junitxml=test-results/pytest-report.xml \
--cov=src \
--cov-report=term-missing \
--cov-report=xml:test-results/coverage.xml
- name: Upload pytest and coverage reports
if: always()
uses: actions/upload-artifact@v4
with:
name: python-test-reports
path: |
app_python/test-results/pytest-report.xml
app_python/test-results/coverage.xml
82 changes: 82 additions & 0 deletions .github/workflows/python-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Python Docker Publish

on:
push:
branches:
- "lab*"
paths:
- app_python/**
- .github/workflows/python-docker.yml
pull_request:
branches:
- master
types:
- closed
paths:
- app_python/**
- .github/workflows/python-docker.yml

jobs:
build-and-push-branch:
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Derive lab+sha tag from branch
id: version
run: |
source_branch="${{ github.ref_name }}"
if [[ "$source_branch" =~ ([0-9]+) ]]; then
lab_number="${BASH_REMATCH[1]}"
lab_number=$((10#$lab_number))
short_sha="${GITHUB_SHA::7}"
echo "branch_tag=1.${lab_number}.${short_sha}" >> "$GITHUB_OUTPUT"
else
echo "Failed to extract lab number from branch: $source_branch" >&2
exit 1
fi
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image (branch)
uses: docker/build-push-action@v6
with:
context: ./app_python
file: ./app_python/Dockerfile
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/devops-app-py:${{ steps.version.outputs.branch_tag }}

build-and-push:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Derive lab version tag from merged branch
id: version
run: |
source_branch="${{ github.event.pull_request.head.ref }}"
if [[ "$source_branch" =~ ([0-9]+) ]]; then
lab_number="${BASH_REMATCH[1]}"
lab_number=$((10#$lab_number))
echo "version_tag=1.${lab_number}" >> "$GITHUB_OUTPUT"
else
echo "Failed to extract lab number from merged branch: $source_branch" >&2
exit 1
fi
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: ./app_python
file: ./app_python/Dockerfile
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/devops-app-py:${{ steps.version.outputs.version_tag }}
${{ secrets.DOCKERHUB_USERNAME }}/devops-app-py:latest
37 changes: 37 additions & 0 deletions .github/workflows/python-snyk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Python Snyk Scan

on:
push:
paths:
- app_python/**
- .github/actions/python-setup/**
- .github/workflows/python-snyk.yml
pull_request:
branches:
- master
paths:
- app_python/**
- .github/actions/python-setup/**
- .github/workflows/python-snyk.yml

jobs:
snyk:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./app_python
steps:
- uses: actions/checkout@v4
- name: Setup Python tooling and dependencies
uses: ./.github/actions/python-setup
- name: Setup Snyk CLI
uses: snyk/actions/setup@master
- name: Run Snyk dependency scan (or skip)
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: |
if [ -z "${SNYK_TOKEN:-}" ]; then
echo "SNYK_TOKEN secret not set; skipping Snyk dependency scan."
exit 0
fi
snyk test --severity-threshold=high
4 changes: 4 additions & 0 deletions app_go/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*
!go.mod
!go.sum
!*.go
33 changes: 33 additions & 0 deletions app_go/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# https://github.com/github/gitignore/blob/53fee13f20a05efc93ef4edcad0c62863520e268/Go.gitignore
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Code coverage profiles and other test artifacts
*.out
coverage.*
*.coverprofile
profile.cov

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
go.work.sum

# env file
.env

# Editor/IDE
.idea/
.vscode/
13 changes: 13 additions & 0 deletions app_go/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM golang:1.25-alpine AS build
WORKDIR /app
# Uncomment for dependency installation
# COPY go.mod go.sum ./
# RUN go mod download
COPY go.mod *.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -o devops-info-service.out

FROM scratch
COPY --from=build /app/devops-info-service.out /
# Use UID:GID to avoid copying user info (/etc/passwd)
USER 10001:10001
CMD ["/devops-info-service.out"]
Loading