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
50 changes: 25 additions & 25 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
this
.idea/
tmp/
runner.env
artifacts/
*~
*backups
.cache
docker-tag
.tox/
.*.swp
*.egg-info
wheelhouse/
test/build/*
PASSED
FAILED
test/.vagrant/*
# Container image specific
.gitignore
.git
.dockerignore
Dockerfile
**/__pycache__
venv*
.vscode/
this
.idea/
tmp/
runner.env
artifacts/
*~
*backups
.cache
docker-tag
.tox/
.*.swp
*.egg-info
wheelhouse/
test/build/*
PASSED
FAILED
test/.vagrant/*
# Container image specific
.gitignore
.git
.dockerignore
Dockerfile
**/__pycache__
venv*
.vscode/
*.env
170 changes: 170 additions & 0 deletions .github/workflows/build-and-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# =============================================================================
# DevOps Toolbox — Build and Publish Pipeline
# =============================================================================
# Builds the DevOps Toolbox image and pushes it to Docker Hub.
#
# TRIGGERS:
# - Push to main → builds and pushes with 'latest' tag
# - Version tag (v*.*.*) → builds and pushes with full semver tags
# - Pull request to main → builds only (no push) to validate Dockerfile
# - Weekly schedule → rebuilds to pick up base image security patches
# - Manual dispatch → allows on-demand builds from the Actions tab
#
# REQUIRED SECRETS (Settings → Secrets and variables → Actions):
# DOCKERHUB_USERNAME — your Docker Hub username (org-level secret)
# DOCKERHUB_TOKEN — Docker Hub access token, NOT your password (org-level secret)
# Generate at: hub.docker.com → Account Settings → Security
# DOCKERHUB_IMAGENAME — full image path, e.g. yourusername/devops-toolbox (repo-level secret)
#
# TAGGING STRATEGY:
# Push to main: latest, sha-<short-commit>
# Tag v1.2.3: 1.2.3, 1.2, 1, latest, sha-<short-commit>
# Pull request: no push, build validation only
# Scheduled: latest, sha-<short-commit>
# =============================================================================

name: Build and Publish

on:
push:
tags:
- 'v*.*.*'
pull_request:
branches:
- main
schedule:
# Weekly rebuild every Monday at 04:00 UTC
# Ensures base image security patches are picked up automatically
- cron: '0 4 * * 1'
workflow_dispatch:

env:
REGISTRY: docker.io
IMAGE_NAME: ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_IMAGENAME }}

jobs:
build:
name: Build and Push
runs-on: ubuntu-latest

permissions:
contents: read
packages: write
# Required for Cosign OIDC-based signing against Sigstore/Fulcio
id-token: write

steps:
# -----------------------------------------------------------------------
# Checkout
# fetch-depth: 0 is required for the metadata action to correctly resolve
# semver tags from the full Git history.
# -----------------------------------------------------------------------
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0

# -----------------------------------------------------------------------
# Cosign — image signing via Sigstore/Fulcio
# Skipped on PRs since there is nothing to sign (no push occurs).
# -----------------------------------------------------------------------
- name: Install Cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@v4.1.1

# -----------------------------------------------------------------------
# Multi-arch setup
# QEMU provides CPU emulation so amd64 runners can build arm64 images.
# Buildx is the extended Docker build client that enables multi-platform
# builds, layer caching, and other BuildKit features.
# -----------------------------------------------------------------------
- name: Set up QEMU
uses: docker/setup-qemu-action@v4.0.0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4.0.0

# -----------------------------------------------------------------------
# Docker Hub login
# Skipped on PRs — credentials are not needed for a build-only run, and
# PRs from forks do not have access to secrets anyway.
# Uses DOCKERHUB_TOKEN (access token), NOT DOCKERHUB_PASSWORD.
# -----------------------------------------------------------------------
- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v4.1.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

# -----------------------------------------------------------------------
# Image metadata and tags
# Derives tags and labels from the Git context automatically:
# - Semver tags from Git version tags (v1.2.3 → 1.2.3, 1.2, 1)
# - 'latest' on pushes to main and scheduled runs
# - Short SHA tag on every build for traceability
# -----------------------------------------------------------------------
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v6.0.0
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
# Semver tags from Git version tags (e.g. v1.2.3)
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
# 'latest' on pushes to main and scheduled runs
type=raw,value=latest,enable={{is_default_branch}}
# Short SHA for full traceability on every build
type=sha,format=short

# -----------------------------------------------------------------------
# Build and push
# - Multi-platform: linux/amd64 + linux/arm64
# - Push is skipped on PRs (validation build only)
# - GHA cache keeps rebuild times fast for unchanged layers
# - context: . is explicit so .dockerignore is respected
# -----------------------------------------------------------------------
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v7.0.0
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

# -----------------------------------------------------------------------
# Cosign image signing
# Signs the published image digest against the Sigstore/Fulcio public
# transparency log, providing cryptographic proof that this image was
# built by this pipeline and has not been tampered with.
# Skipped on PRs since no image was pushed.
# -----------------------------------------------------------------------
- name: Sign the published Docker image
if: github.event_name != 'pull_request'
env:
TAGS: ${{ steps.meta.outputs.tags }}
DIGEST: ${{ steps.build-and-push.outputs.digest }}
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}

# -----------------------------------------------------------------------
# Docker Hub README sync
# Keeps the Docker Hub repository description in sync with README.md.
# Skipped on PRs — there is nothing to sync before a merge.
# -----------------------------------------------------------------------
- name: Sync README to Docker Hub
if: github.event_name != 'pull_request'
uses: peter-evans/dockerhub-description@v5.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: ${{ env.IMAGE_NAME }}
readme-filepath: ./README.md
short-description: ${{ github.event.repository.description }}
enable-url-completion: true
24 changes: 12 additions & 12 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets

# Local History for Visual Studio Code
.history/

# Built Visual Studio Code Extensions
*.vsix
*.env
37 changes: 26 additions & 11 deletions Dockerfile
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
FROM phusion/baseimage:jammy-1.0.1

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get -y install git awscli dnsutils curl

RUN mkdir /etc/service/ddns
COPY ./bootstrap.sh /etc/service/ddns/run

HEALTHCHECK CMD sv status ddns | grep run || exit 1
RUN chmod 755 /etc/service/ddns/run
# ── Build stage: install Python dependencies ──────────────────────────────
FROM python:3.12-slim AS builder

WORKDIR /install
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install/deps -r requirements.txt

# ── Runtime stage ─────────────────────────────────────────────────────────
FROM python:3.12-slim

# Non-root user for least-privilege execution
RUN adduser --disabled-password --gecos "" ddns
WORKDIR /app

# Pull in installed packages from the build stage
COPY --from=builder /install/deps /usr/local
COPY ddns.py .

# Ensure the script is executable (good practice even when called via python)
RUN chmod 755 ddns.py
USER ddns

# Simple liveness probe: process is running
HEALTHCHECK --interval=60s --timeout=5s --start-period=10s --retries=3 \
CMD pgrep -f ddns.py || exit 1
ENTRYPOINT ["python", "-u", "ddns.py"]
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2022 Dice NInja Gaming
Copyright (c) 2022 Mike "Taegost" Wheway

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
21 changes: 0 additions & 21 deletions LICENSE.MD

This file was deleted.

Loading