diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2d323f..65d9aa9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,11 +45,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - pyver: ["3.6", "3.7", "3.8", "3.9", "3.10"] + pyver: ["3.7", "3.8", "3.9", "3.10", "3.11"] fail-fast: true steps: - name: Install deps - run: sudo apt update && sudo apt install -y rpm enchant + run: sudo apt update && sudo apt install -y rpm enchant-2 - name: Checkhout uses: actions/checkout@v2.4.0 - name: Setup Python ${{ matrix.pyver }} diff --git a/.travis.yml b/.travis.yml index 6172691..d9ea9ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ addons: apt: packages: - rpm - - enchant + - enchant-2 install: - if [[ "$(python --version 2>&1)" =~ Python\ (2\.*) ]]; then pip install -U jinja2; else echo "Skipping JINJA2 for $(python --version 2>&1)."; fi - pip install -rtest-requirements.txt diff --git a/rpmvenv/extensions/files/extras.py b/rpmvenv/extensions/files/extras.py index 88b231c..476fd62 100644 --- a/rpmvenv/extensions/files/extras.py +++ b/rpmvenv/extensions/files/extras.py @@ -42,22 +42,32 @@ def generate(config, spec): """Produce file block segments for packaging files.""" for file_ in config.file_extras.files: + file_directive = "" + if file_.file_attr is not None: + # file with attributes modifiers (permissions, user, group) + file_directive = '%attr({0}, {1}, {2}) '.format( + file_.file_attr['permissions'], + file_.file_attr['user'], + file_.file_attr['group'], + ) + if file_.file_type is not None: if file_.file_type_option is not None: # file with a modifier (e.g. config) including an option # (e.g. noreplace) - file_directive = '%{1}({2}) /{0}'.format( + file_directive += '%{1}({2}) /{0}'.format( file_.dest, file_.file_type, file_.file_type_option ) else: # file with a modifier (e.g. doc) but no additional option - file_directive = '%{1} /{0}'.format(file_.dest, - file_.file_type) + file_directive += file_directive + '%{1} /{0}'.format( + file_.dest, + file_.file_type) else: # simple file without an extra modifiers - file_directive = '/{0}'.format(file_.dest) + file_directive += '/{0}'.format(file_.dest) spec.blocks.install.append( 'mkdir -p "%{{buildroot}}/%(dirname {0})"'.format(file_.dest) @@ -69,10 +79,5 @@ def generate(config, spec): ) ) spec.blocks.files.append(file_directive) - spec.blocks.post.append( - 'chown -R ' - '%{{file_permissions_user}}:%{{file_permissions_group}} ' - '/{0}'.format(file_.dest) - ) return spec diff --git a/rpmvenv/extensions/files/option.py b/rpmvenv/extensions/files/option.py index f1647ef..204ba65 100644 --- a/rpmvenv/extensions/files/option.py +++ b/rpmvenv/extensions/files/option.py @@ -1,5 +1,4 @@ """Confpy extension that supports using RPM file paths as config options.""" - from collections.abc import MutableMapping from collections import namedtuple @@ -11,7 +10,11 @@ basestring = str RpmFile = namedtuple('RpmFile', - ['src', 'dest', 'file_type', 'file_type_option']) + ['src', + 'dest', + 'file_type', + 'file_type_option', + 'file_attr']) class FileOption(option.Option): @@ -52,10 +55,22 @@ def coerce(self, value): elif value.get('doc', False): file_type = 'doc' + file_attr = value.get('attr', None) + if file_attr is not None and isinstance(file_attr, MutableMapping): + if 'permissions' not in file_attr: + file_attr['permissions'] = '-' + if 'user' not in file_attr: + file_attr['user'] = '-' + if 'group' not in file_attr: + file_attr['group'] = '-' + elif file_attr is not None: # Erase wrong values + file_attr = None + return RpmFile(src=value['src'], dest=value['dest'], file_type=file_type, - file_type_option=file_type_option) + file_type_option=file_type_option, + file_attr=file_attr,) elif isinstance(value, basestring): try: @@ -63,7 +78,8 @@ def coerce(self, value): return RpmFile(src=src, dest=dest, file_type=None, - file_type_option=None) + file_type_option=None, + file_attr=None,) except ValueError: raise ValueError('The value {0} is missing a :'.format(value)) diff --git a/tests/test_fileoption.py b/tests/test_fileoption.py index 8eefc54..0f8e184 100644 --- a/tests/test_fileoption.py +++ b/tests/test_fileoption.py @@ -64,6 +64,7 @@ def test_parse_dict_no_file_type_explicit(): assert rpm_file.dest == value['dest'] assert rpm_file.file_type is None assert rpm_file.file_type_option is None + assert rpm_file.file_attr is None def test_parse_dict_doc_file(): @@ -72,7 +73,7 @@ def test_parse_dict_doc_file(): value = { 'src': '/foo/bar', 'dest': '/etc/foo', - 'doc': 'foobar' # anything truthy is okay, value is ignored + 'doc': 'foobar', # anything truthy is okay, value is ignored } rpm_file = parser.coerce(value) @@ -100,6 +101,31 @@ def test_parse_dict_config_file_no_option(): assert rpm_file.file_type_option is None +def test_parse_dict_file_with_attributes(): + parser = file_opt.FileOption() + + value = { + 'src': '/foo/bar', + 'dest': '/etc/foo', + 'attr': { + "permissions": "0644", + "user": "testuser" + } + } + + rpm_file = parser.coerce(value) + assert isinstance(rpm_file, file_opt.RpmFile) + assert rpm_file.src == value['src'] + assert rpm_file.dest == value['dest'] + assert rpm_file.file_type is None + assert rpm_file.file_type_option is None + assert rpm_file.file_attr == { + "permissions": "0644", + "user": "testuser", + "group": "-" + } + + def test_parse_dict_config_file_with_option(): parser = file_opt.FileOption()