Compare commits
3 Commits
master
..
13af3aeedf
| Author | SHA1 | Date | |
|---|---|---|---|
| 13af3aeedf | |||
| 3de605d321 | |||
| 9ca3340f10 |
@@ -31,24 +31,6 @@ repos:
|
||||
"-s",
|
||||
]
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: pytest
|
||||
name: Run unit tests
|
||||
entry: make test
|
||||
language: system
|
||||
types: [python]
|
||||
pass_filenames: false
|
||||
|
||||
- repo: https://github.com/python-jsonschema/check-jsonschema
|
||||
rev: 0.37.2
|
||||
hooks:
|
||||
- id: check-jsonschema
|
||||
name: Validate RPM package metadata
|
||||
files: ^rpms/[^/]+/metadata\.yaml$
|
||||
args: [--schemafile, schema/metadata.json]
|
||||
language_version: python3.11
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.14.7
|
||||
hooks:
|
||||
|
||||
@@ -31,18 +31,12 @@ steps:
|
||||
image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
commands:
|
||||
- |
|
||||
HOST="https://artifactapi.k8s.syd1.au.unkin.net"
|
||||
REPO="rpm-vendor-el8"
|
||||
for rpm in $(find /woodpecker/src/git.unkin.net/unkin/rpmbuilder/ -type f -name "*.rpm"); do
|
||||
FILE=$$(basename "$$rpm")
|
||||
# artifactapi has no HEAD route (405); probe the served Packages path.
|
||||
code=$$(curl -s -o /dev/null -w '%{http_code}' "$$HOST/api/v2/remotes/$$REPO/files/Packages/$$FILE" || true)
|
||||
if [ "$$code" = "200" ]; then
|
||||
echo "$$FILE already exists in $$REPO; skipping"
|
||||
continue
|
||||
fi
|
||||
curl -f -X PUT "$$HOST/api/v2/remotes/$$REPO/files/$$FILE" -H "Content-Type: application/x-rpm" --data-binary @"$$rpm"
|
||||
curl --user droneci:$${DRONECI_PASSWORD} --upload-file $rpm https://git.unkin.net/api/packages/unkin/rpm/almalinux/el8/upload
|
||||
done
|
||||
environment:
|
||||
DRONECI_PASSWORD:
|
||||
from_secret: DRONECI_PASSWORD
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
|
||||
@@ -31,18 +31,12 @@ steps:
|
||||
image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
commands:
|
||||
- |
|
||||
HOST="https://artifactapi.k8s.syd1.au.unkin.net"
|
||||
REPO="rpm-vendor-el9"
|
||||
for rpm in $(find /woodpecker/src/git.unkin.net/unkin/rpmbuilder/ -type f -name "*.rpm"); do
|
||||
FILE=$$(basename "$$rpm")
|
||||
# artifactapi has no HEAD route (405); probe the served Packages path.
|
||||
code=$$(curl -s -o /dev/null -w '%{http_code}' "$$HOST/api/v2/remotes/$$REPO/files/Packages/$$FILE" || true)
|
||||
if [ "$$code" = "200" ]; then
|
||||
echo "$$FILE already exists in $$REPO; skipping"
|
||||
continue
|
||||
fi
|
||||
curl -f -X PUT "$$HOST/api/v2/remotes/$$REPO/files/$$FILE" -H "Content-Type: application/x-rpm" --data-binary @"$$rpm"
|
||||
curl --user droneci:$${DRONECI_PASSWORD} --upload-file $rpm https://git.unkin.net/api/packages/unkin/rpm/almalinux/el9/upload
|
||||
done
|
||||
environment:
|
||||
DRONECI_PASSWORD:
|
||||
from_secret: DRONECI_PASSWORD
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
|
||||
@@ -31,18 +31,12 @@ steps:
|
||||
image: git.unkin.net/unkin/fedora42-rpmbuilder:latest
|
||||
commands:
|
||||
- |
|
||||
HOST="https://artifactapi.k8s.syd1.au.unkin.net"
|
||||
REPO="rpm-vendor-f42"
|
||||
for rpm in $(find /woodpecker/src/git.unkin.net/unkin/rpmbuilder/ -type f -name "*.rpm"); do
|
||||
FILE=$$(basename "$$rpm")
|
||||
# artifactapi has no HEAD route (405); probe the served Packages path.
|
||||
code=$$(curl -s -o /dev/null -w '%{http_code}' "$$HOST/api/v2/remotes/$$REPO/files/Packages/$$FILE" || true)
|
||||
if [ "$$code" = "200" ]; then
|
||||
echo "$$FILE already exists in $$REPO; skipping"
|
||||
continue
|
||||
fi
|
||||
curl -f -X PUT "$$HOST/api/v2/remotes/$$REPO/files/$$FILE" -H "Content-Type: application/x-rpm" --data-binary @"$$rpm"
|
||||
curl --user droneci:$${DRONECI_PASSWORD} --upload-file $rpm https://git.unkin.net/api/packages/unkin/rpm/fedora/42/upload
|
||||
done
|
||||
environment:
|
||||
DRONECI_PASSWORD:
|
||||
from_secret: DRONECI_PASSWORD
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
|
||||
@@ -31,18 +31,12 @@ steps:
|
||||
image: git.unkin.net/unkin/fedora43-rpmbuilder:latest
|
||||
commands:
|
||||
- |
|
||||
HOST="https://artifactapi.k8s.syd1.au.unkin.net"
|
||||
REPO="rpm-vendor-f43"
|
||||
for rpm in $(find /woodpecker/src/git.unkin.net/unkin/rpmbuilder/ -type f -name "*.rpm"); do
|
||||
FILE=$$(basename "$$rpm")
|
||||
# artifactapi has no HEAD route (405); probe the served Packages path.
|
||||
code=$$(curl -s -o /dev/null -w '%{http_code}' "$$HOST/api/v2/remotes/$$REPO/files/Packages/$$FILE" || true)
|
||||
if [ "$$code" = "200" ]; then
|
||||
echo "$$FILE already exists in $$REPO; skipping"
|
||||
continue
|
||||
fi
|
||||
curl -f -X PUT "$$HOST/api/v2/remotes/$$REPO/files/$$FILE" -H "Content-Type: application/x-rpm" --data-binary @"$$rpm"
|
||||
curl --user droneci:$${DRONECI_PASSWORD} --upload-file $rpm https://git.unkin.net/api/packages/unkin/rpm/fedora/43/upload
|
||||
done
|
||||
environment:
|
||||
DRONECI_PASSWORD:
|
||||
from_secret: DRONECI_PASSWORD
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
|
||||
@@ -31,18 +31,12 @@ steps:
|
||||
image: git.unkin.net/unkin/fedora44-rpmbuilder:latest
|
||||
commands:
|
||||
- |
|
||||
HOST="https://artifactapi.k8s.syd1.au.unkin.net"
|
||||
REPO="rpm-vendor-f44"
|
||||
for rpm in $(find /woodpecker/src/git.unkin.net/unkin/rpmbuilder/ -type f -name "*.rpm"); do
|
||||
FILE=$$(basename "$$rpm")
|
||||
# artifactapi has no HEAD route (405); probe the served Packages path.
|
||||
code=$$(curl -s -o /dev/null -w '%{http_code}' "$$HOST/api/v2/remotes/$$REPO/files/Packages/$$FILE" || true)
|
||||
if [ "$$code" = "200" ]; then
|
||||
echo "$$FILE already exists in $$REPO; skipping"
|
||||
continue
|
||||
fi
|
||||
curl -f -X PUT "$$HOST/api/v2/remotes/$$REPO/files/$$FILE" -H "Content-Type: application/x-rpm" --data-binary @"$$rpm"
|
||||
curl --user droneci:$${DRONECI_PASSWORD} --upload-file $rpm https://git.unkin.net/api/packages/unkin/rpm/fedora/44/upload
|
||||
done
|
||||
environment:
|
||||
DRONECI_PASSWORD:
|
||||
from_secret: DRONECI_PASSWORD
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
|
||||
@@ -11,7 +11,7 @@ DISTRO ?= almalinux/el9
|
||||
PACKAGES := $(shell find $(ROOT_DIR)/rpms -mindepth 1 -maxdepth 1 -type d -exec test -f {}/metadata.yaml \; -print | xargs -n1 basename | sort)
|
||||
|
||||
# Default target to build all packages
|
||||
.PHONY: all list build clean test
|
||||
.PHONY: all list build clean
|
||||
all: build-all
|
||||
|
||||
# List all available packages
|
||||
@@ -47,10 +47,6 @@ dry-run:
|
||||
@echo "Dry run - showing what would be built for distro $(DISTRO):"
|
||||
$(BUILD_TOOL) build-all --distro $(DISTRO) --dry-run
|
||||
|
||||
# Run unit tests
|
||||
test:
|
||||
@uv run --group dev pytest tests/ -q; rc=$$?; [ $$rc -eq 5 ] && exit 0 || exit $$rc
|
||||
|
||||
# Clean target
|
||||
clean:
|
||||
@echo "Cleaning build artifacts..."
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
[project]
|
||||
name = "rpmbuilder"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.11"
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pytest>=8",
|
||||
"jsonschema>=4",
|
||||
"pyyaml>=6",
|
||||
]
|
||||
@@ -11,9 +11,10 @@ builds:
|
||||
release: 1
|
||||
repository:
|
||||
- almalinux/el8
|
||||
version: 2.1.156
|
||||
version: 2.1.126
|
||||
- image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
release: 1
|
||||
repository:
|
||||
- almalinux/el9
|
||||
version: 2.1.156
|
||||
version: 2.1.126
|
||||
claude_ai: true
|
||||
|
||||
@@ -13,9 +13,9 @@ builds:
|
||||
- almalinux/el8
|
||||
image: git.unkin.net/unkin/almalinux8-rpmbuilder:latest
|
||||
release: 1
|
||||
version: 3.6.10
|
||||
version: 3.6.7
|
||||
- repository:
|
||||
- almalinux/el9
|
||||
image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
release: 1
|
||||
version: 3.6.10
|
||||
version: 3.6.7
|
||||
|
||||
@@ -13,9 +13,9 @@ builds:
|
||||
- almalinux/el8
|
||||
image: git.unkin.net/unkin/almalinux8-rpmbuilder:latest
|
||||
release: 1
|
||||
version: '26.2'
|
||||
version: '26.1'
|
||||
- repository:
|
||||
- almalinux/el9
|
||||
image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
release: 1
|
||||
version: '26.2'
|
||||
version: '26.1'
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
|
||||
set -e
|
||||
|
||||
# Download the pre-built RPM from GitHub releases.
|
||||
# Upstream always publishes the release-1 asset (nzbget-<version>-1.x86_64.rpm);
|
||||
# the source URL must use the upstream asset name, not PACKAGE_RELEASE, which
|
||||
# carries the dist tag (e.g. 1.el9) and does not exist upstream. Only the local
|
||||
# output filename is dist-tagged, mirroring the code-server package.
|
||||
# Download the pre-built RPM from GitHub releases
|
||||
curl -L -o /app/dist/nzbget-${PACKAGE_VERSION}-${PACKAGE_RELEASE}.x86_64.rpm \
|
||||
https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote/github/nzbgetcom/nzbget/releases/download/v$PACKAGE_VERSION/nzbget-${PACKAGE_VERSION}-1.x86_64.rpm
|
||||
https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote/github/nzbgetcom/nzbget/releases/download/v$PACKAGE_VERSION/nzbget-${PACKAGE_VERSION}-${PACKAGE_RELEASE}.x86_64.rpm
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# puppet-initial
|
||||
|
||||
A firstrun bootstrap script and oneshot systemd service that initialises a
|
||||
freshly-provisioned host into Puppet:
|
||||
|
||||
1. Sets the FQDN under `.main.unkin.net`.
|
||||
2. Fetches the Puppet CA certificate from the CA service.
|
||||
3. Registers the node with a noop agent run against the CA.
|
||||
4. Runs the agent a few times against the compile master, then enables the
|
||||
`puppet` service and disables itself.
|
||||
|
||||
## Puppet CA endpoint
|
||||
|
||||
The CA endpoint defaults to the in-cluster puppetserver CA service
|
||||
`puppetca.k8s.syd1.au.unkin.net:8140` (serving the standard
|
||||
`/puppet-ca/v1/certificate/ca` API).
|
||||
|
||||
It is overridable via the environment. The `puppet-initial.service` unit reads
|
||||
`/etc/sysconfig/puppet-initial` (`EnvironmentFile=-`, so the file is optional),
|
||||
which the RPM ships as a commented `%config(noreplace)` example:
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
|-----------------|----------------------------------|-------------------------------------------------------------|
|
||||
| `PUPPETCA_HOST` | `puppetca.k8s.syd1.au.unkin.net` | CA hostname (CA cert fetch + `--server` for registration). |
|
||||
| `PUPPETCA_PORT` | `8140` | CA API port. |
|
||||
|
||||
### Overriding from kickstart
|
||||
|
||||
A kickstart `%post` can point a host at a different CA without rebuilding the
|
||||
RPM by writing the sysconfig file before the service starts:
|
||||
|
||||
```bash
|
||||
%post
|
||||
cat > /etc/sysconfig/puppet-initial <<'EOF'
|
||||
PUPPETCA_HOST=puppetca.k8s.syd1.au.unkin.net
|
||||
PUPPETCA_PORT=8140
|
||||
EOF
|
||||
```
|
||||
@@ -11,9 +11,9 @@ builds:
|
||||
release: '1'
|
||||
repository:
|
||||
- almalinux/el8
|
||||
version: 1.0.5
|
||||
version: 1.0.3
|
||||
- image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
release: '1'
|
||||
repository:
|
||||
- almalinux/el9
|
||||
version: 1.0.5
|
||||
version: 1.0.3
|
||||
|
||||
@@ -15,7 +15,7 @@ license: ${PACKAGE_LICENSE}
|
||||
disable_globbing: false
|
||||
|
||||
depends:
|
||||
- openvox-agent
|
||||
- puppet-agent
|
||||
|
||||
# Files to include in the package
|
||||
contents:
|
||||
@@ -31,13 +31,6 @@ contents:
|
||||
mode: 0644
|
||||
owner: root
|
||||
group: root
|
||||
- src: /app/resources/puppet-initial.sysconfig
|
||||
dst: /etc/sysconfig/puppet-initial
|
||||
type: config|noreplace
|
||||
file_info:
|
||||
mode: 0644
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
# Scripts to run during installation/removal (optional)
|
||||
scripts:
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Puppet CA endpoint. Overridable via the environment (systemd reads
|
||||
# /etc/sysconfig/puppet-initial via EnvironmentFile), so kickstart %post can
|
||||
# point a host at a different CA without rebuilding the RPM. Defaults to the
|
||||
# in-cluster puppetserver CA service.
|
||||
PUPPETCA_HOST="${PUPPETCA_HOST:-puppetca.k8s.syd1.au.unkin.net}"
|
||||
PUPPETCA_PORT="${PUPPETCA_PORT:-8140}"
|
||||
|
||||
# Ensure the hostname is set
|
||||
hostnamectl set-hostname $(hostname -s).main.unkin.net
|
||||
grep '^HOSTNAME=' /etc/sysconfig/network | cut -d= -f2 | grep -q '\.' || sed -i 's/^\(HOSTNAME=[^\.]*\)$/\1.main.unkin.net/' /etc/sysconfig/network
|
||||
|
||||
# Install CA for Puppet
|
||||
test -f /etc/puppetlabs/puppet/ssl/certs/ca.pem || mkdir -p /etc/puppetlabs/puppet/ssl/certs && wget --no-check-certificate "https://${PUPPETCA_HOST}:${PUPPETCA_PORT}/puppet-ca/v1/certificate/ca" -O /etc/puppetlabs/puppet/ssl/certs/ca.pem
|
||||
test -f /etc/puppetlabs/puppet/ssl/certs/ca.pem || mkdir -p /etc/puppetlabs/puppet/ssl/certs && wget --no-check-certificate https://puppetca.query.consul:8140/puppet-ca/v1/certificate/ca -O /etc/puppetlabs/puppet/ssl/certs/ca.pem
|
||||
|
||||
# Registering to Puppet server
|
||||
/opt/puppetlabs/bin/puppet agent --test --server "${PUPPETCA_HOST}" --noop --onetime --no-daemonize --verbose
|
||||
/opt/puppetlabs/bin/puppet agent --test --server puppetca.query.consul --noop --onetime --no-daemonize --verbose
|
||||
|
||||
# Running Puppet agent five times with a 30-second gap between each run, stop puppet service at the end of each run
|
||||
for i in {1..5}; do
|
||||
|
||||
@@ -5,7 +5,6 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
EnvironmentFile=-/etc/sysconfig/puppet-initial
|
||||
ExecStart=/usr/local/bin/puppet-initial
|
||||
RemainAfterExit=true
|
||||
ExecStop=/bin/true
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# Environment overrides for the puppet-initial firstrun bootstrap.
|
||||
# Read by the puppet-initial.service unit (EnvironmentFile=-/etc/sysconfig/puppet-initial).
|
||||
# A kickstart %post can write this file to point a host at a different Puppet CA
|
||||
# without rebuilding the RPM. All values are optional; the defaults below match
|
||||
# the shipped in-cluster puppetserver CA service.
|
||||
|
||||
# Hostname of the Puppet CA service. Used both to fetch the CA certificate
|
||||
# (https://<host>:<port>/puppet-ca/v1/certificate/ca) and as --server for the
|
||||
# initial noop agent registration run.
|
||||
#PUPPETCA_HOST=puppetca.k8s.syd1.au.unkin.net
|
||||
|
||||
# Port the Puppet CA API listens on.
|
||||
#PUPPETCA_PORT=8140
|
||||
@@ -6,7 +6,7 @@ set -e
|
||||
dnf install -y unzip
|
||||
|
||||
# Download and extract vault
|
||||
curl -L -o /app/vault.zip https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote/hashicorp-releases/vault/${PACKAGE_VERSION}/vault_${PACKAGE_VERSION}_linux_amd64.zip
|
||||
curl -L -o /app/vault.zip https://releases.hashicorp.com/vault/${PACKAGE_VERSION}/vault_${PACKAGE_VERSION}_linux_amd64.zip
|
||||
unzip vault.zip
|
||||
|
||||
# Process the nfpm.yaml template with environment variables
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false,
|
||||
"description": "Schema for rpms/*/metadata.yaml files",
|
||||
"properties": {
|
||||
"arch": {
|
||||
"enum": [
|
||||
"amd64",
|
||||
"arm64",
|
||||
"x86_64"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"builds": {
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"image": {
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-zA-Z0-9][a-zA-Z0-9\\-_.:/@]+$",
|
||||
"type": "string"
|
||||
},
|
||||
"release": {
|
||||
"oneOf": [
|
||||
{
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"items": {
|
||||
"enum": [
|
||||
"almalinux/el8",
|
||||
"almalinux/el9",
|
||||
"fedora/42",
|
||||
"fedora/43",
|
||||
"fedora/44"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
},
|
||||
"version": {
|
||||
"minLength": 1,
|
||||
"pattern": "^[0-9]+(\\.[0-9]+)*(-[a-zA-Z0-9]+)*$",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repository",
|
||||
"image",
|
||||
"release",
|
||||
"version"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
},
|
||||
"description": {
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"dist_tag": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"github": {
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-zA-Z0-9\\-_]+/[a-zA-Z0-9\\-_.]+$",
|
||||
"type": "string"
|
||||
},
|
||||
"github_release_pattern": {
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"homepage": {
|
||||
"minLength": 1,
|
||||
"pattern": "^https?://.+",
|
||||
"type": "string"
|
||||
},
|
||||
"license": {
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"maintainer": {
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-zA-Z0-9][a-zA-Z0-9\\-_.]*$",
|
||||
"type": "string"
|
||||
},
|
||||
"platform": {
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"description",
|
||||
"builds"
|
||||
],
|
||||
"title": "RPM Package Metadata",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
# Tests for tools/build and tools/update-gh.
|
||||
# See https://git.unkin.net/unkin/rpmbuilder/issues/162
|
||||
@@ -1,36 +0,0 @@
|
||||
"""Validate every rpms/*/metadata.yaml against schema/metadata.json."""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import jsonschema
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
REPO_ROOT = Path(__file__).parent.parent
|
||||
SCHEMA_FILE = REPO_ROOT / "schema" / "metadata.json"
|
||||
RPMS_DIR = REPO_ROOT / "rpms"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def schema():
|
||||
with open(SCHEMA_FILE) as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def metadata_files():
|
||||
return sorted(RPMS_DIR.glob("*/metadata.yaml"))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("metadata_file", metadata_files(), ids=lambda p: p.parent.name)
|
||||
def test_metadata_valid(metadata_file, schema):
|
||||
with open(metadata_file) as f:
|
||||
data = yaml.safe_load(f)
|
||||
|
||||
validator = jsonschema.Draft7Validator(schema)
|
||||
errors = sorted(validator.iter_errors(data), key=str)
|
||||
|
||||
assert not errors, "\n".join(
|
||||
f" {'.'.join(str(p) for p in e.absolute_path) or '(root)'}: {e.message}"
|
||||
for e in errors
|
||||
)
|
||||
+47
-69
@@ -63,16 +63,6 @@ def _make_session(retries: int = 3, backoff_factor: float = 0.5) -> requests.Ses
|
||||
# requests.Session is thread-safe for concurrent requests.
|
||||
_gitea_session = _make_session()
|
||||
_github_session = _make_session()
|
||||
_artifactapi_session = _make_session()
|
||||
# artifactapi serves an internally-signed TLS cert. Verify against the OS CA
|
||||
# bundle (which includes the internal CA, as dnf/curl in the builder images do)
|
||||
# rather than certifi's Mozilla bundle, which does not trust it.
|
||||
_artifactapi_session.verify = os.getenv('ARTIFACTAPI_CA_BUNDLE') or next(
|
||||
(p for p in ('/etc/pki/tls/certs/ca-bundle.crt',
|
||||
'/etc/ssl/certs/ca-certificates.crt')
|
||||
if os.path.exists(p)),
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
# ==================== VALIDATION SCHEMA ====================
|
||||
@@ -577,77 +567,68 @@ def effective_release(base_release: str, distro: str, use_dist_tag: bool) -> str
|
||||
return f"{base_release}.{tag}" if tag else base_release
|
||||
|
||||
|
||||
def get_vendor_repo(distro: str) -> str:
|
||||
def check_package_exists(package_name: str, version: str, release: str) -> bool:
|
||||
"""
|
||||
Map a distro path to its artifactapi rpm-vendor local repo name.
|
||||
Check if a package version exists in the Gitea package registry.
|
||||
|
||||
Examples:
|
||||
'almalinux/el8' -> 'rpm-vendor-el8'
|
||||
'almalinux/el9' -> 'rpm-vendor-el9'
|
||||
'fedora/42' -> 'rpm-vendor-f42'
|
||||
"""
|
||||
if distro.startswith('almalinux/'):
|
||||
return 'rpm-vendor-' + distro.split('/', 1)[1]
|
||||
if distro.startswith('fedora/'):
|
||||
return 'rpm-vendor-f' + distro.split('/', 1)[1]
|
||||
return 'rpm-vendor'
|
||||
|
||||
|
||||
def get_rpm_arch(arch: str) -> str:
|
||||
"""Map a metadata arch to the RPM architecture used in built filenames."""
|
||||
return {'amd64': 'x86_64', 'arm64': 'aarch64'}.get(arch, arch)
|
||||
|
||||
|
||||
def check_package_exists(
|
||||
package_name: str,
|
||||
version: str,
|
||||
release: str,
|
||||
distro: str,
|
||||
arch: str = 'amd64',
|
||||
) -> bool:
|
||||
"""
|
||||
Check if a built RPM already exists in the artifactapi rpm-vendor repo.
|
||||
|
||||
Each distro publishes to its own local repo (rpm-vendor-el9, rpm-vendor-f42,
|
||||
...), and dist_tag disambiguation is carried in the release string (e.g.
|
||||
'1.el9', '1.fc43'), so the served RPM filename is unique per distro. We probe
|
||||
the served path under Packages/ with a GET (artifactapi has no HEAD route).
|
||||
Distro disambiguation is handled by the caller via the release string:
|
||||
when dist_tag is enabled the caller appends the dist tag to release
|
||||
(e.g. '1.el9', '1.fc43') so each distro maps to a unique version entry.
|
||||
|
||||
Args:
|
||||
package_name: Name of the package
|
||||
version: Version string
|
||||
release: Effective release (may include dist tag suffix, e.g. '1.el9')
|
||||
distro: Target distro (e.g. 'almalinux/el9', 'fedora/42')
|
||||
arch: Package arch from metadata (default 'amd64')
|
||||
release: Effective release (may include dist tag suffix)
|
||||
|
||||
Returns:
|
||||
True if the RPM is already published, False otherwise
|
||||
True if package exists, False otherwise
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
base_url = os.getenv('ARTIFACTAPI_URL', 'https://artifactapi.k8s.syd1.au.unkin.net')
|
||||
repo = get_vendor_repo(distro)
|
||||
rpm_file = f"{package_name}-{version}-{release}.{get_rpm_arch(arch)}.rpm"
|
||||
url = f"{base_url}/api/v2/remotes/{repo}/files/Packages/{rpm_file}"
|
||||
# Get configuration from environment
|
||||
base_url = os.getenv('GITEA_URL', 'https://git.unkin.net')
|
||||
owner = os.getenv('GITEA_OWNER', 'unkin')
|
||||
package_type = os.getenv('GITEA_PACKAGE_TYPE', 'rpm')
|
||||
|
||||
# Get Gitea token from Vault - fail hard if unavailable
|
||||
gitea_token = get_gitea_token()
|
||||
|
||||
try:
|
||||
# Normalize version by removing leading zeros (Gitea does this automatically)
|
||||
# e.g., "2025.08.03" becomes "2025.8.3"
|
||||
normalized_version = normalize_version(version)
|
||||
full_version = f"{normalized_version}-{release}"
|
||||
url = (
|
||||
f"{base_url}/api/v1/packages/{owner}/"
|
||||
f"{package_type}/{package_name}/{full_version}"
|
||||
)
|
||||
|
||||
_gitea_session.headers.update({'Authorization': f'token {gitea_token}'})
|
||||
|
||||
logger.debug(f"Checking package existence: {url}")
|
||||
response = _artifactapi_session.get(url, timeout=10)
|
||||
response = _gitea_session.get(url, timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
logger.debug(f"Package {rpm_file} exists in {repo}")
|
||||
return True
|
||||
if response.status_code == 404:
|
||||
logger.debug(f"Package {rpm_file} not found in {repo} (404)")
|
||||
package_info = response.json()
|
||||
exists = bool(package_info.get('id'))
|
||||
logger.debug(f"Package {package_name}:{full_version} {'exists' if exists else 'not found'}")
|
||||
return exists
|
||||
|
||||
elif response.status_code == 404:
|
||||
logger.debug(f"Package {package_name}:{full_version} not found (404)")
|
||||
return False
|
||||
elif response.status_code == 401:
|
||||
logger.error("Authentication failed. Check GITEA_API_TOKEN.")
|
||||
return False
|
||||
else:
|
||||
logger.warning(
|
||||
f"Unexpected response checking package {package_name}:{full_version}: "
|
||||
f"{response.status_code} - {response.text}"
|
||||
)
|
||||
return False
|
||||
logger.warning(
|
||||
f"Unexpected response checking {rpm_file} in {repo}: "
|
||||
f"{response.status_code}"
|
||||
)
|
||||
return False
|
||||
|
||||
except requests.RequestException as e:
|
||||
logger.error(f"Failed to check {rpm_file} in {repo}: {e}")
|
||||
logger.error(f"Failed to check package {package_name}:{version}-{release}: {e}")
|
||||
return False
|
||||
|
||||
|
||||
@@ -1270,7 +1251,6 @@ class PackageInfo:
|
||||
directory: Path
|
||||
distro: str = 'almalinux/el9'
|
||||
base_image: str = "git.unkin.net/unkin/almalinux9-rpmbuilder:latest"
|
||||
arch: str = 'amd64'
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name}-{self.version}-{self.release} ({self.distro})"
|
||||
@@ -1348,7 +1328,7 @@ class Builder:
|
||||
# Use the first repository as the distro identifier
|
||||
build_distro = repositories[0] if repositories else 'unknown'
|
||||
rel = effective_release(str(release), build_distro, use_dist_tag)
|
||||
packages.append(PackageInfo(package_name, version, rel, package_dir, build_distro, base_image, arch=metadata.get('arch', 'amd64')))
|
||||
packages.append(PackageInfo(package_name, version, rel, package_dir, build_distro, base_image))
|
||||
else:
|
||||
# Build for specific distro
|
||||
for build_config in build_configs:
|
||||
@@ -1361,7 +1341,7 @@ class Builder:
|
||||
# Check if the target distro matches any repository
|
||||
if distro in repositories and base_image and version and release:
|
||||
rel = effective_release(str(release), distro, use_dist_tag)
|
||||
packages.append(PackageInfo(package_name, version, rel, package_dir, distro, base_image, arch=metadata.get('arch', 'amd64')))
|
||||
packages.append(PackageInfo(package_name, version, rel, package_dir, distro, base_image))
|
||||
break
|
||||
else:
|
||||
# If no matching distro found, log a warning
|
||||
@@ -1460,7 +1440,7 @@ class Builder:
|
||||
return False
|
||||
|
||||
rel = effective_release(release, distro, use_dist_tag)
|
||||
package_info = PackageInfo(package, version, rel, package_dir, distro, base_image, arch=metadata.get('arch', 'amd64'))
|
||||
package_info = PackageInfo(package, version, rel, package_dir, distro, base_image)
|
||||
return self._build_package(package_info, dry_run, force, native, buildah)
|
||||
|
||||
def build_all(self, dry_run: bool = False, force: bool = False, parallel: int = 4, distro: str = 'el/9', native: bool = False, buildah: bool = False) -> bool:
|
||||
@@ -1542,9 +1522,7 @@ class Builder:
|
||||
if check_package_exists(
|
||||
package_info.name,
|
||||
package_info.version,
|
||||
package_info.release,
|
||||
package_info.distro,
|
||||
package_info.arch,
|
||||
package_info.release
|
||||
):
|
||||
self.logger.info(
|
||||
f"Skipping {package_info} (already exists in repository)"
|
||||
|
||||
Reference in New Issue
Block a user