Compare commits
1 Commits
bb39f2085b
...
abf9e6471b
| Author | SHA1 | Date | |
|---|---|---|---|
| abf9e6471b |
@ -17,6 +17,8 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Build Packages
|
- name: Build Packages
|
||||||
|
env:
|
||||||
|
VAULT_ROLE_ID: ${{ secrets.RPMBUILDER_VAULT_ROLEID }}
|
||||||
run: |
|
run: |
|
||||||
make all DISTRO=el/8
|
make all DISTRO=el/8
|
||||||
|
|
||||||
@ -41,6 +43,8 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Build Packages
|
- name: Build Packages
|
||||||
|
env:
|
||||||
|
VAULT_ROLE_ID: ${{ secrets.RPMBUILDER_VAULT_ROLEID }}
|
||||||
run: |
|
run: |
|
||||||
make all DISTRO=el/9
|
make all DISTRO=el/9
|
||||||
|
|
||||||
|
|||||||
114
tools/build
114
tools/build
@ -2,7 +2,8 @@
|
|||||||
# /// script
|
# /// script
|
||||||
# dependencies = [
|
# dependencies = [
|
||||||
# "requests",
|
# "requests",
|
||||||
# "pyyaml"
|
# "pyyaml",
|
||||||
|
# "hvac"
|
||||||
# ]
|
# ]
|
||||||
# ///
|
# ///
|
||||||
|
|
||||||
@ -20,12 +21,93 @@ import sys
|
|||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
|
||||||
import shutil
|
|
||||||
import requests
|
import requests
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Tuple, Optional, Dict
|
from typing import List, Tuple
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
import hvac
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== VAULT FUNCTIONS ====================
|
||||||
|
|
||||||
|
def get_vault_client() -> hvac.Client:
|
||||||
|
"""
|
||||||
|
Initialize and authenticate Vault client using AppRole authentication.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Authenticated HVAC client
|
||||||
|
"""
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Get required environment variables
|
||||||
|
vault_addr = os.getenv('VAULT_ADDR', 'https://vault.service.consul:8200')
|
||||||
|
vault_role_id = os.getenv('VAULT_ROLE_ID')
|
||||||
|
|
||||||
|
if not vault_role_id:
|
||||||
|
logger.error("VAULT_ROLE_ID environment variable is required")
|
||||||
|
raise ValueError("VAULT_ROLE_ID environment variable is required")
|
||||||
|
|
||||||
|
# Initialize Vault client
|
||||||
|
client = hvac.Client(url=vault_addr)
|
||||||
|
|
||||||
|
# Authenticate using AppRole
|
||||||
|
try:
|
||||||
|
logger.debug(f"Authenticating to Vault at {vault_addr}")
|
||||||
|
auth_response = client.auth.approle.login(role_id=vault_role_id)
|
||||||
|
|
||||||
|
if not client.is_authenticated():
|
||||||
|
logger.error("Failed to authenticate with Vault")
|
||||||
|
raise Exception("Failed to authenticate with Vault")
|
||||||
|
|
||||||
|
logger.debug("Successfully authenticated with Vault")
|
||||||
|
return client
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Vault authentication failed: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def get_api_tokens() -> Tuple[str, str]:
|
||||||
|
"""
|
||||||
|
Retrieve GitHub and Gitea API tokens from Vault.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (github_token, gitea_token)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception if Vault authentication fails or tokens cannot be retrieved
|
||||||
|
"""
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
client = get_vault_client()
|
||||||
|
|
||||||
|
# Read GitHub token
|
||||||
|
try:
|
||||||
|
github_secret = client.secrets.kv.v2.read_secret_version(
|
||||||
|
path='service/github/neoloc/tokens/read-only-token'
|
||||||
|
)
|
||||||
|
github_token = github_secret['data']['data']['token']
|
||||||
|
logger.debug("Successfully retrieved GitHub token from Vault")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to retrieve GitHub token from Vault: {e}")
|
||||||
|
raise Exception(f"Failed to retrieve GitHub token from Vault: {e}")
|
||||||
|
|
||||||
|
# Read Gitea token
|
||||||
|
try:
|
||||||
|
gitea_secret = client.secrets.kv.v2.read_secret_version(
|
||||||
|
path='service/gitea/unkinben/tokens/read-only-packages'
|
||||||
|
)
|
||||||
|
gitea_token = gitea_secret['data']['data']['token']
|
||||||
|
logger.debug("Successfully retrieved Gitea token from Vault")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to retrieve Gitea token from Vault: {e}")
|
||||||
|
raise Exception(f"Failed to retrieve Gitea token from Vault: {e}")
|
||||||
|
|
||||||
|
if not github_token or not gitea_token:
|
||||||
|
logger.error("One or both API tokens are empty")
|
||||||
|
raise Exception("One or both API tokens are empty")
|
||||||
|
|
||||||
|
return github_token, gitea_token
|
||||||
|
|
||||||
|
|
||||||
# ==================== GITEA API FUNCTIONS ====================
|
# ==================== GITEA API FUNCTIONS ====================
|
||||||
@ -79,12 +161,15 @@ def check_package_exists(package_name: str, version: str, release: str) -> bool:
|
|||||||
|
|
||||||
# Get configuration from environment
|
# Get configuration from environment
|
||||||
base_url = os.getenv('GITEA_URL', 'https://git.unkin.net')
|
base_url = os.getenv('GITEA_URL', 'https://git.unkin.net')
|
||||||
api_token = os.getenv('GITEA_API_TOKEN')
|
|
||||||
owner = os.getenv('GITEA_OWNER', 'unkin')
|
owner = os.getenv('GITEA_OWNER', 'unkin')
|
||||||
package_type = os.getenv('GITEA_PACKAGE_TYPE', 'rpm')
|
package_type = os.getenv('GITEA_PACKAGE_TYPE', 'rpm')
|
||||||
|
|
||||||
if not api_token:
|
# Get API tokens from Vault - fail hard if unavailable
|
||||||
logger.warning("No GITEA_API_TOKEN found. API requests may fail for private repos.")
|
try:
|
||||||
|
_, gitea_token = get_api_tokens()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to retrieve API tokens from Vault: {e}")
|
||||||
|
raise Exception(f"Cannot check package existence without Gitea API token: {e}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Normalize version by removing leading zeros (Gitea does this automatically)
|
# Normalize version by removing leading zeros (Gitea does this automatically)
|
||||||
@ -96,9 +181,7 @@ def check_package_exists(package_name: str, version: str, release: str) -> bool:
|
|||||||
f"{package_type}/{package_name}/{full_version}"
|
f"{package_type}/{package_name}/{full_version}"
|
||||||
)
|
)
|
||||||
|
|
||||||
headers = {}
|
headers = {'Authorization': f'token {gitea_token}'}
|
||||||
if api_token:
|
|
||||||
headers['Authorization'] = f'token {api_token}'
|
|
||||||
|
|
||||||
logger.debug(f"Checking package existence: {url}")
|
logger.debug(f"Checking package existence: {url}")
|
||||||
response = requests.get(url, headers=headers, timeout=30)
|
response = requests.get(url, headers=headers, timeout=30)
|
||||||
@ -629,15 +712,6 @@ class Builder:
|
|||||||
try:
|
try:
|
||||||
# Check if package already exists (unless forced)
|
# Check if package already exists (unless forced)
|
||||||
if not force:
|
if not force:
|
||||||
# Check if we have GITEA_API_TOKEN for reliable package checking
|
|
||||||
api_token = os.getenv('GITEA_API_TOKEN')
|
|
||||||
if not api_token:
|
|
||||||
self.logger.error(
|
|
||||||
f"Skipping {package_info} - GITEA_API_TOKEN missing. "
|
|
||||||
"Cannot verify if package already exists. Use --force to build anyway."
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
if check_package_exists(
|
if check_package_exists(
|
||||||
package_info.name,
|
package_info.name,
|
||||||
package_info.version,
|
package_info.version,
|
||||||
@ -809,4 +883,4 @@ Examples:
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|||||||
111
tools/update-gh
111
tools/update-gh
@ -2,7 +2,8 @@
|
|||||||
# /// script
|
# /// script
|
||||||
# dependencies = [
|
# dependencies = [
|
||||||
# "requests",
|
# "requests",
|
||||||
# "pyyaml"
|
# "pyyaml",
|
||||||
|
# "hvac"
|
||||||
# ]
|
# ]
|
||||||
# ///
|
# ///
|
||||||
|
|
||||||
@ -22,8 +23,91 @@ import logging
|
|||||||
import requests
|
import requests
|
||||||
import yaml
|
import yaml
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Optional, List
|
from typing import Dict, Optional, List, Tuple
|
||||||
import re
|
import re
|
||||||
|
import hvac
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== VAULT FUNCTIONS ====================
|
||||||
|
|
||||||
|
def get_vault_client() -> hvac.Client:
|
||||||
|
"""
|
||||||
|
Initialize and authenticate Vault client using AppRole authentication.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Authenticated HVAC client
|
||||||
|
"""
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Get required environment variables
|
||||||
|
vault_addr = os.getenv('VAULT_ADDR', 'https://vault.service.consul:8200')
|
||||||
|
vault_role_id = os.getenv('VAULT_ROLE_ID')
|
||||||
|
|
||||||
|
if not vault_role_id:
|
||||||
|
logger.error("VAULT_ROLE_ID environment variable is required")
|
||||||
|
raise ValueError("VAULT_ROLE_ID environment variable is required")
|
||||||
|
|
||||||
|
# Initialize Vault client
|
||||||
|
client = hvac.Client(url=vault_addr)
|
||||||
|
|
||||||
|
# Authenticate using AppRole
|
||||||
|
try:
|
||||||
|
logger.debug(f"Authenticating to Vault at {vault_addr}")
|
||||||
|
auth_response = client.auth.approle.login(role_id=vault_role_id)
|
||||||
|
|
||||||
|
if not client.is_authenticated():
|
||||||
|
logger.error("Failed to authenticate with Vault")
|
||||||
|
raise Exception("Failed to authenticate with Vault")
|
||||||
|
|
||||||
|
logger.debug("Successfully authenticated with Vault")
|
||||||
|
return client
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Vault authentication failed: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def get_api_tokens() -> Tuple[str, str]:
|
||||||
|
"""
|
||||||
|
Retrieve GitHub and Gitea API tokens from Vault.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (github_token, gitea_token)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception if Vault authentication fails or tokens cannot be retrieved
|
||||||
|
"""
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
client = get_vault_client()
|
||||||
|
|
||||||
|
# Read GitHub token
|
||||||
|
try:
|
||||||
|
github_secret = client.secrets.kv.v2.read_secret_version(
|
||||||
|
path='service/github/neoloc/tokens/read-only-token'
|
||||||
|
)
|
||||||
|
github_token = github_secret['data']['data']['token']
|
||||||
|
logger.debug("Successfully retrieved GitHub token from Vault")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to retrieve GitHub token from Vault: {e}")
|
||||||
|
raise Exception(f"Failed to retrieve GitHub token from Vault: {e}")
|
||||||
|
|
||||||
|
# Read Gitea token
|
||||||
|
try:
|
||||||
|
gitea_secret = client.secrets.kv.v2.read_secret_version(
|
||||||
|
path='service/gitea/unkinben/tokens/read-only-packages'
|
||||||
|
)
|
||||||
|
gitea_token = gitea_secret['data']['data']['token']
|
||||||
|
logger.debug("Successfully retrieved Gitea token from Vault")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to retrieve Gitea token from Vault: {e}")
|
||||||
|
raise Exception(f"Failed to retrieve Gitea token from Vault: {e}")
|
||||||
|
|
||||||
|
if not github_token or not gitea_token:
|
||||||
|
logger.error("One or both API tokens are empty")
|
||||||
|
raise Exception("One or both API tokens are empty")
|
||||||
|
|
||||||
|
return github_token, gitea_token
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(verbose=False):
|
def setup_logging(verbose=False):
|
||||||
@ -65,13 +149,12 @@ def load_env_vars(env_file: Path) -> Dict[str, str]:
|
|||||||
return env_vars
|
return env_vars
|
||||||
|
|
||||||
|
|
||||||
def get_github_latest_release(repo: str, github_token: str) -> Optional[Dict]:
|
def get_github_latest_release(repo: str) -> Optional[Dict]:
|
||||||
"""
|
"""
|
||||||
Get the latest release from GitHub API.
|
Get the latest release from GitHub API.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
repo: GitHub repository in format "owner/repo"
|
repo: GitHub repository in format "owner/repo"
|
||||||
github_token: GitHub API token
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Latest release info or None if not found
|
Latest release info or None if not found
|
||||||
@ -79,6 +162,9 @@ def get_github_latest_release(repo: str, github_token: str) -> Optional[Dict]:
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Get GitHub token from Vault
|
||||||
|
github_token, _ = get_api_tokens()
|
||||||
|
|
||||||
url = f"https://api.github.com/repos/{repo}/releases/latest"
|
url = f"https://api.github.com/repos/{repo}/releases/latest"
|
||||||
headers = {
|
headers = {
|
||||||
'Authorization': f'token {github_token}',
|
'Authorization': f'token {github_token}',
|
||||||
@ -198,13 +284,12 @@ def update_package_metadata(package_dir: Path, new_version: str, dry_run: bool =
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def check_package_updates(package_dir: Path, github_token: str, dry_run: bool = False) -> bool:
|
def check_package_updates(package_dir: Path, dry_run: bool = False) -> bool:
|
||||||
"""
|
"""
|
||||||
Check for updates for a single package.
|
Check for updates for a single package.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
package_dir: Path to package directory
|
package_dir: Path to package directory
|
||||||
github_token: GitHub API token
|
|
||||||
dry_run: If True, only show what would be done
|
dry_run: If True, only show what would be done
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -237,7 +322,7 @@ def check_package_updates(package_dir: Path, github_token: str, dry_run: bool =
|
|||||||
logger.info(f"Checking {package_name} (current: {current_version}) from {github_repo}")
|
logger.info(f"Checking {package_name} (current: {current_version}) from {github_repo}")
|
||||||
|
|
||||||
# Get latest release from GitHub
|
# Get latest release from GitHub
|
||||||
latest_release = get_github_latest_release(github_repo, github_token)
|
latest_release = get_github_latest_release(github_repo)
|
||||||
if not latest_release:
|
if not latest_release:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -328,14 +413,6 @@ Examples:
|
|||||||
logger.error(f"RPMs directory not found: {rpms_dir}")
|
logger.error(f"RPMs directory not found: {rpms_dir}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Load environment variables
|
|
||||||
env_vars = load_env_vars(env_file)
|
|
||||||
github_token = env_vars.get('GITHUB_API_TOKEN')
|
|
||||||
|
|
||||||
if not github_token:
|
|
||||||
logger.error("GITHUB_API_TOKEN not found in env file")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
success = True
|
success = True
|
||||||
|
|
||||||
if args.package:
|
if args.package:
|
||||||
@ -345,7 +422,7 @@ Examples:
|
|||||||
logger.error(f"Package directory not found: {package_dir}")
|
logger.error(f"Package directory not found: {package_dir}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
success = check_package_updates(package_dir, github_token, args.dry_run)
|
success = check_package_updates(package_dir, args.dry_run)
|
||||||
else:
|
else:
|
||||||
# Check all packages with GitHub repos
|
# Check all packages with GitHub repos
|
||||||
github_packages = find_packages_with_github(rpms_dir)
|
github_packages = find_packages_with_github(rpms_dir)
|
||||||
@ -358,7 +435,7 @@ Examples:
|
|||||||
|
|
||||||
updated_count = 0
|
updated_count = 0
|
||||||
for package_dir in github_packages:
|
for package_dir in github_packages:
|
||||||
if check_package_updates(package_dir, github_token, args.dry_run):
|
if check_package_updates(package_dir, args.dry_run):
|
||||||
updated_count += 1
|
updated_count += 1
|
||||||
|
|
||||||
logger.info(f"Successfully processed {updated_count}/{len(github_packages)} packages")
|
logger.info(f"Successfully processed {updated_count}/{len(github_packages)} packages")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user