Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 06b1797537 | |||
| 0fd817b13f | |||
| c7eddffbb3 | |||
| 17e0fadd44 | |||
| cfd1972d54 | |||
| 8cbd495004 | |||
| e04f19ed03 | |||
| ff054f42bb | |||
| 818a48fa78 | |||
| 111ea50d80 | |||
| 1b46845734 | |||
| 497b99c328 | |||
| 6b070d8c14 | |||
| dff743a00b | |||
| f408d3d705 | |||
| c4c018b1ee | |||
| b18e34c905 |
@@ -1,59 +0,0 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-8:
|
||||
runs-on: almalinux-8
|
||||
container:
|
||||
image: git.unkin.net/unkin/almalinux8-actionsdind:latest
|
||||
options: "--privileged --volume /etc/pki/tls/vault:/etc/pki/tls/vault:ro"
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Build Packages
|
||||
env:
|
||||
VAULT_ROLE_ID: ${{ secrets.RPMBUILDER_VAULT_ROLEID }}
|
||||
run: |
|
||||
./tools/build build-all --distro almalinux/el8
|
||||
|
||||
- name: Show RPMs
|
||||
run: |
|
||||
find /workspace -type f -name "*.rpm"
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: rpms-8
|
||||
path: /workspace/unkin/rpmbuilder/dist/*/*/*.rpm
|
||||
|
||||
build-9:
|
||||
runs-on: almalinux-8
|
||||
container:
|
||||
image: git.unkin.net/unkin/almalinux9-actionsdind:latest
|
||||
options: "--privileged --volume /etc/pki/tls/vault:/etc/pki/tls/vault:ro"
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Build Packages
|
||||
env:
|
||||
VAULT_ROLE_ID: ${{ secrets.RPMBUILDER_VAULT_ROLEID }}
|
||||
run: |
|
||||
./tools/build build-all --distro almalinux/el9
|
||||
|
||||
- name: Show RPMs
|
||||
run: |
|
||||
find /workspace -type f -name "*.rpm"
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: rpms-9
|
||||
path: /workspace/unkin/rpmbuilder/dist/*/*/*.rpm
|
||||
@@ -1,66 +0,0 @@
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy-8:
|
||||
runs-on: almalinux-8
|
||||
container:
|
||||
image: git.unkin.net/unkin/almalinux8-actionsdind:latest
|
||||
options: --privileged
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download Artifacts
|
||||
run: |
|
||||
mkdir -p /workspace/unkin/rpmbuilder/dist/almalinux/el8
|
||||
export PREVIOUS_RUN_ID=$((GITHUB_RUN_NUMBER - 1))
|
||||
curl -L -o /workspace/rpms.zip "https://git.unkin.net/${GITHUB_REPOSITORY}/actions/runs/${PREVIOUS_RUN_ID}/artifacts/rpms-8"
|
||||
unzip /workspace/rpms.zip -d /workspace/unkin/rpmbuilder/dist/almalinux/el8
|
||||
|
||||
- name: Show RPMs
|
||||
run: |
|
||||
find /workspace -type f -name "*.rpm"
|
||||
|
||||
- name: Upload RPMs to Gitea
|
||||
env:
|
||||
DRONECI_PASSWORD: ${{ secrets.DRONECI_PASSWORD }}
|
||||
run: |
|
||||
for rpm in $(find /workspace/unkin/rpmbuilder/dist/almalinux/el8 -type f -name "*.rpm"); do
|
||||
curl --user droneci:${{ secrets.DRONECI_PASSWORD }} --upload-file $rpm https://git.unkin.net/api/packages/unkin/rpm/almalinux/el8/upload
|
||||
done
|
||||
|
||||
deploy-9:
|
||||
runs-on: almalinux-8
|
||||
container:
|
||||
image: git.unkin.net/unkin/almalinux9-actionsdind:latest
|
||||
options: --privileged
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download Artifacts
|
||||
run: |
|
||||
mkdir -p /workspace/unkin/rpmbuilder/dist/almalinux/el9
|
||||
export PREVIOUS_RUN_ID=$((GITHUB_RUN_NUMBER - 1))
|
||||
curl -L -o /workspace/rpms.zip "https://git.unkin.net/${GITHUB_REPOSITORY}/actions/runs/${PREVIOUS_RUN_ID}/artifacts/rpms-9"
|
||||
unzip /workspace/rpms.zip -d /workspace/unkin/rpmbuilder/dist/almalinux/el9
|
||||
|
||||
- name: Show RPMs
|
||||
run: |
|
||||
find /workspace -type f -name "*.rpm"
|
||||
|
||||
- name: Upload RPMs to Gitea
|
||||
env:
|
||||
DRONECI_PASSWORD: ${{ secrets.DRONECI_PASSWORD }}
|
||||
run: |
|
||||
for rpm in $(find /workspace/unkin/rpmbuilder/dist/almalinux/el9 -type f -name "*.rpm"); do
|
||||
curl --user droneci:${{ secrets.DRONECI_PASSWORD }} --upload-file $rpm https://git.unkin.net/api/packages/unkin/rpm/almalinux/el9/upload
|
||||
done
|
||||
+16
-3
@@ -3,12 +3,24 @@ repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-json
|
||||
- id: check-added-large-files
|
||||
args: ['--maxkb=500']
|
||||
- id: check-merge-conflict
|
||||
- id: check-shebang-scripts-are-executable
|
||||
- id: check-symlinks
|
||||
- id: check-toml
|
||||
- id: check-yaml
|
||||
args: [--allow-multiple-documents]
|
||||
- id: check-merge-conflict
|
||||
- id: detect-aws-credentials
|
||||
args: [--allow-missing-credentials]
|
||||
- id: detect-private-key
|
||||
- id: end-of-file-fixer
|
||||
- id: forbid-new-submodules
|
||||
- id: pretty-format-json
|
||||
- id: trailing-whitespace
|
||||
|
||||
- repo: https://github.com/adrienverge/yamllint.git
|
||||
rev: v1.37.1
|
||||
hooks:
|
||||
@@ -18,6 +30,7 @@ repos:
|
||||
"-d {extends: relaxed, rules: {line-length: disable}}",
|
||||
"-s",
|
||||
]
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.14.7
|
||||
hooks:
|
||||
|
||||
@@ -3,14 +3,18 @@ when:
|
||||
|
||||
steps:
|
||||
- name: build rpms
|
||||
image: git.unkin.net/unkin/almalinux8-rpmbuilder:latest
|
||||
image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
commands:
|
||||
- mkdir -p /app/dist
|
||||
- ./tools/build build-all --distro almalinux/el8 --native
|
||||
- mkdir -p /woodpecker/rpms
|
||||
- ln -s /woodpecker/rpms /workspace
|
||||
- dnf install buildah -y
|
||||
- ./tools/build build-all --distro almalinux/el8 --buildah
|
||||
privileged: true
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
|
||||
- name: show rpms
|
||||
image: git.unkin.net/unkin/almalinux8-base:latest
|
||||
commands:
|
||||
- find /app/dist -type f -name "*.rpm"
|
||||
- find /woodpecker/src/git.unkin.net/unkin/rpmbuilder/ -type f -name "*.rpm"
|
||||
|
||||
@@ -5,12 +5,16 @@ steps:
|
||||
- name: build rpms
|
||||
image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
commands:
|
||||
- mkdir -p /app/dist/
|
||||
- ./tools/build build-all --distro almalinux/el9 --native
|
||||
- mkdir -p /woodpecker/rpms
|
||||
- ln -s /woodpecker/rpms /workspace
|
||||
- dnf install buildah -y
|
||||
- ./tools/build build-all --distro almalinux/el9 --buildah
|
||||
privileged: true
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
|
||||
- name: show rpms
|
||||
image: git.unkin.net/unkin/almalinux9-base:latest
|
||||
commands:
|
||||
- find /app/dist -type f -name "*.rpm"
|
||||
- find /woodpecker/src/git.unkin.net/unkin/rpmbuilder/ -type f -name "*.rpm"
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
when:
|
||||
- event: push
|
||||
branch: master
|
||||
|
||||
steps:
|
||||
- name: build-rpms
|
||||
image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
commands:
|
||||
- mkdir -p /woodpecker/rpms
|
||||
- ln -s /woodpecker/rpms /workspace
|
||||
- dnf install buildah -y
|
||||
- ./tools/build build-all --distro almalinux/el8 --buildah
|
||||
privileged: true
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
|
||||
- name: show-rpms
|
||||
image: git.unkin.net/unkin/almalinux9-base:latest
|
||||
commands:
|
||||
- find /woodpecker/src/git.unkin.net/unkin/rpmbuilder/ -type f -name "*.rpm"
|
||||
depends_on: [build-rpms]
|
||||
|
||||
- name: deploy-rpms
|
||||
image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
commands:
|
||||
- |
|
||||
for rpm in $(find /woodpecker/src/git.unkin.net/unkin/rpmbuilder/ -type f -name "*.rpm"); do
|
||||
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
|
||||
depends_on: [build-rpms, show-rpms]
|
||||
@@ -0,0 +1,37 @@
|
||||
when:
|
||||
- event: push
|
||||
branch: master
|
||||
|
||||
steps:
|
||||
- name: build-rpms
|
||||
image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
commands:
|
||||
- mkdir -p /woodpecker/rpms
|
||||
- ln -s /woodpecker/rpms /workspace
|
||||
- dnf install buildah -y
|
||||
- ./tools/build build-all --distro almalinux/el9 --buildah
|
||||
privileged: true
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
|
||||
- name: show-rpms
|
||||
image: git.unkin.net/unkin/almalinux9-base:latest
|
||||
commands:
|
||||
- find /woodpecker/src/git.unkin.net/unkin/rpmbuilder/ -type f -name "*.rpm"
|
||||
depends_on: [build-rpms]
|
||||
|
||||
- name: deploy-rpms
|
||||
image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
commands:
|
||||
- |
|
||||
for rpm in $(find /woodpecker/src/git.unkin.net/unkin/rpmbuilder/ -type f -name "*.rpm"); do
|
||||
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
|
||||
depends_on: [build-rpms, show-rpms]
|
||||
@@ -31,6 +31,11 @@ build-all-native:
|
||||
@echo "Building all packages natively (no Docker) for distro $(DISTRO)..."
|
||||
$(BUILD_TOOL) build-all --distro $(DISTRO) --native
|
||||
|
||||
# Build all packages using Buildah
|
||||
build-all-buildah:
|
||||
@echo "Building all packages using Buildah for distro $(DISTRO)..."
|
||||
$(BUILD_TOOL) build-all --distro $(DISTRO) --buildah
|
||||
|
||||
# Build specific package using Python tool
|
||||
.PHONY: $(PACKAGES)
|
||||
$(PACKAGES):
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
arch: amd64
|
||||
builds:
|
||||
- image: git.unkin.net/unkin/almalinux8-rpmbuilder:latest
|
||||
release: '1'
|
||||
repository: [almalinux/el8]
|
||||
version: 2.1.120
|
||||
- image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
release: '1'
|
||||
repository: [almalinux/el9]
|
||||
version: 2.1.120
|
||||
claude_ai: true
|
||||
description: Claude Code - Anthropic's agentic AI coding tool
|
||||
homepage: https://claude.ai/code
|
||||
license: Proprietary
|
||||
maintainer: Anthropic
|
||||
name: claude-code
|
||||
platform: linux
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Download claude-code binary
|
||||
wget -O /app/claude https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote/claude-ai/claude-code-releases/${PACKAGE_VERSION}/linux-x64/claude
|
||||
chmod +x /app/claude
|
||||
|
||||
# Process the nfpm.yaml template with environment variables
|
||||
envsubst < /app/resources/nfpm.yaml > /app/nfpm.yaml
|
||||
|
||||
# Build the RPM
|
||||
nfpm pkg --config /app/nfpm.yaml --target /app/dist --packager rpm
|
||||
@@ -0,0 +1,33 @@
|
||||
# nfpm.yaml
|
||||
|
||||
name: ${PACKAGE_NAME}
|
||||
version: ${PACKAGE_VERSION}
|
||||
release: ${PACKAGE_RELEASE}
|
||||
arch: ${PACKAGE_ARCH}
|
||||
platform: ${PACKAGE_PLATFORM}
|
||||
section: default
|
||||
priority: extra
|
||||
description: "${PACKAGE_DESCRIPTION}"
|
||||
|
||||
maintainer: ${PACKAGE_MAINTAINER}
|
||||
homepage: ${PACKAGE_HOMEPAGE}
|
||||
license: ${PACKAGE_LICENSE}
|
||||
|
||||
disable_globbing: false
|
||||
|
||||
replaces:
|
||||
- claude-code
|
||||
- claude
|
||||
|
||||
provides:
|
||||
- claude-code
|
||||
- claude
|
||||
|
||||
# Files to include in the package
|
||||
contents:
|
||||
- src: /app/claude
|
||||
dst: /usr/bin/claude
|
||||
file_info:
|
||||
mode: 0755
|
||||
owner: root
|
||||
group: root
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: stern
|
||||
github: stern/stern
|
||||
description: Multi pod and container log tailing for Kubernetes.
|
||||
arch: amd64
|
||||
platform: linux
|
||||
maintainer: stern
|
||||
homepage: https://github.com/stern/stern
|
||||
license: Apache-2.0
|
||||
builds:
|
||||
- repository:
|
||||
- almalinux/el8
|
||||
image: git.unkin.net/unkin/almalinux8-rpmbuilder:latest
|
||||
release: 1
|
||||
version: 1.33.1
|
||||
- repository:
|
||||
- almalinux/el9
|
||||
image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
release: 1
|
||||
version: 1.33.1
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
wget -O /app/stern_${PACKAGE_VERSION}_linux_amd64.tar.gz https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote/github/stern/stern/releases/download/v${PACKAGE_VERSION}/stern_${PACKAGE_VERSION}_linux_amd64.tar.gz
|
||||
tar xf /app/stern_${PACKAGE_VERSION}_linux_amd64.tar.gz
|
||||
|
||||
envsubst < /app/resources/nfpm.yaml > /app/nfpm.yaml
|
||||
|
||||
nfpm pkg --config /app/nfpm.yaml --target /app/dist --packager rpm
|
||||
@@ -0,0 +1,30 @@
|
||||
# nfpm.yaml
|
||||
|
||||
name: ${PACKAGE_NAME}
|
||||
version: ${PACKAGE_VERSION}
|
||||
release: ${PACKAGE_RELEASE}
|
||||
arch: ${PACKAGE_ARCH}
|
||||
platform: ${PACKAGE_PLATFORM}
|
||||
section: default
|
||||
priority: extra
|
||||
description: "${PACKAGE_DESCRIPTION}"
|
||||
|
||||
maintainer: ${PACKAGE_MAINTAINER}
|
||||
homepage: ${PACKAGE_HOMEPAGE}
|
||||
license: ${PACKAGE_LICENSE}
|
||||
|
||||
disable_globbing: false
|
||||
|
||||
replaces:
|
||||
- stern
|
||||
|
||||
provides:
|
||||
- stern
|
||||
|
||||
contents:
|
||||
- src: /app/stern
|
||||
dst: /usr/bin/stern
|
||||
file_info:
|
||||
mode: 0755
|
||||
owner: root
|
||||
group: root
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: tea
|
||||
github: unknown/tea
|
||||
description: The official CLI for Gitea.
|
||||
arch: amd64
|
||||
platform: linux
|
||||
maintainer: Gitea
|
||||
homepage: https://gitea.com/gitea/tea
|
||||
license: MIT
|
||||
builds:
|
||||
- repository:
|
||||
- almalinux/el8
|
||||
image: git.unkin.net/unkin/almalinux8-rpmbuilder:latest
|
||||
release: 1
|
||||
version: 0.14.0
|
||||
- repository:
|
||||
- almalinux/el9
|
||||
image: git.unkin.net/unkin/almalinux9-rpmbuilder:latest
|
||||
release: 1
|
||||
version: 0.14.0
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
curl -L --output /app/tea-linux-amd64 https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote/gitea-dl/tea/${PACKAGE_VERSION}/tea-${PACKAGE_VERSION}-linux-amd64
|
||||
chmod +x /app/tea-linux-amd64
|
||||
|
||||
envsubst < /app/resources/nfpm.yaml > /app/nfpm.yaml
|
||||
|
||||
nfpm pkg --config /app/nfpm.yaml --target /app/dist --packager rpm
|
||||
@@ -0,0 +1,30 @@
|
||||
# nfpm.yaml
|
||||
|
||||
name: ${PACKAGE_NAME}
|
||||
version: ${PACKAGE_VERSION}
|
||||
release: ${PACKAGE_RELEASE}
|
||||
arch: ${PACKAGE_ARCH}
|
||||
platform: ${PACKAGE_PLATFORM}
|
||||
section: default
|
||||
priority: extra
|
||||
description: "${PACKAGE_DESCRIPTION}"
|
||||
|
||||
maintainer: ${PACKAGE_MAINTAINER}
|
||||
homepage: ${PACKAGE_HOMEPAGE}
|
||||
license: ${PACKAGE_LICENSE}
|
||||
|
||||
disable_globbing: false
|
||||
|
||||
replaces:
|
||||
- tea
|
||||
|
||||
provides:
|
||||
- tea
|
||||
|
||||
contents:
|
||||
- src: /app/tea-linux-amd64
|
||||
dst: /usr/bin/tea
|
||||
file_info:
|
||||
mode: 0755
|
||||
owner: root
|
||||
group: root
|
||||
Regular → Executable
+230
-16
@@ -626,6 +626,25 @@ def check_native_build_deps() -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def check_buildah_available() -> bool:
|
||||
"""
|
||||
Check if Buildah is available.
|
||||
|
||||
Returns:
|
||||
True if Buildah is available, False otherwise
|
||||
"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['buildah', 'version'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
return result.returncode == 0
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
return False
|
||||
|
||||
|
||||
def cleanup_container(container_name: str) -> None:
|
||||
"""
|
||||
Remove a Docker container.
|
||||
@@ -750,6 +769,7 @@ def build_package_docker(
|
||||
build_args = [
|
||||
'docker', 'build',
|
||||
'--pull',
|
||||
'--network=host',
|
||||
'-f', str(central_dockerfile),
|
||||
'--build-arg', f'BASE_IMAGE={base_image}',
|
||||
'--build-arg', f'PACKAGE_NAME={package_name}',
|
||||
@@ -782,6 +802,7 @@ def build_package_docker(
|
||||
# Step 2: Create and start container
|
||||
create_args = [
|
||||
'docker', 'create',
|
||||
'--network=host',
|
||||
'--name', container_name,
|
||||
image_name
|
||||
]
|
||||
@@ -958,6 +979,177 @@ def build_package_native(
|
||||
return False
|
||||
|
||||
|
||||
def build_package_buildah(
|
||||
package_dir: Path,
|
||||
package_name: str,
|
||||
package_version: str,
|
||||
package_release: str,
|
||||
dist_dir: Path,
|
||||
repository: str,
|
||||
base_image: str = "git.unkin.net/unkin/almalinux9-rpmbuilder:latest",
|
||||
dry_run: bool = False
|
||||
) -> bool:
|
||||
"""
|
||||
Build a package using Buildah without Docker daemon.
|
||||
|
||||
Args:
|
||||
package_dir: Directory containing the package resources
|
||||
package_name: Name of the package
|
||||
package_version: Package version
|
||||
package_release: Package release number
|
||||
dist_dir: Directory to store built packages
|
||||
repository: Repository path (e.g., 'almalinux/el9')
|
||||
base_image: Base Docker image to use for building
|
||||
dry_run: If True, only show what would be done
|
||||
|
||||
Returns:
|
||||
True if build succeeded, False otherwise
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
# Ensure dist directory exists with repository structure
|
||||
package_dist_dir = dist_dir / repository
|
||||
if not dry_run:
|
||||
package_dist_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Generate container name
|
||||
container_name = f"{package_name}-{package_version}-buildah"
|
||||
|
||||
# Read metadata.yaml to get all package fields
|
||||
metadata_file = package_dir / "metadata.yaml"
|
||||
metadata = {}
|
||||
if metadata_file.exists():
|
||||
try:
|
||||
with open(metadata_file, 'r') as f:
|
||||
metadata = yaml.safe_load(f) or {}
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not read metadata.yaml: {e}")
|
||||
|
||||
logger.info(f"Building RPM for {package_name} version {package_version} using Buildah")
|
||||
|
||||
if dry_run:
|
||||
logger.info(f"[DRY RUN] Would use Buildah to build from: {base_image}")
|
||||
logger.info("[DRY RUN] Would set environment variables:")
|
||||
logger.info(f"[DRY RUN] PACKAGE_NAME={package_name}")
|
||||
logger.info(f"[DRY RUN] PACKAGE_VERSION={package_version}")
|
||||
logger.info(f"[DRY RUN] PACKAGE_RELEASE={package_release}")
|
||||
logger.info(f"[DRY RUN] Would copy artifacts to: {package_dist_dir}")
|
||||
return True
|
||||
|
||||
try:
|
||||
# Step 1: Create a working container from base image
|
||||
from_args = ['buildah', 'from', '--name', container_name, base_image]
|
||||
logger.debug(f"Running: {' '.join(from_args)}")
|
||||
result = subprocess.run(from_args, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
logger.error(f"Failed to create Buildah container from {base_image}")
|
||||
logger.error(f"stderr: {result.stderr}")
|
||||
return False
|
||||
|
||||
# Step 2: Set environment variables
|
||||
env_vars = {
|
||||
'PACKAGE_NAME': package_name,
|
||||
'PACKAGE_VERSION': package_version,
|
||||
'PACKAGE_RELEASE': package_release,
|
||||
'PACKAGE_DESCRIPTION': metadata.get('description', ''),
|
||||
'PACKAGE_MAINTAINER': metadata.get('maintainer', ''),
|
||||
'PACKAGE_HOMEPAGE': metadata.get('homepage', ''),
|
||||
'PACKAGE_LICENSE': metadata.get('license', ''),
|
||||
'PACKAGE_ARCH': metadata.get('arch', 'amd64'),
|
||||
'PACKAGE_PLATFORM': metadata.get('platform', 'linux')
|
||||
}
|
||||
|
||||
for key, value in env_vars.items():
|
||||
config_args = ['buildah', 'config', '--env', f'{key}={value}', container_name]
|
||||
logger.debug(f"Running: {' '.join(config_args)}")
|
||||
result = subprocess.run(config_args, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
logger.error(f"Failed to set environment variable {key}")
|
||||
return False
|
||||
|
||||
# Step 3: Copy resources to container
|
||||
copy_args = [
|
||||
'buildah', 'copy', container_name,
|
||||
str(package_dir / "resources"), '/app/resources'
|
||||
]
|
||||
logger.debug(f"Running: {' '.join(copy_args)}")
|
||||
result = subprocess.run(copy_args, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
logger.error(f"Failed to copy resources to container")
|
||||
logger.error(f"stderr: {result.stderr}")
|
||||
return False
|
||||
|
||||
# Step 4: Create dist directory in container
|
||||
run_args = ['buildah', 'run', container_name, 'mkdir', '-p', '/app/dist']
|
||||
logger.debug(f"Running: {' '.join(run_args)}")
|
||||
result = subprocess.run(run_args, capture_output=True, text=True)
|
||||
|
||||
# Step 5: Run the build script
|
||||
run_args = ['buildah', 'run', '--workingdir', '/app', container_name, '/app/resources/build.sh']
|
||||
logger.debug(f"Running: {' '.join(run_args)}")
|
||||
result = subprocess.run(run_args, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
logger.error(f"Buildah build script failed for {package_name}")
|
||||
logger.error(f"stdout: {result.stdout}")
|
||||
logger.error(f"stderr: {result.stderr}")
|
||||
return False
|
||||
|
||||
# Step 6: Copy artifacts from container to host
|
||||
copy_out_args = [
|
||||
'buildah', 'run', container_name,
|
||||
'find', '/app/dist', '-type', 'f', '-name', '*.rpm'
|
||||
]
|
||||
result = subprocess.run(copy_out_args, capture_output=True, text=True)
|
||||
logger.debug(f"Find RPMs result: {result.stdout}")
|
||||
|
||||
if result.returncode == 0 and result.stdout.strip():
|
||||
rpm_files = result.stdout.strip().split('\n')
|
||||
logger.info(f"Found {len(rpm_files)} RPM files to copy")
|
||||
|
||||
for rpm_file in rpm_files:
|
||||
rpm_file = rpm_file.strip()
|
||||
if rpm_file:
|
||||
# Use buildah mount to copy files out
|
||||
mount_args = ['buildah', 'mount', container_name]
|
||||
mount_result = subprocess.run(mount_args, capture_output=True, text=True)
|
||||
|
||||
if mount_result.returncode == 0:
|
||||
container_path = mount_result.stdout.strip()
|
||||
source_file = Path(container_path) / rpm_file.lstrip('/')
|
||||
|
||||
if source_file.exists():
|
||||
import shutil
|
||||
dest_file = package_dist_dir / source_file.name
|
||||
shutil.copy2(source_file, dest_file)
|
||||
logger.debug(f"Copied {source_file} to {dest_file}")
|
||||
else:
|
||||
logger.error(f"Source file not found: {source_file}")
|
||||
|
||||
# Unmount
|
||||
subprocess.run(['buildah', 'unmount', container_name], capture_output=True)
|
||||
else:
|
||||
logger.error(f"Failed to mount container: {mount_result.stderr}")
|
||||
else:
|
||||
logger.warning(f"No RPM files found or find command failed: {result.stderr}")
|
||||
|
||||
logger.info(f"Successfully built {package_name}-{package_version}-{package_release} using Buildah")
|
||||
return True
|
||||
|
||||
finally:
|
||||
# Step 7: Clean up container
|
||||
rm_args = ['buildah', 'rm', container_name]
|
||||
logger.debug(f"Running: {' '.join(rm_args)}")
|
||||
subprocess.run(rm_args, capture_output=True, text=True)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error building {package_name} with Buildah: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def cleanup_images(image_pattern: str = "*-builder") -> None:
|
||||
"""
|
||||
Clean up Docker images matching a pattern.
|
||||
@@ -1099,7 +1291,8 @@ class Builder:
|
||||
dry_run: bool = False,
|
||||
force: bool = False,
|
||||
distro: str = 'almalinux/el9',
|
||||
native: bool = False
|
||||
native: bool = False,
|
||||
buildah: bool = False
|
||||
) -> bool:
|
||||
"""
|
||||
Build a single package.
|
||||
@@ -1175,9 +1368,9 @@ class Builder:
|
||||
return False
|
||||
|
||||
package_info = PackageInfo(package, version, release, package_dir, distro, base_image)
|
||||
return self._build_package(package_info, dry_run, force, native)
|
||||
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) -> bool:
|
||||
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:
|
||||
"""
|
||||
Build all packages.
|
||||
|
||||
@@ -1199,29 +1392,29 @@ class Builder:
|
||||
self.logger.info(f"Found {len(packages)} packages to process")
|
||||
|
||||
if parallel == 1:
|
||||
return self._build_sequential(packages, dry_run, force, native)
|
||||
return self._build_sequential(packages, dry_run, force, native, buildah)
|
||||
else:
|
||||
return self._build_parallel(packages, dry_run, force, parallel, native)
|
||||
return self._build_parallel(packages, dry_run, force, parallel, native, buildah)
|
||||
|
||||
def _build_sequential(self, packages: List[PackageInfo], dry_run: bool, force: bool, native: bool) -> bool:
|
||||
def _build_sequential(self, packages: List[PackageInfo], dry_run: bool, force: bool, native: bool, buildah: bool) -> bool:
|
||||
"""Build packages sequentially."""
|
||||
success_count = 0
|
||||
|
||||
for package_info in packages:
|
||||
if self._build_package(package_info, dry_run, force, native):
|
||||
if self._build_package(package_info, dry_run, force, native, buildah):
|
||||
success_count += 1
|
||||
|
||||
self.logger.info(f"Built {success_count}/{len(packages)} packages successfully")
|
||||
return success_count == len(packages)
|
||||
|
||||
def _build_parallel(self, packages: List[PackageInfo], dry_run: bool, force: bool, parallel: int, native: bool) -> bool:
|
||||
def _build_parallel(self, packages: List[PackageInfo], dry_run: bool, force: bool, parallel: int, native: bool, buildah: bool) -> bool:
|
||||
"""Build packages in parallel."""
|
||||
success_count = 0
|
||||
|
||||
with ThreadPoolExecutor(max_workers=parallel) as executor:
|
||||
# Submit all build tasks
|
||||
future_to_package = {
|
||||
executor.submit(self._build_package, pkg, dry_run, force, native): pkg
|
||||
executor.submit(self._build_package, pkg, dry_run, force, native, buildah): pkg
|
||||
for pkg in packages
|
||||
}
|
||||
|
||||
@@ -1238,7 +1431,7 @@ class Builder:
|
||||
self.logger.info(f"Built {success_count}/{len(packages)} packages successfully")
|
||||
return success_count == len(packages)
|
||||
|
||||
def _build_package(self, package_info: PackageInfo, dry_run: bool, force: bool, native: bool = False) -> bool:
|
||||
def _build_package(self, package_info: PackageInfo, dry_run: bool, force: bool, native: bool = False, buildah: bool = False) -> bool:
|
||||
"""
|
||||
Build a single package.
|
||||
|
||||
@@ -1264,11 +1457,16 @@ class Builder:
|
||||
return True
|
||||
|
||||
# Check build tool availability (unless dry run)
|
||||
use_native = native or check_native_build_deps()
|
||||
use_docker = not use_native and check_docker_available()
|
||||
use_native = native
|
||||
use_buildah = buildah
|
||||
use_docker = not use_native and not use_buildah and check_docker_available()
|
||||
if not use_native and not use_buildah and not use_docker:
|
||||
use_buildah = check_buildah_available()
|
||||
if not use_native and not use_buildah and not use_docker:
|
||||
use_native = check_native_build_deps()
|
||||
|
||||
if not dry_run and not use_native and not use_docker:
|
||||
self.logger.error("Neither native build dependencies nor Docker is available")
|
||||
if not dry_run and not use_native and not use_buildah and not use_docker:
|
||||
self.logger.error("No build tools available (tried native, Buildah, Docker)")
|
||||
return False
|
||||
|
||||
# Build the package using available tool
|
||||
@@ -1283,6 +1481,18 @@ class Builder:
|
||||
repository=package_info.distro,
|
||||
dry_run=dry_run
|
||||
)
|
||||
elif use_buildah:
|
||||
self.logger.debug(f"Using Buildah to build {package_info.name}")
|
||||
return build_package_buildah(
|
||||
package_dir=package_info.directory,
|
||||
package_name=package_info.name,
|
||||
package_version=package_info.version,
|
||||
package_release=package_info.release,
|
||||
dist_dir=self.dist_dir,
|
||||
repository=package_info.distro,
|
||||
base_image=package_info.base_image,
|
||||
dry_run=dry_run
|
||||
)
|
||||
else:
|
||||
self.logger.debug(f"Using Docker to build {package_info.name}")
|
||||
return build_package_docker(
|
||||
@@ -1557,6 +1767,7 @@ def build(
|
||||
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be built without building"),
|
||||
force: bool = typer.Option(False, "--force", help="Build even if package exists in registry"),
|
||||
native: bool = typer.Option(False, "--native", help="Force native build (skip Docker even if available)"),
|
||||
buildah: bool = typer.Option(False, "--buildah", help="Force Buildah build (requires Buildah)"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose logging")
|
||||
):
|
||||
"""Build a specific package."""
|
||||
@@ -1619,7 +1830,8 @@ def build(
|
||||
dry_run=dry_run,
|
||||
force=force,
|
||||
distro=distro,
|
||||
native=native
|
||||
native=native,
|
||||
buildah=buildah
|
||||
)
|
||||
|
||||
if not success:
|
||||
@@ -1636,6 +1848,7 @@ def build_all(
|
||||
parallel: int = typer.Option(4, help="Number of parallel builds"),
|
||||
distro: str = typer.Option("almalinux/el9", help="Target distro (almalinux/el8, almalinux/el9, or 'all' for all distros)"),
|
||||
native: bool = typer.Option(False, "--native", help="Force native build (skip Docker even if available)"),
|
||||
buildah: bool = typer.Option(False, "--buildah", help="Force Buildah build (requires Buildah)"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose logging")
|
||||
):
|
||||
"""Build all packages."""
|
||||
@@ -1650,7 +1863,8 @@ def build_all(
|
||||
force=force,
|
||||
parallel=parallel,
|
||||
distro=distro,
|
||||
native=native
|
||||
native=native,
|
||||
buildah=buildah
|
||||
)
|
||||
|
||||
if not success:
|
||||
|
||||
+45
-14
@@ -154,6 +154,33 @@ def load_env_vars(env_file: Path) -> Dict[str, str]:
|
||||
return env_vars
|
||||
|
||||
|
||||
def get_claude_ai_latest_version() -> Optional[str]:
|
||||
"""
|
||||
Get the latest claude-code version from downloads.claude.ai.
|
||||
|
||||
Returns:
|
||||
Latest version string or None if not found
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
url = "https://downloads.claude.ai/claude-code-releases/latest"
|
||||
logger.debug(f"Checking claude-code latest version: {url}")
|
||||
response = requests.get(url, timeout=30)
|
||||
|
||||
if response.status_code == 200:
|
||||
version = response.text.strip()
|
||||
logger.debug(f"Latest claude-code version: {version}")
|
||||
return version
|
||||
else:
|
||||
logger.warning(f"Unexpected response from claude.ai: {response.status_code}")
|
||||
return None
|
||||
|
||||
except requests.RequestException as e:
|
||||
logger.error(f"Failed to check claude-code version: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def get_github_latest_release(repo: str) -> Optional[Dict]:
|
||||
"""
|
||||
Get the latest release from GitHub API.
|
||||
@@ -315,26 +342,30 @@ def check_package_updates(package_dir: Path, dry_run: bool = False) -> bool:
|
||||
package_name = metadata.get('name', package_dir.name)
|
||||
current_version = metadata.get('version')
|
||||
github_repo = metadata.get('github')
|
||||
claude_ai = metadata.get('claude_ai', False)
|
||||
|
||||
if not github_repo:
|
||||
logger.debug(f"Package {package_name} has no GitHub repo configured")
|
||||
if not github_repo and not claude_ai:
|
||||
logger.debug(f"Package {package_name} has no GitHub repo or claude_ai configured")
|
||||
return True
|
||||
|
||||
if not current_version:
|
||||
logger.warning(f"Package {package_name} has no version in metadata")
|
||||
return False
|
||||
|
||||
logger.info(f"Checking {package_name} (current: {current_version}) from {github_repo}")
|
||||
|
||||
# Get latest release from GitHub
|
||||
latest_release = get_github_latest_release(github_repo)
|
||||
if not latest_release:
|
||||
return False
|
||||
|
||||
latest_version = normalize_version(latest_release.get('tag_name', ''))
|
||||
if not latest_version:
|
||||
logger.warning(f"Could not determine latest version for {package_name}")
|
||||
return False
|
||||
if claude_ai:
|
||||
logger.info(f"Checking {package_name} (current: {current_version}) from downloads.claude.ai")
|
||||
latest_version = get_claude_ai_latest_version()
|
||||
if not latest_version:
|
||||
return False
|
||||
else:
|
||||
logger.info(f"Checking {package_name} (current: {current_version}) from {github_repo}")
|
||||
latest_release = get_github_latest_release(github_repo)
|
||||
if not latest_release:
|
||||
return False
|
||||
latest_version = normalize_version(latest_release.get('tag_name', ''))
|
||||
if not latest_version:
|
||||
logger.warning(f"Could not determine latest version for {package_name}")
|
||||
return False
|
||||
|
||||
# Compare versions
|
||||
if compare_versions(current_version, latest_version):
|
||||
@@ -373,7 +404,7 @@ def find_packages_with_github(rpms_dir: Path) -> List[Path]:
|
||||
with open(metadata_file, 'r') as f:
|
||||
metadata = yaml.safe_load(f)
|
||||
|
||||
if metadata.get('github'):
|
||||
if metadata.get('github') or metadata.get('claude_ai'):
|
||||
github_packages.append(package_dir)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user