diff --git a/client/prototypes.h b/client/prototypes.h index d38affbd..82058f93 100644 --- a/client/prototypes.h +++ b/client/prototypes.h @@ -908,11 +908,6 @@ TDNFGetFileSize( int *pnSize ); -int -TDNFIsGlob( - const char* pszString - ); - uint32_t TDNFUtilsMakeDir( const char* pszPath diff --git a/client/repolist.c b/client/repolist.c index c50e2b9a..02eaffc1 100644 --- a/client/repolist.c +++ b/client/repolist.c @@ -289,10 +289,39 @@ TDNFLoadRepoData( } } - /* look for setopt settings */ + /* Apply per-repo and glob-pattern setopts. + * Pass 1: glob patterns first. Pass 2: exact repo-id matches, which + * always take precedence over globs regardless of argument order. + * For array-valued keys (baseurl, gpgkey), the exact match resets any + * values set by globs so it replaces rather than appends. */ if (pTdnf->pArgs->cn_repoopts != NULL) { for (pRepo = pReposAll; pRepo; pRepo = pRepo->pNext) { - if ((cn_repo = find_child(pTdnf->pArgs->cn_repoopts, pRepo->pszId)) != NULL) { + if (strcmp(pRepo->pszId, CMDLINE_REPO_NAME) == 0) + continue; + for (cn_repo = pTdnf->pArgs->cn_repoopts->first_child; + cn_repo; cn_repo = cn_repo->next) { + if (!TDNFIsGlob(cn_repo->name)) + continue; + if (fnmatch(cn_repo->name, pRepo->pszId, 0) == 0) { + dwError = TDNFRepoConfigFromCnfTree(pTdnf, pRepo, cn_repo); + BAIL_ON_TDNF_ERROR(dwError); + } + } + } + for (pRepo = pReposAll; pRepo; pRepo = pRepo->pNext) { + for (cn_repo = pTdnf->pArgs->cn_repoopts->first_child; + cn_repo; cn_repo = cn_repo->next) { + struct cnfnode *cn_key; + if (TDNFIsGlob(cn_repo->name)) + continue; + if (strcmp(cn_repo->name, pRepo->pszId) != 0) + continue; + for (cn_key = cn_repo->first_child; cn_key; cn_key = cn_key->next) { + if (strcmp(cn_key->name, TDNF_REPO_KEY_BASEURL) == 0) + TDNF_SAFE_FREE_STRINGARRAY(pRepo->ppszBaseUrls); + else if (strcmp(cn_key->name, TDNF_REPO_KEY_GPGKEY) == 0) + TDNF_SAFE_FREE_STRINGARRAY(pRepo->ppszUrlGPGKeys); + } dwError = TDNFRepoConfigFromCnfTree(pTdnf, pRepo, cn_repo); BAIL_ON_TDNF_ERROR(dwError); } diff --git a/client/utils.c b/client/utils.c index c03d4ce5..effda66b 100644 --- a/client/utils.c +++ b/client/utils.c @@ -123,22 +123,6 @@ TDNFGetCurlError( return dwCurlError; } -int -TDNFIsGlob( - const char *pszString - ) -{ - for ( ; pszString && *pszString; pszString++) - { - char ch = *pszString; - if (ch == '*' || ch == '?' || ch == '[') - { - return 1; - } - } - - return 0; -} uint32_t TDNFUtilsMakeDir( diff --git a/common/prototypes.h b/common/prototypes.h index 992db56f..4345d7c4 100644 --- a/common/prototypes.h +++ b/common/prototypes.h @@ -230,6 +230,10 @@ TDNFDirName( char **ppszDirName ); +int TDNFIsGlob( + const char *pszString +); + int TDNFStrIsValidRepoName( const char *str ); diff --git a/common/utils.c b/common/utils.c index db4b342e..b53e9cbb 100644 --- a/common/utils.c +++ b/common/utils.c @@ -1273,6 +1273,17 @@ TDNFChecksumFromHexDigest( goto cleanup; } +int TDNFIsGlob(const char *pszString) +{ + for ( ; pszString && *pszString; pszString++) + { + char ch = *pszString; + if (ch == '*' || ch == '?' || ch == '[') + return 1; + } + return 0; +} + /* return true if str is a valid reponame */ int TDNFStrIsValidRepoName(const char *str) { diff --git a/pytests/tests/test_repofrompath.py b/pytests/tests/test_repofrompath.py index cbb94fdf..4313bfa8 100644 --- a/pytests/tests/test_repofrompath.py +++ b/pytests/tests/test_repofrompath.py @@ -8,6 +8,7 @@ import os import glob +import fnmatch import shutil import platform import pytest @@ -103,6 +104,50 @@ def test_repofrompath_cmdline_repo(utils): utils.erase_package(pkgname) +def check_repofrompath_skip_md(utils, mdpart, setopt): + workdir = WORKDIR + reponame = 'photon-test' + synced_dir = os.path.join(workdir, reponame) + + create_repo(utils) + + utils.run(['tdnf', + '--repofrompath=synced-repo,{}'.format(synced_dir), + '--repo=synced-repo', 'clean', 'all'], + cwd=workdir) + + ret = utils.run(['tdnf', + '--repofrompath=synced-repo,{}'.format(synced_dir), + '--repo=synced-repo', + setopt, + 'makecache'], + cwd=workdir) + assert ret['retval'] == 0 + + cache_dir = utils.tdnf_config.get('main', 'cachedir') + synced_cache = next( + (os.path.join(cache_dir, f) for f in os.listdir(cache_dir) + if fnmatch.fnmatch(f, 'synced-repo-*')), + None + ) + assert synced_cache is not None, "cache dir for synced-repo not found" + md_dir = os.path.join(synced_cache, 'repodata') + assert len(glob.glob('{}/*{}*'.format(md_dir, mdpart))) == 0, \ + "{} must not be downloaded with {}".format(mdpart, setopt) + + +def test_repofrompath_skip_md_filelists_glob_setopt(utils): + check_repofrompath_skip_md(utils, 'filelists', '--setopt=*.skip_md_filelists=1') + + +def test_repofrompath_skip_md_other_glob_setopt(utils): + check_repofrompath_skip_md(utils, 'other', '--setopt=*.skip_md_other=1') + + +def test_repofrompath_skip_md_filelists_per_repo_setopt(utils): + check_repofrompath_skip_md(utils, 'filelists', '--setopt=synced-repo.skip_md_filelists=1') + + # reposync a repo and install from it using repofromdir def test_repofromdir_created_repo(utils): reponame = 'photon-test' diff --git a/pytests/tests/test_skip_md.py b/pytests/tests/test_skip_md.py index f9f4ef3c..e2aee604 100644 --- a/pytests/tests/test_skip_md.py +++ b/pytests/tests/test_skip_md.py @@ -82,6 +82,17 @@ def test_skip_md_parts(utils): check_skip_md_part(utils, mdpart, False) +def test_skip_md_parts_per_repo_setopt(utils): + for mdpart in ['filelists', 'other']: + repoconf = os.path.join(utils.config['repo_path'], "yum.repos.d", REPOFILENAME) + generate_repofile_skip_md(utils, repoconf, REPOID, mdpart, False) + utils.run(['tdnf', '--repoid={}'.format(REPOID), 'clean', 'all']) + utils.run(['tdnf', '--repoid={}'.format(REPOID), + '--setopt={}.skip_md_{}=1'.format(REPOID, mdpart), 'makecache']) + md_dir = os.path.join(find_cache_dir(utils, REPOID), 'repodata') + assert len(glob.glob('{}/*{}*'.format(md_dir, mdpart))) == 0 + + # even with filelists dropped, trying to install packages with conflicting files # should still fail def test_install_conflict_file(utils): diff --git a/tools/cli/lib/parseargs.c b/tools/cli/lib/parseargs.c index 5de541c4..467457fb 100644 --- a/tools/cli/lib/parseargs.c +++ b/tools/cli/lib/parseargs.c @@ -440,7 +440,8 @@ ParseOption( *pseq_dot = 0; - if (!TDNFStrIsValidRepoName(pszCopyArgs)) { + if (!TDNFIsGlob(pszCopyArgs) && + !TDNFStrIsValidRepoName(pszCopyArgs)) { dwError = ERROR_TDNF_INVALID_REPO_NAME; BAIL_ON_CLI_ERROR(dwError); }