Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fedora-revdep-check python-requests 2.32.0 --repo fedora-40 --repo fedora-40-sou
### Options

- `-v, --verbose` - Show detailed analysis
- `-r, --repo <repo-id>` - Repository to enable (can be specified multiple times, default: rawhide + rawhide-source)
- `-r, --repo <repo-id>` - Repository to enable (can be specified multiple times, default: rawhide, rawhide-source, koji, and koji-source)

## Output

Expand Down
112 changes: 105 additions & 7 deletions fedora_revdep_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,71 @@ def __init__(self, verbose=False, base=None, repos=None):
Args:
verbose: Enable verbose output
base: Optional DNF base object for testing (if None, creates real DNF base)
repos: List of repository IDs to enable (default: ['rawhide', 'rawhide-source'])
repos: List of repository IDs to enable (default: ['rawhide', 'rawhide-source', 'koji', 'koji-source'])
"""
self.verbose = verbose
self.base = base
self.repos = repos if repos is not None else ['rawhide', 'rawhide-source']
self.repos = repos if repos is not None else ['rawhide', 'rawhide-source', 'koji', 'koji-source']
if self.base is None:
self._init_dnf()

def _get_known_repo_config(self, repo_id: str, releasever: str):
"""Get known repository configuration for common Fedora repos.

Args:
repo_id: Repository ID to configure
releasever: Release version string

Returns:
Dictionary with 'metalink' or 'baseurl' key, or None if repo is not known
"""
import re

# Known repository configurations
configs = {
'rawhide': {
'metalink': 'https://mirrors.fedoraproject.org/metalink?repo=rawhide&arch=$basearch'
},
'rawhide-source': {
'metalink': 'https://mirrors.fedoraproject.org/metalink?repo=rawhide-source&arch=$basearch'
},
'koji': {
'baseurl': 'https://kojipkgs.fedoraproject.org/repos/rawhide/latest/$basearch/'
},
'koji-source': {
'baseurl': 'https://kojipkgs.fedoraproject.org/repos/rawhide/latest/src/'
},
'fedora': {
'metalink': f'https://mirrors.fedoraproject.org/metalink?repo=fedora-{releasever}&arch=$basearch'
},
'fedora-source': {
'metalink': f'https://mirrors.fedoraproject.org/metalink?repo=fedora-source-{releasever}&arch=$basearch'
},
}

# Check exact match first
if repo_id in configs:
return configs[repo_id]

# Pattern-based repository configurations
# Each tuple: (regex_pattern, metalink_repo_template)
patterns = [
(r'^f(\d+)$', 'fedora-{version}'),
(r'^f(\d+)-source$', 'fedora-source-{version}'),
(r'^fedora-(\d+)$', 'fedora-{version}'),
(r'^fedora-(\d+)-source$', 'fedora-source-{version}'),
]

for pattern, repo_template in patterns:
match = re.match(pattern, repo_id)
if match:
version = match.group(1)
return {
'metalink': f'https://mirrors.fedoraproject.org/metalink?repo={repo_template.format(version=version)}&arch=$basearch'
}

return None

def _init_dnf(self):
"""Initialize DNF 5 base and load repository metadata."""
if self.verbose:
Expand Down Expand Up @@ -86,7 +143,11 @@ def _init_dnf(self):
for repo in repo_query:
repo.disable()

# Enable specified repositories
# Track which repos were found in system config and which were created
repos_from_config = set()
repos_created = []

# Enable specified repositories, create them if not found
enabled_count = 0

for repo_id in self.repos:
Expand All @@ -99,16 +160,52 @@ def _init_dnf(self):
repo.enable()
enabled_count += 1
found = True
repos_from_config.add(repo_id)
if self.verbose:
print(f" Enabled repo: {repo_id}")

if not found and self.verbose:
print(f" Warning: Repository '{repo_id}' not found in configuration")
# If not found in system config, try to create it if we know the configuration
if not found:
repo_config = self._get_known_repo_config(repo_id, releasever)
if repo_config:
try:
# Create the repository programmatically
repo = repo_sack.create_repo(repo_id)

# Configure the repository
config = repo.get_config()
if 'metalink' in repo_config:
config.metalink().set(libdnf5.conf.Option.Priority_RUNTIME, repo_config['metalink'])
elif 'baseurl' in repo_config:
config.baseurl = repo_config['baseurl']

# Disable GPG check for auto-created repos (following koji.repo pattern)
config.pkg_gpgcheck = "0"

# Enable the repo
repo.enable()
enabled_count += 1
repos_created.append(repo_id)

if self.verbose:
print(f" Created and enabled repo: {repo_id} (using default configuration)")
except Exception as e:
if self.verbose:
import traceback
print(traceback.format_exc())
print(f" Warning: Failed to create repository '{repo_id}': {e}")
elif self.verbose:
print(f" Warning: Repository '{repo_id}' not found in configuration and no default available")

# Show warning if any repos were auto-created
if repos_created and self.verbose:
print(f"Warning: Using default configuration for repositories: {', '.join(repos_created)}")
print(" Consider installing the repository configuration in /etc/yum.repos.d/")

if enabled_count == 0:
raise RuntimeError(
f"Failed to enable any repositories from: {', '.join(self.repos)}. "
"Please ensure the repositories are configured in /etc/yum.repos.d/"
"Please ensure the repositories are configured in /etc/yum.repos.d/ or use known repository IDs."
)

if self.verbose:
Expand Down Expand Up @@ -569,7 +666,8 @@ def main():
help='Enable verbose output')
parser.add_argument('-r', '--repo', action='append', dest='repos',
help='Repository ID to enable (can be specified multiple times). '
'Default: rawhide and rawhide-source')
'Default: rawhide, rawhide-source, koji, and koji-source. '
'Known repositories will be auto-configured if not in /etc/yum.repos.d/')

args = parser.parse_args()

Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/mock_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ def __init__(self, packages: Optional[List[MockPackage]] = None, repos: Optional
self._repos = repos or [
MockRepo('rawhide'),
MockRepo('rawhide-source'),
MockRepo('koji'),
MockRepo('koji-source'),
]
self._vars = MockVars()
self._repo_sack = MockRepoSack(self._repos)
Expand Down