mirror of
https://github.com/jorijn/meshcore-stats.git
synced 2026-03-28 17:42:55 +01:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd455fa551 |
276
.github/workflows/docker-publish.yml
vendored
276
.github/workflows/docker-publish.yml
vendored
@@ -1,276 +0,0 @@
|
||||
# Build and publish Docker images to GitHub Container Registry
|
||||
#
|
||||
# Triggers:
|
||||
# - On release: Build with version tags (X.Y.Z, X.Y, latest)
|
||||
# - On schedule: Rebuild all tags with fresh base image (OS patches)
|
||||
# - Manual: For testing, optional push
|
||||
#
|
||||
# Security:
|
||||
# - All actions pinned by SHA
|
||||
# - Vulnerability scanning with Trivy
|
||||
# - SBOM and provenance attestation
|
||||
|
||||
name: Docker Build and Publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
schedule:
|
||||
# Daily at 4 AM UTC - rebuild with fresh base image
|
||||
- cron: "0 4 * * *"
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- Dockerfile
|
||||
- .dockerignore
|
||||
- docker/**
|
||||
- pyproject.toml
|
||||
- uv.lock
|
||||
- src/**
|
||||
- scripts/**
|
||||
- .github/workflows/docker-publish.yml
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
push:
|
||||
description: "Push image to registry"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
artifact-metadata: write
|
||||
|
||||
concurrency:
|
||||
group: docker-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event_name != 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
# For nightly builds, get the latest release version
|
||||
- name: Get latest release version
|
||||
id: get-version
|
||||
if: github.event_name == 'schedule'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Get latest release tag
|
||||
LATEST_TAG=$(gh release view --json tagName -q '.tagName' 2>/dev/null || echo "")
|
||||
if [ -z "$LATEST_TAG" ]; then
|
||||
echo "No releases found, skipping nightly build"
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
# Strip 'v' prefix if present
|
||||
VERSION=$(echo "$LATEST_TAG" | sed 's/^v//')
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Skip if no releases
|
||||
if: github.event_name == 'schedule' && steps.get-version.outputs.skip == 'true'
|
||||
run: |
|
||||
echo "No releases found, skipping nightly build"
|
||||
exit 0
|
||||
|
||||
- name: Set up QEMU
|
||||
if: "!(github.event_name == 'schedule' && steps.get-version.outputs.skip == 'true')"
|
||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
if: "!(github.event_name == 'schedule' && steps.get-version.outputs.skip == 'true')"
|
||||
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
||||
|
||||
- name: Log in to Container Registry
|
||||
if: "!(github.event_name == 'schedule' && steps.get-version.outputs.skip == 'true')"
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Generate tags based on event type
|
||||
- name: Extract metadata (release)
|
||||
id: meta-release
|
||||
if: github.event_name == 'release'
|
||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
# X.Y.Z
|
||||
type=semver,pattern={{version}}
|
||||
# X.Y
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
# latest
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Extract metadata (nightly)
|
||||
id: meta-nightly
|
||||
if: github.event_name == 'schedule' && steps.get-version.outputs.skip != 'true'
|
||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
# Rebuild version tags with OS patches
|
||||
type=raw,value=${{ steps.get-version.outputs.version }}
|
||||
# Nightly tags
|
||||
type=raw,value=nightly
|
||||
type=raw,value=nightly-{{date 'YYYYMMDD'}}
|
||||
# Also update latest with security patches
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Extract metadata (manual)
|
||||
id: meta-manual
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=sha,prefix=sha-
|
||||
|
||||
# Build image (release - with cache)
|
||||
- name: Build and push (release)
|
||||
id: build-release
|
||||
if: github.event_name == 'release'
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta-release.outputs.tags }}
|
||||
labels: ${{ steps.meta-release.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
provenance: true
|
||||
sbom: true
|
||||
|
||||
# Build image (nightly - no cache, fresh base)
|
||||
- name: Build and push (nightly)
|
||||
id: build-nightly
|
||||
if: github.event_name == 'schedule' && steps.get-version.outputs.skip != 'true'
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta-nightly.outputs.tags }}
|
||||
labels: ${{ steps.meta-nightly.outputs.labels }}
|
||||
pull: true
|
||||
no-cache: true
|
||||
provenance: true
|
||||
sbom: true
|
||||
|
||||
# Build image (manual)
|
||||
- name: Build and push (manual)
|
||||
id: build-manual
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ inputs.push }}
|
||||
tags: ${{ steps.meta-manual.outputs.tags }}
|
||||
labels: ${{ steps.meta-manual.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
provenance: true
|
||||
sbom: true
|
||||
|
||||
# Determine image tag for scanning and testing
|
||||
- name: Determine image tag
|
||||
id: image-tag
|
||||
if: "!(github.event_name == 'schedule' && steps.get-version.outputs.skip == 'true')"
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "release" ]; then
|
||||
# Strip 'v' prefix to match semver tag format from metadata-action
|
||||
echo "tag=$(echo '${{ github.event.release.tag_name }}' | sed 's/^v//')" >> $GITHUB_OUTPUT
|
||||
elif [ "${{ github.event_name }}" = "schedule" ]; then
|
||||
echo "tag=nightly" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "tag=sha-${{ github.sha }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Vulnerability scanning
|
||||
- name: Run Trivy vulnerability scanner
|
||||
if: "!(github.event_name == 'schedule' && steps.get-version.outputs.skip == 'true')"
|
||||
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
|
||||
with:
|
||||
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.image-tag.outputs.tag }}
|
||||
format: "sarif"
|
||||
output: "trivy-results.sarif"
|
||||
severity: "CRITICAL,HIGH"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Upload Trivy scan results
|
||||
if: "!(github.event_name == 'schedule' && steps.get-version.outputs.skip == 'true')"
|
||||
uses: github/codeql-action/upload-sarif@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
|
||||
with:
|
||||
sarif_file: "trivy-results.sarif"
|
||||
continue-on-error: true
|
||||
|
||||
# Smoke test - verify image runs correctly
|
||||
# Skip for manual runs when push is disabled (image not available to pull)
|
||||
- name: Smoke test
|
||||
if: "!(github.event_name == 'schedule' && steps.get-version.outputs.skip == 'true') && !(github.event_name == 'workflow_dispatch' && inputs.push == false)"
|
||||
run: |
|
||||
IMAGE_TAG="${{ steps.image-tag.outputs.tag }}"
|
||||
|
||||
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${IMAGE_TAG}
|
||||
|
||||
# Test that Python and key modules are available
|
||||
docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${IMAGE_TAG} \
|
||||
python -c "from meshmon.db import init_db; from meshmon.env import get_config; print('Smoke test passed')"
|
||||
|
||||
# Attestation (releases only)
|
||||
- name: Generate attestation
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
subject-digest: ${{ steps.build-release.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
build-pr:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
||||
|
||||
- name: Build image (PR)
|
||||
id: build-pr
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64
|
||||
load: true
|
||||
push: false
|
||||
tags: meshcore-stats:pr-${{ github.event.pull_request.number }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Smoke test (PR)
|
||||
run: |
|
||||
docker run --rm meshcore-stats:pr-${{ github.event.pull_request.number }} \
|
||||
python -c "from meshmon.db import init_db; from meshmon.env import get_config; print('Smoke test passed')"
|
||||
33
.github/workflows/release-please.yml
vendored
33
.github/workflows/release-please.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: Release Please
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
# Note: We use a fine-grained PAT (RELEASE_PLEASE_TOKEN) instead of GITHUB_TOKEN
|
||||
# because GITHUB_TOKEN cannot trigger other workflows (like docker-publish.yml).
|
||||
# This is a GitHub security feature to prevent infinite workflow loops.
|
||||
#
|
||||
# The PAT requires these permissions (scoped to this repository only):
|
||||
# - Contents: Read and write (for creating releases and pushing tags)
|
||||
# - Pull requests: Read and write (for creating/updating release PRs)
|
||||
#
|
||||
# To rotate: Settings > Developer settings > Fine-grained tokens
|
||||
# Recommended rotation: Every 90 days
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
release-please:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Release Please
|
||||
uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4
|
||||
with:
|
||||
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
|
||||
config-file: release-please-config.json
|
||||
manifest-file: .release-please-manifest.json
|
||||
119
.github/workflows/test.yml
vendored
119
.github/workflows/test.yml
vendored
@@ -1,119 +0,0 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, feat/*]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: test-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Set up uv
|
||||
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
|
||||
with:
|
||||
enable-cache: true
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --locked --extra dev
|
||||
|
||||
- name: Set up matplotlib cache
|
||||
run: |
|
||||
echo "MPLCONFIGDIR=$RUNNER_TEMP/matplotlib" >> "$GITHUB_ENV"
|
||||
mkdir -p "$RUNNER_TEMP/matplotlib"
|
||||
|
||||
- name: Run tests with coverage
|
||||
run: |
|
||||
uv run pytest \
|
||||
--cov=src/meshmon \
|
||||
--cov=scripts \
|
||||
--cov-report=xml \
|
||||
--cov-report=html \
|
||||
--cov-report=term-missing \
|
||||
--cov-fail-under=95 \
|
||||
--junitxml=test-results.xml \
|
||||
-n auto \
|
||||
--tb=short \
|
||||
-q
|
||||
|
||||
- name: Coverage summary
|
||||
if: always()
|
||||
run: |
|
||||
{
|
||||
echo "### Coverage (Python ${{ matrix.python-version }})"
|
||||
if [ -f .coverage ]; then
|
||||
uv run coverage report -m
|
||||
else
|
||||
echo "No coverage data found."
|
||||
fi
|
||||
echo ""
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Upload coverage HTML report
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
if: always() && matrix.python-version == '3.14'
|
||||
with:
|
||||
name: coverage-report-html-${{ matrix.python-version }}
|
||||
path: htmlcov/
|
||||
if-no-files-found: warn
|
||||
retention-days: 7
|
||||
|
||||
- name: Upload coverage XML report
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
if: always() && matrix.python-version == '3.14'
|
||||
with:
|
||||
name: coverage-report-xml-${{ matrix.python-version }}
|
||||
path: coverage.xml
|
||||
if-no-files-found: warn
|
||||
retention-days: 7
|
||||
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
if: always()
|
||||
with:
|
||||
name: test-results-${{ matrix.python-version }}
|
||||
path: test-results.xml
|
||||
if-no-files-found: warn
|
||||
retention-days: 7
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
with:
|
||||
python-version: "3.14"
|
||||
|
||||
- name: Set up uv
|
||||
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
|
||||
with:
|
||||
enable-cache: true
|
||||
python-version: "3.14"
|
||||
|
||||
- name: Install linters
|
||||
run: uv sync --locked --extra dev --no-install-project
|
||||
|
||||
- name: Run ruff
|
||||
run: uv run ruff check src/ tests/ scripts/
|
||||
|
||||
- name: Run mypy
|
||||
run: uv run mypy src/meshmon --ignore-missing-imports --no-error-summary
|
||||
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
|
||||
This changelog is automatically generated by [release-please](https://github.com/googleapis/release-please) based on [Conventional Commits](https://www.conventionalcommits.org/).
|
||||
|
||||
## [0.2.16](https://github.com/jorijn/meshcore-stats/compare/v0.2.15...v0.2.16) (2026-02-09)
|
||||
|
||||
## Fork 01 by iarv@an0n.eu
|
||||
|
||||
### Features
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
A monitoring system for MeshCore LoRa mesh networks. Collects metrics from companion and repeater nodes, stores them in SQLite, and generates a static dashboard with interactive charts.
|
||||
|
||||
**Live demo:** [meshcore.jorijn.com](https://meshcore.jorijn.com)
|
||||
## Fork 01 by iarv@an0n.eu
|
||||
|
||||
<p>
|
||||
<img src="docs/screenshot-1.png" width="49%" alt="MeshCore Stats Dashboard">
|
||||
|
||||
Reference in New Issue
Block a user