From fd113730b03886474a30fa53ac1833bed89f466f Mon Sep 17 00:00:00 2001 From: Jakub Kadlčík Date: Sep 25 2018 12:02:38 +0000 Subject: Merge #303 Rewrite copr-cli to use APIv3 --- diff --git a/beaker-tests/Sanity/copr-cli-basic-operations/runtest.sh b/beaker-tests/Sanity/copr-cli-basic-operations/runtest.sh index 983f7cf..036fc61 100755 --- a/beaker-tests/Sanity/copr-cli-basic-operations/runtest.sh +++ b/beaker-tests/Sanity/copr-cli-basic-operations/runtest.sh @@ -204,11 +204,11 @@ rlJournalStart rlRun "copr-cli edit-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_64 --delete-comps" rlRun "copr-cli get-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_64 | grep '\"comps_name\": null'" rlRun "copr-cli edit-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_64 --repos 'http://foo/repo http://bar/repo' --packages 'gcc'" - rlRun "copr-cli get-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_64 | grep '\"repos\": \"http://foo/repo http://bar/repo\"'" - rlRun "copr-cli get-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_64 | grep '\"buildroot_pkgs\": \"gcc\"'" + rlRun "copr-cli get-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_64 | jq '.repos == [\"http://foo/repo\", \"http://bar/repo\"]'" + rlRun "copr-cli get-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_64 | jq '.buildroot_pkgs == [\"gcc\"]'" rlRun "copr-cli edit-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_64 --repos '' --packages ''" - rlRun "copr-cli get-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_64 | grep '\"repos\": \"\"'" - rlRun "copr-cli get-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_64 | grep '\"buildroot_pkgs\": \"\"'" + rlRun "copr-cli get-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_64 | jq '.repos == []'" + rlRun "copr-cli get-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_64 | jq '.buildroot_pkgs == []'" rlRun "copr-cli edit-chroot ${NAME_PREFIX}EditChrootProject/fedora-27-x86_65" 1 rm $TMPCOMPS @@ -248,154 +248,97 @@ rlJournalStart ## test package creation and editing OUTPUT=`mktemp` - SOURCE_JSON=`mktemp` + SOURCE_DICT=`mktemp` # create special repo for our test rlRun "copr-cli create --chroot $CHROOT ${NAME_PREFIX}Project4" - # invalid package data - rlRun "copr-cli add-package-tito ${NAME_PREFIX}Project4 --name test_package_tito --git-url invalid_url" 1 - - # Tito package creation - rlRun "copr-cli add-package-tito ${NAME_PREFIX}Project4 --name test_package_tito --git-url http://github.com/clime/example.git --test on --webhook-rebuild on --git-branch foo --git-dir bar" - rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name test_package_tito > $OUTPUT" - cat $OUTPUT | jq '.source_json' | sed -r 's/"(.*)"/\1/g' | sed -r 's/\\(.)/\1/g' > $SOURCE_JSON - rlAssertEquals "package.name == \"test_package_tito\"" `cat $OUTPUT | jq '.name'` '"test_package_tito"' - rlAssertEquals "package.webhook_rebuild == \"true\"" `cat $OUTPUT | jq '.webhook_rebuild'` 'true' - rlAssertEquals "package.source_type == \"scm\"" `cat $OUTPUT | jq '.source_type'` '"scm"' - rlAssertEquals "package.source_json.srpm_build_method == \"tito_test"\" `cat $SOURCE_JSON | jq '.srpm_build_method'` '"tito_test"' - rlAssertEquals "package.source_json.clone_url == \"http://github.com/clime/example.git\"" `cat $SOURCE_JSON | jq '.clone_url'` '"http://github.com/clime/example.git"' - rlAssertEquals "package.source_json.committish == \"foo\"" `cat $SOURCE_JSON | jq '.committish'` '"foo"' - rlAssertEquals "package.source_json.subdirectory == \"bar\"" `cat $SOURCE_JSON | jq '.subdirectory'` '"bar"' - - # Tito package editing - rlRun "copr-cli edit-package-tito ${NAME_PREFIX}Project4 --name test_package_tito --git-url http://github.com/clime/example2.git --test off --webhook-rebuild off --git-branch bar --git-dir foo" - rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name test_package_tito > $OUTPUT" - cat $OUTPUT | jq '.source_json' | sed -r 's/"(.*)"/\1/g' | sed -r 's/\\(.)/\1/g' > $SOURCE_JSON - rlAssertEquals "package.name == \"test_package_tito\"" `cat $OUTPUT | jq '.name'` '"test_package_tito"' - rlAssertEquals "package.webhook_rebuild == \"false\"" `cat $OUTPUT | jq '.webhook_rebuild'` 'false' - rlAssertEquals "package.source_type == \"scm\"" `cat $OUTPUT | jq '.source_type'` '"scm"' - rlAssertEquals "package.source_json.srpm_build_method == \"tito\"" `cat $SOURCE_JSON | jq '.srpm_build_method'` '"tito"' - rlAssertEquals "package.source_json.clone_url == \"http://github.com/clime/example2.git\"" `cat $SOURCE_JSON | jq '.clone_url'` '"http://github.com/clime/example2.git"' - rlAssertEquals "package.source_json.committish == \"bar\"" `cat $SOURCE_JSON | jq '.committish'` '"bar"' - rlAssertEquals "package.source_json.subdirectory == \"foo\"" `cat $SOURCE_JSON | jq '.subdirectory'` '"foo"' - - ## Package listing - rlAssertEquals "len(package_list) == 1" `copr-cli list-packages ${NAME_PREFIX}Project4 | jq '. | length'` 1 - # PyPI package creation rlRun "copr-cli add-package-pypi ${NAME_PREFIX}Project4 --name test_package_pypi --packagename pyp2rpm --packageversion 1.5 --pythonversions 3 2" rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name test_package_pypi > $OUTPUT" - cat $OUTPUT | jq '.source_json' | sed -r 's/"(.*)"/\1/g' | sed -r 's/\\(.)/\1/g' > $SOURCE_JSON + cat $OUTPUT | jq '.source_dict' > $SOURCE_DICT rlAssertEquals "package.name == \"test_package_pypi\"" `cat $OUTPUT | jq '.name'` '"test_package_pypi"' rlAssertEquals "package.source_type == \"pypi\"" `cat $OUTPUT | jq '.source_type'` '"pypi"' - rlRun `cat $SOURCE_JSON | jq '.python_versions == ["3", "2"]'` 0 "package.source_json.python_versions == [\"3\", \"2\"]" - rlAssertEquals "package.source_json.pypi_package_name == \"pyp2rpm\"" `cat $SOURCE_JSON | jq '.pypi_package_name'` '"pyp2rpm"' - rlAssertEquals "package.source_json.pypi_package_version == \"bar\"" `cat $SOURCE_JSON | jq '.pypi_package_version'` '"1.5"' + rlRun `cat $SOURCE_DICT | jq '.python_versions == ["3", "2"]'` 0 "package.source_dict.python_versions == [\"3\", \"2\"]" + rlAssertEquals "package.source_dict.pypi_package_name == \"pyp2rpm\"" `cat $SOURCE_DICT | jq '.pypi_package_name'` '"pyp2rpm"' + rlAssertEquals "package.source_dict.pypi_package_version == \"bar\"" `cat $SOURCE_DICT | jq '.pypi_package_version'` '"1.5"' # PyPI package editing rlRun "copr-cli edit-package-pypi ${NAME_PREFIX}Project4 --name test_package_pypi --packagename motionpaint --packageversion 1.4 --pythonversions 2 3" rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name test_package_pypi > $OUTPUT" - cat $OUTPUT | jq '.source_json' | sed -r 's/"(.*)"/\1/g' | sed -r 's/\\(.)/\1/g' > $SOURCE_JSON + cat $OUTPUT | jq '.source_dict' > $SOURCE_DICT rlAssertEquals "package.name == \"test_package_pypi\"" `cat $OUTPUT | jq '.name'` '"test_package_pypi"' rlAssertEquals "package.source_type == \"pypi\"" `cat $OUTPUT | jq '.source_type'` '"pypi"' - rlRun `cat $SOURCE_JSON | jq '.python_versions == ["2", "3"]'` 0 "package.source_json.python_versions == [\"2\", \"3\"]" - rlAssertEquals "package.source_json.pypi_package_name == \"motionpaint\"" `cat $SOURCE_JSON | jq '.pypi_package_name'` '"motionpaint"' - rlAssertEquals "package.source_json.pypi_package_version == \"bar\"" `cat $SOURCE_JSON | jq '.pypi_package_version'` '"1.4"' - rlAssertEquals "package.source_json.spec_template == \"\"" `cat $SOURCE_JSON | jq '.spec_template'` '""' + rlRun `cat $SOURCE_DICT | jq '.python_versions == ["2", "3"]'` 0 "package.source_dict.python_versions == [\"2\", \"3\"]" + rlAssertEquals "package.source_dict.pypi_package_name == \"motionpaint\"" `cat $SOURCE_DICT | jq '.pypi_package_name'` '"motionpaint"' + rlAssertEquals "package.source_dict.pypi_package_version == \"bar\"" `cat $SOURCE_DICT | jq '.pypi_package_version'` '"1.4"' + rlAssertEquals "package.source_dict.spec_template == \"\"" `cat $SOURCE_DICT | jq '.spec_template'` '""' # PyPI package templates rlRun "copr-cli edit-package-pypi ${NAME_PREFIX}Project4 --name test_package_pypi --template fedora" rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name test_package_pypi > $OUTPUT" - cat $OUTPUT | jq '.source_json' | sed -r 's/"(.*)"/\1/g' | sed -r 's/\\(.)/\1/g' > $SOURCE_JSON - rlAssertEquals "package.source_json.spec_template == \"fedora\"" `cat $SOURCE_JSON | jq '.spec_template'` '"fedora"' - - ## Package listing - rlAssertEquals "len(package_list) == 2" `copr-cli list-packages ${NAME_PREFIX}Project4 | jq '. | length'` 2 - - # MockSCM package creation - rlRun "copr-cli add-package-mockscm ${NAME_PREFIX}Project4 --name test_package_mockscm --scm-type git --scm-url http://github.com/clime/example.git --scm-branch foo --spec example.spec" - rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name test_package_mockscm > $OUTPUT" - cat $OUTPUT | jq '.source_json' | sed -r 's/"(.*)"/\1/g' | sed -r 's/\\(.)/\1/g' > $SOURCE_JSON - rlAssertEquals "package.name == \"test_package_mockscm\"" `cat $OUTPUT | jq '.name'` '"test_package_mockscm"' - rlAssertEquals "package.source_type == \"scm\"" `cat $OUTPUT | jq '.source_type'` '"scm"' - rlAssertEquals "package.source_json.type == \"git\"" `cat $SOURCE_JSON | jq '.type'` '"git"' - rlAssertEquals "package.source_json.clone_url == \"http://github.com/clime/example.git\"" `cat $SOURCE_JSON | jq '.clone_url'` '"http://github.com/clime/example.git"' - rlAssertEquals "package.source_json.committish == \"foo\"" `cat $SOURCE_JSON | jq '.committish'` '"foo"' - rlAssertEquals "package.source_json.spec == \"example.spec\"" `cat $SOURCE_JSON | jq '.spec'` '"example.spec"' - rlAssertEquals "package.source_json.srpm_build_method == \"rpkg\"" `cat $SOURCE_JSON | jq '.srpm_build_method'` '"rpkg"' - - # MockSCM package editing - rlRun "copr-cli edit-package-mockscm ${NAME_PREFIX}Project4 --name test_package_mockscm --scm-type svn --scm-url http://github.com/clime/example2.git --scm-branch bar --spec example2.spec" - rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name test_package_mockscm > $OUTPUT" - cat $OUTPUT | jq '.source_json' | sed -r 's/"(.*)"/\1/g' | sed -r 's/\\(.)/\1/g' > $SOURCE_JSON - rlAssertEquals "package.name == \"test_package_mockscm\"" `cat $OUTPUT | jq '.name'` '"test_package_mockscm"' - rlAssertEquals "package.source_type == \"scm\"" `cat $OUTPUT | jq '.source_type'` '"scm"' - rlAssertEquals "package.source_json.type == \"svn\"" `cat $SOURCE_JSON | jq '.type'` '"svn"' - rlAssertEquals "package.source_json.clone_url == \"http://github.com/clime/example2.git\"" `cat $SOURCE_JSON | jq '.clone_url'` '"http://github.com/clime/example2.git"' - rlAssertEquals "package.source_json.committish == \"bar\"" `cat $SOURCE_JSON | jq '.committish'` '"bar"' - rlAssertEquals "package.source_json.spec == \"example2.spec\"" `cat $SOURCE_JSON | jq '.spec'` '"example2.spec"' - rlAssertEquals "package.source_json.srpm_build_method == \"rpkg\"" `cat $SOURCE_JSON | jq '.srpm_build_method'` '"rpkg"' + cat $OUTPUT | jq '.source_dict' | sed -r 's/"(.*)"/\1/g' | sed -r 's/\\(.)/\1/g' > $SOURCE_DICT + rlAssertEquals "package.source_dict.spec_template == \"fedora\"" `cat $SOURCE_DICT | jq '.spec_template'` '"fedora"' ## Package listing - rlAssertEquals "len(package_list) == 3" `copr-cli list-packages ${NAME_PREFIX}Project4 | jq '. | length'` 3 + rlAssertEquals "len(package_list) == 1" `copr-cli list-packages ${NAME_PREFIX}Project4 | jq '. | length'` 1 # RubyGems package creation rlRun "copr-cli add-package-rubygems ${NAME_PREFIX}Project4 --name xxx --gem yyy" rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name xxx > $OUTPUT" - cat $OUTPUT | jq '.source_json' | sed -r 's/"(.*)"/\1/g' | sed -r 's/\\(.)/\1/g' > $SOURCE_JSON + cat $OUTPUT | jq '.source_dict' > $SOURCE_DICT rlAssertEquals "package.name == \"xxx\"" `cat $OUTPUT | jq '.name'` '"xxx"' rlAssertEquals "package.source_type == \"rubygems\"" `cat $OUTPUT | jq '.source_type'` '"rubygems"' - rlAssertEquals "package.source_json.gem_name == \"yyy\"" `cat $SOURCE_JSON | jq '.gem_name'` '"yyy"' + rlAssertEquals "package.source_dict.gem_name == \"yyy\"" `cat $SOURCE_DICT | jq '.gem_name'` '"yyy"' # RubyGems package editing rlRun "copr-cli edit-package-rubygems ${NAME_PREFIX}Project4 --name xxx --gem zzz" rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name xxx > $OUTPUT" - cat $OUTPUT | jq '.source_json' | sed -r 's/"(.*)"/\1/g' | sed -r 's/\\(.)/\1/g' > $SOURCE_JSON + cat $OUTPUT | jq '.source_dict' > $SOURCE_DICT rlAssertEquals "package.name == \"xxx\"" `cat $OUTPUT | jq '.name'` '"xxx"' rlAssertEquals "package.source_type == \"rubygems\"" `cat $OUTPUT | jq '.source_type'` '"rubygems"' - rlAssertEquals "package.source_json.gem_name == \"zzz\"" `cat $SOURCE_JSON | jq '.gem_name'` '"zzz"' + rlAssertEquals "package.source_dict.gem_name == \"zzz\"" `cat $SOURCE_DICT | jq '.gem_name'` '"zzz"' ## Package listing - rlAssertEquals "len(package_list) == 4" `copr-cli list-packages ${NAME_PREFIX}Project4 | jq '. | length'` 4 + rlAssertEquals "len(package_list) == 2" `copr-cli list-packages ${NAME_PREFIX}Project4 | jq '. | length'` 2 ## Package reseting - rlRun "copr-cli add-package-tito ${NAME_PREFIX}Project4 --name test_package_reset --git-url http://github.com/clime/example.git" + rlRun "copr-cli add-package-scm ${NAME_PREFIX}Project4 --name test_package_reset --clone-url http://github.com/clime/example.git" # before reset rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name test_package_reset > $OUTPUT" - cat $OUTPUT | jq '.source_json' | sed -r 's/"(.*)"/\1/g' | sed -r 's/\\(.)/\1/g' > $SOURCE_JSON + cat $OUTPUT | jq '.source_dict' > $SOURCE_DICT rlAssertEquals "package.source_type == \"scm\"" `cat $OUTPUT | jq '.source_type'` '"scm"' - rlAssertEquals "package.source_json.clone_url == \"http://github.com/clime/example.git\"" `cat $SOURCE_JSON | jq '.clone_url'` '"http://github.com/clime/example.git"' + rlAssertEquals "package.source_dict.clone_url == \"http://github.com/clime/example.git\"" `cat $SOURCE_DICT | jq '.clone_url'` '"http://github.com/clime/example.git"' # _do_ reset rlRun "copr-cli reset-package ${NAME_PREFIX}Project4 --name test_package_reset" # after reset rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name test_package_reset > $OUTPUT" - cat $OUTPUT | jq '.source_json' | sed -r 's/"(.*)"/\1/g' | sed -r 's/\\(.)/\1/g' > $SOURCE_JSON + cat $OUTPUT | jq '.source_dict' > $SOURCE_DICT rlAssertEquals "package.source_type == \"unset\"" `cat $OUTPUT | jq '.source_type'` '"unset"' - rlAssertEquals "package.source_json == \"{}\"" `cat $OUTPUT | jq '.source_json'` '"{}"' + rlAssertEquals "package.source_dict == \"{}\"" `cat $OUTPUT | jq '.source_dict'` '{}' ## Package listing - rlAssertEquals "len(package_list) == 5" `copr-cli list-packages ${NAME_PREFIX}Project4 | jq '. | length'` 5 + rlAssertEquals "len(package_list) == 3" `copr-cli list-packages ${NAME_PREFIX}Project4 | jq '. | length'` 3 ## Package deletion - rlRun "copr-cli add-package-tito ${NAME_PREFIX}Project4 --name test_package_delete --git-url http://github.com/clime/example.git" + rlRun "copr-cli add-package-scm ${NAME_PREFIX}Project4 --name test_package_delete --clone-url http://github.com/clime/example.git" rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name test_package_delete > /dev/null" ## Package listing - rlAssertEquals "len(package_list) == 6" `copr-cli list-packages ${NAME_PREFIX}Project4 | jq '. | length'` 6 + rlAssertEquals "len(package_list) == 4" `copr-cli list-packages ${NAME_PREFIX}Project4 | jq '. | length'` 4 rlRun "copr-cli delete-package ${NAME_PREFIX}Project4 --name test_package_delete" rlRun "copr-cli get-package ${NAME_PREFIX}Project4 --name test_package_delete" 1 # package cannot be fetched now (cause it is deleted) ## Package listing - rlAssertEquals "len(package_list) == 5" `copr-cli list-packages ${NAME_PREFIX}Project4 | jq '. | length'` 5 + rlAssertEquals "len(package_list) == 3" `copr-cli list-packages ${NAME_PREFIX}Project4 | jq '. | length'` 3 ## Test package listing attributes rlRun "copr-cli create --chroot $CHROOT ${NAME_PREFIX}Project5" - rlRun "copr-cli add-package-tito ${NAME_PREFIX}Project5 --name example --git-url http://github.com/clime/example.git" + rlRun "copr-cli add-package-scm ${NAME_PREFIX}Project5 --name example --clone-url http://github.com/clime/example.git" BUILDS=`mktemp` LATEST_BUILD=`mktemp` @@ -412,10 +355,10 @@ rlJournalStart rlAssertEquals "And there is no latest succeeded build." `cat $LATEST_SUCCEEDED_BUILD` 'null' # run the build and wait - rlRun "copr-cli buildtito --git-url http://github.com/clime/example.git ${NAME_PREFIX}Project5 | grep 'Created builds:' | sed 's/Created builds: \([0-9][0-9]*\)/\1/g' > succeeded_example_build_id" + rlRun "copr-cli buildscm --clone-url http://github.com/clime/example.git ${NAME_PREFIX}Project5 | grep 'Created builds:' | sed 's/Created builds: \([0-9][0-9]*\)/\1/g' > succeeded_example_build_id" # this build should fail - rlRun "copr-cli buildtito --git-url http://github.com/clime/example.git --git-branch noluck --test on ${NAME_PREFIX}Project5 | grep 'Created builds:' | sed 's/Created builds: \([0-9][0-9]*\)/\1/g' > failed_example_build_id" + rlRun "copr-cli buildscm --clone-url http://github.com/clime/example.git --commit noluck ${NAME_PREFIX}Project5 | grep 'Created builds:' | sed 's/Created builds: \([0-9][0-9]*\)/\1/g' > failed_example_build_id" # run the tests after build rlRun "copr-cli get-package ${NAME_PREFIX}Project5 --name example --with-all-builds --with-latest-build --with-latest-succeeded-build > $OUTPUT" @@ -441,11 +384,11 @@ rlJournalStart # create special repo for our test rlRun "copr-cli create --chroot $CHROOT --chroot fedora-27-x86_64 ${NAME_PREFIX}Project6" - # create tito package - rlRun "copr-cli add-package-tito ${NAME_PREFIX}Project6 --name test_package_tito --git-url http://github.com/clime/example.git --test on" + # create a package + rlRun "copr-cli add-package-scm ${NAME_PREFIX}Project6 --name test_package_scm --clone-url http://github.com/clime/example.git" # build the package - rlRun "copr-cli build-package --name test_package_tito ${NAME_PREFIX}Project6 --timeout 10000 -r $CHROOT" # TODO: timeout not honored + rlRun "copr-cli build-package --name test_package_scm ${NAME_PREFIX}Project6 --timeout 10000 -r $CHROOT" # TODO: timeout not honored # create pyp2rpm package rlRun "copr-cli add-package-pypi ${NAME_PREFIX}Project6 --name test_package_pypi --template fedora --packagename motionpaint --pythonversions 3 2" @@ -478,7 +421,7 @@ rlJournalStart rlRun "copr-cli create --chroot $CHROOT --chroot fedora-27-x86_64 ${NAME_PREFIX}Project9" && sleep 65 rlRun "curl -X POST $FRONTEND_URL/coprs/update_search_index/" rlRun "curl $FRONTEND_URL/coprs/fulltext/?fulltext=${NAME_VAR}Project9 --silent | grep -E \"href=.*${NAME_VAR}Project9.*\"" 1 # search results _not_ returned - rlRun "copr-cli add-package-tito ${NAME_PREFIX}Project9 --name test_package_tito --git-url http://github.com/clime/example.git --test on" # insert package to the copr + rlRun "copr-cli add-package-scm ${NAME_PREFIX}Project9 --name test_package_scm --clone-url http://github.com/clime/example.git" # insert package to the copr rlRun "curl -X POST $FRONTEND_URL/coprs/update_search_index/" # update the index again rlRun "curl $FRONTEND_URL/coprs/fulltext/?fulltext=${NAME_VAR}Project9 --silent | grep -E \"href=.*${NAME_VAR}Project9.*\"" 0 # search results are returned now @@ -543,7 +486,7 @@ rlJournalStart # FIXME: this test is not a reliable reproducer. Depends on timing as few others. # TODO: Remove this. rlRun "copr-cli create ${NAME_PREFIX}TestConsequentDeleteActions --chroot $CHROOT" 0 - rlRun "copr-cli add-package-tito ${NAME_PREFIX}TestConsequentDeleteActions --name example --git-url http://github.com/clime/example.git" + rlRun "copr-cli add-package-scm ${NAME_PREFIX}TestConsequentDeleteActions --name example --clone-url http://github.com/clime/example.git" rlRun "copr-cli build-package --name example ${NAME_PREFIX}TestConsequentDeleteActions" rlAssertEquals "Test that the project was successfully created on backend" `curl -w '%{response_code}' -silent -o /dev/null $BACKEND_URL/results/${NAME_PREFIX}TestConsequentDeleteActions/` 200 rlRun "python <<< \"from copr.client import CoprClient; client = CoprClient.create_from_file_config('/root/.config/copr'); client.delete_package('${NAME_VAR}TestConsequentDeleteActions', 'example', '$OWNER'); client.delete_project('${NAME_VAR}TestConsequentDeleteActions', '$OWNER')\"" @@ -552,7 +495,7 @@ rlJournalStart # Bug 1368259 - Deleting a build from a group project doesn't delete backend files rlRun "copr-cli create ${NAME_PREFIX}TestDeleteGroupBuild --chroot $CHROOT" 0 - rlRun "copr-cli add-package-tito ${NAME_PREFIX}TestDeleteGroupBuild --name example --git-url http://github.com/clime/example.git" + rlRun "copr-cli add-package-scm ${NAME_PREFIX}TestDeleteGroupBuild --name example --clone-url http://github.com/clime/example.git" rlRun "copr-cli build-package --name example ${NAME_PREFIX}TestDeleteGroupBuild | grep 'Created builds:' | sed 's/Created builds: \([0-9][0-9]*\)/\1/g' > TestDeleteGroupBuild_example_build_id.txt" BUILD_ID=`cat TestDeleteGroupBuild_example_build_id.txt` MYTMPDIR=`mktemp -d -p .` && cd $MYTMPDIR @@ -578,15 +521,15 @@ rlJournalStart # Bug 1370704 - Internal Server Error (too many values to unpack) rlRun "copr-cli create ${NAME_PREFIX}TestBug1370704 --chroot $CHROOT" 0 - rlRun "copr-cli add-package-tito ${NAME_PREFIX}TestBug1370704 --name example --git-url http://github.com/clime/example.git" + rlRun "copr-cli add-package-scm ${NAME_PREFIX}TestBug1370704 --name example --clone-url http://github.com/clime/example.git" rlRun "copr-cli build-package --name example ${NAME_PREFIX}TestBug1370704" rlAssertEquals "Test OK return code from the monitor API" `curl -w '%{response_code}' -silent -o /dev/null ${FRONTEND_URL}/api/coprs/${NAME_PREFIX}TestBug1370704/monitor/` 200 # Bug 1393361 - get_project_details returns incorrect yum_repos rlRun "copr-cli create ${NAME_PREFIX}TestBug1393361-1 --chroot fedora-27-x86_64" 0 rlRun "copr-cli create ${NAME_PREFIX}TestBug1393361-2 --chroot fedora-27-x86_64" 0 - rlRun "copr-cli buildtito ${NAME_PREFIX}TestBug1393361-2 --git-url https://github.com/clime/example.git" 0 - rlRun "copr-cli buildtito ${NAME_PREFIX}TestBug1393361-1 --git-url https://github.com/clime/example.git" 0 + rlRun "copr-cli buildscm ${NAME_PREFIX}TestBug1393361-2 --clone-url https://github.com/clime/example.git" 0 + rlRun "copr-cli buildscm ${NAME_PREFIX}TestBug1393361-1 --clone-url https://github.com/clime/example.git" 0 rlRun "curl --silent ${FRONTEND_URL}/api/coprs/${NAME_PREFIX}TestBug1393361-1/detail/ | grep TestBug1393361-1/fedora-27-x86_64" 0 rlRun "curl --silent ${FRONTEND_URL}/api/coprs/${NAME_PREFIX}TestBug1393361-2/detail/ | grep TestBug1393361-2/fedora-27-x86_64" 0 diff --git a/cli/copr-cli.spec b/cli/copr-cli.spec index cf48416..21bea3d 100644 --- a/cli/copr-cli.spec +++ b/cli/copr-cli.spec @@ -29,7 +29,7 @@ BuildRequires: libxslt BuildRequires: util-linux %if %{with python3} -Requires: python3-copr >= 1.63 +Requires: python3-copr >= 1.89 Requires: python3-jinja2 Requires: python3-simplejson @@ -43,7 +43,7 @@ BuildRequires: python3-pytest BuildRequires: python3-setuptools BuildRequires: python3-simplejson %else -Requires: python-copr >= 1.63 +Requires: python-copr >= 1.89 Requires: python-jinja2 Requires: python-simplejson diff --git a/cli/copr_cli/main.py b/cli/copr_cli/main.py index c44e74e..7ce171c 100644 --- a/cli/copr_cli/main.py +++ b/cli/copr_cli/main.py @@ -13,9 +13,9 @@ from collections import defaultdict import logging if six.PY2: - from urlparse import urlparse + from urlparse import urlparse, urljoin else: - from urllib.parse import urlparse + from urllib.parse import urlparse, urljoin if sys.version_info < (2, 7): class NullHandler(logging.Handler): @@ -31,7 +31,10 @@ from copr import CoprClient from copr.client.responses import CoprResponse import copr.exceptions as copr_exceptions -from .util import ProgressBar +from copr.v3 import (Client, config_from_file, CoprException, CoprRequestException, CoprNoConfigException, + CoprConfigException, CoprNoResultException) + +from .util import ProgressBar, json_dumps from .build_config import MockProfile import pkg_resources @@ -57,24 +60,24 @@ except NameError: pass class Commands(object): - def __init__(self, config): - self.config = config + def __init__(self, config_path): try: - self.client = CoprClient.create_from_file_config(config) - except (copr_exceptions.CoprNoConfException, - copr_exceptions.CoprConfigException): - sys.stderr.write(no_config_warning.format(config or "~/.config/copr")) - self.client = CoprClient( - copr_url=u"http://copr.fedoraproject.org", - no_config=True - ) + self.config_path = config_path + self.config = config_from_file(config_path) + + except (CoprNoConfigException, + CoprConfigException): + sys.stderr.write(no_config_warning.format(config_path or "~/.config/copr")) + self.config = {"copr_url": "http://copr.fedoraproject.org", "no_config": True} + + self.client = Client(self.config) def requires_api_auth(func): """ Decorator that checks config presence """ def wrapper(self, args): - if self.client.no_config: + if "no_config" in self.config: sys.stderr.write("Error: Operation requires api authentication\n") sys.exit(6) @@ -89,13 +92,13 @@ class Commands(object): """ def wrapper(self, args): - if self.client.no_config and args.username is None: + if "no_config" in self.config and args.username is None: sys.stderr.write( "Error: Operation requires username\n" "Pass username to command or create `~/.config/copr`\n") sys.exit(6) - if args.username is None and self.client.username is None: + if args.username is None and self.config["username"] is None: sys.stderr.write( "Error: Operation requires username\n" "Pass username to command or add it to `~/.config/copr`\n") @@ -107,6 +110,15 @@ class Commands(object): wrapper.__name__ = func.__name__ return wrapper + def parse_name(self, name): + m = re.match(r"([^/]+)/(.*)", name) + if m: + owner = m.group(1) + name = m.group(2) + else: + owner = self.config["username"] + return owner, name + def _watch_builds(self, build_ids): """ :param build_ids: list of build IDs @@ -125,27 +137,21 @@ class Commands(object): if build_id in done: continue - build_details = self.client.get_build_details(build_id) - - if build_details.output != "ok": - errmsg = " Build {1}: Unable to get build status: {0}". \ - format(build_details.error, build_id) - raise copr_exceptions.CoprRequestException(errmsg) - + build_details = self.client.build_proxy.get(build_id) now = datetime.datetime.now() - if prevstatus[build_id] != build_details.status: - prevstatus[build_id] = build_details.status + if prevstatus[build_id] != build_details.state: + prevstatus[build_id] = build_details.state print(" {0} Build {2}: {1}".format( now.strftime("%H:%M:%S"), - build_details.status, build_id)) + build_details.state, build_id)) sys.stdout.flush() - if build_details.status in ["failed"]: + if build_details.state in ["failed"]: failed_ids.append(build_id) - if build_details.status in ["succeeded", "skipped", - "failed", "canceled"]: + if build_details.state in ["succeeded", "skipped", + "failed", "canceled"]: done.add(build_id) - if build_details.status == "unknown": + if build_details.state == "unknown": raise copr_exceptions.CoprBuildException( "Unknown status.") @@ -166,7 +172,7 @@ class Commands(object): """ Simply print out the current user as defined in copr config. """ - print(self.client.username) + print(self.config["username"]) def action_new_webhook_secret(self, args): """ @@ -194,28 +200,28 @@ class Commands(object): :param args: argparse arguments provided by the user """ - self.client.authentication_check() + self.client.build_proxy.auth_check() bar = None progress_callback = None + build_function = self.client.build_proxy.create_from_url builds = [] for pkg in args.pkgs: if os.path.exists(pkg): bar = ProgressBar(max=os.path.getsize(pkg)) + build_function = self.client.build_proxy.create_from_file + data = {"path": pkg} # pylint: disable=function-redefined def progress_callback(monitor): bar.next(n=8192) print('Uploading package {0}'.format(pkg)) + else: + data = {"url": pkg} - data = { - "pkgs": [pkg], - "progress_callback": progress_callback, - } - - builds.append(self.process_build(args, self.client.create_new_build, data, bar=bar)) + builds.append(self.process_build(args, build_function, data, bar=bar, progress_callback=progress_callback)) return builds @@ -226,45 +232,13 @@ class Commands(object): :param args: argparse arguments provided by the user """ - username, copr = parse_name(args.copr) - data = { "pypi_package_name": args.packagename, "pypi_package_version": args.packageversion, "spec_template": args.spec_template, "python_versions": args.pythonversions, } - return self.process_build(args, self.client.create_new_build_pypi, data) - - @requires_api_auth - def action_build_tito(self, args): - """ - Method called when the 'buildtito' action has been selected by the user. - - :param args: argparse arguments provided by the user - """ - data = { - "git_url": args.git_url, - "git_dir": args.git_dir, - "git_branch": args.git_branch, - "tito_test": args.tito_test, - } - return self.process_build(args, self.client.create_new_build_tito, data) - - @requires_api_auth - def action_build_mock(self, args): - """ - Method called when the 'build-mock' action has been selected by the user. - - :param args: argparse arguments provided by the user - """ - data = { - "scm_type": args.scm_type, - "scm_url": args.scm_url, - "scm_branch": args.scm_branch, - "spec": args.spec, - } - return self.process_build(args, self.client.create_new_build_mock, data) + return self.process_build(args, self.client.build_proxy.create_from_pypi, data) @requires_api_auth def action_build_scm(self, args): @@ -279,9 +253,9 @@ class Commands(object): "subdirectory": args.subdirectory, "spec": args.spec, "scm_type": args.scm_type, - "srpm_build_method": args.srpm_build_method, + "source_build_method": args.srpm_build_method, } - return self.process_build(args, self.client.create_new_build_scm, data) + return self.process_build(args, self.client.build_proxy.create_from_scm, data) @requires_api_auth def action_build_rubygems(self, args): @@ -291,7 +265,7 @@ class Commands(object): :param args: argparse arguments provided by the user """ data = {"gem_name": args.gem_name} - return self.process_build(args, self.client.create_new_build_rubygems, data) + return self.process_build(args, self.client.build_proxy.create_from_rubygems, data) @requires_api_auth def action_build_custom(self, args): @@ -306,7 +280,7 @@ class Commands(object): for arg in ['script_chroot', 'script_builddeps', 'script_resultdir']: data[arg] = getattr(args, arg) - return self.process_build(args, self.client.create_new_build_custom, data) + return self.process_build(args, self.client.build_proxy.create_from_custom, data) @requires_api_auth def action_build_distgit(self, args): @@ -315,29 +289,36 @@ class Commands(object): :param args: argparse arguments provided by the user """ - data = {"clone_url": args.clone_url, "branch": args.branch} - return self.process_build(args, self.client.create_new_build_distgit, data) + data = {"clone_url": args.clone_url, "committish": args.branch} + return self.process_build(args, self.client.build_proxy.create_from_scm, data) - def process_build(self, args, build_function, data, bar=None): - username, copr = parse_name(args.copr) + def process_build(self, args, build_function, data, bar=None, progress_callback=None): + username, copr = self.parse_name(args.copr) try: - result = build_function(username=username, projectname=copr, chroots=args.chroots, memory=args.memory, - timeout=args.timeout, background=args.background, **data) - finally: - if bar: - bar.finish() + buildopts = {"memory": args.memory, "timeout": args.timeout, "chroots": args.chroots, + "background": args.background, "progress_callback": progress_callback} + result = build_function(ownername=username, projectname=copr, buildopts=buildopts, **data) - if result.output != "ok": - sys.stderr.write(result.error + "\n") - return - print(result.message) + builds = result if type(result) == list else [result] + print("Build was added to {0}:".format(builds[0].projectname)) + + for build in builds: + url = urljoin(self.config["copr_url"], "/coprs/build/{}".format(build.id)) + print(" {}".format(url)) + + build_ids = [build.id for build in builds] + print("Created builds: {0}".format(" ".join(map(str, build_ids)))) - build_ids = [bw.build_id for bw in result.builds_list] - print("Created builds: {0}".format(" ".join(map(str, build_ids)))) + if not args.nowait: + self._watch_builds(build_ids) - if not args.nowait: - self._watch_builds(build_ids) + except CoprException as ex: + sys.stderr.write(str(ex) + "\n") + sys.exit(1) + finally: + if bar: + bar.finish() @requires_api_auth @@ -348,19 +329,19 @@ class Commands(object): :param args: argparse arguments provided by the user """ - username, copr = parse_name(args.name) - result = self.client.create_project( - username=username, projectname=copr, description=args.description, + username, copr = self.parse_name(args.name) + project = self.client.project_proxy.add( + ownername=username, projectname=copr, description=args.description, instructions=args.instructions, chroots=args.chroots, - repos=args.repos, initial_pkgs=args.initial_pkgs, - disable_createrepo=args.disable_createrepo, + additional_repos=args.repos, # packages=args.initial_pkgs, @TODO remove packages + devel_mode=args.disable_createrepo, unlisted_on_hp=ON_OFF_MAP[args.unlisted_on_hp], enable_net=ON_OFF_MAP[args.enable_net], persistent=args.persistent, auto_prune=ON_OFF_MAP[args.auto_prune], use_bootstrap_container=ON_OFF_MAP[args.use_bootstrap_container], ) - print(result.message) + print("New project was successfully created.") @requires_api_auth def action_modify_project(self, args): @@ -369,11 +350,11 @@ class Commands(object): :param args: argparse arguments provided by the user """ - username, copr = parse_name(args.name) - result = self.client.modify_project( - username=username, projectname=copr, + username, copr = self.parse_name(args.name) + project = self.client.project_proxy.edit( + ownername=username, projectname=copr, description=args.description, instructions=args.instructions, - repos=args.repos, disable_createrepo=args.disable_createrepo, + additional_repos=args.repos, devel_mode=args.disable_createrepo, unlisted_on_hp=ON_OFF_MAP[args.unlisted_on_hp], enable_net=ON_OFF_MAP[args.enable_net], auto_prune=ON_OFF_MAP[args.auto_prune], @@ -388,9 +369,9 @@ class Commands(object): :param args: argparse arguments provided by the user """ - username, copr = parse_name(args.copr) - result = self.client.delete_project(username=username, projectname=copr) - print(result.message) + username, copr = self.parse_name(args.copr) + project = self.client.project_proxy.delete(ownername=username, projectname=copr) + print("Project {} has been deleted.".format(project.name)) @requires_api_auth def action_fork(self, args): @@ -399,29 +380,34 @@ class Commands(object): :param args: argparse arguments provided by the user """ - username, copr = parse_name(args.dst) - result = self.client.fork_project(source=args.src, username=username, projectname=copr, confirm=args.confirm) - print(result.message) + srcownername, srcprojectname = self.parse_name(args.src) + dstownername, dstprojectname = self.parse_name(args.dst) + try: + dst = self.client.project_proxy.get(ownername=dstownername, projectname=dstprojectname) + except CoprNoResultException: + dst = None + + project = self.client.project_proxy.fork(ownername=srcownername, projectname=srcprojectname, + dstownername=dstownername, dstprojectname=dstprojectname, + confirm=args.confirm) + if not dst: + print("Forking project {}/{} for you into {}.\nPlease be aware that it may take a few minutes " + "to duplicate backend data.".format(srcownername, srcprojectname, project.full_name)) + else: + print("Updating packages in {} from {}/{}.\nPlease be aware that it may take a few minutes " + "to duplicate backend data.".format(project.full_name, srcownername, srcprojectname)) def action_mock_config(self, args): - """ Method called when the 'list' action has been selected by the + """ Method called when the 'mock-config' action has been selected by the user. :param args: argparse arguments provided by the user """ - username = self.client.username - project = args.project.split("/") - if len(project) != 2: - args.project = username + "/" + args.project - - result = self.client.get_build_config(args.project, args.chroot) - if result.output != "ok": - sys.stderr.write(result.error + "\n") - sys.stderr.write("Un-expected data returned, please report this issue\n") - - print(MockProfile(result.build_config)) + ownername, projectname = self.parse_name(args.project) + build_config = self.client.project_chroot_proxy.get_build_config(ownername, projectname, args.chroot) + print(MockProfile(build_config)) @check_username_presence @@ -432,28 +418,37 @@ class Commands(object): :param args: argparse arguments provided by the user """ - username = args.username or self.client.username - result = self.client.get_projects_list(username) - # import ipdb; ipdb.set_trace() - if result.output != "ok": - sys.stderr.write(result.error + "\n") + username = args.username or self.config["username"] + try: + projects = self.client.project_proxy.get_list(username) + if not projects: + sys.stderr.write("No copr retrieved for user: '{0}'\n".format(username)) + return + + for project in projects: + print("Name: {}".format(project.name)) + print(" Description: {}".format(project.description)) + if project.chroot_repos: + print(" Repo(s):") + for name, url in project.chroot_repos.items(): + print(" {}: {}".format(name, url)) + if project.additional_repos: + print(" Additional repo: {}".format(" ".join(project.additional_repos))) + print("") + + except CoprRequestException as ex: + sys.stderr.write(str(ex) + "\n") sys.stderr.write("Un-expected data returned, please report this issue\n") - elif not result.projects_list: - sys.stderr.write("No copr retrieved for user: '{0}'\n".format(username)) - return - - for prj in result.projects_list: - print(prj) def action_status(self, args): - result = self.client.get_build_details(args.build_id) - print(result.status) + build = self.client.build_proxy.get(args.build_id) + print(build.state) def action_download_build(self, args): - result = self.client.get_build_details(args.build_id) - base_len = len(os.path.split(result.results)) + build = self.client.build_proxy.get(args.build_id) + base_len = len(os.path.split(build.results)) - for chroot, url in result.results_by_chroot.items(): + for chroot, url in build.results_by_chroot.items(): if args.chroots and chroot not in args.chroots: continue @@ -469,15 +464,15 @@ class Commands(object): user. :param args: argparse arguments provided by the user """ - result = self.client.cancel_build(args.build_id) - print(result.status) + build = self.client.build_proxy.cancel(args.build_id) + print(build.state) def action_watch_build(self, args): self._watch_builds(args.build_id) def action_delete_build(self, args): - result = self.client.delete_build(args.build_id) - print(result.status) + build = self.client.build_proxy.delete(args.build_id) + print("Build deleted") ######################################################### ### Chroot actions ### @@ -491,12 +486,12 @@ class Commands(object): :param args: argparse arguments provided by the user """ owner, copr, chroot = parse_chroot_path(args.coprchroot) - result = self.client.edit_chroot( + project_chroot = self.client.project_chroot_proxy.edit( ownername=owner, projectname=copr, chrootname=chroot, - upload_comps=args.upload_comps, delete_comps=args.delete_comps, - packages=args.packages, repos=args.repos + comps=args.upload_comps, delete_comps=args.delete_comps, + additional_packages=args.packages, additional_repos=args.repos ) - print(result.message) + print("Edit chroot operation was successful.") def action_get_chroot(self, args): """ Method called when the 'get-chroot' action has been selected by the @@ -505,35 +500,18 @@ class Commands(object): :param args: argparse arguments provided by the user """ owner, copr, chroot = parse_chroot_path(args.coprchroot) - result = self.client.get_chroot( + project_chroot = self.client.project_chroot_proxy.get( ownername=owner, projectname=copr, chrootname=chroot ) - print(simplejson.dumps(result.chroot, indent=4, sort_keys=True, for_json=True)) + print(json_dumps(project_chroot)) ######################################################### ### Package actions ### ######################################################### @requires_api_auth - def action_add_or_edit_package_tito(self, args): - ownername, projectname = parse_name(args.copr) - data = { - "package_name": args.name, - "git_url": args.git_url, - "git_dir": args.git_dir, - "git_branch": args.git_branch, - "tito_test": ON_OFF_MAP[args.tito_test], - "webhook_rebuild": ON_OFF_MAP[args.webhook_rebuild], - } - if args.create: - result = self.client.add_package_tito(ownername=ownername, projectname=projectname, **data) - else: - result = self.client.edit_package_tito(ownername=ownername, projectname=projectname, **data) - print(result.message) - - @requires_api_auth def action_add_or_edit_package_pypi(self, args): - ownername, projectname = parse_name(args.copr) + ownername, projectname = self.parse_name(args.copr) data = { "package_name": args.name, "pypi_package_name": args.packagename, @@ -543,31 +521,14 @@ class Commands(object): "webhook_rebuild": ON_OFF_MAP[args.webhook_rebuild], } if args.create: - result = self.client.add_package_pypi(ownername=ownername, projectname=projectname, **data) - else: - result = self.client.edit_package_pypi(ownername=ownername, projectname=projectname, **data) - print(result.message) - - @requires_api_auth - def action_add_or_edit_package_mockscm(self, args): - ownername, projectname = parse_name(args.copr) - data = { - "package_name": args.name, - "scm_type": args.scm_type, - "scm_url": args.scm_url, - "scm_branch": args.scm_branch, - "spec": args.spec, - "webhook_rebuild": ON_OFF_MAP[args.webhook_rebuild], - } - if args.create: - result = self.client.add_package_mockscm(ownername=ownername, projectname=projectname, **data) + package = self.client.package_proxy.add(ownername, projectname, args.name, "pypi", data) else: - result = self.client.edit_package_mockscm(ownername=ownername, projectname=projectname, **data) - print(result.message) + package = self.client.package_proxy.edit(ownername, projectname, args.name, "pypi", data) + print("Create or edit operation was successful.") @requires_api_auth def action_add_or_edit_package_scm(self, args): - ownername, projectname = parse_name(args.copr) + ownername, projectname = self.parse_name(args.copr) data = { "package_name": args.name, "clone_url": args.clone_url, @@ -575,32 +536,32 @@ class Commands(object): "subdirectory": args.subdirectory, "spec": args.spec, "scm_type": args.scm_type, - "srpm_build_method": args.srpm_build_method, + "source_build_method": args.srpm_build_method, "webhook_rebuild": ON_OFF_MAP[args.webhook_rebuild], } if args.create: - result = self.client.add_package_scm(ownername=ownername, projectname=projectname, **data) + package = self.client.package_proxy.add(ownername, projectname, args.name, "scm", data) else: - result = self.client.edit_package_scm(ownername=ownername, projectname=projectname, **data) - print(result.message) + package = self.client.package_proxy.edit(ownername, projectname, args.name, "scm", data) + print("Create or edit operation was successful.") @requires_api_auth def action_add_or_edit_package_rubygems(self, args): - ownername, projectname = parse_name(args.copr) + ownername, projectname = self.parse_name(args.copr) data = { "package_name": args.name, "gem_name": args.gem_name, "webhook_rebuild": ON_OFF_MAP[args.webhook_rebuild], } if args.create: - result = self.client.add_package_rubygems(ownername=ownername, projectname=projectname, **data) + package = self.client.package_proxy.add(ownername, projectname, args.name, "rubygems", data) else: - result = self.client.edit_package_rubygems(ownername=ownername, projectname=projectname, **data) - print(result.message) + package = self.client.package_proxy.edit(ownername, projectname, args.name, "rubygems", data) + print("Create or edit operation was successful.") @requires_api_auth def action_add_or_edit_package_custom(self, args): - ownername, projectname = parse_name(args.copr) + ownername, projectname = self.parse_name(args.copr) data = { "package_name": args.name, "script": ''.join(args.script.readlines()), @@ -610,80 +571,91 @@ class Commands(object): "webhook_rebuild": ON_OFF_MAP[args.webhook_rebuild], } if args.create: - result = self.client.add_package_custom(ownername=ownername, projectname=projectname, **data) + package = self.client.package_proxy.add(ownername, projectname, args.name, "custom", data) else: - result = self.client.edit_package_custom(ownername=ownername, projectname=projectname, **data) - print(result.message) + package = self.client.package_proxy.edit(ownername, projectname, args.name, "custom", data) + print("Create or edit operation was successful.") def action_list_packages(self, args): - ownername, projectname = parse_name(args.copr) - data = { - "with_latest_build": args.with_latest_build, - "with_latest_succeeded_build": args.with_latest_succeeded_build, - "with_all_builds": args.with_all_builds, - } - result = self.client.get_packages_list(ownername=ownername, projectname=projectname, **data) - print(simplejson.dumps(result.packages_list, indent=4, sort_keys=True, for_json=True)) + ownername, projectname = self.parse_name(args.copr) + packages = self.client.package_proxy.get_list(ownername=ownername, projectname=projectname) + packages_with_builds = [self._package_with_builds(p, args) for p in packages] + print(json_dumps(packages_with_builds)) def action_list_package_names(self, args): - ownername, projectname = parse_name(args.copr) - result = self.client.get_packages_list(ownername=ownername, projectname=projectname) - for package in result.packages_list: + ownername, projectname = self.parse_name(args.copr) + packages = self.client.package_proxy.get_list(ownername=ownername, projectname=projectname) + for package in packages: print(package.name) def action_get_package(self, args): - ownername, projectname = parse_name(args.copr) - data = { - "pkg_name": args.name, - "with_latest_build": args.with_latest_build, - "with_latest_succeeded_build": args.with_latest_succeeded_build, - "with_all_builds": args.with_all_builds, - } - result = self.client.get_package(ownername=ownername, projectname=projectname, **data) - print(simplejson.dumps(result.package, indent=4, sort_keys=True, for_json=True)) + ownername, projectname = self.parse_name(args.copr) + package = self.client.package_proxy.get(ownername=ownername, projectname=projectname, packagename=args.name) + package = self._package_with_builds(package, args) + print(json_dumps(package)) + + def _package_with_builds(self, package, args): + ownername, projectname = self.parse_name(args.copr) + kwargs = {"ownername": ownername, "projectname": projectname, "packagename": package.name} + pagination = {"limit": 1, "order": "id", "order_type": "DESC"} + + if args.with_latest_build: + builds = self.client.build_proxy.get_list(pagination=pagination, **kwargs) + package["latest_build"] = builds[0] if builds else None + + if args.with_latest_succeeded_build: + builds = self.client.build_proxy.get_list(status="succeeded", pagination=pagination, **kwargs) + package["latest_succeeded_build"] = builds[0] if builds else None + + if args.with_all_builds: + builds = self.client.build_proxy.get_list(**kwargs) + package["builds"] = builds + + return package def action_delete_package(self, args): - ownername, projectname = parse_name(args.copr) - data = { "pkg_name": args.name } - result = self.client.delete_package(ownername=ownername, projectname=projectname, **data) - print(result.message) + ownername, projectname = self.parse_name(args.copr) + package = self.client.package_proxy.delete(ownername=ownername, projectname=projectname, packagename=args.name) + print("Package was successfully deleted.") def action_reset_package(self, args): - ownername, projectname = parse_name(args.copr) - data = { "pkg_name": args.name } - result = self.client.reset_package(ownername=ownername, projectname=projectname, **data) - print(result.message) + ownername, projectname = self.parse_name(args.copr) + package = self.client.package_proxy.reset(ownername=ownername, projectname=projectname, packagename=args.name) + print("Package's default source was successfully reseted.") def action_build_package(self, args): - ownername, projectname = parse_name(args.copr) - data = { - "pkg_name": args.name, + ownername, projectname = self.parse_name(args.copr) + buildopts = { "chroots": args.chroots, #"memory": args.memory, "timeout": args.timeout } + try: + build = self.client.package_proxy.build(ownername=ownername, projectname=projectname, + packagename=args.name, buildopts=buildopts) + print("Build was added to {0}.".format(build.projectname)) + print("Created builds: {0}".format(build.id)) - result = self.client.build_package(ownername=ownername, projectname=projectname, **data) - - if result.output != "ok": - sys.stderr.write(result.error + "\n") - return - print(result.message) - - build_ids = [bw.build_id for bw in result.builds_list] - print("Created builds: {0}".format(" ".join(map(str, build_ids)))) - - if not args.nowait: - self._watch_builds(build_ids) + if not args.nowait: + self._watch_builds([build.id]) + except CoprRequestException as ex: + sys.stderr.write(str(ex) + "\n") def action_build_module(self, args): """ Build module via Copr MBS """ - ownername, projectname = parse_name(args.copr) - modulemd = open(args.yaml, "rb") if args.yaml else args.url - response = self.client.build_module(modulemd, ownername, projectname) - print(response.message if response.output == "ok" else response.error) + ownername, projectname = self.parse_name(args.copr) + + try: + if args.yaml: + module = self.client.module_proxy.build_from_file(ownername, projectname, args.yaml) + else: + module = self.client.module_proxy.build_from_url(ownername, projectname, args.url) + print("Created module {}".format(module.nsv)) + + except CoprRequestException as ex: + print(ex) def setup_parser(): @@ -766,7 +738,7 @@ def setup_parser(): help="Description of the copr") parser_create.add_argument("--instructions", help="Instructions for the copr") - parser_create.add_argument("--disable_createrepo", + parser_create.add_argument("--disable_createrepo", type=str2bool, help="Disable metadata auto generation") parser_create.add_argument("--enable-net", choices=["on", "off"], default="off", help="If net should be enabled for builds in this project (default is off)") @@ -793,7 +765,7 @@ def setup_parser(): help="Instructions for the copr") parser_modify.add_argument("--repo", dest="repos", action="append", help="Repository to add to this copr") - parser_modify.add_argument("--disable_createrepo", + parser_modify.add_argument("--disable_createrepo", type=str2bool, help="Disable metadata auto generation") parser_modify.add_argument("--enable-net", choices=["on", "off"], help="If net should be enabled for builds in this project (default is \"don't change\")") @@ -822,16 +794,6 @@ def setup_parser(): ### Source-type related options ### ######################################################### - parser_tito_args_parent = argparse.ArgumentParser(add_help=False) - parser_tito_args_parent.add_argument("--git-url", metavar="URL", dest="git_url", required=True, - help="URL to a project managed by Tito") - parser_tito_args_parent.add_argument("--git-dir", metavar="DIRECTORY", dest="git_dir", - help="Relative path from Git root to directory containing .spec file") - parser_tito_args_parent.add_argument("--git-branch", metavar="BRANCH", dest="git_branch", - help="Git branch that you want to build from") - parser_tito_args_parent.add_argument("--test", dest="tito_test", choices=["on", "off"], - help="Build the last commit instead of the last release tag") - parser_pypi_args_parent = argparse.ArgumentParser(add_help=False) parser_pypi_args_parent.add_argument("--pythonversions", nargs="*", type=int, metavar="VERSION", default=[3, 2], help="For what Python versions to build (by default: 3 2)") @@ -842,15 +804,6 @@ def setup_parser(): parser_pypi_args_parent.add_argument("--template", "-t", dest="spec_template", help="Spec template to be used to build srpm with pyp2rpm") - parser_mockscm_args_parent = argparse.ArgumentParser(add_help=False) - parser_mockscm_args_parent.add_argument("--scm-type", metavar="TYPE", dest="scm_type", choices=["git", "svn"], default="git", - help="specify versioning tool, default is 'git'") - parser_mockscm_args_parent.add_argument("--scm-url", metavar="URL", dest="scm_url", - help="url to a project versioned by Git or SVN, required") - parser_mockscm_args_parent.add_argument("--scm-branch", metavar="BRANCH", dest="scm_branch", help="") - parser_mockscm_args_parent.add_argument("--spec", dest="spec", metavar="FILE", - help="relative path from SCM root to .spec file, required") - parser_scm_args_parent = argparse.ArgumentParser(add_help=False) parser_scm_args_parent.add_argument("--clone-url", required=True, help="clone url to a project versioned by Git or SVN, required") @@ -940,16 +893,6 @@ def setup_parser(): help="DEPRECATED. Use SCM source type instead.") parser_build_distgit.set_defaults(func="action_build_distgit") - # create the parser for the "buildtito" command - parser_build_tito = subparsers.add_parser("buildtito", parents=[parser_tito_args_parent, parser_build_parent], - help="DEPRECATED. Use SCM source type instead.") - parser_build_tito.set_defaults(func="action_build_tito") - - # create the parser for the "buildmock" command - parser_build_mock = subparsers.add_parser("buildmock", parents=[parser_mockscm_args_parent, parser_build_parent], - help="DEPRECATED. Use SCM source type instead.") - parser_build_mock.set_defaults(func="action_build_mock") - # create the parser for the "buildscm" command parser_build_scm = subparsers.add_parser("buildscm", parents=[parser_scm_args_parent, parser_build_parent], help="Builds package from Git/DistGit/SVN repository.") @@ -1025,17 +968,6 @@ def setup_parser(): parser_add_or_edit_package_parent.add_argument("--webhook-rebuild", choices=["on", "off"], help="Enable auto-rebuilding.") - # Tito edit/create - parser_add_package_tito = subparsers.add_parser("add-package-tito", - help="DEPRECATED. Use SCM source type instead.", - parents=[parser_tito_args_parent, parser_add_or_edit_package_parent]) - parser_add_package_tito.set_defaults(func="action_add_or_edit_package_tito", create=True) - - parser_edit_package_tito = subparsers.add_parser("edit-package-tito", - help="DEPRECATED. Use SCM source type instead.", - parents=[parser_tito_args_parent, parser_add_or_edit_package_parent]) - parser_edit_package_tito.set_defaults(func="action_add_or_edit_package_tito", create=False) - # PyPI edit/create parser_add_package_pypi = subparsers.add_parser("add-package-pypi", help="Creates a new PyPI package", @@ -1047,16 +979,6 @@ def setup_parser(): parents=[parser_pypi_args_parent, parser_add_or_edit_package_parent]) parser_edit_package_pypi.set_defaults(func="action_add_or_edit_package_pypi", create=False) - # MockSCM edit/create - parser_add_package_mockscm = subparsers.add_parser("add-package-mockscm", - help="DEPRECATED. Use SCM source type instead.", - parents=[parser_mockscm_args_parent, parser_add_or_edit_package_parent]) - parser_add_package_mockscm.set_defaults(func="action_add_or_edit_package_mockscm", create=True) - - parser_edit_package_mockscm = subparsers.add_parser("edit-package-mockscm", - help="DEPRECATED. Use SCM source type instead.", - parents=[parser_mockscm_args_parent, parser_add_or_edit_package_parent]) - parser_edit_package_mockscm.set_defaults(func="action_add_or_edit_package_mockscm", create=False) # SCM edit/create parser_add_package_scm = subparsers.add_parser("add-package-scm", @@ -1171,16 +1093,6 @@ def setup_parser(): return parser -def parse_name(name): - m = re.match(r"([^/]+)/(.*)", name) - if m: - owner = m.group(1) - name = m.group(2) - else: - owner = None - return owner, name - - def parse_chroot_path(path): m = re.match(r"(([^/]+)/)?([^/]+)/(.*)", path) if m: @@ -1197,6 +1109,15 @@ def enable_debug(): sys.stderr.write("# Debug log enabled #\n") +def str2bool(v): + if v.lower() in ("yes", "true", "on", "t", "y", "1"): + return True + elif v.lower() in ("no", "false", "off", "f", "n", "0"): + return False + else: + raise argparse.ArgumentTypeError("Boolean value expected.") + + def main(argv=sys.argv[1:]): try: # Set up parser for global args @@ -1222,14 +1143,14 @@ def main(argv=sys.argv[1:]): except copr_exceptions.CoprUnknownResponseException as e: sys.stderr.write("\nError: {0}\n".format(e)) sys.exit(5) - except copr_exceptions.CoprRequestException as e: + except (CoprRequestException, CoprNoResultException) as e: sys.stderr.write("\nSomething went wrong:") sys.stderr.write("\nError: {0}\n".format(e)) sys.exit(1) except argparse.ArgumentTypeError as e: sys.stderr.write("\nError: {0}".format(e)) sys.exit(2) - except copr_exceptions.CoprException as e: + except CoprException as e: sys.stderr.write("\nError: {0}\n".format(e)) sys.exit(3) diff --git a/cli/copr_cli/util.py b/cli/copr_cli/util.py index 1887aca..3f9420d 100644 --- a/cli/copr_cli/util.py +++ b/cli/copr_cli/util.py @@ -1,5 +1,8 @@ # coding: utf-8 +import simplejson + + try: from progress.bar import Bar except ImportError: @@ -50,3 +53,15 @@ if progress: suffix = "%(downloaded)s %(download_speed)s eta %(eta_td)s" else: ProgressBar = DummyBar + + +def serializable(result): + if isinstance(result, dict): + new_result = result.copy() + new_result.pop("__response__", None) + return new_result + return result + + +def json_dumps(result): + return simplejson.dumps(serializable(result), indent=4, sort_keys=True, for_json=True) diff --git a/cli/tests/resources/list_projects_expected.txt b/cli/tests/resources/list_projects_expected.txt index 6a29b29..9c54005 100644 --- a/cli/tests/resources/list_projects_expected.txt +++ b/cli/tests/resources/list_projects_expected.txt @@ -1,11 +1,11 @@ Name: perl516 Description: A recent stable release of Perl with a number of additional utilities, scripts, and database connectors for MySQL and PostgreSQL. This version provides a large number of new features and enhancements, including new debugging options, improved Unicode support, and better performance. - Yum repo(s): + Repo(s): epel-6-x86_64: http://copr-be.cloud.fedoraproject.org/results/rhscl/perl516/epel-6-x86_64/ Additional repo: http://copr-be.cloud.fedoraproject.org/results/rhscl/httpd24/epel-6-$basearch/ http://copr-be.cloud.fedoraproject.org/results/msuchy/scl-utils/epel-6-$basearch/ http://people.redhat.com/~msuchy/rhscl-1.1-rhel-6-candidate-perl516/ Name: ruby193 Description: A recent stable release of Ruby with Rails 3.2.8 and a large collection of Ruby gems. This Software Collection gives developers on Red Hat Enterprise Linux 6 access to Ruby 1.9, which provides a number of new features and enhancements, including improved Unicode support, enhanced threading, and faster load times. - Yum repo(s): + Repo(s): epel-6-x86_64: http://copr-be.cloud.fedoraproject.org/results/rhscl/ruby193/epel-6-x86_64/ Additional repo: http://copr-be.cloud.fedoraproject.org/results/msuchy/scl-utils/epel-6-$basearch/ http://copr-be.cloud.fedoraproject.org/results/rhscl/httpd24/epel-6-$basearch/ http://copr-be.cloud.fedoraproject.org/results/rhscl/v8314/epel-6-$basearch/ diff --git a/cli/tests/resources/list_projects_response.json b/cli/tests/resources/list_projects_response.json index cb26da3..ea01474 100644 --- a/cli/tests/resources/list_projects_response.json +++ b/cli/tests/resources/list_projects_response.json @@ -1 +1,28 @@ -{"output": "ok", "repos": [{"yum_repos": {"epel-6-x86_64": "http://copr-be.cloud.fedoraproject.org/results/rhscl/perl516/epel-6-x86_64/"}, "additional_repos": "http://copr-be.cloud.fedoraproject.org/results/rhscl/httpd24/epel-6-$basearch/ http://copr-be.cloud.fedoraproject.org/results/msuchy/scl-utils/epel-6-$basearch/ http://people.redhat.com/~msuchy/rhscl-1.1-rhel-6-candidate-perl516/", "description": "A recent stable release of Perl with a number of additional utilities, scripts, and database connectors for MySQL and PostgreSQL. This version provides a large number of new features and enhancements, including new debugging options, improved Unicode support, and better performance.", "name": "perl516", "instructions": ""}, {"yum_repos": {"epel-6-x86_64": "http://copr-be.cloud.fedoraproject.org/results/rhscl/ruby193/epel-6-x86_64/"}, "additional_repos": "http://copr-be.cloud.fedoraproject.org/results/msuchy/scl-utils/epel-6-$basearch/ http://copr-be.cloud.fedoraproject.org/results/rhscl/httpd24/epel-6-$basearch/ http://copr-be.cloud.fedoraproject.org/results/rhscl/v8314/epel-6-$basearch/", "description": "A recent stable release of Ruby with Rails 3.2.8 and a large collection of Ruby gems. This Software Collection gives developers on Red Hat Enterprise Linux 6 access to Ruby 1.9, which provides a number of new features and enhancements, including improved Unicode support, enhanced threading, and faster load times.", "name": "ruby193", "instructions": ""}]} \ No newline at end of file +[ + { + "chroot_repos": { + "epel-6-x86_64": "http://copr-be.cloud.fedoraproject.org/results/rhscl/perl516/epel-6-x86_64/" + }, + "additional_repos": [ + "http://copr-be.cloud.fedoraproject.org/results/rhscl/httpd24/epel-6-$basearch/", + "http://copr-be.cloud.fedoraproject.org/results/msuchy/scl-utils/epel-6-$basearch/", + "http://people.redhat.com/~msuchy/rhscl-1.1-rhel-6-candidate-perl516/" + ], + "description": "A recent stable release of Perl with a number of additional utilities, scripts, and database connectors for MySQL and PostgreSQL. This version provides a large number of new features and enhancements, including new debugging options, improved Unicode support, and better performance.", + "name": "perl516", + "instructions": "" + }, + { + "chroot_repos": { + "epel-6-x86_64": "http://copr-be.cloud.fedoraproject.org/results/rhscl/ruby193/epel-6-x86_64/" + }, + "additional_repos": [ + "http://copr-be.cloud.fedoraproject.org/results/msuchy/scl-utils/epel-6-$basearch/", + "http://copr-be.cloud.fedoraproject.org/results/rhscl/httpd24/epel-6-$basearch/", + "http://copr-be.cloud.fedoraproject.org/results/rhscl/v8314/epel-6-$basearch/" + ], + "description": "A recent stable release of Ruby with Rails 3.2.8 and a large collection of Ruby gems. This Software Collection gives developers on Red Hat Enterprise Linux 6 access to Ruby 1.9, which provides a number of new features and enhancements, including improved Unicode support, enhanced threading, and faster load times.", + "name": "ruby193", + "instructions": "" + } +] diff --git a/cli/tests/test_cli.py b/cli/tests/test_cli.py index 60a5ed5..1085de0 100644 --- a/cli/tests/test_cli.py +++ b/cli/tests/test_cli.py @@ -1,20 +1,12 @@ import os import argparse -from collections import defaultdict import json -from pprint import pprint import pytest +from munch import Munch import six -import time import copr -from copr.client.parsers import ProjectListParser, CommonMsgErrorOutParser -from copr.client.responses import CoprResponse -from copr.exceptions import CoprConfigException, CoprNoConfException, \ - CoprRequestException, CoprUnknownResponseException, CoprException, \ - CoprBuildException -from copr.client import CoprClient -import copr_cli +from copr.exceptions import CoprUnknownResponseException, CoprBuildException from copr_cli.main import no_config_warning @@ -46,17 +38,26 @@ else: from copr_cli import main -def test_parse_name(): - assert main.parse_name("foo") == (None, "foo") - assert main.parse_name("frostyx/foo") == ("frostyx", "foo") - assert main.parse_name("@copr/foo") == ("@copr", "foo") +mock_config = { + "username": None, + "copr_url": "http://copr/", + "login": "", + "token": "", +} -@mock.patch('copr_cli.main.CoprClient') -def test_error_keyboard_interrupt(mock_cc, capsys): - mock_client = MagicMock(no_config=False) - mock_client.get_build_details.side_effect = KeyboardInterrupt() - mock_cc.create_from_file_config.return_value = mock_client +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_parse_name(config_from_file): + cmd = main.Commands(config_path=None) + assert cmd.parse_name("foo") == (None, "foo") + assert cmd.parse_name("frostyx/foo") == ("frostyx", "foo") + assert cmd.parse_name("@copr/foo") == ("@copr", "foo") + + +@mock.patch('copr.v3.proxies.build.BuildProxy.get') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_error_keyboard_interrupt(config_from_file, build_proxy_get, capsys): + build_proxy_get.side_effect = KeyboardInterrupt() with pytest.raises(SystemExit) as err: main.main(argv=["status", "123"]) @@ -66,13 +67,11 @@ def test_error_keyboard_interrupt(mock_cc, capsys): assert "Interrupted by user" in stderr -@mock.patch('copr_cli.main.CoprClient') -def test_error_copr_request(mock_cc, capsys): +@mock.patch('copr.v3.proxies.build.BuildProxy.get') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_error_copr_request(config_from_file, build_proxy_get, capsys): error_msg = "error message" - - mock_client = MagicMock(no_config=False) - mock_client.get_build_details.side_effect = CoprRequestException(error_msg) - mock_cc.create_from_file_config.return_value = mock_client + build_proxy_get.side_effect = copr.v3.CoprRequestException(error_msg) with pytest.raises(SystemExit) as err: main.main(argv=["status", "123"]) @@ -84,13 +83,9 @@ def test_error_copr_request(mock_cc, capsys): @mock.patch('copr_cli.main.setup_parser') -@mock.patch('copr_cli.main.CoprClient') -def test_error_argument_error(mock_cc, mock_setup_parser, capsys): +def test_error_argument_error(mock_setup_parser, capsys): error_msg = "error message" - mock_client = MagicMock(no_config=False) - mock_cc.create_from_file_config.return_value = mock_client - mock_setup_parser.return_value.parse_args.side_effect = \ argparse.ArgumentTypeError(error_msg) @@ -102,11 +97,7 @@ def test_error_argument_error(mock_cc, mock_setup_parser, capsys): assert error_msg in stderr -@mock.patch('copr_cli.main.CoprClient') -def test_error_no_args(mock_cc, capsys): - mock_client = MagicMock(no_config=False) - mock_cc.create_from_file_config.return_value = mock_client - +def test_error_no_args(capsys): for func_name in ["status", "build", "delete", "create"]: with pytest.raises(SystemExit) as err: main.main(argv=[func_name]) @@ -117,14 +108,11 @@ def test_error_no_args(mock_cc, capsys): assert "usage: copr" in stderr -@mock.patch('copr_cli.main.CoprClient') -def test_error_copr_common_exception(mock_cc, capsys): +@mock.patch('copr.v3.proxies.build.BuildProxy.get') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_error_copr_common_exception(config_from_file, build_proxy_get, capsys): error_msg = "error message" - - mock_client = MagicMock(no_config=False) - mock_client.get_build_details.side_effect = \ - CoprException(error_msg) - mock_cc.create_from_file_config.return_value = mock_client + build_proxy_get.side_effect = copr.v3.CoprException(error_msg) with pytest.raises(SystemExit) as err: main.main(argv=["status", "123"]) @@ -134,14 +122,11 @@ def test_error_copr_common_exception(mock_cc, capsys): assert error_msg in stderr -@mock.patch('copr_cli.main.CoprClient') -def test_error_copr_build_exception(mock_cc, capsys): +@mock.patch('copr_cli.main.Commands.action_build') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_error_copr_build_exception(config_from_file, action_build, capsys): error_msg = "error message" - - mock_client = MagicMock(no_config=False) - mock_client.create_new_build.side_effect = \ - CoprBuildException(error_msg) - mock_cc.create_from_file_config.return_value = mock_client + action_build.side_effect = CoprBuildException(error_msg) with pytest.raises(SystemExit) as err: main.main(argv=["build", "prj1", "src1"]) @@ -151,14 +136,11 @@ def test_error_copr_build_exception(mock_cc, capsys): assert error_msg in stderr -@mock.patch('copr_cli.main.CoprClient') -def test_error_copr_unknown_response(mock_cc, capsys): +@mock.patch('copr_cli.main.Commands.action_status') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_error_copr_unknown_response(config_from_file, action_status, capsys): error_msg = "error message" - - mock_client = MagicMock(no_config=False) - mock_client.get_build_details.side_effect = \ - CoprUnknownResponseException(error_msg) - mock_cc.create_from_file_config.return_value = mock_client + action_status.side_effect = CoprUnknownResponseException(error_msg) with pytest.raises(SystemExit) as err: main.main(argv=["status", "123"]) @@ -168,9 +150,9 @@ def test_error_copr_unknown_response(mock_cc, capsys): assert error_msg in stderr -@mock.patch('copr_cli.main.CoprClient') -def test_cancel_build_no_config(mock_cc, capsys): - mock_cc.create_from_file_config.side_effect = CoprNoConfException() +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_cancel_build_no_config(config_from_file, capsys): + config_from_file.side_effect = copr.v3.CoprNoConfigException() with pytest.raises(SystemExit) as err: main.main(argv=["cancel", "123400"]) @@ -185,13 +167,11 @@ def test_cancel_build_no_config(mock_cc, capsys): assert expected_warning in err -@mock.patch('copr_cli.main.CoprClient') -def test_cancel_build_response(mock_cc, capsys): +@mock.patch('copr.v3.proxies.build.BuildProxy.cancel') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_cancel_build_response(config_from_file, build_proxy_cancel, capsys): response_status = "foobar" - - mock_client = MagicMock(no_config=False, ) - mock_client.cancel_build.return_value = MagicMock(status=response_status) - mock_cc.create_from_file_config.return_value = mock_client + build_proxy_cancel.return_value = MagicMock(state=response_status) main.main(argv=["cancel", "123"]) out, err = capsys.readouterr() @@ -204,22 +184,18 @@ def read_res(name): return open(filepath).read() -@mock.patch('copr_cli.main.CoprClient') -def test_list_project(mock_cc, capsys): +@mock.patch('copr_cli.main.config_from_file') +@mock.patch('copr.v3.proxies.project.ProjectProxy.get_list') +def test_list_project(get_list, config_from_file, capsys): response_data = json.loads(read_res('list_projects_response.json')) expected_output = read_res('list_projects_expected.txt') # no config - mock_cc.create_from_file_config.side_effect = CoprNoConfException() - mocked_client = MagicMock(CoprClient(no_config=True)) - - control_response = CoprResponse(client=None, method="", data=response_data, - parsers=[ProjectListParser, CommonMsgErrorOutParser]) - mocked_client.get_projects_list.return_value = control_response - mock_cc.return_value = mocked_client + config_from_file.side_effect = copr.v3.CoprNoConfigException() + control_response = [Munch(x) for x in response_data] + get_list.return_value = control_response main.main(argv=["list", "rhscl"]) - out, err = capsys.readouterr() assert expected_output in out @@ -227,9 +203,9 @@ def test_list_project(mock_cc, capsys): assert expected_warning in err -@mock.patch('copr_cli.main.CoprClient') -def test_list_project_no_username(mock_cc, capsys): - mock_cc.create_from_file_config.side_effect = CoprNoConfException() +@mock.patch('copr_cli.main.config_from_file') +def test_list_project_no_username(config_from_file, capsys): + config_from_file.side_effect = copr.v3.CoprNoConfigException() with pytest.raises(SystemExit) as err: main.main(argv=["list"]) @@ -239,10 +215,8 @@ def test_list_project_no_username(mock_cc, capsys): assert "Pass username to command or create `~/.config/copr`" in err -@mock.patch('copr_cli.main.CoprClient') -def test_list_project_no_username2(mock_cc, capsys): - mock_cc.create_from_file_config.return_value = CoprClient() - +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_list_project_no_username2(config_from_file, capsys): with pytest.raises(SystemExit) as err: main.main(argv=["list"]) @@ -251,31 +225,10 @@ def test_list_project_no_username2(mock_cc, capsys): assert "Pass username to command or add it to `~/.config/copr`" in err -@mock.patch('copr_cli.main.CoprClient') -def test_list_project_error_msg(mock_cc, capsys): - mock_client = MagicMock(no_config=False, username="dummy") - mock_cc.create_from_file_config.return_value = mock_client - - mock_response = MagicMock(response=CoprResponse(None, None, None), - output="notok", error="error_msg", - projects_list=[]) - - mock_client.get_projects_list.return_value = mock_response - main.main(argv=["list", "projectname"]) - - out, err = capsys.readouterr() - assert "error_msg" in err - - -@mock.patch('copr_cli.main.CoprClient') -def test_list_project_empty_list(mock_cc, capsys): - mock_client = MagicMock(no_config=False, username="dummy") - mock_cc.create_from_file_config.return_value = mock_client - - mock_response = MagicMock(response=CoprResponse(None, None, None), - output="ok", projects_list=[]) - - mock_client.get_projects_list.return_value = mock_response +@mock.patch('copr.v3.proxies.project.ProjectProxy.get_list') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_list_project_empty_list(config_from_file, get_list, capsys): + get_list.return_value = [] main.main(argv=["list", "projectname"]) out, err = capsys.readouterr() @@ -283,28 +236,22 @@ def test_list_project_empty_list(mock_cc, capsys): assert "No copr retrieved for user: dummy" -@mock.patch('copr_cli.main.CoprClient') -def test_status_response(mock_cc, capsys): +@mock.patch('copr.v3.proxies.build.BuildProxy.get') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_status_response(config_from_file, build_proxy_get, capsys): response_status = "foobar" - - mock_client = MagicMock(no_config=False) - mock_client.get_build_details.return_value = \ - MagicMock(status=response_status) - mock_cc.create_from_file_config.return_value = mock_client + build_proxy_get.return_value = MagicMock(state=response_status) main.main(argv=["status", "123"]) out, err = capsys.readouterr() assert "{0}\n".format(response_status) in out -@mock.patch('copr_cli.main.CoprClient') -def test_debug_by_status_response(mock_cc, capsys): +@mock.patch('copr.v3.proxies.build.BuildProxy.get') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_debug_by_status_response(config_from_file, build_proxy_get, capsys): response_status = "foobar" - - mock_client = MagicMock(no_config=False) - mock_client.get_build_details.return_value = \ - MagicMock(status=response_status) - mock_cc.create_from_file_config.return_value = mock_client + build_proxy_get.return_value = MagicMock(state=response_status) main.main(argv=["--debug", "status", "123"]) stdout, stderr = capsys.readouterr() @@ -312,11 +259,7 @@ def test_debug_by_status_response(mock_cc, capsys): assert "Debug log enabled " in stderr -@mock.patch('copr_cli.main.CoprClient') -def test_status_response_no_args(mock_cc, capsys): - mock_client = MagicMock(no_config=False) - mock_cc.create_from_file_config.return_value = mock_client - +def test_status_response_no_args(capsys): with pytest.raises(SystemExit) as err: main.main(argv=["status"]) @@ -326,25 +269,21 @@ def test_status_response_no_args(mock_cc, capsys): assert "usage: copr" in stderr -@mock.patch('copr_cli.main.CoprClient') -def test_delete_project(mock_cc, capsys): - response_message = "foobar" - - mock_client = MagicMock(no_config=False) - mock_client.delete_project.return_value = \ - MagicMock(message=response_message) - mock_cc.create_from_file_config.return_value = mock_client +@mock.patch('copr.v3.proxies.project.ProjectProxy.delete') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_delete_project(config_from_file, project_proxy_delete, capsys): + project_proxy_delete.return_value = Munch(name="foo") main.main(argv=["delete", "foo"]) out, err = capsys.readouterr() - assert "{0}\n".format(response_message) in out + assert out == "Project foo has been deleted.\n" @mock.patch('copr_cli.main.subprocess') -@mock.patch('copr_cli.main.CoprClient') -def test_download_build(mock_cc, mock_sp, capsys): - mock_client = MagicMock(no_config=False) - mock_client.get_build_details.return_value = \ +@mock.patch('copr.v3.proxies.build.BuildProxy.get') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_download_build(config_from_file, build_proxy_get, mock_sp, capsys): + build_proxy_get.return_value = \ MagicMock( data={"chroots": { u'epel-6-x86_64': u'succeeded', u'epel-6-i386': u'succeeded' @@ -355,7 +294,6 @@ def test_download_build(mock_cc, mock_sp, capsys): u'epel-6-i386': u'http://example.com/results/epel-6-i386/python-copr-1.50-1.fc20', } ) - mock_cc.create_from_file_config.return_value = mock_client mock_sp.call.return_value = None main.main(argv=["download-build", "foo"]) @@ -379,10 +317,10 @@ def test_download_build(mock_cc, mock_sp, capsys): @mock.patch('copr_cli.main.subprocess') -@mock.patch('copr_cli.main.CoprClient') -def test_download_build_select_chroot(mock_cc, mock_sp, capsys): - mock_client = MagicMock(no_config=False) - mock_client.get_build_details.return_value = \ +@mock.patch('copr.v3.proxies.build.BuildProxy.get') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_download_build_select_chroot(config_from_file, build_proxy_get, mock_sp, capsys): + build_proxy_get.return_value = \ MagicMock( data={"chroots": { u'epel-6-x86_64': u'succeeded', u'epel-6-i386': u'succeeded' @@ -393,7 +331,6 @@ def test_download_build_select_chroot(mock_cc, mock_sp, capsys): u'epel-6-i386': u'http://example.com/results/epel-6-i386/python-copr-1.50-1.fc20', } ) - mock_cc.create_from_file_config.return_value = mock_client mock_sp.call.return_value = None main.main(argv=["download-build", "foo", "-r", "epel-6-x86_64"]) @@ -409,15 +346,9 @@ def test_download_build_select_chroot(mock_cc, mock_sp, capsys): assert mock_sp.call.call_args_list == expected_sp_call_args -@mock.patch('copr_cli.main.CoprClient') -def test_create_project(mock_cc, capsys): - response_message = "foobar" - - mock_client = MagicMock(no_config=False) - mock_client.create_project.return_value = \ - MagicMock(message=response_message) - mock_cc.create_from_file_config.return_value = mock_client - +@mock.patch('copr.v3.proxies.project.ProjectProxy.add') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_create_project(config_from_file, project_proxy_add, capsys): main.main(argv=[ "create", "foo", "--chroot", "f20", "--chroot", "f21", @@ -429,24 +360,22 @@ def test_create_project(mock_cc, capsys): stdout, stderr = capsys.readouterr() - mock_client.create_project.assert_called_with(auto_prune=True, - username=None, persistent=False, projectname="foo", description="desc string", + project_proxy_add.assert_called_with(auto_prune=True, + ownername=None, persistent=False, projectname="foo", description="desc string", instructions="instruction string", chroots=["f20", "f21"], - repos=["repo1", "repo2"], initial_pkgs=["pkg1"], - unlisted_on_hp=None, disable_createrepo=None, enable_net=False, + additional_repos=["repo1", "repo2"], + unlisted_on_hp=None, devel_mode=None, enable_net=False, use_bootstrap_container=None) - assert "{0}\n".format(response_message) in stdout + assert stdout == "New project was successfully created.\n" -@mock.patch('copr_cli.main.CoprClient') -def test_create_build_no_wait_ok(mock_cc, capsys): - response_message = "foobar" - - mock_client = MagicMock(no_config=False) - mock_client.create_new_build.return_value = MagicMock(output="ok", message=response_message) - - mock_cc.create_from_file_config.return_value = mock_client +@mock.patch('copr.v3.proxies.BaseProxy.auth_check', return_value=True) +@mock.patch('copr.v3.proxies.build.BuildProxy.create_from_url') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +@mock.patch('copr_cli.main.Commands._watch_builds') +def test_create_build_no_wait_ok(watch_builds, config_from_file, create_from_url, auth_check, capsys): + create_from_url.return_value = Munch(projectname="foo", id=123) main.main(argv=[ "build", "--nowait", @@ -454,78 +383,57 @@ def test_create_build_no_wait_ok(mock_cc, capsys): ]) stdout, stderr = capsys.readouterr() - assert response_message in stdout assert "Created builds" in stdout - - assert not mock_client._watch_build.called + assert "Build was added to foo" in stdout + assert not watch_builds.called -@mock.patch('copr_cli.main.CoprClient') -def test_create_build_no_wait_error(mock_cc, capsys): +@mock.patch('copr.v3.proxies.BaseProxy.auth_check', return_value=True) +@mock.patch('copr.v3.proxies.build.BuildProxy.create_from_url') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +@mock.patch('copr_cli.main.Commands._watch_builds') +def test_create_build_no_wait_error(watch_builds, config_from_file,create_from_url, autch_check, capsys): response_message = "foobar" + create_from_url.side_effect = copr.v3.CoprRequestException(response_message) - mock_client = MagicMock(no_config=False) - mock_client.create_new_build.return_value = MagicMock(output="notok", error=response_message) - - mock_cc.create_from_file_config.return_value = mock_client - - main.main(argv=[ - "build", "--nowait", - "copr_name", "http://example.com/pkgs.srpm" - ]) + with pytest.raises(SystemExit) as err: + main.main(argv=[ + "build", "--nowait", + "copr_name", "http://example.com/pkgs.srpm" + ]) stdout, stderr = capsys.readouterr() assert response_message in stderr - - assert not mock_client._watch_build.called + assert not watch_builds.called @mock.patch('copr_cli.main.time') -@mock.patch('copr_cli.main.CoprClient') -def test_create_build_wait_succeeded_no_sleep(mock_cc, mock_time, capsys): - response_message = "foobar" - - mock_client = MagicMock(no_config=False) - mock_client.create_new_build.return_value = MagicMock( - output="ok", - message=response_message, - builds_list=[ - MagicMock(build_id=x) - for x in range(3) - ]) - mock_client.get_build_details.return_value = MagicMock( - status="succeeded", output="ok" - ) - mock_cc.create_from_file_config.return_value = mock_client +@mock.patch('copr.v3.proxies.BaseProxy.auth_check', return_value=True) +@mock.patch('copr.v3.proxies.build.BuildProxy.create_from_url') +@mock.patch('copr.v3.proxies.build.BuildProxy.get') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_create_build_wait_succeeded_no_sleep(config_from_file, build_proxy_get, + create_from_url, auth_check, mock_time, capsys): + create_from_url.return_value = Munch(projectname="foo", id=123) + build_proxy_get.return_value = Munch(state="succeeded") main.main(argv=[ "build", "copr_name", "http://example.com/pkgs.srpm" ]) stdout, stderr = capsys.readouterr() - - assert response_message in stdout assert "Created builds" in stdout assert "Watching build" in stdout assert not mock_time.sleep.called -@mock.patch('copr_cli.main.CoprClient') -def test_create_build_wait_error_status(mock_cc, capsys): - response_message = "foobar" - - mock_client = MagicMock(no_config=False) - mock_client.create_new_build.return_value = MagicMock( - output="ok", - message=response_message, - builds_list=[ - MagicMock(build_id=x) - for x in ["1", "2", "3"] - ]) - mock_client.get_build_details.return_value = MagicMock( - output="notok" - ) - mock_cc.create_from_file_config.return_value = mock_client +@mock.patch('copr.v3.proxies.BaseProxy.auth_check', return_value=True) +@mock.patch('copr.v3.proxies.build.BuildProxy.create_from_url') +@mock.patch('copr.v3.proxies.build.BuildProxy.get') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_create_build_wait_error_status(config_from_file, build_proxy_get, create_from_url, auth_check, capsys): + create_from_url.return_value = Munch(projectname="foo", id=123) + build_proxy_get.side_effect = copr.v3.CoprRequestException() with pytest.raises(SystemExit) as err: main.main(argv=[ "build", @@ -534,27 +442,17 @@ def test_create_build_wait_error_status(mock_cc, capsys): assert exit_wrap(err.value) == 1 stdout, stderr = capsys.readouterr() - assert response_message in stdout assert "Created builds" in stdout assert "Watching build" in stdout -@mock.patch('copr_cli.main.CoprClient') -def test_create_build_wait_unknown_build_status(mock_cc, capsys): - response_message = "foobar" - - mock_client = MagicMock(no_config=False) - mock_client.create_new_build.return_value = MagicMock( - output="ok", - message=response_message, - builds_list=[ - MagicMock(build_id=x) - for x in ["1", "2", "3"] - ]) - mock_client.get_build_details.return_value = MagicMock( - output="ok", status="unknown" - ) - mock_cc.create_from_file_config.return_value = mock_client +@mock.patch('copr.v3.proxies.BaseProxy.auth_check', return_value=True) +@mock.patch('copr.v3.proxies.build.BuildProxy.create_from_url') +@mock.patch('copr.v3.proxies.build.BuildProxy.get') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_create_build_wait_unknown_build_status(config_from_file, build_proxy_get, create_from_url, auth_check, capsys): + create_from_url.return_value = Munch(projectname="foo", id=123) + build_proxy_get.return_value = Munch(state="unknown") with pytest.raises(SystemExit) as err: main.main(argv=[ "build", @@ -563,26 +461,17 @@ def test_create_build_wait_unknown_build_status(mock_cc, capsys): assert exit_wrap(err.value) == 1 stdout, stderr = capsys.readouterr() - assert response_message in stdout assert "Created builds" in stdout assert "Watching build" in stdout -@mock.patch('copr_cli.main.CoprClient') -def test_create_build_wait_keyboard_interrupt(mock_cc, capsys): - response_message = "foobar" - - mock_client = MagicMock(no_config=False) - mock_client.create_new_build.return_value = MagicMock( - output="ok", - message=response_message, - builds_list=[ - MagicMock(build_id=x) - for x in ["1", "2", "3"] - ]) - mock_client.get_build_details.side_effect = KeyboardInterrupt - - mock_cc.create_from_file_config.return_value = mock_client +@mock.patch('copr.v3.proxies.BaseProxy.auth_check', return_value=True) +@mock.patch('copr.v3.proxies.build.BuildProxy.create_from_url') +@mock.patch('copr.v3.proxies.build.BuildProxy.get') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) +def test_create_build_wait_keyboard_interrupt(config_from_file, build_proxy_get, create_from_url, autch_check, capsys): + create_from_url.return_value = Munch(projectname="foo", id=123) + build_proxy_get.side_effect = KeyboardInterrupt main.main(argv=[ "build", @@ -590,26 +479,20 @@ def test_create_build_wait_keyboard_interrupt(mock_cc, capsys): ]) stdout, stderr = capsys.readouterr() - assert response_message in stdout assert "Created builds" in stdout assert "Watching build" in stdout @mock.patch('copr_cli.main.time') -@mock.patch('copr_cli.main.CoprClient') +@mock.patch('copr_cli.main.config_from_file', return_value=mock_config) class TestCreateBuild(object): - def test_create_build_wait_succeeded_complex(self, mock_cc, mock_time, capsys): - response_message = "foobar" - - mock_client = MagicMock(no_config=False) - mock_client.create_new_build.return_value = MagicMock( - output="ok", - message=response_message, - builds_list=[ - MagicMock(build_id=x) - for x in range(3) - ]) + @mock.patch('copr.v3.proxies.BaseProxy.auth_check', return_value=True) + @mock.patch('copr.v3.proxies.build.BuildProxy.create_from_url') + @mock.patch('copr.v3.proxies.build.BuildProxy.get') + def test_create_build_wait_succeeded_complex(self, build_proxy_get, create_from_url, auth_check, + config_from_file, mock_time, capsys): + create_from_url.return_value = Munch(projectname="foo", id=1) self.stage = 0 def incr(*args, **kwargs): @@ -617,21 +500,19 @@ class TestCreateBuild(object): def result_map(build_id, *args, **kwargs): if self.stage == 0: - return MagicMock(status="pending", output="ok") + return Munch(state="pending") elif self.stage == 1: smap = {0: "pending", 1: "starting", 2: "running"} - return MagicMock(status=smap[build_id], output="ok") + return Munch(state=smap[build_id]) elif self.stage == 2: smap = {0: "starting", 1: "running", 2: "succeeded"} - return MagicMock(status=smap[build_id], output="ok") + return Munch(state=smap[build_id]) elif self.stage == 3: smap = {0: "skipped", 1: "succeeded", 2: "succeeded"} - return MagicMock(status=smap[build_id], output="ok") + return Munch(state=smap[build_id]) mock_time.sleep.side_effect = incr - - mock_client.get_build_details.side_effect = result_map - mock_cc.create_from_file_config.return_value = mock_client + build_proxy_get.side_effect = result_map main.main(argv=[ "build", @@ -639,24 +520,16 @@ class TestCreateBuild(object): ]) stdout, stderr = capsys.readouterr() - - assert response_message in stdout assert "Created builds" in stdout assert "Watching build" in stdout assert len(mock_time.sleep.call_args_list) == 3 - def test_create_build_wait_failed_complex(self, mock_cc, mock_time, capsys): - response_message = "foobar" - - mock_client = MagicMock(no_config=False) - mock_client.create_new_build.return_value = MagicMock( - output="ok", - message=response_message, - builds_list=[ - MagicMock(build_id=x) - for x in range(3) - ]) - + @mock.patch('copr.v3.proxies.BaseProxy.auth_check', return_value=True) + @mock.patch('copr.v3.proxies.build.BuildProxy.create_from_url') + @mock.patch('copr.v3.proxies.build.BuildProxy.get') + def test_create_build_wait_failed_complex(self, build_proxy_get, create_from_url, auth_check, + config_from_file, mock_time, capsys): + create_from_url.return_value = Munch(projectname="foo", id=1) self.stage = 0 def incr(*args, **kwargs): @@ -664,21 +537,19 @@ class TestCreateBuild(object): def result_map(build_id, *args, **kwargs): if self.stage == 0: - return MagicMock(status="pending", output="ok") + return Munch(state="pending") elif self.stage == 1: smap = {0: "pending", 1: "starting", 2: "running"} - return MagicMock(status=smap[build_id], output="ok") + return Munch(state=smap[build_id]) elif self.stage == 2: smap = {0: "failed", 1: "running", 2: "succeeded"} - return MagicMock(status=smap[build_id], output="ok") + return Munch(state=smap[build_id]) elif self.stage == 3: smap = {0: "failed", 1: "failed", 2: "succeeded"} - return MagicMock(status=smap[build_id], output="ok") + return Munch(state=smap[build_id]) mock_time.sleep.side_effect = incr - - mock_client.get_build_details.side_effect = result_map - mock_cc.create_from_file_config.return_value = mock_client + build_proxy_get.side_effect = result_map with pytest.raises(SystemExit) as err: main.main(argv=[ @@ -687,9 +558,7 @@ class TestCreateBuild(object): ]) stdout, stderr = capsys.readouterr() - - assert response_message in stdout assert "Created builds" in stdout assert "Watching build" in stdout - assert "Build(s) 0, 1 failed" in stderr + assert "Build(s) 1 failed" in stderr assert len(mock_time.sleep.call_args_list) == 3