From 8b9bf8d71453763f3f15838d516d788aa722b3a4 Mon Sep 17 00:00:00 2001 From: "Eric D. Helms" Date: Fri, 7 Apr 2023 16:36:31 -0400 Subject: [PATCH] Refactor Copr support into modules --- obal/data/module_utils/copr_wrapper.py | 26 ++++++ obal/data/modules/copr_build.py | 50 +++++++++++ obal/data/modules/copr_chroot.py | 2 +- obal/data/modules/copr_fork.py | 57 +++++++++++++ obal/data/modules/copr_project.py | 2 +- obal/data/roles/build_package/tasks/copr.yml | 85 ++++++++++--------- .../roles/copr_project/defaults/main.yaml | 1 + obal/data/roles/copr_repo/tasks/main.yml | 35 ++++---- .../testrepo/copr/package_manifest.yaml | 17 +--- tests/test_functional.py | 21 ++--- 10 files changed, 209 insertions(+), 87 deletions(-) create mode 100644 obal/data/module_utils/copr_wrapper.py create mode 100644 obal/data/modules/copr_build.py create mode 100644 obal/data/modules/copr_fork.py diff --git a/obal/data/module_utils/copr_wrapper.py b/obal/data/module_utils/copr_wrapper.py new file mode 100644 index 00000000..391a7995 --- /dev/null +++ b/obal/data/module_utils/copr_wrapper.py @@ -0,0 +1,26 @@ +""" +A copr-cli wrapper +""" +from subprocess import check_output, CalledProcessError + +class CoprCliCommandError(Exception): + """Raised when copr-cli command fails""" + def __init__(self, message, command): + self.message = message + self.command = command + super(CoprCliCommandError, self).__init__(message) #pylint: disable-all + +def copr_cli(command, executable='copr-cli'): + """ + Run a copr-cli command + """ + try: + return check_output([executable] + command, universal_newlines=True) + except CalledProcessError as error: + raise CoprCliCommandError(error.output, error.cmd) + +def full_name(user, project): + """ + Returns a full Copr name: user/project + """ + return "{}/{}".format(user, project) diff --git a/obal/data/modules/copr_build.py b/obal/data/modules/copr_build.py new file mode 100644 index 00000000..7cc9a773 --- /dev/null +++ b/obal/data/modules/copr_build.py @@ -0,0 +1,50 @@ +""" +Release a package to Copr +""" + +import re + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.copr_wrapper import copr_cli, CoprCliCommandError, full_name # pylint:disable=import-error,no-name-in-module + + +def main(): + """ + Release a package to Copr + """ + module = AnsibleModule( + argument_spec=dict( + user=dict(type='str', required=True), + project=dict(type='str', required=True), + srpm=dict(type='path', required=True), + wait=dict(type='bool', required=False, default=True) + ) + ) + + user = module.params['user'] + project = module.params['project'] + srpm = module.params['srpm'] + wait = module.params['wait'] + + command = [ + 'build', + full_name(user, project), + srpm + ] + + if not wait: + command.append('--nowait') + + try: + output = copr_cli(command) + except CoprCliCommandError as error: + module.fail_json(msg='Copr build failed', command=error.command, output=error.message, + repo_name=repo_name, srpm=srpm) + + builds = re.findall(r'^Build was added to.+:\n^\s(.+)', output, re.MULTILINE) + + module.exit_json(changed=True, output=output, builds=builds) + + +if __name__ == '__main__': + main() diff --git a/obal/data/modules/copr_chroot.py b/obal/data/modules/copr_chroot.py index 6c85d13b..a8a40dbf 100644 --- a/obal/data/modules/copr_chroot.py +++ b/obal/data/modules/copr_chroot.py @@ -30,7 +30,7 @@ def main(): command = [ 'edit-chroot', - "%s/%s" % (full_name(user, project), chroot) + "{}/{}".format(full_name(user, project), chroot) ] if external_repos: diff --git a/obal/data/modules/copr_fork.py b/obal/data/modules/copr_fork.py new file mode 100644 index 00000000..d64f4181 --- /dev/null +++ b/obal/data/modules/copr_fork.py @@ -0,0 +1,57 @@ +""" +Fork a Copr project +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.copr import copr_cli, CoprCliCommandError, full_name # pylint:disable=import-error,no-name-in-module + + +def main(): + """ + Fork a Copr project + """ + module = AnsibleModule( + argument_spec=dict( + src_user=dict(type='str', required=True), + src_project=dict(type='str', required=True), + dest_user=dict(type='str', required=True), + dest_project=dict(type='str', required=True), + delete_after_days=dict(type='str', required=False, default=None), + ) + ) + + src_user = module.params['src_user'] + src_project = module.params['src_project'] + dest_user = module.params['dest_user'] + dest_project = module.params['dest_project'] + delete_after_days = module.params['delete_after_days'] + + command = [ + 'fork', + full_name(src_user, src_project), + full_name(dest_user, dest_project) + ] + + try: + fork_output = copr_cli(command) + except CoprCliCommandError as error: + module.fail_json(msg='Copr project forking failed', command=' '.join(error.command), output=error.message) + + if delete_after_days: + modify_command = [ + 'modify', + full_name(dest_user, dest_project), + '--delete-after-days', + delete_after_days + ] + + try: + output = copr_cli(modify_command) + except CoprCliCommandError as error: + module.fail_json(msg='Copr project forking failed', command=' '.join(error.command), output=error.message) + + module.exit_json(changed=True, output=fork_output) + + +if __name__ == '__main__': + main() diff --git a/obal/data/modules/copr_project.py b/obal/data/modules/copr_project.py index 889f7615..7c18aa35 100644 --- a/obal/data/modules/copr_project.py +++ b/obal/data/modules/copr_project.py @@ -35,7 +35,7 @@ def main(): 'create', full_name(user, project), '--description', - "%s" % description + description ] for chroot in chroots: diff --git a/obal/data/roles/build_package/tasks/copr.yml b/obal/data/roles/build_package/tasks/copr.yml index 41487cc1..926eba36 100644 --- a/obal/data/roles/build_package/tasks/copr.yml +++ b/obal/data/roles/build_package/tasks/copr.yml @@ -1,53 +1,56 @@ --- +- name: Create temporary build directory + tempfile: + state: directory + suffix: srpms + register: srpm_directory + when: srpm_directory is not defined + +- name: 'Build SRPM' + srpm: + package: "{{ inventory_dir }}/{{ package_base_dir }}/{{ inventory_hostname }}" + output: "{{ srpm_directory.path if 'path' in srpm_directory else srpm_directory }}" + source_location: "{{ source_location | default(omit) }}" + source_system: "{{ source_system | default(omit) }}" + register: srpm_build + - when: not build_package_scratch block: - - name: 'Release to copr' - tito_release: - directory: "{{ inventory_dir }}/{{ package_base_dir }}/{{ inventory_hostname }}" - arguments: "{{ build_package_tito_args }}" - test: "{{ build_package_test }}" - scratch: "{{ build_package_scratch }}" - releasers: "{{ releasers }}" - releaser_arguments: "{{ build_package_tito_releaser_args }}" - register: build_package_tito_release - - - name: 'Wait for tasks to finish' - include_tasks: wait.yml - when: build_package_wait|bool + - name: 'Run build' + copr_build: + user: "{{ copr_project_user }}" + project: "{{ copr_project_name }}" + srpm: "{{ srpm_build.path }}" + wait: "{{ build_package_wait }}" + register: copr_builds - when: build_package_scratch block: - - name: Define copr repo name + - name: Define Copr scratch project name set_fact: - copr_repo_name: "{{ copr_user }}/{{ build_package_scratch_repo }}-{{ 999999999 | random | to_uuid }}" - run_once: true - when: copr_repo_name is not defined - - - name: 'Write copr repo name to vars file' - copy: - content: "{{ 'copr_repo: ' + copr_repo_name | to_yaml }}" - dest: "{{ obal_tmp_dir }}/copr_repo" - mode: '0640' - run_once: true + copr_scratch_project: "{{ copr_project_name }}-scratch-{{ 999999999 | random | to_uuid }}" + when: copr_scratch_project is not defined - - include_role: - name: copr_repo - run_once: true + - name: Define Copr scatch user name + set_fact: + copr_scratch_user: "{{ copr_project_user }}" + when: copr_scratch_user is not defined - - name: 'Build SRPM' - tito_build: - directory: "{{ inventory_dir }}/{{ package_base_dir }}/{{ inventory_hostname }}" - srpm: true - scl: "{{ scl }}" - register: srpm_build + - name: Create a Copr fork for scratch builds + copr_fork: + src_user: "{{ copr_project_user }}" + src_project: "{{ copr_project_name }}" + dest_user: "{{ copr_scratch_user }}" + dest_project: "{{ copr_scratch_project }}" + delete_after_days: 4 - name: 'Run build' - command: >- - copr-cli build - {% if not build_package_wait | bool %}--nowait{% endif %} - {{ copr_repo_name }} - {{ srpm_build.path }} - register: build_status + copr_build: + user: "{{ copr_scratch_user }}" + project: "{{ copr_scratch_project }}" + srpm: "{{ srpm_build.path }}" + wait: "{{ build_package_wait }}" + register: copr_builds - - debug: - msg: "{{ build_status.stdout_lines | join('\n') }}" +- debug: + msg: "{{ copr_builds }}" diff --git a/obal/data/roles/copr_project/defaults/main.yaml b/obal/data/roles/copr_project/defaults/main.yaml index ed97d539..0184c98b 100644 --- a/obal/data/roles/copr_project/defaults/main.yaml +++ b/obal/data/roles/copr_project/defaults/main.yaml @@ -1 +1,2 @@ --- +copr_project_chroots: [] diff --git a/obal/data/roles/copr_repo/tasks/main.yml b/obal/data/roles/copr_repo/tasks/main.yml index d52bab75..8b129f55 100644 --- a/obal/data/roles/copr_repo/tasks/main.yml +++ b/obal/data/roles/copr_repo/tasks/main.yml @@ -1,19 +1,20 @@ --- -- name: 'Create copr scratch build repo' - command: > - copr-cli - create - {{ copr_repo_name }} - --chroot {{ copr_repo_chroot }} - --description '{{ copr_repo_description }}' - --unlisted-on-hp on - --repo '{{ copr_repo_external_repos | join(' ') }}' - changed_when: false +- name: 'Create Copr repo' + copr_repo: + repo_name: "{{ copr_repo_name }}" + chroots: "{{ copr_repo_chroots | map(attribute='name') }}" + description: "{{ copr_repo_description | default(omit) }}" + delete_after_days: "{{ copr_repo_delete_after_days | default(omit) }}" + unlisted_on_homepage: "{{ copr_repo_unlisted_on_homepage | default(omit) }}" + register: create_output -- name: 'Add build packages to chroot' - command: > - copr-cli - edit-chroot - {{ copr_repo_name }}/{{ copr_repo_chroot }} - --packages '{{ copr_repo_packages | join(' ') }}' - changed_when: false +- name: Configure chroots + copr_chroot: + repo_name: "{{ copr_repo_name }}" + chroot: "{{ chroot.name }}" + external_repos: "{{ chroot.external_repos | default(omit) }}" + buildroot_packages: "{{ chroot.buildroot_packages | default(omit) }}" + modules: "{{ chroot.modules | default(omit) }}" + loop: "{{ copr_repo_chroots }}" + loop_control: + loop_var: chroot diff --git a/tests/fixtures/testrepo/copr/package_manifest.yaml b/tests/fixtures/testrepo/copr/package_manifest.yaml index 6773ae5f..a2d6ac51 100644 --- a/tests/fixtures/testrepo/copr/package_manifest.yaml +++ b/tests/fixtures/testrepo/copr/package_manifest.yaml @@ -1,21 +1,10 @@ --- packages: vars: - releasers: - - copr - scl: copr-scl - copr_user: "@fake-user" - copr_repo_name: copr-repo-scratch - diff_package_copr_repo: copr-repo-staging - diff_package_type: copr + copr_project_user: example + copr_project_name: foreman + copr_scratch_project: foreman-1234 build_package_build_system: copr - build_package_scratch_repo: copr-repo-scratch - copr_repo_chroot: epel-7-x86_64 - copr_repo_external_repos: - - http://mirror.centos.org/centos/7/sclo/x86_64/rh/ - copr_repo_packages: - - scl-utils-build - - rh-ruby24-build hosts: hello: {} diff --git a/tests/test_functional.py b/tests/test_functional.py index 3f20153b..19fe713c 100644 --- a/tests/test_functional.py +++ b/tests/test_functional.py @@ -662,10 +662,9 @@ def test_obal_scratch_copr_hello_nowait(): assert os.path.exists('packages/hello/hello-2.10.tar.gz') expected_log = [ - "copr-cli create copr-repo-scratch --chroot epel-7-x86_64 --description 'Scratch Builds' --unlisted-on-hp on --repo http://mirror.centos.org/centos/7/sclo/x86_64/rh/", # noqa: E501 - "copr-cli edit-chroot copr-repo-scratch/epel-7-x86_64 --packages 'scl-utils-build rh-ruby24-build'", # noqa: E501 - "tito build --srpm --scl copr-scl", - "copr-cli build --nowait copr-repo-scratch hello-2.10-1.src.rpm" + "copr-cli fork example/foreman example/foreman-1234", # noqa: E501 + "copr-cli modify example/foreman-1234 --delete-after-days 4", # noqa: E501 + "copr-cli build example/foreman-1234 /tmp/SRPMs/hello-2.10-1.src.rpm --nowait" ] assert_mockbin_log(expected_log) @@ -677,10 +676,9 @@ def test_obal_scratch_copr_hello(): assert os.path.exists('packages/hello/hello-2.10.tar.gz') expected_log = [ - "copr-cli create copr-repo-scratch --chroot epel-7-x86_64 --description 'Scratch Builds' --unlisted-on-hp on --repo http://mirror.centos.org/centos/7/sclo/x86_64/rh/", # noqa: E501 - "copr-cli edit-chroot copr-repo-scratch/epel-7-x86_64 --packages 'scl-utils-build rh-ruby24-build'", # noqa: E501 - "tito build --srpm --scl copr-scl", - "copr-cli build copr-repo-scratch hello-2.10-1.src.rpm", + "copr-cli fork example/foreman example/foreman-1234", # noqa: E501 + "copr-cli modify example/foreman-1234 --delete-after-days 4", # noqa: E501 + "copr-cli build example/foreman-1234 /tmp/SRPMs/hello-2.10-1.src.rpm", # copr-cli build waits by default, so there is no "watch-build" step here ] assert_mockbin_log(expected_log) @@ -693,8 +691,7 @@ def test_obal_release_copr_hello_nowait(): assert os.path.exists('packages/hello/hello-2.10.tar.gz') expected_log = [ - "copr-cli get-package @fake-user/copr-repo-staging --name hello --with-latest-build", - "tito release --yes copr", + "copr-cli build example/foreman /tmp/SRPMs/hello-2.10-1.src.rpm --nowait" ] assert_mockbin_log(expected_log) @@ -706,9 +703,7 @@ def test_obal_release_copr_hello(): assert os.path.exists('packages/hello/hello-2.10.tar.gz') expected_log = [ - "copr-cli get-package @fake-user/copr-repo-staging --name hello --with-latest-build", - "tito release --yes copr", - "copr-cli watch-build" + "copr-cli build example/foreman /tmp/SRPMs/hello-2.10-1.src.rpm" ] assert_mockbin_log(expected_log)