Compare commits
10
Commits
2026.10.09
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e05e6a34cd | ||
|
|
dc1c2d2766 | ||
|
|
520cf73c13 | ||
|
|
44f539a9dc | ||
|
|
e056c864ed | ||
|
|
aa0468a219 | ||
|
|
6a3e01e127 | ||
|
|
853103d93d | ||
|
|
866cf4ca50 | ||
|
|
41512c340c
|
No files matched your search
@@ -1,169 +0,0 @@
|
|||||||
name: Build, Scan & Publish Image
|
|
||||||
|
|
||||||
# Publishing a Gitea release (for example with tag 2026.09.25) builds the image,
|
|
||||||
# scans it with Grype (fails on fixable HIGH/CRITICAL findings), and pushes it to
|
|
||||||
# this repository's registry. A release pushes the release tag plus "latest"
|
|
||||||
# (non-prereleases only). A manual run builds and pushes only the tag supplied
|
|
||||||
# in the input.
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
tag:
|
|
||||||
description: Image tag to build and push when run manually
|
|
||||||
required: false
|
|
||||||
default: latest
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: image-${{ github.ref }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-scan-push:
|
|
||||||
runs-on: running-man
|
|
||||||
env:
|
|
||||||
REGISTRY: git.sysmd.uk
|
|
||||||
IMAGE: git.sysmd.uk/mdaleo404/kovi
|
|
||||||
COSIGN_VERSION: v3.0.5
|
|
||||||
GRYPE_VERSION: v0.110.0
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
submodules: false
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Resolve release tag
|
|
||||||
env:
|
|
||||||
EVENT_TAG: ${{ github.event.release.tag_name }}
|
|
||||||
INPUT_TAG: ${{ github.event.inputs.tag }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
TAG="${EVENT_TAG:-}"
|
|
||||||
if [ -z "$TAG" ]; then TAG="${INPUT_TAG:-}"; fi
|
|
||||||
if [ -z "$TAG" ]; then TAG="${GITHUB_REF_NAME:-}"; fi
|
|
||||||
if [ -z "$TAG" ]; then TAG="$(git describe --tags --exact-match 2>/dev/null || echo "")"; fi
|
|
||||||
if [ -z "$TAG" ]; then echo "Could not determine a tag for this run." >&2; exit 1; fi
|
|
||||||
SAFE_TAG="${TAG//\//-}"
|
|
||||||
case "$SAFE_TAG" in
|
|
||||||
[A-Za-z0-9_]*) ;;
|
|
||||||
*) SAFE_TAG="v$SAFE_TAG" ;;
|
|
||||||
esac
|
|
||||||
{
|
|
||||||
echo "TAG=$SAFE_TAG"
|
|
||||||
echo "IMAGE_REF=${IMAGE}:${SAFE_TAG}"
|
|
||||||
} >> "$GITHUB_ENV"
|
|
||||||
if [ -n "${EVENT_TAG:-}" ] && [ "${{ github.event.release.prerelease }}" != "true" ]; then
|
|
||||||
echo "PUSH_LATEST=1" >> "$GITHUB_ENV"
|
|
||||||
fi
|
|
||||||
echo "Resolved image reference: ${IMAGE}:${SAFE_TAG}"
|
|
||||||
|
|
||||||
- name: Login to registry
|
|
||||||
run: |
|
|
||||||
echo "$REGISTRY_TOKEN" | docker login "${REGISTRY}" -u "$REGISTRY_USER" --password-stdin
|
|
||||||
env:
|
|
||||||
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
|
||||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
||||||
|
|
||||||
- name: Setup Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v4
|
|
||||||
|
|
||||||
- name: Build image
|
|
||||||
env:
|
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
TAGS=(-t "${IMAGE_REF}")
|
|
||||||
if [ "${PUSH_LATEST:-}" = "1" ]; then TAGS+=(-t "${IMAGE}:latest"); fi
|
|
||||||
docker buildx build \
|
|
||||||
--load \
|
|
||||||
--no-cache \
|
|
||||||
--secret id=gitea_token,env=GITEA_TOKEN \
|
|
||||||
"${TAGS[@]}" \
|
|
||||||
.
|
|
||||||
|
|
||||||
- name: Install Cosign
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
curl -fLO "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64"
|
|
||||||
chmod +x cosign-linux-amd64
|
|
||||||
mv cosign-linux-amd64 /usr/local/bin/cosign
|
|
||||||
|
|
||||||
- name: Install Grype
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
VERSION_NO_V="${GRYPE_VERSION#v}"
|
|
||||||
FILE="grype_${VERSION_NO_V}_linux_amd64.tar.gz"
|
|
||||||
BASE="https://github.com/anchore/grype/releases/download/${GRYPE_VERSION}"
|
|
||||||
|
|
||||||
curl -fLO "${BASE}/${FILE}"
|
|
||||||
curl -fLO "${BASE}/grype_${VERSION_NO_V}_checksums.txt"
|
|
||||||
curl -fLO "${BASE}/grype_${VERSION_NO_V}_checksums.txt.sig"
|
|
||||||
curl -fLO "${BASE}/grype_${VERSION_NO_V}_checksums.txt.pem"
|
|
||||||
|
|
||||||
cosign verify-blob \
|
|
||||||
--signature "grype_${VERSION_NO_V}_checksums.txt.sig" \
|
|
||||||
--certificate "grype_${VERSION_NO_V}_checksums.txt.pem" \
|
|
||||||
--certificate-identity-regexp "https://github.com/anchore/grype" \
|
|
||||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
|
|
||||||
"grype_${VERSION_NO_V}_checksums.txt"
|
|
||||||
|
|
||||||
CHECK=$(grep " ${FILE}$" "grype_${VERSION_NO_V}_checksums.txt")
|
|
||||||
[ -n "$CHECK" ] || { echo "Missing checksum"; exit 1; }
|
|
||||||
|
|
||||||
echo "$CHECK" | sha256sum -c -
|
|
||||||
|
|
||||||
tar -xzf "${FILE}"
|
|
||||||
mv grype /usr/local/bin/
|
|
||||||
|
|
||||||
- name: Scan image with Grype
|
|
||||||
id: grype
|
|
||||||
run: |
|
|
||||||
grype "${IMAGE_REF}" -o json > grype.json
|
|
||||||
|
|
||||||
echo "Fixable HIGH/CRITICAL vulnerabilities:"
|
|
||||||
jq -r '
|
|
||||||
.matches[]
|
|
||||||
| select(
|
|
||||||
(
|
|
||||||
.vulnerability.severity == "High" or
|
|
||||||
.vulnerability.severity == "Critical"
|
|
||||||
)
|
|
||||||
and
|
|
||||||
(
|
|
||||||
(.vulnerability.fix.versions | length) > 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
| "\(.artifact.name)@\(.artifact.version) -> \(.vulnerability.id) [\(.vulnerability.severity)] | fixed: \(.vulnerability.fix.versions[0])"
|
|
||||||
' grype.json
|
|
||||||
|
|
||||||
# Fail the build on fixable HIGH/CRITICAL vulnerabilities.
|
|
||||||
COUNT=$(jq '
|
|
||||||
[
|
|
||||||
.matches[]
|
|
||||||
| select(
|
|
||||||
(
|
|
||||||
.vulnerability.severity == "High" or
|
|
||||||
.vulnerability.severity == "Critical"
|
|
||||||
)
|
|
||||||
and
|
|
||||||
(
|
|
||||||
(.vulnerability.fix.versions | length) > 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
| length
|
|
||||||
' grype.json)
|
|
||||||
echo "Fixable HIGH/CRITICAL count: ${COUNT}"
|
|
||||||
if [ "$COUNT" != "0" ]; then
|
|
||||||
echo "::error::Grype found ${COUNT} fixable HIGH/CRITICAL vulnerabilities."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Push image
|
|
||||||
run: |
|
|
||||||
docker push "${IMAGE_REF}"
|
|
||||||
if [ "${PUSH_LATEST:-}" = "1" ]; then docker push "${IMAGE}:latest"; fi
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
name: Build, Scan & Publish Dev
|
||||||
|
|
||||||
|
# Build, scan, and reconcile on push to main and nightly. Publishes a moving
|
||||||
|
# "dev" tag on real pushes only, once the reconciler gate passes.
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
schedule:
|
||||||
|
- cron: "0 3 * * *"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: image-main
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-scan-push:
|
||||||
|
runs-on: running-man
|
||||||
|
env:
|
||||||
|
REGISTRY: git.sysmd.uk
|
||||||
|
IMAGE: git.sysmd.uk/mdaleo404/kovi
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
submodules: false
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Resolve dev tag
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
echo "IMAGE_REF=${IMAGE}:dev" >> "$GITHUB_ENV"
|
||||||
|
echo "Dev image: ${IMAGE}:dev"
|
||||||
|
|
||||||
|
- name: Login to registry
|
||||||
|
run: |
|
||||||
|
echo "$REGISTRY_TOKEN" | docker login "${REGISTRY}" -u "$REGISTRY_USER" --password-stdin
|
||||||
|
env:
|
||||||
|
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||||
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Setup Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
|
- name: Build image
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
docker buildx build --load --no-cache -t "${IMAGE_REF}" .
|
||||||
|
|
||||||
|
- name: Install Grype
|
||||||
|
uses: https://git.sysmd.uk/mdaleo404/actions/install-grype@v1
|
||||||
|
|
||||||
|
- name: Scan image with Grype
|
||||||
|
id: scan
|
||||||
|
continue-on-error: true
|
||||||
|
uses: https://git.sysmd.uk/mdaleo404/actions/scan-image@v1
|
||||||
|
with:
|
||||||
|
image: ${{ env.IMAGE_REF }}
|
||||||
|
fail-on-fixable: 'false'
|
||||||
|
|
||||||
|
- name: Reconcile Grype ignores
|
||||||
|
id: reconcile
|
||||||
|
continue-on-error: true
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.AUTOUPDATE_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
curl -fsSL -H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
"https://git.sysmd.uk/api/v1/repos/Homelab/renovate-runner/raw/scripts/reconcile-grype.sh?ref=main" \
|
||||||
|
-o /tmp/reconcile-grype.sh
|
||||||
|
bash /tmp/reconcile-grype.sh
|
||||||
|
|
||||||
|
- name: Notify Node-RED on vulnerabilities
|
||||||
|
if: steps.reconcile.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
jq -n --arg repo "${GITHUB_REPOSITORY,,}" '{ repo: $repo }' \
|
||||||
|
| curl -s -X POST https://nodered.sysmd.uk/vulns-alert \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data-binary @-
|
||||||
|
|
||||||
|
- name: Fail on actionable vulnerabilities
|
||||||
|
if: steps.reconcile.outcome == 'failure'
|
||||||
|
run: exit 1
|
||||||
|
|
||||||
|
- name: Push dev image
|
||||||
|
if: github.event_name != 'schedule' && steps.reconcile.outcome != 'failure'
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
docker push "${IMAGE_REF}"
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
name: Publish Release Image
|
||||||
|
|
||||||
|
# Promote the scanned dev image to the release tag (and latest); never rebuilds.
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: release-${{ github.ref }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
promote:
|
||||||
|
runs-on: running-man
|
||||||
|
env:
|
||||||
|
REGISTRY: git.sysmd.uk
|
||||||
|
IMAGE: git.sysmd.uk/mdaleo404/kovi
|
||||||
|
steps:
|
||||||
|
- name: Login to registry
|
||||||
|
run: |
|
||||||
|
echo "$REGISTRY_TOKEN" | docker login "${REGISTRY}" -u "$REGISTRY_USER" --password-stdin
|
||||||
|
env:
|
||||||
|
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||||
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Setup Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
|
- name: Resolve tags
|
||||||
|
env:
|
||||||
|
EVENT_TAG: ${{ github.event.release.tag_name }}
|
||||||
|
PRERELEASE: ${{ github.event.release.prerelease }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
TAG="${EVENT_TAG:?release tag missing}"
|
||||||
|
{
|
||||||
|
echo "TAG=${TAG}"
|
||||||
|
echo "SOURCE_REF=${IMAGE}:dev"
|
||||||
|
} >> "$GITHUB_ENV"
|
||||||
|
if [ "${PRERELEASE:-}" != "true" ]; then
|
||||||
|
echo "PUSH_LATEST=1" >> "$GITHUB_ENV"
|
||||||
|
fi
|
||||||
|
echo "Promoting ${IMAGE}:dev -> ${IMAGE}:${TAG}"
|
||||||
|
|
||||||
|
- name: Promote image
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
TAGS=(-t "${IMAGE}:${TAG}")
|
||||||
|
if [ "${PUSH_LATEST:-}" = "1" ]; then TAGS+=(-t "${IMAGE}:latest"); fi
|
||||||
|
docker buildx imagetools create "${TAGS[@]}" "${SOURCE_REF}"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
data/
|
||||||
|
tests/fixture-statistics.sqlite3
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Managed by renovate-runner/scripts/reconcile-grype.sh - do not edit by hand.
|
||||||
|
# Entries are High,Critical findings with a fix that no base image update provides.
|
||||||
|
ignore: []
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
FROM node:22-trixie-slim@sha256:154ba2f4d6fec323d28e4f4bb86bba4677f1223391a1979cf521304e03a98dfa
|
FROM node:22-trixie-slim@sha256:2117f713a417f3aff682ac8f5bc2ebbdf1beed51f36e67ab1e0fd3866b6d2d8f
|
||||||
|
|
||||||
# kovi does not vendor or copy Calibre code. The container installs Debian's
|
# kovi does not vendor or copy Calibre code. The container installs Debian's
|
||||||
# unmodified Calibre package and calls its fetch-ebook-metadata CLI as a separate
|
# unmodified Calibre package and calls its fetch-ebook-metadata CLI as a separate
|
||||||
|
|||||||
Reference in new issue
Block a user