├── .container ├── dump │ └── .gitkeep └── web │ └── config.toml ├── .coveragerc ├── .github └── workflows │ ├── codeql.yml │ ├── label-when-deployed.yaml │ └── pre-commit.yml ├── .gitignore ├── .mergify.yml ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── .s2i └── environment ├── .zuul.yaml ├── CODEOWNERS ├── Containerfile.dev ├── LICENSE ├── Makefile ├── README.rst ├── alembic.ini ├── anitya.toml.example ├── anitya.wsgi ├── anitya ├── __init__.py ├── admin.py ├── api.py ├── api_v2.py ├── app.py ├── auth.py ├── authentication.py ├── check_service.py ├── compat.py ├── config.py ├── db │ ├── __init__.py │ ├── events.py │ ├── meta.py │ ├── migrations │ │ ├── README.md │ │ ├── __init__.py │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 136d119ab015_rename_project_latest_known_cursor_to_.py │ │ │ ├── 16aa7da2764c_add_projectversion_commit_url.py │ │ │ ├── 1ab95561edae_convert_date_to_rpm.py │ │ │ ├── 1bf8aead6179_add_check_times.py │ │ │ ├── 1f839c54e428_add_archive_flag.py │ │ │ ├── 24b6734e8565_creation_date_on_version.py │ │ │ ├── 27342bce1d0f_populate_project_version_scheme.py │ │ │ ├── 2925648d8cc3_add_a_version_prefix_field.py │ │ │ ├── 2d1aa7ff82a5_remove_code_google_backend.py │ │ │ ├── 314651690dc7_add_error_counter_to_project.py │ │ │ ├── 34b9bb5fa388_make_ecosystem_non_nullable.py │ │ │ ├── 3fae8239eeec_add_an_api_token_table.py │ │ │ ├── 540bdcf7edbc_convert_github_url_to_owner_project.py │ │ │ ├── 571bd07533a9_add_insecure_column_to_projects_table.py │ │ │ ├── 5b8b587d7dbe_add_version_filter_column.py │ │ │ ├── 5e209766aead_add_release_check_to_project.py │ │ │ ├── 6ac0e42df937_drop_logs_table.py │ │ │ ├── 708f6f26b4b6_add_version_pattern.py │ │ │ ├── 7a8c4aa92678_add_missing_github_owner_project_pairs.py │ │ │ ├── 8040ef9a9dda_add_a_version_scheme_column_to_projects.py │ │ │ ├── 8ba7d4c42044_remove_socialauth.py │ │ │ ├── 8be6e153962c_remove_cursor.py │ │ │ ├── 921c612ba0da_model_upstream_ecosystems.py │ │ │ ├── 92a2e9eba0a7_add_project_latest_known_cursor.py │ │ │ ├── 9c29da0af3af_populate_ecosystem_data.py │ │ │ ├── __init__.py │ │ │ ├── a52d2fe99d4f_users.py │ │ │ ├── ac10bf3f974c_.py │ │ │ ├── b13662e5d288_add_admin_flag.py │ │ │ ├── b9201d816075_remove_the_backends_and_ecosystems_.py │ │ │ ├── c8735fa14a0a_add_cascade_delete.py │ │ │ ├── d6170cfc2814_add_pre_release_flag.py │ │ │ ├── e34988f3e2f4_add_statistics_to_run.py │ │ │ ├── ebc827e80373_remove_unique_attribute_from_username.py │ │ │ └── feeaa70ead67_social_authentication_table.py │ └── models.py ├── debug.py ├── forms.py ├── lib │ ├── __init__.py │ ├── backends │ │ ├── __init__.py │ │ ├── bitbucket.py │ │ ├── cgit.py │ │ ├── cpan.py │ │ ├── cran.py │ │ ├── crates.py │ │ ├── custom.py │ │ ├── debian.py │ │ ├── drupal6.py │ │ ├── drupal7.py │ │ ├── folder.py │ │ ├── freshmeat.py │ │ ├── gitea.py │ │ ├── github.py │ │ ├── gitlab.py │ │ ├── gnome.py │ │ ├── gnu.py │ │ ├── gogs.py │ │ ├── hackage.py │ │ ├── launchpad.py │ │ ├── maven.py │ │ ├── npmjs.py │ │ ├── packagist.py │ │ ├── pagure.py │ │ ├── pear.py │ │ ├── pecl.py │ │ ├── pypi.py │ │ ├── rubygems.py │ │ ├── sourceforge.py │ │ ├── sourceforge_git.py │ │ ├── sourcehut.py │ │ └── stackage.py │ ├── ecosystems │ │ ├── __init__.py │ │ ├── crates.py │ │ ├── maven.py │ │ ├── npm.py │ │ ├── pypi.py │ │ └── rubygems.py │ ├── exceptions.py │ ├── plugins.py │ ├── utilities.py │ ├── versions │ │ ├── __init__.py │ │ ├── base.py │ │ ├── calver.py │ │ ├── python.py │ │ ├── rpm.py │ │ └── semver.py │ └── xml2dict.py ├── mail_logging.py ├── reverse_proxy.py ├── sar.py ├── static │ ├── css │ │ ├── avatars.css │ │ ├── cnucnu.css │ │ ├── fonts │ │ │ ├── Cantarell-Bold-webfont.eot │ │ │ ├── Cantarell-Bold-webfont.svg │ │ │ ├── Cantarell-Bold-webfont.ttf │ │ │ ├── Cantarell-Bold-webfont.woff │ │ │ ├── Cantarell-BoldOblique-webfont.eot │ │ │ ├── Cantarell-BoldOblique-webfont.svg │ │ │ ├── Cantarell-BoldOblique-webfont.ttf │ │ │ ├── Cantarell-BoldOblique-webfont.woff │ │ │ ├── Cantarell-Oblique-webfont.eot │ │ │ ├── Cantarell-Oblique-webfont.svg │ │ │ ├── Cantarell-Oblique-webfont.ttf │ │ │ ├── Cantarell-Oblique-webfont.woff │ │ │ ├── Cantarell-Regular-webfont.eot │ │ │ ├── Cantarell-Regular-webfont.svg │ │ │ ├── Cantarell-Regular-webfont.ttf │ │ │ ├── Cantarell-Regular-webfont.woff │ │ │ ├── Comfortaa_Bold-webfont.eot │ │ │ ├── Comfortaa_Bold-webfont.svg │ │ │ ├── Comfortaa_Bold-webfont.ttf │ │ │ ├── Comfortaa_Bold-webfont.woff │ │ │ ├── Comfortaa_Regular-webfont.eot │ │ │ ├── Comfortaa_Regular-webfont.svg │ │ │ ├── Comfortaa_Regular-webfont.ttf │ │ │ ├── Comfortaa_Regular-webfont.woff │ │ │ ├── Comfortaa_Thin-webfont.eot │ │ │ ├── Comfortaa_Thin-webfont.svg │ │ │ ├── Comfortaa_Thin-webfont.ttf │ │ │ └── Comfortaa_Thin-webfont.woff │ │ ├── footer.css │ │ ├── images │ │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ │ ├── ui-bg_flat_0_eeeeee_40x100.png │ │ │ ├── ui-bg_flat_55_ffffff_40x100.png │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ │ ├── ui-bg_highlight-soft_100_f6f6f6_1x100.png │ │ │ ├── ui-bg_highlight-soft_25_0073ea_1x100.png │ │ │ ├── ui-bg_highlight-soft_50_dddddd_1x100.png │ │ │ ├── ui-icons_0073ea_256x240.png │ │ │ ├── ui-icons_454545_256x240.png │ │ │ ├── ui-icons_666666_256x240.png │ │ │ ├── ui-icons_ff0084_256x240.png │ │ │ └── ui-icons_ffffff_256x240.png │ │ ├── navbar.css │ │ └── text.css │ ├── fedora_logo.png │ ├── github_logo.png │ ├── google_logo.png │ ├── ico │ │ └── favicon.ico │ ├── img │ │ └── spinner.gif │ ├── package-lock.json │ └── package.json ├── templates │ ├── __init__.py │ ├── distro_add_edit.html │ ├── distro_delete.html │ ├── distros.html │ ├── flags.html │ ├── functions.html │ ├── index.html │ ├── login.html │ ├── login_debug.html │ ├── logs.html │ ├── mapping.html │ ├── master.html │ ├── project.html │ ├── project_archive.html │ ├── project_delete.html │ ├── project_flag.html │ ├── project_new.html │ ├── project_versions_delete.html │ ├── projects.html │ ├── regex_delete.html │ ├── search.html │ ├── settings.html │ ├── updates.html │ ├── user_delete.html │ ├── users.html │ └── version_delete.html ├── tests │ ├── __init__.py │ ├── base.py │ ├── db │ │ ├── __init__.py │ │ ├── test_events.py │ │ ├── test_meta.py │ │ └── test_models.py │ ├── lib │ │ ├── __init__.py │ │ ├── backends │ │ │ ├── __init__.py │ │ │ ├── test_bitbucket.py │ │ │ ├── test_cgit.py │ │ │ ├── test_cpan.py │ │ │ ├── test_cran.py │ │ │ ├── test_crates.py │ │ │ ├── test_custom.py │ │ │ ├── test_debian.py │ │ │ ├── test_drupal6.py │ │ │ ├── test_drupal7.py │ │ │ ├── test_folder.py │ │ │ ├── test_freshmeat.py │ │ │ ├── test_gitea.py │ │ │ ├── test_github.py │ │ │ ├── test_gitlab.py │ │ │ ├── test_gnome.py │ │ │ ├── test_gnu.py │ │ │ ├── test_gogs.py │ │ │ ├── test_hackage.py │ │ │ ├── test_init.py │ │ │ ├── test_launchpad.py │ │ │ ├── test_maven.py │ │ │ ├── test_npmjs.py │ │ │ ├── test_packagist.py │ │ │ ├── test_pagure.py │ │ │ ├── test_pear.py │ │ │ ├── test_pecl.py │ │ │ ├── test_pypi.py │ │ │ ├── test_rubygems.py │ │ │ ├── test_sourceforge.py │ │ │ ├── test_sourceforge_git.py │ │ │ ├── test_sourcehut.py │ │ │ └── test_stackage.py │ │ ├── test_exceptions.py │ │ ├── test_plugins.py │ │ ├── test_utilities.py │ │ └── versions │ │ │ ├── test_base.py │ │ │ ├── test_calver.py │ │ │ ├── test_python.py │ │ │ ├── test_rpm.py │ │ │ └── test_semver.py │ ├── request-data │ │ ├── anitya.tests.lib.backends.test_bitbucket.BitBucketBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_bitbucket.BitBucketBackendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_cgit.CgitBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_cgit.CgitBackendtests.test_get_versions_project_homepage_only │ │ ├── anitya.tests.lib.backends.test_cgit.CgitBackendtests.test_get_versions_project_version_url_only │ │ ├── anitya.tests.lib.backends.test_cgit.CgitBackendtests.test_get_versions_project_without_any_release │ │ ├── anitya.tests.lib.backends.test_cpan.CpanBackendtests.test_cpan_check_feed │ │ ├── anitya.tests.lib.backends.test_cpan.CpanBackendtests.test_cpan_get_version │ │ ├── anitya.tests.lib.backends.test_cpan.CpanBackendtests.test_cpan_get_versions │ │ ├── anitya.tests.lib.backends.test_cran.CranBackendTests.test_check_feed │ │ ├── anitya.tests.lib.backends.test_cran.CranBackendTests.test_get_version │ │ ├── anitya.tests.lib.backends.test_cran.CranBackendTests.test_get_version_missing_project │ │ ├── anitya.tests.lib.backends.test_cran.CranBackendTests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_cran.CranBackendTests.test_get_versions_missing_project │ │ ├── anitya.tests.lib.backends.test_crates.CratesBackendTests.test_get_ordered_versions │ │ ├── anitya.tests.lib.backends.test_crates.CratesBackendTests.test_get_version │ │ ├── anitya.tests.lib.backends.test_crates.CratesBackendTests.test_get_version_missing │ │ ├── anitya.tests.lib.backends.test_crates.CratesBackendTests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_custom.CustomBackendtests.test_custom_get_version │ │ ├── anitya.tests.lib.backends.test_custom.CustomBackendtests.test_custom_get_versions │ │ ├── anitya.tests.lib.backends.test_custom.CustomBackendtests.test_custom_get_versions_unstable │ │ ├── anitya.tests.lib.backends.test_debian.DebianBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_debian.DebianBackendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_debian.DebianBackendtests.test_get_versions_http_404 │ │ ├── anitya.tests.lib.backends.test_debian.DebianBackendtests.test_get_versions_no_z_release │ │ ├── anitya.tests.lib.backends.test_drupal6.Drupal6Backendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_drupal6.Drupal6Backendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_drupal7.Drupal7Backendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_drupal7.Drupal7Backendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_folder.FolderBackendtests.test_get_version_gnash │ │ ├── anitya.tests.lib.backends.test_folder.FolderBackendtests.test_get_version_phpMyAdmin │ │ ├── anitya.tests.lib.backends.test_folder.FolderBackendtests.test_get_version_subsurface │ │ ├── anitya.tests.lib.backends.test_folder.FolderBackendtests.test_get_versions_fake_project │ │ ├── anitya.tests.lib.backends.test_folder.FolderBackendtests.test_get_versions_gnash │ │ ├── anitya.tests.lib.backends.test_folder.FolderBackendtests.test_get_versions_phpMyAdmin │ │ ├── anitya.tests.lib.backends.test_folder.FolderBackendtests.test_get_versions_subsurface │ │ ├── anitya.tests.lib.backends.test_freshmeat.FreshmeatBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_freshmeat.FreshmeatBackendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_gitea.GiteaBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_gitea.GiteaBackendtests.test_get_versions_project_homepage_only │ │ ├── anitya.tests.lib.backends.test_gitea.GiteaBackendtests.test_get_versions_project_invalid_owner_repo │ │ ├── anitya.tests.lib.backends.test_gitea.GiteaBackendtests.test_get_versions_project_version_url_only │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_gargoyle │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_version_invalid_unknown_project │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_version_valid_with_version_url │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_version_valid_without_version_url │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_versions_filter │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_versions_invalid_unknown_project │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_versions_no_token │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_versions_no_version_retrieved │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_versions_releases_only │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_versions_unauthorized │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_versions_valid_with_version_url │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_versions_valid_without_version_url │ │ ├── anitya.tests.lib.backends.test_github.GithubBackendtests.test_plexus_utils │ │ ├── anitya.tests.lib.backends.test_gitlab.GitlabBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_gitlab.GitlabBackendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_gitlab.GitlabBackendtests.test_get_versions_no_version_retrieved │ │ ├── anitya.tests.lib.backends.test_gnome.GnomeBackendtests.test_custom_get_version │ │ ├── anitya.tests.lib.backends.test_gnome.GnomeBackendtests.test_custom_get_versions │ │ ├── anitya.tests.lib.backends.test_gnu.GnuBackendtests.test_custom_get_version │ │ ├── anitya.tests.lib.backends.test_gnu.GnuBackendtests.test_custom_get_versions │ │ ├── anitya.tests.lib.backends.test_gogs.GogsBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_gogs.GogsBackendtests.test_get_versions_project_homepage_only │ │ ├── anitya.tests.lib.backends.test_gogs.GogsBackendtests.test_get_versions_project_version_url_only │ │ ├── anitya.tests.lib.backends.test_gogs.GogsBackendtests.test_get_versions_project_without_any_release │ │ ├── anitya.tests.lib.backends.test_hackage.HackageBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_hackage.HackageBackendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_init.BaseBackendTests.test_call_url_last_change │ │ ├── anitya.tests.lib.backends.test_init.BaseBackendTests.test_expand_subdirs │ │ ├── anitya.tests.lib.backends.test_launchpad.LaunchpadBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_launchpad.LaunchpadBackendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_maven.MavenBackendTest.test_dots_in_artifact_id │ │ ├── anitya.tests.lib.backends.test_maven.MavenBackendTest.test_maven_coordinates_in_name │ │ ├── anitya.tests.lib.backends.test_maven.MavenBackendTest.test_maven_coordinates_in_version_url │ │ ├── anitya.tests.lib.backends.test_maven.MavenBackendTest.test_maven_get_version_by_url │ │ ├── anitya.tests.lib.backends.test_maven.MavenBackendTest.test_maven_get_versions │ │ ├── anitya.tests.lib.backends.test_npmjs.NpmjsBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_npmjs.NpmjsBackendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_npmjs.NpmjsBackendtests.test_npmjs_check_feed │ │ ├── anitya.tests.lib.backends.test_packagist.PackagistBackendtests.test_packagist_get_version │ │ ├── anitya.tests.lib.backends.test_packagist.PackagistBackendtests.test_packagist_get_versions │ │ ├── anitya.tests.lib.backends.test_packagist.PackagistBackendtests.test_pypi_get_version │ │ ├── anitya.tests.lib.backends.test_packagist.PackagistBackendtests.test_pypi_get_versions │ │ ├── anitya.tests.lib.backends.test_pagure.PagureBackendtests.test_pagure_get_version │ │ ├── anitya.tests.lib.backends.test_pagure.PagureBackendtests.test_pagure_get_versions │ │ ├── anitya.tests.lib.backends.test_pear.PearBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_pear.PearBackendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_pear.PearBackendtests.test_pear_check_feed │ │ ├── anitya.tests.lib.backends.test_pecl.PeclBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_pecl.PeclBackendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_pecl.PeclBackendtests.test_pecl_check_feed │ │ ├── anitya.tests.lib.backends.test_pypi.PypiBackendtests.test_get_version_missing_project │ │ ├── anitya.tests.lib.backends.test_pypi.PypiBackendtests.test_pypi_check_feed │ │ ├── anitya.tests.lib.backends.test_pypi.PypiBackendtests.test_pypi_get_version │ │ ├── anitya.tests.lib.backends.test_pypi.PypiBackendtests.test_pypi_get_versions │ │ ├── anitya.tests.lib.backends.test_pypi.PypiBackendtests.test_pypi_get_versions_yanked │ │ ├── anitya.tests.lib.backends.test_rubygems.RubygemsBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_rubygems.RubygemsBackendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_rubygems.RubygemsBackendtests.test_rubygems_check_feed │ │ ├── anitya.tests.lib.backends.test_sourceforge.SourceforgeBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_sourceforge.SourceforgeBackendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_sourceforge_git.SourceforgeGitBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_sourceforge_git.SourceforgeGitBackendtests.test_get_versions │ │ ├── anitya.tests.lib.backends.test_sourceforge_git.SourceforgeGitBackendtests.test_get_versions_url_not_valid │ │ ├── anitya.tests.lib.backends.test_sourcehut.SourceHutBackendTests.test_get_version │ │ ├── anitya.tests.lib.backends.test_sourcehut.SourceHutBackendTests.test_get_versions_project_homepage_only │ │ ├── anitya.tests.lib.backends.test_sourcehut.SourceHutBackendTests.test_get_versions_project_version_url_only │ │ ├── anitya.tests.lib.backends.test_sourcehut.SourceHutBackendTests.test_get_versions_project_without_any_release │ │ ├── anitya.tests.lib.backends.test_stackage.HackageBackendtests.test_get_version │ │ ├── anitya.tests.lib.backends.test_stackage.HackageBackendtests.test_get_versions │ │ ├── anitya.tests.lib.test_utilities.CheckProjectReleaseTests.test_check_project_version_too_long │ │ ├── anitya.tests.test_flask_api.AnityaWebAPItests.test_api_get_version │ │ ├── anitya.tests.test_flask_api.AnityaWebAPItests.test_api_get_version_exists │ │ ├── anitya.tests.test_flask_api_v2.LiveAuthenticationTests.test_invalid_project_monitoring_request │ │ └── anitya.tests.test_flask_api_v2.LiveAuthenticationTests.test_valid_project_monitoring_request │ ├── test_alembic.py │ ├── test_app.py │ ├── test_authentication.py │ ├── test_check_service.py │ ├── test_config.py │ ├── test_distro.py │ ├── test_flask.py │ ├── test_flask_admin.py │ ├── test_flask_api.py │ ├── test_flask_api_v2.py │ ├── test_project.py │ ├── test_reverse_proxy.py │ ├── test_sar.py │ └── test_wsgi.py ├── ui.py └── wsgi.py ├── container-compose.yml ├── createdb.py ├── docs ├── Makefile ├── _templates │ └── layout.html ├── admin-guide.rst ├── admin-user-guide.rst ├── api.rst ├── conf.py ├── contributing.rst ├── database.rst ├── generate_db_schema ├── glossary.rst ├── images │ └── database.png ├── index.rst ├── integrating-with-anitya.rst ├── release-notes.rst └── user-guide.rst ├── files ├── alembic.ini ├── anitya.spec ├── anitya.toml.sample └── config.toml.sample ├── mypy.cfg ├── news ├── PR1891.other ├── _template.rst └── get-authors.py ├── poetry.lock ├── pyproject.toml ├── renovate.json ├── runserver.py └── tox.ini /.container/dump/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/.container/dump/.gitkeep -------------------------------------------------------------------------------- /.container/web/config.toml: -------------------------------------------------------------------------------- 1 | amqp_url = "amqp://anitya:anitya@rabbitmq:5672" 2 | 3 | topic_prefix = "org.release-monitoring.dev" 4 | publish_exchange = "amq.topic" 5 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | include = 4 | anitya/* 5 | 6 | 7 | [report] 8 | fail_under = 88 9 | precision = 2 10 | exclude_lines = 11 | pragma: no cover 12 | if __name__ == .__main__.: 13 | omit = 14 | anitya/tests/* 15 | anitya/db/migrations 16 | anitya/db/migrations/* 17 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | schedule: 9 | - cron: "26 9 * * 4" 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | language: [ javascript, python ] 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 28 | 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v3 31 | with: 32 | languages: ${{ matrix.language }} 33 | queries: +security-and-quality 34 | 35 | - name: Autobuild 36 | uses: github/codeql-action/autobuild@v3 37 | if: ${{ matrix.language == 'javascript' || matrix.language == 'python' }} 38 | 39 | - name: Perform CodeQL Analysis 40 | uses: github/codeql-action/analyze@v3 41 | with: 42 | category: "/language:${{ matrix.language }}" 43 | upload: False 44 | output: sarif-results 45 | 46 | - name: filter-sarif 47 | uses: advanced-security/filter-sarif@main 48 | with: 49 | # filter out all test files 50 | # filter out all static files 51 | patterns: | 52 | -anitya/tests/*.py 53 | -anitya/static/*.js 54 | input: sarif-results/${{ matrix.language }}.sarif 55 | output: sarif-results/${{ matrix.language }}.sarif 56 | 57 | - name: Upload SARIF 58 | uses: github/codeql-action/upload-sarif@v3 59 | with: 60 | sarif_file: sarif-results/${{ matrix.language }}.sarif 61 | -------------------------------------------------------------------------------- /.github/workflows/label-when-deployed.yaml: -------------------------------------------------------------------------------- 1 | name: Apply labels when deployed 2 | 3 | on: 4 | push: 5 | branches: 6 | - staging 7 | - production 8 | 9 | jobs: 10 | label: 11 | name: Apply labels 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Staging deployment 16 | uses: fedora-infra/label-when-in-branch@v1 17 | with: 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | branch: staging 20 | label: deployed:staging 21 | 22 | - name: Production deployment 23 | uses: fedora-infra/label-when-in-branch@v1 24 | with: 25 | token: ${{ secrets.GITHUB_TOKEN }} 26 | branch: production 27 | label: deployed:prod 28 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: pre-commit 2 | 3 | on: 4 | pull_request: 5 | branches: ["master"] 6 | 7 | jobs: 8 | pre-commit: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 12 | - uses: actions/setup-python@v5.6.0 13 | - uses: snok/install-poetry@v1 14 | - name: Install tox 15 | run: | 16 | pip install tox 17 | - name: Run pre-commit 18 | run: | 19 | tox -e pre-commit 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | *.pyc 3 | build/ 4 | dist/ 5 | *egg-info/ 6 | *eggs/ 7 | .cache/ 8 | *.egg 9 | *.retry 10 | pip-wheel-metadata/ 11 | venv/ 12 | .venv/ 13 | 14 | # Docs 15 | anitya/static/docs/ 16 | docs/_build/ 17 | docs/docblocks/ 18 | 19 | # Anitya 20 | anitya.db 21 | *.sqlite 22 | *.cfg 23 | anitya.toml 24 | 25 | # Tests 26 | coverage.xml 27 | htmlcov 28 | .pytest_cache/ 29 | .tox 30 | .coverage 31 | !mypy.cfg 32 | 33 | # IDE related files 34 | .vscode/ 35 | .idea/ 36 | *~ 37 | *.swp 38 | *.swo 39 | 40 | # Vagrant dev environment 41 | .vagrant/ 42 | 43 | # Container dev environment 44 | .container/postgresql/* 45 | .container/dump/* 46 | !.container/postgresql/.gitkeep 47 | 48 | # Javascript 49 | anitya/static/node_modules/ 50 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: renovate 3 | actions: 4 | merge: 5 | method: rebase 6 | conditions: 7 | - author=renovate[bot] 8 | - label!=no-mergify 9 | - "#changes-requested-reviews-by=0" 10 | - check-success=fedora/check 11 | - name: default 12 | actions: 13 | merge: 14 | method: rebase 15 | conditions: 16 | - label!=no-mergify 17 | - '#approved-reviews-by>=1' 18 | - check-success=fedora/check 19 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_language_version: 2 | python: python3 3 | 4 | repos: 5 | - repo: https://github.com/psf/black 6 | rev: 23.1.0 7 | hooks: 8 | - id: black 9 | - repo: https://github.com/pycqa/flake8 10 | rev: 6.0.0 11 | hooks: 12 | - id: flake8 13 | - repo: https://github.com/pycqa/isort 14 | rev: 5.12.0 15 | hooks: 16 | - id: isort 17 | - repo: local 18 | hooks: 19 | - id: pylint 20 | name: pylint 21 | entry: pylint 22 | language: system 23 | types: [python] 24 | - repo: https://github.com/pre-commit/mirrors-mypy 25 | rev: v1.3.0 26 | hooks: 27 | - id: mypy 28 | language: system 29 | pass_filenames: false 30 | args: [--config-file, mypy.cfg, anitya] 31 | 32 | - repo: https://github.com/maxbrunet/pre-commit-renovate 33 | rev: 34.160.0 34 | hooks: 35 | - id: renovate-config-validator 36 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | # Formats to generate beside HTML 4 | formats: 5 | - epub 6 | - pdf 7 | 8 | # Build configuration 9 | build: 10 | os: ubuntu-20.04 11 | tools: 12 | python: "3.10" 13 | jobs: 14 | post_create_environment: 15 | # Install poetry 16 | # https://python-poetry.org/docs/#installing-manually 17 | - pip install poetry 18 | post_install: 19 | # VIRTUAL_ENV needs to be set manually for now. 20 | # See https://github.com/readthedocs/readthedocs.org/pull/11152/ 21 | - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install 22 | 23 | # Sphinx configuration 24 | sphinx: 25 | configuration: docs/conf.py 26 | -------------------------------------------------------------------------------- /.s2i/environment: -------------------------------------------------------------------------------- 1 | APP_MODULE=anitya.wsgi:application 2 | UPGRADE_PIP_TO_LATEST=true 3 | -------------------------------------------------------------------------------- /.zuul.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - project: 3 | check: 4 | jobs: 5 | - fi-tox-mypy 6 | - fi-tox-lint 7 | - fi-tox-format 8 | - fi-tox-python310 9 | - fi-tox-python311 10 | - fi-tox-python312 11 | - fi-tox-docs: 12 | vars: 13 | dependencies: 14 | - graphviz 15 | - python3-sphinxcontrib-httpdomain 16 | - python3-sqlalchemy_schemadisplay 17 | - fi-tox-bandit 18 | - fi-tox-diff-cover: 19 | vars: 20 | tox_envlist: "py312,diff-cover" 21 | job_dependencies: "python312" 22 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @fedora-infra/anitya 2 | -------------------------------------------------------------------------------- /Containerfile.dev: -------------------------------------------------------------------------------- 1 | ARG FEDORA_VERSION 2 | FROM registry.fedoraproject.org/fedora:${FEDORA_VERSION} 3 | 4 | WORKDIR /app 5 | 6 | # Package installation steps are separated so they 7 | # can be cached individually on Dockerfile changes 8 | 9 | RUN dnf upgrade --refresh -y 10 | 11 | # Install helpful development packages (for tests and setup) 12 | RUN dnf install -y git poetry python3-tox which python3.10 python3.11 13 | 14 | # System dependencies for Anitya 15 | RUN dnf install -y gcc graphviz libffi-devel python3-devel python3-psycopg2 16 | 17 | # Install npm to manage javascript dependencies 18 | RUN dnf install -y npm 19 | 20 | # Dependencies for Anitya 21 | RUN dnf install -y \ 22 | fedora-messaging \ 23 | python3-alembic \ 24 | python3-arrow \ 25 | python3-authlib \ 26 | python3-beautifulsoup4 \ 27 | python3-dateutil \ 28 | python3-defusedxml \ 29 | python3-flask-login \ 30 | python3-flask-wtf \ 31 | python3-jinja2 \ 32 | python3-ordered-set \ 33 | python3-toml \ 34 | python3-semver \ 35 | python3-sqlalchemy \ 36 | python3-sqlalchemy_schemadisplay \ 37 | python3-sseclient \ 38 | python3-straight-plugin \ 39 | python3-wtforms 40 | 41 | RUN dnf autoremove -y && dnf clean all -y 42 | 43 | # Copy over configuration files 44 | RUN mkdir -p /etc/fedora-messaging 45 | COPY .container/web/config.toml /etc/fedora-messaging 46 | 47 | # Install javascript dependencies 48 | COPY anitya/static /app/static 49 | RUN pushd static && npm install && popd 50 | 51 | # Set the poetry to use system packages 52 | RUN poetry config virtualenvs.options.system-site-packages true 53 | 54 | CMD ["sh", "-c", "poetry install && poetry run $START_COMMAND"] 55 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .SILENT: 2 | 3 | _CHECK_PODMAN := $(shell command -v podman 2> /dev/null) 4 | define compose-tool 5 | $(if $(_CHECK_PODMAN), podman-compose, docker-compose) -f container-compose.yml 6 | endef 7 | 8 | define container-tool 9 | $(if $(_CHECK_PODMAN), podman, docker) 10 | endef 11 | 12 | define download_dump 13 | wget https://infrastructure.fedoraproject.org/infra/db-dumps/anitya.dump.xz -O ./.container/dump/anitya.dump.xz 14 | endef 15 | 16 | define remove_dump 17 | rm -f .container/dump/anitya.dump.xz 18 | endef 19 | 20 | up: 21 | $(call compose-tool) up -d 22 | # It takes some time before the postgres container is up, we need to wait before calling 23 | # the next step 24 | sleep 15 25 | $(MAKE) init-db 26 | @echo "Empty database initialized. Run dump-restore to fill it by production dump." 27 | restart: 28 | $(MAKE) halt && $(MAKE) up 29 | halt: 30 | $(call compose-tool) down -t1 31 | bash-web: 32 | $(call container-tool) exec -it anitya-web bash -c "bash" 33 | bash-check: 34 | $(call container-tool) exec -it anitya-check-service bash -c "bash" 35 | init-db: 36 | $(call container-tool) exec -it anitya-web bash -c "poetry run python3 createdb.py" 37 | dump-restore: init-db 38 | $(call download_dump) 39 | $(call container-tool) exec -it postgres bash -c 'createuser anitya && xzcat /dump/anitya.dump.xz | psql anitya' 40 | $(call remove_dump) 41 | logs: 42 | $(call container-tool) logs -f anitya-web anitya-check-service rabbitmq postgres 43 | clean: halt 44 | $(call container-tool) rmi "localhost/anitya-base:latest" "docker.io/library/postgres:13.4" "docker.io/library/rabbitmq:3.8.16-management-alpine" 45 | tests: 46 | $(call container-tool) exec -it anitya-web bash -c "tox $(PARAM)" 47 | 48 | .PHONY: up restart halt bash-web \ 49 | init-db dump-restore logs clean tests 50 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | 2 | .. image:: https://img.shields.io/pypi/v/anitya.svg 3 | :target: https://pypi.org/project/anitya/ 4 | 5 | .. image:: https://img.shields.io/pypi/pyversions/anitya.svg 6 | :target: https://pypi.org/project/anitya/ 7 | 8 | .. image:: https://readthedocs.org/projects/anitya/badge/?version=latest 9 | :alt: Documentation Status 10 | :target: https://anitya.readthedocs.io/en/latest/?badge=latest 11 | 12 | .. image:: https://img.shields.io/badge/renovate-enabled-brightgreen.svg 13 | :target: https://renovatebot.com/ 14 | 15 | .. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit 16 | :target: https://pre-commit.com/ 17 | 18 | 19 | ====== 20 | Anitya 21 | ====== 22 | 23 | Anitya is a release monitoring project. It provides a user-friendly interface 24 | to add, edit, or browse projects. A cron job can be configured to regularly 25 | scan for new releases of projects. When Anitya discovers a new release for a 26 | project, it publishes a RabbitMQ messages via `fedora messaging`_. 27 | This makes it easy to integrate with Anitya and perform actions when a new 28 | release is created for a project. For example, the Fedora project runs a service 29 | called `the-new-hotness `_ 30 | which files a Bugzilla bug against a package when the upstream project makes a 31 | new release. 32 | 33 | For more information, check out the `documentation`_! 34 | 35 | 36 | Development 37 | =========== 38 | 39 | For details on how to contribute, check out the `contribution guide`_. 40 | 41 | 42 | .. _documentation: https://anitya.readthedocs.io/ 43 | .. _contribution guide: https://anitya.readthedocs.io/en/latest/contributing.html 44 | .. _fedora messaging: https://fedora-messaging.readthedocs.io/en/latest 45 | -------------------------------------------------------------------------------- /alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # path to migration scripts 5 | script_location = anitya/db/migrations 6 | 7 | # template used to generate migration files 8 | # file_template = %%(rev)s_%%(slug)s 9 | 10 | # max length of characters to apply to the 11 | # "slug" field 12 | #truncate_slug_length = 40 13 | 14 | # set to 'true' to run the environment during 15 | # the 'revision' command, regardless of autogenerate 16 | # revision_environment = false 17 | 18 | # set to 'true' to allow .pyc and .pyo files without 19 | # a source .py file to be detected as revisions in the 20 | # versions/ directory 21 | # sourceless = false 22 | 23 | #sqlalchemy.url = driver://user:pass@localhost/dbname 24 | sqlalchemy.url = postgresql://postgres:anypasswordworkslocally@postgres/anitya 25 | 26 | 27 | # Logging configuration 28 | [loggers] 29 | keys = root,sqlalchemy,alembic 30 | 31 | [handlers] 32 | keys = console 33 | 34 | [formatters] 35 | keys = generic 36 | 37 | [logger_root] 38 | level = WARN 39 | handlers = console 40 | qualname = 41 | 42 | [logger_sqlalchemy] 43 | level = WARN 44 | handlers = 45 | qualname = sqlalchemy.engine 46 | [logger_alembic] 47 | level = INFO 48 | handlers = 49 | qualname = alembic 50 | 51 | [handler_console] 52 | class = StreamHandler 53 | args = (sys.stderr,) 54 | level = NOTSET 55 | formatter = generic 56 | 57 | [formatter_generic] 58 | format = %(levelname)-5.5s [%(name)s] %(message)s 59 | datefmt = %H:%M:%S 60 | -------------------------------------------------------------------------------- /anitya.toml.example: -------------------------------------------------------------------------------- 1 | # This is a TOML-format file. For the spec, see https://github.com/toml-lang/toml#spec 2 | 3 | # URL to the database 4 | db_url = 'postgresql://postgres:anypasswordworkslocally@postgres/anitya' 5 | 6 | # List of web administrators. The values should be the value of the "id" column 7 | # for the user in the "users" table of the database. They need to log in before 8 | # this record is created. An example value would be 9 | # "65536ed7-bdd3-4a1e-8252-10d874fd706b" 10 | # You can also find this infromation in the settings page when logged in to Anitya 11 | anitya_web_admins = [] 12 | 13 | preferred_url_scheme = "https" 14 | 15 | authlib_enabled_backends = ["Fedora"] 16 | 17 | fedora_client_id = "fedora" 18 | fedora_client_secret = "secret" 19 | fedora_server_metadata_url = "https://id.stg.fedoraproject.org/.well-known/openid-configuration" 20 | [fedora_client_kwargs] 21 | scope = "openid profile email" 22 | token_endpoint_auth_method = "client_secret_post" 23 | 24 | 25 | blacklisted_users = [] 26 | 27 | # The logging configuration, in dictConfig format. 28 | [anitya_log_config] 29 | version = 1 30 | disable_existing_loggers = false 31 | 32 | [anitya_log_config.formatters] 33 | [anitya_log_config.formatters.simple] 34 | format = "[%(name)s %(levelname)s] %(message)s" 35 | 36 | [anitya_log_config.handlers] 37 | [anitya_log_config.handlers.console] 38 | class = "logging.StreamHandler" 39 | formatter = "simple" 40 | stream = "ext://sys.stdout" 41 | 42 | [anitya_log_config.loggers] 43 | [anitya_log_config.loggers.anitya] 44 | level = "DEBUG" 45 | propagate = false 46 | handlers = ["console"] 47 | 48 | [anitya_log_config.root] 49 | level = "INFO" 50 | handlers = ["console"] 51 | -------------------------------------------------------------------------------- /anitya.wsgi: -------------------------------------------------------------------------------- 1 | """ WSGI application """ 2 | # This file represents the WSGI application that could be served 3 | # by any web server application like Apache or Flask 4 | # pylint: disable=W0611 5 | from anitya.wsgi import application # noqa: F401 6 | -------------------------------------------------------------------------------- /anitya/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Module anitya""" 4 | from importlib import metadata 5 | 6 | __api_version__ = "1.2" 7 | __version__ = metadata.version(__package__) 8 | -------------------------------------------------------------------------------- /anitya/compat.py: -------------------------------------------------------------------------------- 1 | # This file is part of the Anitya project. 2 | # 3 | # Copyright (C) 2017 Red Hat, Inc. 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | """ 19 | Handles compatibility imports for Anitya. 20 | """ 21 | from __future__ import absolute_import, unicode_literals 22 | 23 | # flask_wtf.Form was renamed to flask_wtf.FlaskForm 24 | # flask_wtf.Form will be removed in flask_wtf-1.0. 25 | try: 26 | from flask_wtf import FlaskForm # pylint: disable=W0611 27 | except ImportError: 28 | from flask_wtf import ( # pylint: disable=W0611 # pragma: no cover # noqa: F401 29 | Form as FlaskForm, 30 | ) 31 | -------------------------------------------------------------------------------- /anitya/db/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is a part of the Anitya project. 3 | # 4 | # Copyright © 2018 Red Hat, Inc. 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | """ 20 | This package contains all the database-related code, including SQLAlchemy models, 21 | Alembic migrations, and a scoped session object configured from :mod:`anitya.config` 22 | """ 23 | 24 | # You need to import the events to register them with application 25 | # If they are not imported, there wouldn't be triggered 26 | from . import events # noqa: F401 27 | from .meta import Base, BaseQuery, Page, Session, initialize # noqa: F401 28 | from .models import Project # noqa: F401 29 | from .models import ( # noqa: F401 30 | ApiToken, 31 | Distro, 32 | Packages, 33 | ProjectFlag, 34 | ProjectVersion, 35 | Run, 36 | User, 37 | ) 38 | -------------------------------------------------------------------------------- /anitya/db/migrations/README.md: -------------------------------------------------------------------------------- 1 | Generating new revisions 2 | ======================== 3 | 4 | For a branch based on `origin/master` that includes schema changes: 5 | 6 | $ rm /var/tmp/anitya-dev.sqlite 7 | $ git checkout origin/master 8 | $ python createdb.py 9 | $ git checkout 10 | $ alembic stamp head 11 | $ alembic revision --autogenerate -m ""` 12 | 13 | Table creation is handled by `createdb.py`, so edit the generated migration 14 | to remove the table creation commands. 15 | -------------------------------------------------------------------------------- /anitya/db/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/db/migrations/__init__.py -------------------------------------------------------------------------------- /anitya/db/migrations/env.py: -------------------------------------------------------------------------------- 1 | """env""" 2 | 3 | from __future__ import with_statement 4 | 5 | import sys 6 | from logging.config import fileConfig 7 | from os.path import dirname 8 | 9 | from alembic import context 10 | from sqlalchemy import engine_from_config, pool 11 | 12 | from anitya.db import Base 13 | 14 | # this is the Alembic Config object, which provides 15 | # access to the values within the .ini file in use. 16 | # Don't check this with mypy, it doesn't work well with 17 | # proxy class 18 | config = context.config # type: ignore 19 | 20 | # Interpret the config file for Python logging. 21 | # This line sets up loggers basically. 22 | fileConfig(str(config.config_file_name)) 23 | 24 | # add your model's MetaData object here 25 | # for 'autogenerate' support 26 | sys.path.append(dirname(dirname(__file__))) 27 | target_metadata = Base.metadata 28 | 29 | # other values from the config, defined by the needs of env.py, 30 | # can be acquired: 31 | # my_important_option = config.get_main_option("my_important_option") 32 | # ... etc. 33 | 34 | 35 | def run_migrations_offline(): 36 | """Run migrations in 'offline' mode. 37 | 38 | This configures the context with just a URL 39 | and not an Engine, though an Engine is acceptable 40 | here as well. By skipping the Engine creation 41 | we don't even need a DBAPI to be available. 42 | 43 | Calls to context.execute() here emit the given string to the 44 | script output. 45 | 46 | """ 47 | url = config.get_main_option("sqlalchemy.url") 48 | context.configure(url=url, target_metadata=target_metadata) 49 | 50 | with context.begin_transaction(): 51 | context.run_migrations() 52 | 53 | 54 | def run_migrations_online(): 55 | """Run migrations in 'online' mode. 56 | 57 | In this scenario we need to create an Engine 58 | and associate a connection with the context. 59 | 60 | """ 61 | engine = engine_from_config( 62 | config.get_section(config.config_ini_section), 63 | prefix="sqlalchemy.", 64 | poolclass=pool.NullPool, 65 | ) 66 | 67 | connection = engine.connect() 68 | context.configure(connection=connection, target_metadata=target_metadata) 69 | 70 | try: 71 | with context.begin_transaction(): 72 | context.run_migrations() 73 | finally: 74 | connection.close() 75 | 76 | 77 | if context.is_offline_mode(): 78 | run_migrations_offline() 79 | else: 80 | run_migrations_online() 81 | -------------------------------------------------------------------------------- /anitya/db/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision} 5 | Create Date: ${create_date} 6 | """ 7 | 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = ${repr(up_revision)} 15 | down_revision = ${repr(down_revision)} 16 | 17 | 18 | def upgrade(): 19 | ${upgrades if upgrades else "pass"} 20 | 21 | 22 | def downgrade(): 23 | ${downgrades if downgrades else "pass"} 24 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/136d119ab015_rename_project_latest_known_cursor_to_.py: -------------------------------------------------------------------------------- 1 | """rename Project.latest_known_cursor to latest_version_cursor 2 | 3 | Revision ID: 136d119ab015 4 | Revises: 92a2e9eba0a7 5 | Create Date: 2019-12-20 20:56:25.783460 6 | """ 7 | 8 | from alembic import op 9 | 10 | # revision identifiers, used by Alembic. 11 | revision = "136d119ab015" # pylint: disable=C0103 12 | down_revision = "92a2e9eba0a7" # pylint: disable=C0103 13 | 14 | 15 | def upgrade(): 16 | """Upgrade""" 17 | with op.batch_alter_table("projects") as batch_op: 18 | batch_op.alter_column( 19 | "latest_known_cursor", new_column_name="latest_version_cursor" 20 | ) 21 | 22 | 23 | def downgrade(): 24 | """Downgrade""" 25 | with op.batch_alter_table("projects") as batch_op: 26 | batch_op.alter_column( 27 | "latest_version_cursor", new_column_name="latest_known_cursor" 28 | ) 29 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/16aa7da2764c_add_projectversion_commit_url.py: -------------------------------------------------------------------------------- 1 | """add ProjectVersion.commit_url 2 | 3 | Revision ID: 16aa7da2764c 4 | Revises: 314651690dc7 5 | Create Date: 2019-12-19 14:54:56.036040 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "16aa7da2764c" 13 | down_revision = "314651690dc7" 14 | 15 | 16 | def upgrade(): 17 | """Upgrade""" 18 | op.add_column( 19 | "projects_versions", 20 | sa.Column("commit_url", sa.String(length=200), nullable=True), 21 | ) 22 | 23 | 24 | def downgrade(): 25 | """Downgrade""" 26 | with op.batch_alter_table("projects_versions") as batch_op: 27 | batch_op.drop_column("commit_url") 28 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/1ab95561edae_convert_date_to_rpm.py: -------------------------------------------------------------------------------- 1 | """Convert Date to RPM 2 | 3 | Revision ID: 1ab95561edae 4 | Revises: 6ac0e42df937 5 | Create Date: 2019-01-17 09:45:34.158432 6 | """ 7 | 8 | from alembic import op 9 | 10 | # revision identifiers, used by Alembic. 11 | revision = "1ab95561edae" 12 | down_revision = "6ac0e42df937" 13 | 14 | 15 | def upgrade(): 16 | """Any project using Date version scheme should now use RPM version scheme.""" 17 | op.execute( 18 | """ 19 | UPDATE projects 20 | SET version_scheme='RPM' 21 | WHERE version_scheme='Date' 22 | """ 23 | ) 24 | 25 | 26 | def downgrade(): 27 | """No-op, as project works fine with the old version scheme.""" 28 | raise NotImplementedError 29 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/1bf8aead6179_add_check_times.py: -------------------------------------------------------------------------------- 1 | """Add check times 2 | 3 | Revision ID: 1bf8aead6179 4 | Revises: b13662e5d288 5 | Create Date: 2018-12-07 08:58:45.152649 6 | """ 7 | 8 | import arrow 9 | import sqlalchemy as sa 10 | from alembic import op 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "1bf8aead6179" 14 | down_revision = "b13662e5d288" 15 | 16 | 17 | def upgrade(): 18 | """Add next_check and last_check columns to the projects table.""" 19 | op.add_column( 20 | "projects", 21 | sa.Column( 22 | "last_check", 23 | sa.TIMESTAMP(timezone=True), 24 | default=arrow.utcnow().datetime, 25 | server_default=sa.sql.functions.current_timestamp(), 26 | ), 27 | ) 28 | 29 | op.add_column( 30 | "projects", 31 | sa.Column( 32 | "next_check", 33 | sa.TIMESTAMP(timezone=True), 34 | default=arrow.utcnow().datetime, 35 | server_default=sa.sql.functions.current_timestamp(), 36 | ), 37 | ) 38 | op.create_index( 39 | op.f("ix_projects_last_check"), "projects", ["last_check"], unique=False 40 | ) 41 | op.create_index( 42 | op.f("ix_projects_next_check"), "projects", ["next_check"], unique=False 43 | ) 44 | 45 | 46 | def downgrade(): 47 | """Drop next_check and last_check columns to the projects table.""" 48 | op.drop_column("projects", "last_check") 49 | op.drop_column("projects", "next_check") 50 | op.drop_index(op.f("ix_projects_next_check"), table_name="projects") 51 | op.drop_index(op.f("ix_projects_last_check"), table_name="projects") 52 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/1f839c54e428_add_archive_flag.py: -------------------------------------------------------------------------------- 1 | """Add archive flag 2 | 3 | Revision ID: 1f839c54e428 4 | Revises: d6170cfc2814 5 | Create Date: 2020-07-13 12:00:39.800783 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "1f839c54e428" 13 | down_revision = "d6170cfc2814" 14 | 15 | 16 | def upgrade(): 17 | """Add archived column to the projects table.""" 18 | op.add_column( 19 | "projects", 20 | sa.Column( 21 | "archived", 22 | sa.Boolean(), 23 | default=False, 24 | server_default="FALSE", 25 | nullable=False, 26 | ), 27 | ) 28 | 29 | 30 | def downgrade(): 31 | """Drop archived column to the projects table.""" 32 | op.drop_column("projects", "archived") 33 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/24b6734e8565_creation_date_on_version.py: -------------------------------------------------------------------------------- 1 | """Creation date on version 2 | 3 | Revision ID: 24b6734e8565 4 | Revises: 34b9bb5fa388 5 | Create Date: 2018-10-01 11:17:19.457383 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "24b6734e8565" 13 | down_revision = "34b9bb5fa388" 14 | 15 | 16 | def upgrade(): 17 | """Add `created_on` date column to projects_versions table.""" 18 | op.add_column( 19 | "projects_versions", 20 | sa.Column( 21 | "created_on", sa.DateTime, default=sa.sql.functions.current_timestamp() 22 | ), 23 | ) 24 | 25 | 26 | def downgrade(): 27 | """Drop the `created_on` column from the projects_version table.""" 28 | op.drop_column("projects_versions", "created_on") 29 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/27342bce1d0f_populate_project_version_scheme.py: -------------------------------------------------------------------------------- 1 | """Populate project version scheme 2 | 3 | Revision ID: 27342bce1d0f 4 | Revises: 24b6734e8565 5 | Create Date: 2018-10-03 07:07:47.573097 6 | """ 7 | 8 | from alembic import op 9 | 10 | # revision identifiers, used by Alembic. 11 | revision = "27342bce1d0f" 12 | down_revision = "24b6734e8565" 13 | 14 | 15 | def upgrade(): 16 | """Populate projects version_scheme column by RPM value. 17 | This was the only available version value before this update. 18 | In newer version of Anitya you can change the value when editing project. 19 | """ 20 | op.execute( 21 | """ 22 | UPDATE projects 23 | SET version_scheme='RPM' 24 | WHERE version_scheme is null 25 | """ 26 | ) 27 | 28 | 29 | def downgrade(): 30 | """No need to downgrade, there was only one versioning scheme previously""" 31 | raise NotImplementedError 32 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/2925648d8cc3_add_a_version_prefix_field.py: -------------------------------------------------------------------------------- 1 | """Add a version_prefix field 2 | 3 | Revision ID: 2925648d8cc3 4 | Revises: 571bd07533a9 5 | Create Date: 2015-10-22 09:49:10.757572 6 | 7 | """ 8 | 9 | import sqlalchemy as sa 10 | from alembic import op 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "2925648d8cc3" 14 | down_revision = "571bd07533a9" 15 | 16 | 17 | def upgrade(): 18 | """Add the `version_prefix` column on the projects table.""" 19 | op.add_column( 20 | "projects", sa.Column("version_prefix", sa.String(200), nullable=True) 21 | ) 22 | 23 | 24 | def downgrade(): 25 | """Drop the `version_prefix` column of the projects table.""" 26 | op.drop_column("projects", "version_prefix") 27 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/2d1aa7ff82a5_remove_code_google_backend.py: -------------------------------------------------------------------------------- 1 | """Remove code google backend 2 | 3 | Revision ID: 2d1aa7ff82a5 4 | Revises: 8be6e153962c 5 | Create Date: 2021-03-17 16:08:13.478078 6 | """ 7 | 8 | from alembic import op 9 | 10 | # revision identifiers, used by Alembic. 11 | revision = "2d1aa7ff82a5" 12 | down_revision = "8be6e153962c" 13 | 14 | 15 | def upgrade(): 16 | """ 17 | Change backend of projects with code google backend to custom. 18 | """ 19 | op.execute( 20 | """ 21 | UPDATE projects 22 | SET backend='custom' 23 | WHERE backend='Google code' 24 | """ 25 | ) 26 | 27 | 28 | def downgrade(): 29 | """ 30 | This change can't be reverted. 31 | """ 32 | pass 33 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/314651690dc7_add_error_counter_to_project.py: -------------------------------------------------------------------------------- 1 | """Add error counter to project 2 | 3 | Revision ID: 314651690dc7 4 | Revises: 136d119ab015 5 | Create Date: 2019-12-17 11:55:12.472854 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "314651690dc7" 13 | down_revision = "136d119ab015" 14 | 15 | 16 | def upgrade(): 17 | """Add error_counter column to project.""" 18 | op.add_column( 19 | "projects", 20 | sa.Column( 21 | "error_counter", sa.Integer(), default=0, server_default=sa.text("0") 22 | ), 23 | ) 24 | op.create_index( 25 | op.f("ix_projects_error_counter"), "projects", ["error_counter"], unique=False 26 | ) 27 | 28 | 29 | def downgrade(): 30 | """Remove error_counter column from project.""" 31 | op.drop_column("projects", "error_counter") 32 | op.drop_index(op.f("ix_projects_error_counter"), table_name="projects") 33 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/34b9bb5fa388_make_ecosystem_non_nullable.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of the Anitya project. 4 | # Copyright © 2018 Red Hat, Inc. 5 | # 6 | # This copyrighted material is made available to anyone wishing to use, 7 | # modify, copy, or redistribute it subject to the terms and conditions 8 | # of the GNU General Public License v.2, or (at your option) any later 9 | # version. This program is distributed in the hope that it will be 10 | # useful, but WITHOUT ANY WARRANTY expressed or implied, including the 11 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR 12 | # PURPOSE. See the GNU General Public License for more details. You 13 | # should have received a copy of the GNU General Public License along 14 | # with this program; if not, write to the Free Software Foundation, 15 | # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | """ 17 | Make ecosystem non-nullable. 18 | 19 | Revision ID: 34b9bb5fa388 20 | Revises: 3fae8239eeec 21 | Create Date: 2018-01-15 22:10:34.624110 22 | """ 23 | 24 | import sqlalchemy as sa 25 | from alembic import op 26 | 27 | # revision identifiers, used by Alembic. 28 | revision = "34b9bb5fa388" 29 | down_revision = "3fae8239eeec" 30 | 31 | 32 | def upgrade(): 33 | """Make the ecosystem_name non-nullable after setting null instances to the homepage.""" 34 | op.execute( 35 | """ 36 | UPDATE projects 37 | SET ecosystem_name=homepage 38 | WHERE ecosystem_name IS NULL 39 | """ 40 | ) 41 | op.alter_column( 42 | "projects", 43 | "ecosystem_name", 44 | existing_type=sa.VARCHAR(length=200), 45 | nullable=False, 46 | ) 47 | 48 | 49 | def downgrade(): 50 | """Make the ecosystem_name nullable.""" 51 | op.alter_column( 52 | "projects", 53 | "ecosystem_name", 54 | existing_type=sa.VARCHAR(length=200), 55 | nullable=True, 56 | ) 57 | op.execute( 58 | """ 59 | UPDATE projects 60 | SET ecosystem_name=NULL 61 | WHERE ecosystem_name=homepage 62 | """ 63 | ) 64 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/540bdcf7edbc_convert_github_url_to_owner_project.py: -------------------------------------------------------------------------------- 1 | """Convert GitHub URL to owner/project 2 | 3 | Revision ID: 540bdcf7edbc 4 | Revises: 27342bce1d0f 5 | Create Date: 2018-10-08 11:23:33.361838 6 | """ 7 | 8 | from alembic import op 9 | 10 | # revision identifiers, used by Alembic. 11 | revision = "540bdcf7edbc" 12 | down_revision = "27342bce1d0f" 13 | 14 | 15 | def upgrade(): 16 | """Convert GitHub URL to owner/project to work with the new GitHub backend.""" 17 | op.execute( 18 | """ 19 | UPDATE projects 20 | SET version_url=trim(substr(version_url, 19), '/') 21 | WHERE backend = 'GitHub' AND version_url LIKE 'http://github.com/%' 22 | """ 23 | ) 24 | op.execute( 25 | """ 26 | UPDATE projects 27 | SET version_url=trim(substr(version_url, 20), '/') 28 | WHERE backend = 'GitHub' AND version_url LIKE 'https://github.com/%' 29 | """ 30 | ) 31 | 32 | 33 | def downgrade(): 34 | """No-op, as owner/project works fine with the old GitHub backend.""" 35 | raise NotImplementedError 36 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/571bd07533a9_add_insecure_column_to_projects_table.py: -------------------------------------------------------------------------------- 1 | """Add insecure column to projects table 2 | 3 | Revision ID: 571bd07533a9 4 | Revises: None 5 | Create Date: 2015-03-23 17:18:11.421783 6 | 7 | """ 8 | 9 | import sqlalchemy as sa 10 | from alembic import op 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "571bd07533a9" 14 | down_revision = None 15 | 16 | 17 | def upgrade(): 18 | """Add the `insecure` column on the projects table.""" 19 | op.add_column( 20 | "projects", 21 | sa.Column( 22 | "insecure", 23 | sa.Boolean, 24 | default=False, 25 | server_default="FALSE", 26 | nullable=False, 27 | ), 28 | ) 29 | 30 | 31 | def downgrade(): 32 | """Drop the `insecure` column of the projects table.""" 33 | op.drop_column("projects", "insecure") 34 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/5b8b587d7dbe_add_version_filter_column.py: -------------------------------------------------------------------------------- 1 | """Add version_filter column 2 | 3 | Revision ID: 5b8b587d7dbe 4 | Revises: 1f839c54e428 5 | Create Date: 2020-06-29 13:11:50.961730 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "5b8b587d7dbe" 13 | down_revision = "1f839c54e428" 14 | 15 | 16 | def upgrade(): 17 | """Add the `version_filter` column on the projects table.""" 18 | op.add_column( 19 | "projects", sa.Column("version_filter", sa.String(200), nullable=True) 20 | ) 21 | 22 | 23 | def downgrade(): 24 | """Drop the `version_filter` column of the projects table.""" 25 | op.drop_column("projects", "version_filter") 26 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/5e209766aead_add_release_check_to_project.py: -------------------------------------------------------------------------------- 1 | """Add release check to project 2 | 3 | Revision ID: 5e209766aead 4 | Revises: 708f6f26b4b6 5 | Create Date: 2019-06-06 14:53:06.236820 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "5e209766aead" 13 | down_revision = "708f6f26b4b6" 14 | 15 | 16 | def upgrade(): 17 | """Add releases_only column to the projects table.""" 18 | op.add_column( 19 | "projects", 20 | sa.Column( 21 | "releases_only", 22 | sa.Boolean(), 23 | default=False, 24 | server_default="FALSE", 25 | nullable=False, 26 | ), 27 | ) 28 | 29 | 30 | def downgrade(): 31 | """Drop releases_only column to the projects table.""" 32 | op.drop_column("projects", "releases_only") 33 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/6ac0e42df937_drop_logs_table.py: -------------------------------------------------------------------------------- 1 | """Drop logs table 2 | 3 | Revision ID: 6ac0e42df937 4 | Revises: 7a8c4aa92678 5 | Create Date: 2018-10-11 11:42:44.947483 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "6ac0e42df937" 13 | down_revision = "1bf8aead6179" 14 | 15 | 16 | def upgrade(): 17 | """ 18 | Drop logs table. 19 | Add and fill check_successful column to projects table. 20 | """ 21 | op.drop_table("logs") 22 | 23 | op.add_column("projects", sa.Column("check_successful", sa.Boolean, default=None)) 24 | 25 | op.execute( 26 | """ 27 | UPDATE projects 28 | SET check_successful=TRUE 29 | WHERE (logs='Version retrieved correctly' 30 | OR logs='No new version found') 31 | AND check_successful IS NULL 32 | """ 33 | ) 34 | 35 | op.execute( 36 | """ 37 | UPDATE projects 38 | SET check_successful=FALSE 39 | WHERE logs IS NOT NULL 40 | AND check_successful IS NULL 41 | """ 42 | ) 43 | 44 | 45 | def downgrade(): 46 | """ 47 | Create the logs table. 48 | Drop updated column from projects table. 49 | """ 50 | op.create_table( 51 | "logs", 52 | sa.Column("id", sa.Integer(), nullable=False), 53 | sa.Column("user", sa.String(length=200), nullable=False), 54 | sa.Column("project", sa.String(length=200), nullable=True), 55 | sa.Column("distro", sa.String(length=200), nullable=True), 56 | sa.Column("description", sa.Text(), nullable=False), 57 | sa.Column("created_on", sa.DateTime(), nullable=True), 58 | sa.PrimaryKeyConstraint("id"), 59 | ) 60 | op.create_index(op.f("ix_logs_distro"), "logs", ["distro"], unique=False) 61 | op.create_index(op.f("ix_logs_project"), "logs", ["project"], unique=False) 62 | op.create_index(op.f("ix_logs_user"), "logs", ["user"], unique=False) 63 | op.drop_column("projects", "check_successful") 64 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/708f6f26b4b6_add_version_pattern.py: -------------------------------------------------------------------------------- 1 | """Add version pattern 2 | 3 | Revision ID: 708f6f26b4b6 4 | Revises: e34988f3e2f4 5 | Create Date: 2019-04-25 16:51:03.302314 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "708f6f26b4b6" 13 | down_revision = "e34988f3e2f4" 14 | 15 | 16 | def upgrade(): 17 | """Add version_pattern to projects table.""" 18 | op.add_column( 19 | "projects", sa.Column("version_pattern", sa.String(200), nullable=True) 20 | ) 21 | 22 | 23 | def downgrade(): 24 | """Remove version_pattern from project table.""" 25 | op.drop_column("projects", "version_pattern") 26 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/7a8c4aa92678_add_missing_github_owner_project_pairs.py: -------------------------------------------------------------------------------- 1 | """Add missing GitHub owner/project pairs 2 | 3 | Revision ID: 7a8c4aa92678 4 | Revises: 540bdcf7edbc 5 | Create Date: 2018-10-08 12:25:07.006729 6 | """ 7 | 8 | from alembic import op 9 | 10 | # revision identifiers, used by Alembic. 11 | revision = "7a8c4aa92678" 12 | down_revision = "540bdcf7edbc" 13 | 14 | 15 | def upgrade(): 16 | """Populate missing GitHub owner/project pairs from homepage.""" 17 | op.execute( 18 | """ 19 | UPDATE projects 20 | SET version_url=trim(substr(trim(homepage), 19), '/') 21 | WHERE backend = 'GitHub' 22 | AND trim(homepage) LIKE 'http://github.com/%' 23 | AND (version_url IS NULL OR version_url = '') 24 | """ 25 | ) 26 | op.execute( 27 | """ 28 | UPDATE projects 29 | SET version_url=trim(substr(trim(homepage), 20), '/') 30 | WHERE backend = 'GitHub' 31 | AND trim(homepage) LIKE 'https://github.com/%' 32 | AND (version_url IS NULL OR version_url = '') 33 | """ 34 | ) 35 | 36 | 37 | def downgrade(): 38 | """No-op, as empty version_url wouldn't have worked before anyway.""" 39 | raise NotImplementedError 40 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/8040ef9a9dda_add_a_version_scheme_column_to_projects.py: -------------------------------------------------------------------------------- 1 | """ 2 | Add a version scheme column to Projects 3 | 4 | Revision ID: 8040ef9a9dda 5 | Revises: b9201d816075 6 | Create Date: 2017-04-19 14:50:54.736648 7 | """ 8 | 9 | import sqlalchemy as sa 10 | from alembic import op 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "8040ef9a9dda" 14 | down_revision = "b9201d816075" 15 | 16 | 17 | def upgrade(): 18 | """Add the version_scheme column to the projects table.""" 19 | op.add_column( 20 | "projects", sa.Column("version_scheme", sa.String(length=50), nullable=True) 21 | ) 22 | 23 | 24 | def downgrade(): 25 | """Remove the version_scheme column from the projects table.""" 26 | op.drop_column("projects", "version_scheme") 27 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/8be6e153962c_remove_cursor.py: -------------------------------------------------------------------------------- 1 | """Remove cursor 2 | 3 | Revision ID: 8be6e153962c 4 | Revises: 5b8b587d7dbe 5 | Create Date: 2021-02-12 12:56:51.230774 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "8be6e153962c" 13 | down_revision = "5b8b587d7dbe" 14 | 15 | 16 | def upgrade(): 17 | """ 18 | Remove latest_version_cursor from project. 19 | """ 20 | op.drop_column("projects", "latest_version_cursor") 21 | 22 | 23 | def downgrade(): 24 | """ 25 | Add latest_version_cursor back to project. 26 | """ 27 | op.add_column( 28 | "projects", 29 | sa.Column( 30 | "latest_version_cursor", 31 | sa.VARCHAR(length=200), 32 | autoincrement=False, 33 | nullable=True, 34 | ), 35 | ) 36 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/921c612ba0da_model_upstream_ecosystems.py: -------------------------------------------------------------------------------- 1 | """Model upstream ecosystems 2 | 3 | Revision ID: 921c612ba0da 4 | Revises: 2925648d8cc3 5 | Create Date: 2016-08-09 18:44:53.119461 6 | 7 | """ 8 | 9 | import sqlalchemy as sa 10 | from alembic import op 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "921c612ba0da" 14 | down_revision = "2925648d8cc3" 15 | 16 | 17 | def upgrade(): 18 | """Upgrade""" 19 | op.add_column( 20 | "projects", sa.Column("ecosystem_name", sa.String(length=200), nullable=True) 21 | ) 22 | op.create_unique_constraint( 23 | "UNIQ_PROJECT_NAME_PER_ECOSYSTEM", "projects", ["name", "ecosystem_name"] 24 | ) 25 | op.create_foreign_key( 26 | "FK_ECOSYSTEM_FOR_PROJECT", 27 | "projects", 28 | "ecosystems", 29 | ["ecosystem_name"], 30 | ["name"], 31 | onupdate="cascade", 32 | ondelete="set null", 33 | ) 34 | 35 | 36 | def downgrade(): 37 | """Downgrade""" 38 | op.drop_constraint("FK_ECOSYSTEM_FOR_PROJECT", "projects", type_="foreignkey") 39 | op.drop_constraint("UNIQ_PROJECT_NAME_PER_ECOSYSTEM", "projects", type_="unique") 40 | op.drop_column("projects", "ecosystem_name") 41 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/92a2e9eba0a7_add_project_latest_known_cursor.py: -------------------------------------------------------------------------------- 1 | """add Project.latest_known_cursor 2 | 3 | Revision ID: 92a2e9eba0a7 4 | Revises: 5e209766aead 5 | Create Date: 2019-12-12 10:11:38.652950 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "92a2e9eba0a7" 13 | down_revision = "5e209766aead" 14 | 15 | 16 | def upgrade(): 17 | """Upgrade""" 18 | op.add_column( 19 | "projects", 20 | sa.Column("latest_known_cursor", sa.String(length=200), nullable=True), 21 | ) 22 | 23 | 24 | def downgrade(): 25 | """Downgrade""" 26 | with op.batch_alter_table("projects") as batch_op: 27 | batch_op.drop_column("latest_known_cursor") 28 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/9c29da0af3af_populate_ecosystem_data.py: -------------------------------------------------------------------------------- 1 | """Populate missing ecosystem data 2 | 3 | Revision ID: 9c29da0af3af 4 | Revises: 921c612ba0da 5 | Create Date: 2017-01-11 22:23:58.497998 6 | 7 | """ 8 | 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "9c29da0af3af" 13 | down_revision = "921c612ba0da" 14 | 15 | 16 | def upgrade(): 17 | """Upgrade""" 18 | # We use a subquery instead of an UPDATE FROM with a table join 19 | # due to the fact that SQLite doesn't allow joins in update statements 20 | op.execute( 21 | """ 22 | UPDATE projects 23 | SET ecosystem_name=( 24 | SELECT ecosystems.name 25 | FROM projects AS subquery_projects 26 | INNER JOIN ecosystems ON 27 | subquery_projects.backend = ecosystems.default_backend_name 28 | WHERE projects.id = subquery_projects.id 29 | ) 30 | WHERE ecosystem_name is null 31 | """ 32 | ) 33 | 34 | 35 | def downgrade(): 36 | """Downgrade""" 37 | # Do nothing on downgrade - column removal will be handled by previous 38 | # revision 39 | pass 40 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/db/migrations/versions/__init__.py -------------------------------------------------------------------------------- /anitya/db/migrations/versions/ac10bf3f974c_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: ac10bf3f974c 4 | Revises: ('c8735fa14a0a', '7a8c4aa92678') 5 | Create Date: 2018-11-06 08:27:37.278677 6 | """ 7 | 8 | # revision identifiers, used by Alembic. 9 | revision = "ac10bf3f974c" 10 | down_revision = ("c8735fa14a0a", "7a8c4aa92678") 11 | 12 | 13 | def upgrade(): 14 | """Upgrade""" 15 | pass 16 | 17 | 18 | def downgrade(): 19 | """Downgrade""" 20 | pass 21 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/b13662e5d288_add_admin_flag.py: -------------------------------------------------------------------------------- 1 | """Add admin flag 2 | 3 | Revision ID: b13662e5d288 4 | Revises: ac10bf3f974c 5 | Create Date: 2018-11-14 10:02:06.593605 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "b13662e5d288" 13 | down_revision = "ac10bf3f974c" 14 | 15 | 16 | def upgrade(): 17 | """Add 'admin' flag to users table.""" 18 | op.add_column("users", sa.Column("admin", sa.Boolean, default=False)) 19 | 20 | 21 | def downgrade(): 22 | """Drop 'admin' flag from users table.""" 23 | op.drop_column("users", "admin") 24 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/c8735fa14a0a_add_cascade_delete.py: -------------------------------------------------------------------------------- 1 | """Add cascade delete 2 | 3 | Revision ID: c8735fa14a0a 4 | Revises: 34b9bb5fa388 5 | Create Date: 2018-09-04 13:54:40.031238 6 | """ 7 | 8 | from alembic import op 9 | 10 | # revision identifiers, used by Alembic. 11 | revision = "c8735fa14a0a" 12 | down_revision = "34b9bb5fa388" 13 | 14 | 15 | def upgrade(): 16 | """Rename column `distro` in packages table.""" 17 | op.alter_column("packages", "distro", new_column_name="distro_name") 18 | 19 | 20 | def downgrade(): 21 | """Do nothing""" 22 | pass 23 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/d6170cfc2814_add_pre_release_flag.py: -------------------------------------------------------------------------------- 1 | """Add pre-release flag 2 | 3 | Revision ID: d6170cfc2814 4 | Revises: 16aa7da2764c 5 | Create Date: 2020-07-08 12:09:51.416356 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "d6170cfc2814" 13 | down_revision = "16aa7da2764c" 14 | 15 | 16 | def upgrade(): 17 | """ 18 | Add new pre-release column to project. 19 | """ 20 | op.add_column( 21 | "projects", 22 | sa.Column("pre_release_filter", sa.String(length=200), nullable=True), 23 | ) 24 | 25 | 26 | def downgrade(): 27 | """ 28 | Drop pre-release columns from project. 29 | """ 30 | op.drop_column("projects", "pre_release_filter") 31 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/e34988f3e2f4_add_statistics_to_run.py: -------------------------------------------------------------------------------- 1 | """Add statistics to run 2 | 3 | Revision ID: e34988f3e2f4 4 | Revises: 1ab95561edae 5 | Create Date: 2019-03-14 15:41:11.614870 6 | """ 7 | 8 | import sqlalchemy as sa 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "e34988f3e2f4" 13 | down_revision = "1ab95561edae" 14 | 15 | 16 | def upgrade(): 17 | """ 18 | Add statistics column to runs table. 19 | Remove status column to runs table. 20 | """ 21 | op.add_column("runs", sa.Column("total_count", sa.Integer(), default=0)) 22 | op.add_column("runs", sa.Column("error_count", sa.Integer(), default=0)) 23 | op.add_column("runs", sa.Column("ratelimit_count", sa.Integer(), default=0)) 24 | op.add_column("runs", sa.Column("success_count", sa.Integer(), default=0)) 25 | op.drop_column("runs", "status") 26 | 27 | 28 | def downgrade(): 29 | """ 30 | Remove statistics column to runs table. 31 | Add status column to runs table. 32 | """ 33 | op.drop_column("runs", "total_count") 34 | op.drop_column("runs", "error_count") 35 | op.drop_column("runs", "ratelimit_count") 36 | op.drop_column("runs", "success_count") 37 | op.add_column( 38 | "runs", 39 | sa.Column("status", sa.String(20), primary_key=True, server_default="ended"), 40 | ) 41 | -------------------------------------------------------------------------------- /anitya/db/migrations/versions/ebc827e80373_remove_unique_attribute_from_username.py: -------------------------------------------------------------------------------- 1 | """Remove unique attribute from username 2 | 3 | Revision ID: ebc827e80373 4 | Revises: 8ba7d4c42044 5 | Create Date: 2024-12-05 16:24:01.098473 6 | """ 7 | 8 | from alembic import op 9 | 10 | # revision identifiers, used by Alembic. 11 | revision = "ebc827e80373" 12 | down_revision = "8ba7d4c42044" 13 | 14 | 15 | def upgrade(): 16 | """Alembic migration.""" 17 | # ### commands auto generated by Alembic - please adjust! ### 18 | # Remove the index and recreate it as there is no way to remove just the unique 19 | # constrain from index 20 | op.drop_index("ix_users_username", "users") 21 | op.create_index("ix_users_username", "users", ["username"]) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | """Downgrade migration.""" 27 | # ### commands auto generated by Alembic - please adjust! ### 28 | op.drop_index("ix_users_username", "users") 29 | op.create_index(op.f("ix_users_username"), "users", ["username"], unique=True) 30 | # ### end Alembic commands ### 31 | -------------------------------------------------------------------------------- /anitya/debug.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | This module contains blueprints that are only included 5 | when application is running in debug mode. 6 | """ 7 | import flask 8 | import flask_login 9 | 10 | from anitya.db import Session, User 11 | 12 | debug_blueprint = flask.Blueprint( 13 | "anitya_debug", __name__, static_folder="static", template_folder="templates" 14 | ) 15 | 16 | 17 | @debug_blueprint.route("/login/debug/<name>") 18 | def login(name: str): 19 | """ 20 | Debug login for prepared users. 21 | 22 | Params: 23 | name: User to log as in. 24 | """ 25 | user = User.query.filter(User.username == name).first() 26 | 27 | # Create user if it doesn't exist yet 28 | if not user: 29 | user = User( 30 | username=name, email=(name + "@example.com"), admin=(name == "admin") 31 | ) 32 | Session.add(user) 33 | Session.commit() 34 | 35 | flask_login.login_user(user) 36 | if flask.session["next_url"]: 37 | return flask.redirect(flask.session["next_url"]) 38 | return flask.redirect("/") 39 | -------------------------------------------------------------------------------- /anitya/lib/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | (c) 2014-2016 - Copyright Red Hat Inc 4 | 5 | Authors: 6 | Pierre-Yves Chibon <pingou@pingoured.fr> 7 | 8 | anitya internal library. 9 | """ 10 | -------------------------------------------------------------------------------- /anitya/lib/backends/custom.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | (c) 2014 - Copyright Red Hat Inc 5 | 6 | Authors: 7 | Pierre-Yves Chibon <pingou@pingoured.fr> 8 | 9 | """ 10 | 11 | from anitya.lib.backends import REGEX, BaseBackend, get_versions_by_regex 12 | 13 | REGEX_ALIASES = {"DEFAULT": REGEX} 14 | 15 | 16 | class CustomBackend(BaseBackend): 17 | """The custom class for project having a special hosting. 18 | 19 | This backend allows to specify a version_url and a regex that will 20 | be used to retrieve the version information. 21 | """ 22 | 23 | name = "custom" 24 | examples = [ 25 | "https://subsurface-divelog.org/downloads/", 26 | "https://www.geany.org/Download/Releases", 27 | ] 28 | more_info = ( 29 | "More information in the <a href='" 30 | "/static/docs/user-guide.html#regular-expressions'> " 31 | "user-guide.html#regular-expressions</a>" 32 | ) 33 | default_regex = REGEX % {"name": "{project name}"} 34 | 35 | @classmethod 36 | def get_version_url(cls, project): 37 | """Method called to retrieve the url used to check for new version 38 | of the project provided, project that relies on the backend of this plugin. 39 | 40 | Attributes: 41 | project (:obj:`anitya.db.models.Project`): Project object whose backend 42 | corresponds to the current plugin. 43 | 44 | Returns: 45 | str: url used for version checking 46 | """ 47 | url = project.version_url 48 | 49 | return url 50 | 51 | @classmethod 52 | def get_versions(cls, project): 53 | """Method called to retrieve all the versions (that can be found) 54 | of the projects provided, project that relies on the backend of 55 | this plugin. 56 | 57 | :arg Project project: a :class:`anitya.db.models.Project` object whose backend 58 | corresponds to the current plugin. 59 | :return: a list of all the possible releases found 60 | :return type: list 61 | :raise AnityaPluginException: a 62 | :class:`anitya.lib.exceptions.AnityaPluginException` exception 63 | when the versions cannot be retrieved correctly 64 | 65 | """ 66 | url = cls.get_version_url(project) 67 | 68 | regex = REGEX_ALIASES["DEFAULT"] 69 | if project.regex: 70 | regex = REGEX_ALIASES.get(project.regex, project.regex) 71 | 72 | if "%(name)" in regex: 73 | regex = regex % {"name": project.name.replace("+", r"\+")} 74 | 75 | return get_versions_by_regex(url, regex, project, insecure=project.insecure) 76 | 77 | @classmethod 78 | def check_feed(cls): # pragma: no cover 79 | """Method called to retrieve the latest uploads to a given backend, 80 | via, for example, RSS or an API. 81 | 82 | Not Supported 83 | 84 | Returns: 85 | :obj:`list`: A list of 4-tuples, containing the project name, homepage, the 86 | backend, and the version. 87 | 88 | Raises: 89 | NotImplementedError: If backend does not 90 | support batch updates. 91 | 92 | """ 93 | raise NotImplementedError() 94 | -------------------------------------------------------------------------------- /anitya/lib/backends/debian.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | (c) 2014 - Copyright Red Hat Inc 5 | 6 | Authors: 7 | Pierre-Yves Chibon <pingou@pingoured.fr> 8 | 9 | """ 10 | 11 | from anitya.lib.backends import BaseBackend, get_versions_by_regex 12 | 13 | # Debian packagers upload the original source tarball in the format 14 | # <name>_<version>.orig.<compression format>. So, for example, 15 | # reprepro_4.13.1.orig.tar.gz. 16 | DEBIAN_REGEX = ( 17 | r"(?i)%(name)s(?:[-_]?(?:minsrc|src|source))?[-_]([^-/_\s]+?)(?:[-_]" 18 | r"(?:minsrc|src|source|asc))?\.(?:orig\.)?(?:tar|t[bglx]z|tbz2|zip)" 19 | ) 20 | 21 | 22 | class DebianBackend(BaseBackend): 23 | """The custom class for projects hosted on ftp.debian.org. 24 | 25 | This backend allows to specify a version_url and a regex that will 26 | be used to retrieve the version information. 27 | """ 28 | 29 | name = "Debian project" 30 | examples = [ 31 | "http://ftp.debian.org/debian/pool/main/q/qpdf/", 32 | "http://ftp.debian.org/debian/pool/main/g/guake/", 33 | ] 34 | 35 | @classmethod 36 | def get_version_url(cls, project): 37 | """Method called to retrieve the url used to check for new version 38 | of the project provided, project that relies on the backend of this plugin. 39 | 40 | Attributes: 41 | project (:obj:`anitya.db.models.Project`): Project object whose backend 42 | corresponds to the current plugin. 43 | 44 | Returns: 45 | str: url used for version checking 46 | """ 47 | 48 | if project.name.startswith("lib"): 49 | short = project.name[:4] 50 | else: 51 | short = project.name[0] 52 | 53 | return f"http://ftp.debian.org/debian/pool/main/{short}/{project.name}/" # noqa: E231 54 | 55 | @classmethod 56 | def get_versions(cls, project): 57 | """Method called to retrieve all the versions (that can be found) 58 | of the projects provided, project that relies on the backend of 59 | this plugin. 60 | 61 | :arg Project project: a :class:`anitya.db.models.Project` object whose backend 62 | corresponds to the current plugin. 63 | :return: a list of all the possible releases found 64 | :return type: list 65 | :raise AnityaPluginException: a 66 | :class:`anitya.lib.exceptions.AnityaPluginException` exception 67 | when the versions cannot be retrieved correctly 68 | 69 | """ 70 | url = cls.get_version_url(project) 71 | regex = DEBIAN_REGEX % {"name": project.name} 72 | 73 | return get_versions_by_regex(url, regex, project) 74 | 75 | @classmethod 76 | def check_feed(cls): # pragma: no cover 77 | """Method called to retrieve the latest uploads to a given backend, 78 | via, for example, RSS or an API. 79 | 80 | Not Supported 81 | 82 | Returns: 83 | :obj:`list`: A list of 4-tuples, containing the project name, homepage, the 84 | backend, and the version. 85 | 86 | Raises: 87 | NotImplementedError: If backend does not 88 | support batch updates. 89 | 90 | """ 91 | raise NotImplementedError() 92 | -------------------------------------------------------------------------------- /anitya/lib/backends/freshmeat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | (c) 2014 - Copyright Red Hat Inc 5 | 6 | Authors: 7 | Pierre-Yves Chibon <pingou@pingoured.fr> 8 | 9 | """ 10 | 11 | from anitya.lib.backends import BaseBackend, get_versions_by_regex 12 | 13 | REGEX = '<a href="/projects/[^/]*/releases/[0-9]*">([^<]*)</a>' 14 | 15 | 16 | class FreshmeatBackend(BaseBackend): 17 | """The custom class for projects hosted on freshmeat.net. 18 | 19 | This backend allows to specify a version_url and a regex that will 20 | be used to retrieve the version information. 21 | """ 22 | 23 | name = "Freshmeat" 24 | examples = [ 25 | "http://freecode.com/projects/atmail", 26 | "http://freecode.com/projects/awstats", 27 | ] 28 | 29 | @classmethod 30 | def get_version_url(cls, project): 31 | """Method called to retrieve the url used to check for new version 32 | of the project provided, project that relies on the backend of this plugin. 33 | 34 | Attributes: 35 | project (:obj:`anitya.db.models.Project`): Project object whose backend 36 | corresponds to the current plugin. 37 | 38 | Returns: 39 | str: url used for version checking 40 | """ 41 | url_template = "http://freshmeat.net/projects/%(name)s" 42 | 43 | url = url_template % {"name": project.name} 44 | 45 | return url 46 | 47 | @classmethod 48 | def get_versions(cls, project): 49 | """Method called to retrieve all the versions (that can be found) 50 | of the projects provided, project that relies on the backend of 51 | this plugin. 52 | 53 | :arg Project project: a :class:`anitya.db.models.Project` object whose backend 54 | corresponds to the current plugin. 55 | :return: a list of all the possible releases found 56 | :return type: list 57 | :raise AnityaPluginException: a 58 | :class:`anitya.lib.exceptions.AnityaPluginException` exception 59 | when the versions cannot be retrieved correctly 60 | 61 | """ 62 | url = cls.get_version_url(project) 63 | 64 | return get_versions_by_regex(url, REGEX, project) 65 | 66 | @classmethod 67 | def check_feed(cls): 68 | """Method called to retrieve the latest uploads to a given backend, 69 | via, for example, RSS or an API. 70 | 71 | Not Supported 72 | 73 | Returns: 74 | :obj:`list`: A list of 4-tuples, containing the project name, homepage, the 75 | backend, and the version. 76 | 77 | Raises: 78 | NotImplementedError: If backend does not 79 | support batch updates. 80 | 81 | """ 82 | raise NotImplementedError() 83 | -------------------------------------------------------------------------------- /anitya/lib/backends/hackage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | (c) 2014 - Copyright Red Hat Inc 5 | 6 | Authors: 7 | Pierre-Yves Chibon <pingou@pingoured.fr> 8 | 9 | """ 10 | 11 | 12 | from anitya.lib.backends import REGEX, BaseBackend, get_versions_by_regex 13 | 14 | 15 | class HackageBackend(BaseBackend): 16 | """The custom class for projects hosted on hackage. 17 | 18 | This backend allows to specify a version_url and a regex that will 19 | be used to retrieve the version information. 20 | """ 21 | 22 | name = "Hackage" 23 | examples = [ 24 | "https://hackage.haskell.org/package/Hs2lib", 25 | "https://hackage.haskell.org/package/Biobase", 26 | ] 27 | 28 | @classmethod 29 | def get_version_url(cls, project): 30 | """Method called to retrieve the url used to check for new version 31 | of the project provided, project that relies on the backend of this plugin. 32 | 33 | Attributes: 34 | project (:obj:`anitya.db.models.Project`): Project object whose backend 35 | corresponds to the current plugin. 36 | 37 | Returns: 38 | str: url used for version checking 39 | """ 40 | url = f"https://hackage.haskell.org/package/{project.name}" # noqa: E231 41 | 42 | return url 43 | 44 | @classmethod 45 | def get_versions(cls, project): 46 | """Method called to retrieve all the versions (that can be found) 47 | of the projects provided, project that relies on the backend of 48 | this plugin. 49 | 50 | :arg Project project: a :class:`anitya.db.models.Project` object whose backend 51 | corresponds to the current plugin. 52 | :return: a list of all the possible releases found 53 | :return type: list 54 | :raise AnityaPluginException: a 55 | :class:`anitya.lib.exceptions.AnityaPluginException` exception 56 | when the versions cannot be retrieved correctly 57 | 58 | """ 59 | url = cls.get_version_url(project) 60 | 61 | regex = REGEX % {"name": project.name} 62 | 63 | return get_versions_by_regex(url, regex, project) 64 | 65 | @classmethod 66 | def check_feed(cls): 67 | # See https://hackage.haskell.org/api#recentPackages 68 | # It should be possible to query this, but I can't figure out how to 69 | # get it to give me non-html. 70 | # url = 'https://hackage.haskell.org/packages/recent/revisions' 71 | raise NotImplementedError() 72 | -------------------------------------------------------------------------------- /anitya/lib/backends/launchpad.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | (c) 2014 - Copyright Red Hat Inc 5 | 6 | Authors: 7 | Pierre-Yves Chibon <pingou@pingoured.fr> 8 | 9 | """ 10 | 11 | 12 | from anitya.lib.backends import REGEX, BaseBackend, get_versions_by_regex 13 | 14 | 15 | class LaunchpadBackend(BaseBackend): 16 | """The custom class for projects hosted on launchpad.net. 17 | 18 | This backend allows to specify a version_url and a regex that will 19 | be used to retrieve the version information. 20 | """ 21 | 22 | name = "Launchpad" 23 | examples = ["https://launchpad.net/terminator/", "https://launchpad.net/exaile"] 24 | 25 | @classmethod 26 | def get_version_url(cls, project): 27 | """Method called to retrieve the url used to check for new version 28 | of the project provided, project that relies on the backend of this plugin. 29 | 30 | Attributes: 31 | project (:obj:`anitya.db.models.Project`): Project object whose backend 32 | corresponds to the current plugin. 33 | 34 | Returns: 35 | str: url used for version checking 36 | """ 37 | url = f"https://launchpad.net/{project.name}/+download" # noqa: E231 38 | 39 | return url 40 | 41 | @classmethod 42 | def get_versions(cls, project): 43 | """Method called to retrieve all the versions (that can be found) 44 | of the projects provided, project that relies on the backend of 45 | this plugin. 46 | 47 | :arg Project project: a :class:`anitya.db.models.Project` object whose backend 48 | corresponds to the current plugin. 49 | :return: a list of all the possible releases found 50 | :return type: list 51 | :raise AnityaPluginException: a 52 | :class:`anitya.lib.exceptions.AnityaPluginException` exception 53 | when the versions cannot be retrieved correctly 54 | 55 | """ 56 | url = cls.get_version_url(project) 57 | 58 | regex = REGEX % {"name": project.name} 59 | 60 | return get_versions_by_regex(url, regex, project) 61 | 62 | @classmethod 63 | def check_feed(cls): # pragma: no cover 64 | """Method called to retrieve the latest uploads to a given backend, 65 | via, for example, RSS or an API. 66 | 67 | Not Supported 68 | 69 | Returns: 70 | :obj:`list`: A list of 4-tuples, containing the project name, homepage, the 71 | backend, and the version. 72 | 73 | Raises: 74 | NotImplementedError: If backend does not 75 | support batch updates. 76 | 77 | """ 78 | raise NotImplementedError() 79 | -------------------------------------------------------------------------------- /anitya/lib/backends/sourceforge.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | (c) 2014 - Copyright Red Hat Inc 5 | 6 | Authors: 7 | Pierre-Yves Chibon <pingou@pingoured.fr> 8 | 9 | """ 10 | 11 | from anitya.lib.backends import REGEX, BaseBackend, get_versions_by_regex 12 | 13 | 14 | class SourceforgeBackend(BaseBackend): 15 | """The custom class for projects hosted on sourceforge.net. 16 | 17 | This backend allows to specify a version_url and a regex that will 18 | be used to retrieve the version information. 19 | """ 20 | 21 | name = "Sourceforge" 22 | examples = [ 23 | "https://sourceforge.net/projects/filezilla/", 24 | "https://sourceforge.net/projects/file-folder-ren/", 25 | ] 26 | 27 | @classmethod 28 | def get_version_url(cls, project): 29 | """Method called to retrieve the url used to check for new version 30 | of the project provided, project that relies on the backend of this plugin. 31 | 32 | Attributes: 33 | project (:obj:`anitya.db.models.Project`): Project object whose backend 34 | corresponds to the current plugin. 35 | 36 | Returns: 37 | str: url used for version checking 38 | """ 39 | url_template = "https://sourceforge.net/projects/%(name)s/rss?limit=200" 40 | 41 | url = url_template % { 42 | "name": (project.version_url or project.name).replace("+", r"\+") 43 | } 44 | 45 | return url 46 | 47 | @classmethod 48 | def get_versions(cls, project): 49 | """Method called to retrieve all the versions (that can be found) 50 | of the projects provided, project that relies on the backend of 51 | this plugin. 52 | 53 | :arg Project project: a :class:`anitya.db.models.Project` object whose backend 54 | corresponds to the current plugin. 55 | :return: a list of all the possible releases found 56 | :return type: list 57 | :raise AnityaPluginException: a 58 | :class:`anitya.lib.exceptions.AnityaPluginException` exception 59 | when the versions cannot be retrieved correctly 60 | 61 | """ 62 | url = cls.get_version_url(project) 63 | regex = REGEX % {"name": project.name.replace("+", r"\+")} 64 | 65 | return get_versions_by_regex(url, regex, project) 66 | 67 | @classmethod 68 | def check_feed(cls): # pragma: no cover 69 | """Method called to retrieve the latest uploads to a given backend, 70 | via, for example, RSS or an API. 71 | 72 | Not all backends may support this. It can be used to look for updates 73 | much more quickly than scanning all known projects. 74 | 75 | Returns: 76 | :obj:`list`: A list of 4-tuples, containing the project name, homepage, the 77 | backend, and the version. 78 | 79 | Raises: 80 | AnityaPluginException: A 81 | :obj:`anitya.lib.exceptions.AnityaPluginException` exception 82 | when the versions cannot be retrieved correctly 83 | NotImplementedError: If backend does not 84 | support batch updates. 85 | 86 | """ 87 | raise NotImplementedError() 88 | -------------------------------------------------------------------------------- /anitya/lib/backends/stackage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | (c) 2015 - Copyright Red Hat Inc 5 | Authors: 6 | Jens Petersen <petersen@redhat.com> 7 | """ 8 | 9 | 10 | from anitya.lib.backends import BaseBackend, get_versions_by_regex 11 | 12 | 13 | class StackageBackend(BaseBackend): 14 | """The custom class for Haskell projects hosted on Stackage.org. 15 | This backend allows to specify a version_url and a regex that will 16 | be used to retrieve the version information. 17 | """ 18 | 19 | name = "Stackage" 20 | examples = [ 21 | "https://www.stackage.org/package/conduit", 22 | "https://www.stackage.org/package/cabal-install", 23 | ] 24 | 25 | @classmethod 26 | def get_version_url(cls, project): 27 | """Method called to retrieve the url used to check for new version 28 | of the project provided, project that relies on the backend of this plugin. 29 | 30 | Attributes: 31 | project (:obj:`anitya.db.models.Project`): Project object whose backend 32 | corresponds to the current plugin. 33 | 34 | Returns: 35 | str: url used for version checking 36 | """ 37 | url = f"https://www.stackage.org/package/{project.name}" # noqa: E231 38 | 39 | return url 40 | 41 | @classmethod 42 | def get_versions(cls, project): 43 | """Method called to retrieve all the versions (that can be found) 44 | of the projects provided, project that relies on the backend of 45 | this plugin. 46 | :arg Project project: a :class:`anitya.db.models.Project` object whose backend 47 | corresponds to the current plugin. 48 | :return: a list of all the possible releases found 49 | :return type: list 50 | :raise AnityaPluginException: a 51 | :class:`anitya.lib.exceptions.AnityaPluginException` exception 52 | when the versions cannot be retrieved correctly 53 | """ 54 | url = cls.get_version_url(project) 55 | 56 | regex = ( 57 | rf"<a href=\"https://hackage.haskell.org/package/{project.name}\">" # noqa: E231 58 | r"<span class=\"version\">([\d.]*).*</span></a>" 59 | ) 60 | 61 | return get_versions_by_regex(url, regex, project) 62 | 63 | @classmethod 64 | def check_feed(cls): # pragma: no cover 65 | """Method called to retrieve the latest uploads to a given backend, 66 | via, for example, RSS or an API. 67 | 68 | Not Supported 69 | 70 | Returns: 71 | :obj:`list`: A list of 4-tuples, containing the project name, homepage, the 72 | backend, and the version. 73 | 74 | Raises: 75 | NotImplementedError: If backend does not 76 | support batch updates. 77 | 78 | """ 79 | raise NotImplementedError() 80 | -------------------------------------------------------------------------------- /anitya/lib/ecosystems/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | # (c) 2016 - Copyright Red Hat Inc 5 | 6 | """ 7 | The Anitya ecosystems API. 8 | 9 | Authors: 10 | Nick Coghlan <ncoghlan@redhat.com> 11 | 12 | """ 13 | 14 | 15 | from typing import List 16 | 17 | from anitya.lib.versions import GLOBAL_DEFAULT as DEFAULT_VERSION_SCHEME 18 | 19 | 20 | class BaseEcosystem(object): 21 | """ 22 | The base class that all the different ecosystems should extend. 23 | 24 | Attributes: 25 | name (str): The ecosystem name. This name is used to associate projects 26 | with an ecosystem and is user-facing. It is also used in URLs. 27 | default_backend (str): The default backend to use for projects in this 28 | ecosystem if they don't explicitly define one to use. 29 | default_version_scheme (str): The default version scheme to use for 30 | projects in this ecosystem if a they don't explicitly define one to 31 | use. 32 | aliases (list): A list of alternate names for this ecosystem. These 33 | should be lowercase. 34 | """ 35 | 36 | name: str 37 | default_backend: str 38 | default_version_scheme: str = DEFAULT_VERSION_SCHEME 39 | aliases: List[str] = [] 40 | -------------------------------------------------------------------------------- /anitya/lib/ecosystems/crates.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is a part of the Anitya project. 3 | # 4 | # Copyright © 2017 Igor Gnatenko <ignatenkobrain@fedoraproject.org> 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | """ 20 | The crates.io ecosystem. 21 | """ 22 | 23 | from . import BaseEcosystem 24 | 25 | 26 | class CratesEcosystem(BaseEcosystem): 27 | """The crates.io ecosystem class.""" 28 | 29 | name = "crates.io" 30 | default_backend = "crates.io" 31 | aliases = ["Cargo"] 32 | -------------------------------------------------------------------------------- /anitya/lib/ecosystems/maven.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | (c) 2016 - Copyright Red Hat Inc 5 | 6 | Authors: 7 | Nick Coghlan <ncoghlan@redhat.com> 8 | 9 | """ 10 | from . import BaseEcosystem 11 | 12 | 13 | class MavenEcosystem(BaseEcosystem): 14 | """The Maven ecosystem class""" 15 | 16 | name = "maven" 17 | default_backend = "Maven Central" 18 | -------------------------------------------------------------------------------- /anitya/lib/ecosystems/npm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | (c) 2016 - Copyright Red Hat Inc 5 | 6 | Authors: 7 | Nick Coghlan <ncoghlan@redhat.com> 8 | 9 | """ 10 | from . import BaseEcosystem 11 | 12 | 13 | class NpmEcosystem(BaseEcosystem): 14 | """The npm ecosystem class""" 15 | 16 | name = "npm" 17 | default_backend = "npmjs" 18 | -------------------------------------------------------------------------------- /anitya/lib/ecosystems/pypi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | (c) 2016 - Copyright Red Hat Inc 5 | 6 | Authors: 7 | Nick Coghlan <ncoghlan@redhat.com> 8 | 9 | """ 10 | from . import BaseEcosystem 11 | 12 | 13 | class PypiEcosystem(BaseEcosystem): 14 | """The PyPI ecosystem class""" 15 | 16 | name = "pypi" 17 | default_backend = "PyPI" 18 | -------------------------------------------------------------------------------- /anitya/lib/ecosystems/rubygems.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | (c) 2016 - Copyright Red Hat Inc 5 | 6 | Authors: 7 | Nick Coghlan <ncoghlan@redhat.com> 8 | 9 | """ 10 | from . import BaseEcosystem 11 | 12 | 13 | class RubygemsEcosystem(BaseEcosystem): 14 | """The rubygems ecosystem class""" 15 | 16 | name = "rubygems" 17 | default_backend = "Rubygems" 18 | -------------------------------------------------------------------------------- /anitya/lib/versions/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright © 2017 Red Hat, Inc. 4 | # 5 | # This copyrighted material is made available to anyone wishing to use, 6 | # modify, copy, or redistribute it subject to the terms and conditions 7 | # of the GNU General Public License v.2, or (at your option) any later 8 | # version. This program is distributed in the hope that it will be 9 | # useful, but WITHOUT ANY WARRANTY expressed or implied, including the 10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR 11 | # PURPOSE. See the GNU General Public License for more details. You 12 | # should have received a copy of the GNU General Public License along 13 | # with this program; if not, write to the Free Software Foundation, 14 | # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 15 | # 16 | # Any Red Hat trademarks that are incorporated in the source 17 | # code or documentation are not subject to the GNU General Public 18 | # License and may only be used or replicated with the express permission 19 | # of Red Hat, Inc. 20 | """ 21 | This package defines a set of version scheme plugins. 22 | 23 | Anitya attempts to determine the newest version for a project. To do this, it 24 | must be able to both parse and compare all the major version schemes, and handle 25 | unknown version schemes in a reasonable way. 26 | 27 | The general approach is as follows: 28 | 29 | 1. If a project has its ``version_scheme`` column defined in the database, that 30 | version scheme is used. 31 | 32 | 2. If the project does not have ``version_scheme`` set, but is in an ecosystem 33 | with a default version scheme, the ecosystem default is used. 34 | 35 | 2. If the project is not a part of an ecosystem or if the ecosystem has no 36 | default scheme, but the backend it uses has a default version scheme defined, 37 | the backend default is used. 38 | 39 | 4. If all else fails, Anitya uses the value of :data:`GLOBAL_DEFAULT`. 40 | """ 41 | from __future__ import unicode_literals 42 | 43 | from .base import Version, v_prefix # noqa: F401 44 | from .calver import CalendarVersion # noqa: F401 45 | from .rpm import RpmVersion # noqa: F401 46 | from .semver import SemanticVersion # noqa: F401 47 | 48 | #: The default version scheme to use when the project itself, its ecosystem, 49 | #: and its backend all have no version scheme set. 50 | GLOBAL_DEFAULT = "RPM" 51 | -------------------------------------------------------------------------------- /anitya/lib/xml2dict.py: -------------------------------------------------------------------------------- 1 | """ 2 | Thunder Chen<nkchenz@gmail.com> 2007.9.1 3 | Released under the GPLv2 at https://code.google.com/archive/p/xml2dict/ 4 | 5 | Adjusted by Pierre-Yves Chibon <pingou@pingoured.fr> 6 | """ 7 | 8 | import re 9 | 10 | import defusedxml.ElementTree as ET 11 | import six 12 | 13 | 14 | class object_dict(dict): 15 | """object view of dict, you can 16 | >>> a = object_dict() 17 | >>> a.fish = 'fish' 18 | >>> a['fish'] 19 | 'fish' 20 | >>> a['water'] = 'water' 21 | >>> a.water 22 | 'water' 23 | >>> a.test = {'value': 1} 24 | >>> a.test2 = object_dict({'name': 'test2', 'value': 2}) 25 | >>> a.test, a.test2.name, a.test2.value 26 | (1, 'test2', 2) 27 | """ 28 | 29 | def __init__(self, initd=None): 30 | if initd is None: 31 | initd = {} 32 | dict.__init__(self, initd) 33 | 34 | def __getattr__(self, item): 35 | d = self.__getitem__(item) 36 | # if value is the only key in object, you can omit it 37 | if isinstance(d, dict) and "value" in d and len(d) == 1: 38 | return d["value"] 39 | else: 40 | return d 41 | 42 | def __setattr__(self, item, value): 43 | self.__setitem__(item, value) 44 | 45 | 46 | class XML2Dict(object): 47 | """Class XML2Dict""" 48 | 49 | def _parse_node(self, node): 50 | node_tree = object_dict() 51 | # Save attrs and text, hope there will not be a child with same name 52 | if node.text: 53 | node_tree.value = node.text 54 | for k, v in node.attrib.items(): 55 | k, v = self._namespace_split(k, object_dict({"value": v})) 56 | node_tree[k] = v 57 | # Save childrens 58 | for child in list(node): 59 | tag, tree = self._namespace_split(child.tag, self._parse_node(child)) 60 | # the first time, so store it in dict 61 | if tag not in node_tree: 62 | node_tree[tag] = tree 63 | continue 64 | old = node_tree[tag] 65 | if not isinstance(old, list): 66 | node_tree.pop(tag) 67 | # multi times, so change old dict to a list 68 | node_tree[tag] = [old] 69 | node_tree[tag].append(tree) # add the new one 70 | 71 | return node_tree 72 | 73 | def _namespace_split(self, tag, value): 74 | """ 75 | Split the tag '{http://cs.sfsu.edu/csc867/myscheduler}patients' 76 | ns = http://cs.sfsu.edu/csc867/myscheduler 77 | name = patients 78 | """ 79 | result = re.compile(r"\{(.*)\}(.*)").search(tag) 80 | if result: 81 | value.namespace, tag = result.groups() 82 | return (tag, value) 83 | 84 | def parse(self, file): 85 | """parse a xml file to a dict""" 86 | with open(file, "rb") as f: # pragma: no cover 87 | return self.fromstring(f.read()) 88 | 89 | def fromstring(self, s): 90 | """parse a string""" 91 | if isinstance(s, six.text_type): 92 | s = s.encode("utf-8") 93 | t = ET.fromstring(s) 94 | root_tag, root_tree = self._namespace_split(t.tag, self._parse_node(t)) 95 | return object_dict({root_tag: root_tree}) 96 | -------------------------------------------------------------------------------- /anitya/reverse_proxy.py: -------------------------------------------------------------------------------- 1 | """Reverse proxy class for flask.""" 2 | 3 | # -*- coding: utf-8 -*- 4 | # 5 | # Copyright © 2024 Red Hat, Inc. 6 | # 7 | # This copyrighted material is made available to anyone wishing to use, 8 | # modify, copy, or redistribute it subject to the terms and conditions 9 | # of the GNU General Public License v.2, or (at your option) any later 10 | # version. This program is distributed in the hope that it will be 11 | # useful, but WITHOUT ANY WARRANTY expressed or implied, including the 12 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR 13 | # PURPOSE. See the GNU General Public License for more details. You 14 | # should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, 16 | # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | # 18 | # Any Red Hat trademarks that are incorporated in the source 19 | # code or documentation are not subject to the GNU General Public 20 | # License and may only be used or replicated with the express permission 21 | # of Red Hat, Inc. 22 | # 23 | 24 | 25 | class ReverseProxied(object): 26 | """ 27 | Because we are reverse proxied from an load balancer 28 | use environ/config to signal https 29 | since flask ignores preferred_url_scheme in url_for calls 30 | """ 31 | 32 | def __init__(self, wsgi_app, config): 33 | """Init reverse proxy.""" 34 | self.wsgi_app = wsgi_app 35 | self.config = config 36 | 37 | def __call__(self, environ, start_response): 38 | """ 39 | Set the scheme to correct one. 40 | 41 | Default is http:// 42 | """ 43 | # if one of x_forwarded or preferred_url is https, prefer it. 44 | forwarded_scheme = environ.get("HTTP_X_FORWARDED_PROTO", None) 45 | preferred_scheme = self.config.get("PREFERRED_URL_SCHEME", None) 46 | if "https" in [forwarded_scheme, preferred_scheme]: 47 | environ["wsgi.url_scheme"] = "https" 48 | return self.wsgi_app(environ, start_response) 49 | -------------------------------------------------------------------------------- /anitya/sar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright 2018 Red Hat, Inc. 5 | # 6 | # This copyrighted material is made available to anyone wishing to use, 7 | # modify, copy, or redistribute it subject to the terms and conditions 8 | # of the GNU General Public License v.2, or (at your option) any later 9 | # version. This program is distributed in the hope that it will be 10 | # useful, but WITHOUT ANY WARRANTY expressed or implied, including the 11 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR 12 | # PURPOSE. See the GNU General Public License for more details. You 13 | # should have received a copy of the GNU General Public License along 14 | # with this program; if not, write to the Free Software Foundation, 15 | # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | # 17 | # Any Red Hat trademarks that are incorporated in the source 18 | # code or documentation are not subject to the GNU General Public 19 | # License and may only be used or replicated with the express permission 20 | # of Red Hat, Inc. 21 | # 22 | """ 23 | This script is used for GDPR SAR (General Data Protection Regulation 24 | Subject Access Requests). 25 | It returns information about specific user saved by Anitya. 26 | It reads SAR_USERNAME and SAR_EMAIL environment variables as keys for 27 | getting data from database. 28 | 29 | Authors: 30 | Michal Konecny <mkonecny@redhat.com> 31 | """ 32 | 33 | import json 34 | import logging 35 | import os 36 | import sys 37 | 38 | from anitya import db 39 | from anitya.config import config 40 | 41 | _log = logging.getLogger("anitya") 42 | 43 | 44 | def main(): 45 | """ 46 | Retrieve database entry for user. 47 | """ 48 | _log.debug("SAR script start") 49 | db.initialize(config) 50 | sar_username = os.getenv("SAR_USERNAME") 51 | sar_email = os.getenv("SAR_EMAIL") 52 | 53 | users = [] 54 | 55 | if sar_email: 56 | _log.debug("Find users by e-mail %s", sar_email) 57 | users = users + db.User.query.filter_by(email=sar_email).all() 58 | 59 | if sar_username: 60 | _log.debug("Find users by username %s", sar_username) 61 | users = users + db.User.query.filter_by(username=sar_username).all() 62 | 63 | users_list = [] 64 | for user in users: 65 | user_dict = user.to_dict() 66 | users_list.append(user_dict) 67 | 68 | json.dump(users_list, sys.stdout) 69 | _log.debug("SAR script end") 70 | 71 | 72 | if __name__ == "__main__": 73 | main() 74 | -------------------------------------------------------------------------------- /anitya/static/css/avatars.css: -------------------------------------------------------------------------------- 1 | .centered { 2 | display: block; 3 | margin: 0 auto; 4 | } 5 | -------------------------------------------------------------------------------- /anitya/static/css/cnucnu.css: -------------------------------------------------------------------------------- 1 | .pagination{ 2 | margin: 0; 3 | } 4 | 5 | .errors { 6 | color:red; 7 | } 8 | 9 | .intro { 10 | background-color: rgba(0,0,0,0.8); 11 | -moz-border-radius: 15px; 12 | border-radius: 15px; 13 | padding: 10px 30px 10px 30px; 14 | margin: auto; 15 | margin-top: 20px; 16 | max-width: 600px; 17 | color: #fff; 18 | } 19 | 20 | #edit_btn, #flag_btn { 21 | margin-top: -6px; 22 | } 23 | 24 | #flag_btn { 25 | margin-right: 6px; 26 | } 27 | 28 | #submit_btn { 29 | margin-right: 6px; /* Gives space between Cancel button */ 30 | } 31 | 32 | textarea#reason { 33 | width: 600px; 34 | height: 120px; 35 | } 36 | 37 | #logs_form div, #flags_form div { 38 | padding-left: 2px; 39 | padding-right: 2px; 40 | } 41 | 42 | #flag_list td, #flag_list th { 43 | padding: 6px; 44 | } 45 | 46 | table input[type="text"] { 47 | width: 300px; 48 | } 49 | 50 | #default_regex_txt { 51 | background-color: #eee; 52 | } 53 | 54 | label { 55 | white-space: nowrap; 56 | } 57 | 58 | .ui-front { 59 | z-index: 10000!important; 60 | } 61 | 62 | .nav form { 63 | padding-top: 0.2em; 64 | } 65 | 66 | .logs-table-seperator { 67 | margin-top: 15px; 68 | } 69 | 70 | .sort-arrow { 71 | display: inline-block; 72 | margin-left: 5px; 73 | width: 0; 74 | height: 0; 75 | border-left: 5px solid transparent; 76 | border-right: 5px solid transparent; 77 | } 78 | 79 | .arrow-up { 80 | border-bottom: 5px solid #000; 81 | } 82 | 83 | .arrow-down { 84 | border-top: 5px solid #000; 85 | } 86 | -------------------------------------------------------------------------------- /anitya/static/css/fonts/Cantarell-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Cantarell-Bold-webfont.eot -------------------------------------------------------------------------------- /anitya/static/css/fonts/Cantarell-Bold-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Cantarell-Bold-webfont.ttf -------------------------------------------------------------------------------- /anitya/static/css/fonts/Cantarell-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Cantarell-Bold-webfont.woff -------------------------------------------------------------------------------- /anitya/static/css/fonts/Cantarell-BoldOblique-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Cantarell-BoldOblique-webfont.eot -------------------------------------------------------------------------------- /anitya/static/css/fonts/Cantarell-BoldOblique-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Cantarell-BoldOblique-webfont.ttf -------------------------------------------------------------------------------- /anitya/static/css/fonts/Cantarell-BoldOblique-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Cantarell-BoldOblique-webfont.woff -------------------------------------------------------------------------------- /anitya/static/css/fonts/Cantarell-Oblique-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Cantarell-Oblique-webfont.eot -------------------------------------------------------------------------------- /anitya/static/css/fonts/Cantarell-Oblique-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Cantarell-Oblique-webfont.ttf -------------------------------------------------------------------------------- /anitya/static/css/fonts/Cantarell-Oblique-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Cantarell-Oblique-webfont.woff -------------------------------------------------------------------------------- /anitya/static/css/fonts/Cantarell-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Cantarell-Regular-webfont.eot -------------------------------------------------------------------------------- /anitya/static/css/fonts/Cantarell-Regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Cantarell-Regular-webfont.ttf -------------------------------------------------------------------------------- /anitya/static/css/fonts/Cantarell-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Cantarell-Regular-webfont.woff -------------------------------------------------------------------------------- /anitya/static/css/fonts/Comfortaa_Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Comfortaa_Bold-webfont.eot -------------------------------------------------------------------------------- /anitya/static/css/fonts/Comfortaa_Bold-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Comfortaa_Bold-webfont.ttf -------------------------------------------------------------------------------- /anitya/static/css/fonts/Comfortaa_Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Comfortaa_Bold-webfont.woff -------------------------------------------------------------------------------- /anitya/static/css/fonts/Comfortaa_Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Comfortaa_Regular-webfont.eot -------------------------------------------------------------------------------- /anitya/static/css/fonts/Comfortaa_Regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Comfortaa_Regular-webfont.ttf -------------------------------------------------------------------------------- /anitya/static/css/fonts/Comfortaa_Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Comfortaa_Regular-webfont.woff -------------------------------------------------------------------------------- /anitya/static/css/fonts/Comfortaa_Thin-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Comfortaa_Thin-webfont.eot -------------------------------------------------------------------------------- /anitya/static/css/fonts/Comfortaa_Thin-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Comfortaa_Thin-webfont.ttf -------------------------------------------------------------------------------- /anitya/static/css/fonts/Comfortaa_Thin-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/fonts/Comfortaa_Thin-webfont.woff -------------------------------------------------------------------------------- /anitya/static/css/footer.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer styles */ 2 | 3 | html, 4 | body { 5 | height: 100%; 6 | } 7 | 8 | .max-width { 9 | min-width: 100%; 10 | } 11 | 12 | #wrap { 13 | min-height: 100%; 14 | height: auto; 15 | margin: 0 auto -20px; 16 | padding: 0 0 20px; 17 | } 18 | 19 | #footer { 20 | height: 40px; 21 | background-color: #f5f5f5; 22 | } 23 | 24 | .credit { 25 | margin-top: 10px; 26 | } 27 | -------------------------------------------------------------------------------- /anitya/static/css/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /anitya/static/css/images/ui-bg_flat_0_eeeeee_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-bg_flat_0_eeeeee_40x100.png -------------------------------------------------------------------------------- /anitya/static/css/images/ui-bg_flat_55_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-bg_flat_55_ffffff_40x100.png -------------------------------------------------------------------------------- /anitya/static/css/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /anitya/static/css/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /anitya/static/css/images/ui-bg_highlight-soft_100_f6f6f6_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-bg_highlight-soft_100_f6f6f6_1x100.png -------------------------------------------------------------------------------- /anitya/static/css/images/ui-bg_highlight-soft_25_0073ea_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-bg_highlight-soft_25_0073ea_1x100.png -------------------------------------------------------------------------------- /anitya/static/css/images/ui-bg_highlight-soft_50_dddddd_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-bg_highlight-soft_50_dddddd_1x100.png -------------------------------------------------------------------------------- /anitya/static/css/images/ui-icons_0073ea_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-icons_0073ea_256x240.png -------------------------------------------------------------------------------- /anitya/static/css/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /anitya/static/css/images/ui-icons_666666_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-icons_666666_256x240.png -------------------------------------------------------------------------------- /anitya/static/css/images/ui-icons_ff0084_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-icons_ff0084_256x240.png -------------------------------------------------------------------------------- /anitya/static/css/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/css/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /anitya/static/css/navbar.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 35px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | .navbar { 7 | margin-bottom: 20px; 8 | } 9 | 10 | ul#flashes { 11 | margin-top: 20px; 12 | } 13 | -------------------------------------------------------------------------------- /anitya/static/css/text.css: -------------------------------------------------------------------------------- 1 | /* new bulletproof @font-face syntax from http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax */ 2 | @font-face { 3 | font-family: 'ComfortaaThin'; 4 | src: url('fonts/Comfortaa_Thin-webfont.eot?#iefix') format('embedded-opentype'), 5 | url('fonts/Comfortaa_Thin-webfont.ttf') format('truetype'), 6 | url('fonts/Comfortaa_Thin-webfont.svg#webfontReo2lGxG') format('svg'); 7 | font-weight: normal; 8 | font-style: normal; 9 | } 10 | 11 | @font-face { 12 | font-family: 'ComfortaaRegular'; 13 | src: url('fonts/Comfortaa_Regular-webfont.eot?#iefix') format('embedded-opentype'), 14 | url('fonts/Comfortaa_Regular-webfont.ttf') format('truetype'), 15 | url('fonts/Comfortaa_Regular-webfont.svg#webfontxbL3cos8') format('svg'); 16 | font-weight: normal; 17 | font-style: normal; 18 | } 19 | 20 | @font-face { 21 | font-family: 'ComfortaaBold'; 22 | src: url('fonts/Comfortaa_Bold-webfont.eot?#iefix') format('embedded-opentype'), 23 | url('fonts/Comfortaa_Bold-webfont.ttf') format('truetype'), 24 | url('fonts/Comfortaa_Bold-webfont.svg#webfontjkcnhWWT') format('svg'); 25 | font-weight: normal; 26 | font-style: normal; 27 | } 28 | 29 | @font-face { 30 | font-family: 'CantarellRegular'; 31 | src: url('fonts/Cantarell-Regular-webfont.eot?#iefix') format('embedded-opentype'), 32 | url('fonts/Cantarell-Regular-webfont.ttf') format('truetype'), 33 | url('fonts/Cantarell-Regular-webfont.svg#webfontPQ4tPnyo') format('svg'); 34 | font-weight: normal; 35 | font-style: normal; 36 | } 37 | 38 | @font-face { 39 | font-family: 'CantarellBold'; 40 | src: url('fonts/Cantarell-Bold-webfont.eot?#iefix') format('embedded-opentype'), 41 | url('fonts/Cantarell-Bold-webfont.ttf') format('truetype'), 42 | url('fonts/Cantarell-Bold-webfont.svg#webfont3gCLDhwY') format('svg'); 43 | font-weight: normal; 44 | font-style: normal; 45 | } 46 | 47 | @font-face { 48 | font-family: 'CantarellBoldOblique'; 49 | src: url('fonts/Cantarell-BoldOblique-webfont.eot?#iefix') format('embedded-opentype'), 50 | url('fonts/Cantarell-BoldOblique-webfont.ttf') format('truetype'), 51 | url('fonts/Cantarell-BoldOblique-webfont.svg#webfont3gCLDhwY') format('svg'); 52 | font-weight: normal; 53 | font-style: normal; 54 | } 55 | 56 | @font-face { 57 | font-family: 'CantarellOblique'; 58 | src: url('fonts/Cantarell-Oblique-webfont.eot?#iefix') format('embedded-opentype'), 59 | url('fonts/Cantarell-Oblique-webfont.ttf') format('truetype'), 60 | url('fonts/Cantarell-Oblique-webfont.svg#webfont3gCLDhwY') format('svg'); 61 | font-weight: normal; 62 | font-style: normal; 63 | } 64 | -------------------------------------------------------------------------------- /anitya/static/fedora_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/fedora_logo.png -------------------------------------------------------------------------------- /anitya/static/github_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/github_logo.png -------------------------------------------------------------------------------- /anitya/static/google_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/google_logo.png -------------------------------------------------------------------------------- /anitya/static/ico/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/ico/favicon.ico -------------------------------------------------------------------------------- /anitya/static/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/static/img/spinner.gif -------------------------------------------------------------------------------- /anitya/static/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "anitya", 3 | "version": "1.0.0", 4 | "description": ".. image:: https://img.shields.io/pypi/v/anitya.svg :target: https://pypi.org/project/anitya/", 5 | "main": "index.js", 6 | "directories": { 7 | "doc": "docs" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "build": "webpack" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/Zlopez/anitya.git" 16 | }, 17 | "keywords": [], 18 | "author": "", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/Zlopez/anitya/issues" 22 | }, 23 | "homepage": "https://github.com/Zlopez/anitya#readme", 24 | "dependencies": { 25 | "@popperjs/core": "^2.11.6", 26 | "bootstrap": "^5.2.2", 27 | "jquery": "^3.6.1", 28 | "jquery-ui": "^1.13.2", 29 | "moment": "^2.29.4" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /anitya/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/templates/__init__.py -------------------------------------------------------------------------------- /anitya/templates/distro_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | {% from "functions.html" import render_field_in_row %} 3 | 4 | {% block title %}Delete Distribution · Anitya{% endblock %} 5 | 6 | {% block body %} 7 | 8 | <div class="page-header"> 9 | <h1>Delete disribution: {{ distro.name }}</h1> 10 | </div> 11 | 12 | <form method="POST" action="{{ url_for('anitya_ui.delete_distro', distro_name=distro.name) }}" > 13 | <button type="submit" class="btn btn-success"> 14 | Submit 15 | </button> 16 | <a href="{{ url_for('anitya_ui.distros') }}"> 17 | <button type="button" class="btn btn-danger"> 18 | Cancel 19 | </button> 20 | </a> 21 | {{ form.csrf_token }} 22 | </form> 23 | 24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /anitya/templates/distros.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block title %}Distributions Participating · Anitya{% endblock %} 4 | 5 | {% block body %} 6 | 7 | <div class="page-header"> 8 | <h1>Distributions participating</h1> 9 | </div> 10 | 11 | <div class="row show-grid"> 12 | <div class="col-sm-6"> 13 | {% if total_page > 1 %} 14 | <ul class="pagination pagination-sm"> 15 | {% if page > 1%} 16 | <li class="page-item"> 17 | <a class="page-link" href="{{ page_url }}?page={{page - 1}}"> 18 | « 19 | </a> 20 | {% else %} 21 | <li class="page-item disabled"> 22 | <a class="page-link"> « </a> 23 | {% endif %} 24 | </li> 25 | <li class="page-item disabled"> 26 | <a class="page-link"> {{ page }} / {{ total_page }} </a> 27 | </li> 28 | {% if page < total_page %} 29 | <li class="page-item"> 30 | <a class="page-link" href="{{ page_url }}?page={{page + 1}}"> 31 | » 32 | </a> 33 | {% else %} 34 | <li class="page-item disabled"> 35 | <a class="page-link"> » </a> 36 | {% endif %} 37 | </li> 38 | </ul> 39 | {% endif %} 40 | </div> 41 | </div> 42 | 43 | <div class="row"> 44 | <p> 45 | Here is the list of all the distributions that anitya knows about. 46 | </p> 47 | <p> 48 | <a href="{{ url_for('anitya_ui.add_distro') }}"> 49 | <button type="submit" class="btn btn-outline-success"> 50 | Add a new distribution 51 | </button> 52 | </a> 53 | </p> 54 | <div class="list-group"> 55 | {% for distro in distros %} 56 | <div class="list-group-item"> 57 | <a href="{{ url_for('anitya_ui.distro', distroname=distro.name) }}"> 58 | {{distro.name}} 59 | </a> 60 | {% if is_admin %} 61 | <div class="d-flex float-end"> 62 | <a href="{{ url_for('anitya_ui.edit_distro', distro_name=distro.name) }}"> 63 | <button type="submit" class="btn btn-info btn-sm me-2"> 64 | Edit 65 | </button> 66 | </a> 67 | <a href="{{ url_for('anitya_ui.delete_distro', distro_name=distro.name) }}"> 68 | <button type="submit" class="btn btn-warning btn-sm"> 69 | Delete 70 | </button> 71 | </a> 72 | </div> 73 | {% endif %} 74 | </div> 75 | {% else %} 76 | <blockquote> 77 | Oops, this is embarrassing. It seems that no distributions are participating 78 | currently. 79 | </blockquote> 80 | {% endfor %} 81 | </div> 82 | </div> 83 | 84 | {% endblock %} 85 | -------------------------------------------------------------------------------- /anitya/templates/functions.html: -------------------------------------------------------------------------------- 1 | {% macro render_field_in_row(field, after="", docs="") %} 2 | <td> 3 | {%- if docs -%}<a href="{{ docs }}">{%- endif -%} 4 | {{ field.label }} 5 | {%- if docs -%}</a>{%- endif -%} 6 | </td> 7 | <td> 8 | {{ field(**kwargs)|safe }} 9 | </td> 10 | {%- if after -%} <td>{{ after }}</td>{%- endif -%} 11 | {%- if field.errors -%} 12 | {%- for error in field.errors -%} 13 | <td class="errors">{{ error }}</td> 14 | {%- endfor -%} 15 | {%- endif -%} 16 | 17 | {% endmacro %} 18 | -------------------------------------------------------------------------------- /anitya/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block title %}Anitya{% endblock %} 4 | 5 | {% block body %} 6 | <div class="row justify-content-center"> 7 | <div class="col"> 8 | <div class="intro"> 9 | <h3> 10 | Watch for releases of your favorite projects 11 | </h3> 12 | <p>We all have our favorite projects and we all like to use their 13 | latest versions with their latest features. However, it is hard 14 | to keep in touch with all of them and be informed when they 15 | release a new version. Anitya can help!</p> 16 | </div> 17 | </div> 18 | 19 | <!-- Three columns of text below the carousel --> 20 | <div class="row p-2 justify-content-center"> 21 | <div class="col-4"> 22 | <h2>Announce</h2> 23 | <p>We monitor upstream releases and broadcast them on 24 | <a href="https://fedora-messaging.readthedocs.io/en/latest">Fedora messaging</a> bus. </p> 25 | </div> 26 | <div class="col-4"> 27 | <h2>Search</h2> 28 | <p>Currently {{ total }} projects are being monitored by Anitya. 29 | Your project of interest might be there, or not. To check it 30 | <a href="{{ url_for('anitya_ui.projects') }}">browse the list of all projects</a> 31 | or simply search for them!</p> 32 | <form action="{{ url_for('anitya_ui.projects_search') }}" role="form" class="d-flex"> 33 | <input id="searchbox" type="text" name="pattern" placeholder="Project name" class="form-control me-2"/> 34 | <button type="submit" class="btn btn-outline-success">Search</button> 35 | </form> 36 | <p><small>You may use * to search for pattern (ie: py*).</small></p> 37 | </div> 38 | </div><!-- /.row --> 39 | 40 | {% endblock %} 41 | 42 | {% block jscript %} 43 | 44 | {% endblock %} 45 | -------------------------------------------------------------------------------- /anitya/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block title %}Login · Anitya{% endblock %} 4 | 5 | {% block body %} 6 | <div class="d-flex align-itmes-start gap-3"> 7 | {% for backend in available_backends %} 8 | <a title="{{ backend }}" class="btn btn-outline-primary" role="button" href="{{ url_for('anitya_auth.login', name=backend.lower()) }}"> 9 | <img src="{{url_for('anitya_ui.static', filename=backend.lower() + '_logo.png')}}"> 10 | <span>{{ backend }}</span> 11 | </a> 12 | {% endfor %} 13 | </div> 14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /anitya/templates/login_debug.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block title %}Login · Anitya{% endblock %} 4 | 5 | {% block body %} 6 | <div class="d-flex align-itmes-start gap-3"> 7 | <a title="user" class="btn btn-outline-primary" role="button" href="{{ url_for('anitya_debug.login', name='user') }}"> 8 | <span>User</span> 9 | </a> 10 | <a title="admin" class="btn btn-outline-primary" role="button" href="{{ url_for('anitya_debug.login', name='admin') }}"> 11 | <span>Admin</span> 12 | </a> 13 | </div> 14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /anitya/templates/logs.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block title %}Logs · Anitya{% endblock %} 4 | 5 | {%block header %} 6 | {% if refresh %} 7 | <meta http-equiv="refresh" content="5" > 8 | {% endif %} 9 | {% endblock %} 10 | 11 | 12 | {% block body %} 13 | 14 | <div class="page-header"> 15 | <h1>Logs</h1> 16 | {% if refresh %} 17 | <p>This page should refresh automatically every 5 seconds</p> 18 | <a href="{{ url_for('anitya_ui.browse_logs') }}?page={{page}}"> 19 | <button type="submit" class="btn btn-danger" id="checknow"> 20 | Remove auto-refresh 21 | </button> 22 | </a> 23 | {% else %} 24 | <a href="{{ url_for('anitya_ui.browse_logs') }}?page={{page}}&refresh=True"> 25 | <button type="submit" class="btn btn-success" id="checknow"> 26 | Activate auto-refresh 27 | </button> 28 | </a> 29 | {% endif %} 30 | </div> 31 | 32 | <div class="row show-grid mt-2"> 33 | <div class="col-sm-4"> 34 | {% if total_page > 1 %} 35 | <ul class="pagination pagination-sm"> 36 | {% if page > 1%} 37 | <li class="page-item"> 38 | <a class="page-link" href="{{ page_url }}?page={{page - 1}}"> 39 | « 40 | </a> 41 | {% else %} 42 | <li class="page-item disabled"> 43 | <a class="page-link"> « </a> 44 | {% endif %} 45 | </li> 46 | <li class="page-item disabled"> 47 | <a class="page-link"> {{ page }} / {{ total_page }} </a> 48 | </li> 49 | {% if page < total_page %} 50 | <li class="page-item"> 51 | <a class="page-link" href="{{ page_url }}?page={{page + 1}}"> 52 | » 53 | </a> 54 | {% else %} 55 | <li class="page-item disabled"> 56 | <a class="page-link"> » </a> 57 | {% endif %} 58 | </li> 59 | </ul> 60 | {% endif %} 61 | </div> 62 | 63 | </div> 64 | 65 | <table class="table table-striped logs-table-seperator"> 66 | <tr> 67 | <th>Name</th> 68 | <th>Updated</th> 69 | <th>Status</th> 70 | <th>Description</th> 71 | </tr> 72 | {% for project in projects %} 73 | <tr> 74 | <td> 75 | <a href="{{ url_for('anitya_ui.project', project_id=project.id) }}"> 76 | {{ project.name }}</a> 77 | </td> 78 | <td width="240px"> 79 | {{ project.last_check.strftime('%Y-%m-%d %H:%M:%S') }} (UTC) 80 | </td> 81 | {% if project.check_successful is none %} 82 | <td>Not updated</td> 83 | {% elif not project.check_successful %} 84 | <td style="color:red;">Fail</td> 85 | {% else %} 86 | <td style="color:green;">OK</td> 87 | {% endif %} 88 | <td>{{ project.logs }}</td> 89 | </tr> 90 | {% endfor %} 91 | </table> 92 | 93 | {% endblock %} 94 | -------------------------------------------------------------------------------- /anitya/templates/mapping.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | {% from "functions.html" import render_field_in_row %} 3 | 4 | {% block title %}Distribution Mapping · Anitya{% endblock %} 5 | 6 | {% block body %} 7 | 8 | <div class="page-header"> 9 | <h1>Project: {{ project.name }}</h1> 10 | </div> 11 | 12 | 13 | {% if package %} 14 | <form method="POST" action="{{ url_for('anitya_ui.edit_project_mapping', 15 | project_id=project.id, pkg_id=package.id) }}" > 16 | {% else %} 17 | <form method="POST" action="{{ url_for('anitya_ui.map_project', project_id=project.id) }}" > 18 | {% endif %} 19 | {{ form.csrf_token }} 20 | <div class="row"> 21 | <div class="col-md-6"> 22 | <div class="list-group"> 23 | <div class="list-group-item"> 24 | <table class="table"> 25 | <tr> 26 | {{ render_field_in_row(form.distro) }} 27 | <td></td> 28 | </tr> 29 | 30 | <tr> 31 | {{ render_field_in_row(form.package_name) }} 32 | <td></td> 33 | </tr> 34 | </table> 35 | </div> 36 | </div> 37 | </div> 38 | </div> 39 | <button type="submit" class="btn btn-success me-2 mt-2"> 40 | {% if package %} 41 | Edit mapping 42 | {% else %} 43 | Add mapping to project 44 | {% endif %} 45 | </button> 46 | 47 | <a href="{{ url_for('anitya_ui.project', project_id=project.id) }}"> 48 | <button type="button" class="btn btn-danger mt-2"> 49 | Cancel 50 | </button> 51 | </a> 52 | </form> 53 | <br/> 54 | <div class="row"> 55 | <div class="col-md-6"> 56 | <p> 57 | Missing your favorite distribution?<br\> 58 | You can add it through <a href="{{ url_for('anitya_ui.distros') }}">distros</a> 59 | </p> 60 | </div> 61 | </div> 62 | 63 | {% endblock %} 64 | 65 | {% block jscript %} 66 | <script type="text/javascript"> 67 | // Tooltips 68 | $("#distro").prop('title', "Official distribution name"); 69 | $("#package_name").prop('title', "Package name used in the distribution"); 70 | </script> 71 | {% endblock %} 72 | -------------------------------------------------------------------------------- /anitya/templates/project_flag.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | {% from "functions.html" import render_field_in_row %} 3 | 4 | {% block title %}Flag Project · Anitya{% endblock %} 5 | 6 | {% block body %} 7 | 8 | <div class="page-header"> 9 | <h1>Flag project</h1> 10 | </div> 11 | <form method="POST" action="{{ url_for('anitya_ui.flag_project', project_id=project.id) }}" > 12 | <div class="row"> 13 | <div class="col-md-12"> 14 | <p> Flag a project for admin review. Use this form to request that the project 15 | be deleted or otherwise changed in a way that can't be done by a regular 16 | user.</p> 17 | <div class="list-group"> 18 | <div class="list-group-item"> 19 | <table class="table"> 20 | <tr> 21 | {{ render_field_in_row(form.reason, tabindex=1) }} 22 | </tr> 23 | </table> 24 | </div> 25 | </div> 26 | </div> 27 | </div> 28 | <button type="submit" class="btn btn-success mt-2" id="submit_btn" tabindex=2> 29 | Submit 30 | </button> 31 | <a href="{{ url_for('anitya_ui.project', project_id=project.id) }}"> 32 | <button type="button" class="btn btn-danger mt-2" tabindex=3> 33 | Cancel 34 | </button> 35 | </a> 36 | {{ form.csrf_token }} 37 | </form> 38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /anitya/templates/project_versions_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block body %} 4 | 5 | <div class="page-header"> 6 | <h1>Project: {{ project.name }} - Delete all versions</h1> 7 | </div> 8 | 9 | <div class="row mt-2"> 10 | <div class="col-md-5"> 11 | <div class="list-group"> 12 | <form method="POST" action="{{ 13 | url_for('anitya_ui.delete_project_versions', 14 | project_id=project.id) }}" > 15 | {{ form.csrf_token }} 16 | <div class="list-group-item"> 17 | <h4 class="list-group-item-heading"> 18 | Project 19 | </h4> 20 | <div class="list-group-item-text"> 21 | {{ project.name }} 22 | </div> 23 | </div> 24 | 25 | <button type="submit" name="confirm" value="Yes" class="btn btn-warning mt-2"> 26 | Delete versions 27 | </button> 28 | 29 | <a href="{{ url_for('anitya_ui.project', project_id=project.id) }}"> 30 | <button type="button" class="btn btn-danger mt-2"> 31 | Cancel 32 | </button> 33 | </a> 34 | 35 | </form> 36 | 37 | </div> 38 | </div> 39 | </div> 40 | 41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /anitya/templates/regex_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block body %} 4 | 5 | <div class="page-header"> 6 | <h1>Project: {{ project.name }} - Delete package</h1> 7 | </div> 8 | 9 | <div class="row mt-2"> 10 | <div class="col-md-5"> 11 | <div class="list-group"> 12 | <form method="POST" action="{{ 13 | url_for('anitya_ui.delete_project_mapping', 14 | project_id=project.id, 15 | distro_name=package.distro_name, 16 | pkg_name=package.package_name) }}" > 17 | {{ form.csrf_token }} 18 | <div class="list-group-item"> 19 | <h4 class="list-group-item-heading"> 20 | Linux distribution 21 | </h4> 22 | <div class="list-group-item-text"> 23 | {{ package.distro_name }} 24 | </div> 25 | </div> 26 | <div class="list-group-item"> 27 | <h4 class="list-group-item-heading"> 28 | Package name 29 | </h4> 30 | <div class="list-group-item-text"> 31 | {{ package.package_name }} 32 | </div> 33 | </div> 34 | 35 | <button type="submit" name="confirm" value="Yes" class="btn btn-warning mt-2"> 36 | Delete mapping 37 | </button> 38 | 39 | <a href="{{ url_for('anitya_ui.project', project_id=project.id) }}"> 40 | <button type="button" class="btn btn-danger mt-2"> 41 | Cancel 42 | </button> 43 | </a> 44 | 45 | </form> 46 | 47 | </div> 48 | </div> 49 | </div> 50 | 51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /anitya/templates/settings.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block title %}Settings · Anitya{% endblock %} 4 | 5 | 6 | {% block body %} 7 | <div class="intro"> 8 | <h3> 9 | User ID 10 | </h3> 11 | <p> 12 | Your user ID is {{ user.id }} 13 | </p> 14 | </div> 15 | 16 | <div class="intro"> 17 | <h3> 18 | API Tokens 19 | </h3> 20 | <p> 21 | API keys are tokens used to authenticate you when using the REST API. 22 | These keys are like passwords; make sure to store them safely. 23 | </p> 24 | </div> 25 | 26 | {% if user.api_tokens %} 27 | <div class="list-group mt-2"> 28 | {% for api_token in user.api_tokens %} 29 | <div class="list-group-item"> 30 | <div class="row me-2"> 31 | <form role="form" class="d-flex gap-2" method="POST" 32 | action="{{ url_for('.delete_token', token=api_token.token) }}"> 33 | <div class="col-3"> 34 | <label for="{{ api_token.token }}" class="col-form-label"> 35 | {{ api_token.description or '' }} 36 | </label> 37 | </div> 38 | <div class="col-7"> 39 | <input id="{{ api_token.token }}" class="form-control input-sm disabled" 40 | type="text" value="{{ api_token.token }}" readonly> 41 | </div> 42 | <div class="col-2"> 43 | <button class="btn btn-danger w-100" type="submit" 44 | onclick="return confirm('Are you sure to delete this token?');" 45 | title="Remove API Token"> 46 | Remove API Token 47 | </button> 48 | </div> 49 | {{ form.csrf_token }} 50 | </form> 51 | </div> 52 | </div> 53 | {% endfor %} 54 | </div> 55 | {% endif %} 56 | 57 | <div class="row mt-2"> 58 | <form role="form" class="d-flex gap-2" action="{{ url_for('.new_token') }}" method="POST"> 59 | <div class="col-3"> 60 | {{ form.description.label(class="col-form-label") }} 61 | </div> 62 | <div class="col-7"> 63 | {{ form.description(class="form-control input-sm") }} 64 | </div> 65 | <div class="col-2"> 66 | <button class="btn btn-primary w-100" type="submit" title="Create API Token"> 67 | Create API Token 68 | </button> 69 | </div> 70 | {{ form.csrf_token }} 71 | </form> 72 | {% endblock %} 73 | -------------------------------------------------------------------------------- /anitya/templates/user_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | {% from "functions.html" import render_field_in_row %} 3 | 4 | {% block title %}Delete User · Anitya{% endblock %} 5 | 6 | {% block body %} 7 | 8 | <div class="page-header"> 9 | <h1>Delete user: {{ user.username }}</h1> 10 | </div> 11 | 12 | <form method="POST" action="{{ url_for('anitya_ui.delete_user', user_id=user.id) }}" > 13 | <button type="submit" name="confirm" value="Yes" class="btn btn-success"> 14 | Submit 15 | </button> 16 | <a href="{{ url_for('anitya_ui.browse_users') }}"> 17 | <button type="button" class="btn btn-danger"> 18 | Cancel 19 | </button> 20 | </a> 21 | {{ form.csrf_token }} 22 | </form> 23 | 24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /anitya/templates/version_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block body %} 4 | 5 | <div class="page-header"> 6 | <h1>Project: {{ project.name }} - Delete version</h1> 7 | </div> 8 | 9 | <div class="row mt-2"> 10 | <div class="col-md-5"> 11 | <div class="list-group"> 12 | <form method="POST" action="{{ 13 | url_for('anitya_ui.delete_project_version', 14 | project_id=project.id, 15 | version=version) }}" > 16 | {{ form.csrf_token }} 17 | <div class="list-group-item"> 18 | <h4 class="list-group-item-heading"> 19 | Project 20 | </h4> 21 | <div class="list-group-item-text"> 22 | {{ project.name }} 23 | </div> 24 | </div> 25 | <div class="list-group-item"> 26 | <h4 class="list-group-item-heading"> 27 | Version 28 | </h4> 29 | <div class="list-group-item-text"> 30 | {{ version }} 31 | </div> 32 | </div> 33 | 34 | <button type="submit" name="confirm" value="Yes" class="btn btn-warning mt-2"> 35 | Delete version 36 | </button> 37 | 38 | <a href="{{ url_for('anitya_ui.project', project_id=project.id) }}"> 39 | <button type="button" class="btn btn-danger mt-2"> 40 | Cancel 41 | </button> 42 | </a> 43 | 44 | </form> 45 | </div> 46 | </div> 47 | </div> 48 | 49 | {% endblock %} 50 | -------------------------------------------------------------------------------- /anitya/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright © 2017 Red Hat, Inc. 4 | # 5 | # This copyrighted material is made available to anyone wishing to use, 6 | # modify, copy, or redistribute it subject to the terms and conditions 7 | # of the GNU General Public License v.2, or (at your option) any later 8 | # version. This program is distributed in the hope that it will be 9 | # useful, but WITHOUT ANY WARRANTY expressed or implied, including the 10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR 11 | # PURPOSE. See the GNU General Public License for more details. You 12 | # should have received a copy of the GNU General Public License along 13 | # with this program; if not, write to the Free Software Foundation, 14 | # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 15 | # 16 | # Any Red Hat trademarks that are incorporated in the source 17 | # code or documentation are not subject to the GNU General Public 18 | # License and may only be used or replicated with the express permission 19 | # of Red Hat, Inc. 20 | # 21 | """ 22 | Anitya tests. 23 | """ 24 | -------------------------------------------------------------------------------- /anitya/tests/db/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/tests/db/__init__.py -------------------------------------------------------------------------------- /anitya/tests/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/tests/lib/__init__.py -------------------------------------------------------------------------------- /anitya/tests/lib/backends/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/anitya/tests/lib/backends/__init__.py -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_cran.CranBackendTests.test_get_version: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.11.0 at release-monitoring.org] 10 | method: GET 11 | uri: https://crandb.r-pkg.org/whisker 12 | response: 13 | body: {string: !!python/unicode '{"Package":"whisker","Maintainer":"Edwin de Jonge 14 | <edwindjonge@gmail.com>","License":"GPL-3","Title":"{{mustache}} for R, logicless 15 | templating","Type":"Package","LazyLoad":"yes","Author":"Edwin de Jonge","Description":"logicless 16 | templating, reuse templates in many programming<U+000a>languages including 17 | R","Version":"0.3-2","URL":"http://github.com/edwindj/whisker","Date":"2010-12-23","Collate":"''whisker.R'' 18 | ''section.R'' ''parseTemplate.R'' ''partials.R''<U+000a>''inverted.R'' ''pkg.R'' 19 | ''iteratelist.R'' ''delim.R'' ''markdown.R''<U+000a>''rowsplit.R''","Repository":"CRAN","Date/Publication":"2013-04-28 20 | 07:55:20","crandb_file_date":"2013-04-28 01:55:21","Suggests":{"markdown":"*"},"Packaged":"2013-04-27 21 | 22:49:33 UTC; Edwin","NeedsCompilation":"no","date":"2013-04-28T07:55:20+00:00","releases":["3.0.1","3.0.2","3.0.3","3.1.0","3.1.1"]}'} 22 | headers: 23 | connection: [keep-alive] 24 | content-length: ['827'] 25 | content-type: [application/json] 26 | date: ['Wed, 14 Mar 2018 03:43:47 GMT'] 27 | etag: ['"9DG53ZQA3SWD31JBGQC35B0FH"'] 28 | server: [nginx/1.4.6 (Ubuntu)] 29 | vary: [Accept] 30 | x-couch-request-id: [c392ddd81e] 31 | x-couchdb-body-time: ['0'] 32 | status: {code: 200, message: OK} 33 | version: 1 34 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_cran.CranBackendTests.test_get_version_missing_project: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.11.0 at release-monitoring.org] 10 | method: GET 11 | uri: https://crandb.r-pkg.org/non-existent-name-that-cannot-exist 12 | response: 13 | body: {string: !!python/unicode '{"error":"not_found","reason":"document not found"} 14 | 15 | '} 16 | headers: 17 | cache-control: [must-revalidate] 18 | connection: [keep-alive] 19 | content-length: ['52'] 20 | content-type: [application/json] 21 | date: ['Wed, 14 Mar 2018 03:43:47 GMT'] 22 | server: [nginx/1.4.6 (Ubuntu)] 23 | x-couch-request-id: [7e923005d7] 24 | x-couchdb-body-time: ['0'] 25 | status: {code: 404, message: Object Not Found} 26 | version: 1 27 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_cran.CranBackendTests.test_get_versions: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.11.0 at release-monitoring.org] 10 | method: GET 11 | uri: https://crandb.r-pkg.org/whisker/all 12 | response: 13 | body: {string: !!python/unicode '{"_id":"whisker","_rev":"2-25867cf941bf2cf85daeed4d120df5c1","name":"whisker","versions":{"0.1":{"Package":"whisker","Maintainer":"Edwin 14 | de Jonge <edwindjonge@gmail.com>","License":"GPL-3","Title":"{{mustache}} 15 | for R, logicless templating","Type":"Package","LazyLoad":"yes","Author":"Edwin 16 | de Jonge","Description":"logicless templating, reuse templates in many programming<U+000a>languages 17 | including R","Version":"0.1","URL":"http://github.com/edwindj/whisker","Date":"2010-12-23","Collate":"''whisker.R'' 18 | ''section.R'' ''parseTemplate.R'' ''partials.R''<U+000a>''inverted.R'' ''pkg.R'' 19 | ''iteratelist.R''","Depends":{},"Repository":"CRAN","Date/Publication":"2011-09-05 20 | 19:37:50","crandb_file_date":"2011-09-05 15:37:53","date":"2011-09-05T19:37:50+00:00","releases":["2.13.2","2.14.0","2.14.1","2.14.2","2.15.0","2.15.1","2.15.2","2.15.3","3.0.0"]},"0.3-2":{"Package":"whisker","Maintainer":"Edwin 21 | de Jonge <edwindjonge@gmail.com>","License":"GPL-3","Title":"{{mustache}} 22 | for R, logicless templating","Type":"Package","LazyLoad":"yes","Author":"Edwin 23 | de Jonge","Description":"logicless templating, reuse templates in many programming<U+000a>languages 24 | including R","Version":"0.3-2","URL":"http://github.com/edwindj/whisker","Date":"2010-12-23","Collate":"''whisker.R'' 25 | ''section.R'' ''parseTemplate.R'' ''partials.R''<U+000a>''inverted.R'' ''pkg.R'' 26 | ''iteratelist.R'' ''delim.R'' ''markdown.R''<U+000a>''rowsplit.R''","Repository":"CRAN","Date/Publication":"2013-04-28 27 | 07:55:20","crandb_file_date":"2013-04-28 01:55:21","Suggests":{"markdown":"*"},"Packaged":"2013-04-27 28 | 22:49:33 UTC; Edwin","NeedsCompilation":"no","date":"2013-04-28T07:55:20+00:00","releases":["3.0.1","3.0.2","3.0.3","3.1.0","3.1.1"]}},"timeline":{"0.1":"2011-09-05T19:37:50+00:00","0.3-2":"2013-04-28T07:55:20+00:00"},"latest":"0.3-2","title":"{{mustache}} 29 | for R, logicless templating","archived":false,"revdeps":8}'} 30 | headers: 31 | connection: [keep-alive] 32 | content-length: ['1853'] 33 | content-type: [application/json] 34 | date: ['Wed, 14 Mar 2018 03:43:48 GMT'] 35 | etag: ['"9DG53ZQA3SWD31JBGQC35B0FH"'] 36 | server: [nginx/1.4.6 (Ubuntu)] 37 | vary: [Accept] 38 | x-couch-request-id: [32c7928dfd] 39 | x-couchdb-body-time: ['0'] 40 | status: {code: 200, message: OK} 41 | version: 1 42 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_cran.CranBackendTests.test_get_versions_missing_project: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.11.0 at release-monitoring.org] 10 | method: GET 11 | uri: https://crandb.r-pkg.org/non-existent-name-that-cannot-exist/all 12 | response: 13 | body: {string: !!python/unicode '{"error":"not_found","reason":"document not found"} 14 | 15 | '} 16 | headers: 17 | cache-control: [must-revalidate] 18 | connection: [keep-alive] 19 | content-length: ['52'] 20 | content-type: [application/json] 21 | date: ['Wed, 14 Mar 2018 03:43:48 GMT'] 22 | server: [nginx/1.4.6 (Ubuntu)] 23 | x-couch-request-id: [222d1ea70e] 24 | x-couchdb-body-time: ['0'] 25 | status: {code: 404, message: Object Not Found} 26 | version: 1 27 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_crates.CratesBackendTests.test_get_ordered_versions: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.10.1 at release-monitoring.org] 10 | method: GET 11 | uri: https://crates.io/api/v1/crates/itoa/versions 12 | response: 13 | body: 14 | string: !!binary | 15 | H4sIAAAAAAAAA6WU226DMAyG3yXXQJxwGnkOrjpNKGpSFZUFxKHThHj3GbR2MEFHurs4cWz/X36l 16 | J1ddN3lpGiJee3KsZauJIHlbSuJgqDFWmWxxjwOLXWAuj1IIBXDBkwPmqCKrZHvGBCqrnF4ZnYo0 17 | dKxBweMeo6r8MEUp1Zj+vcR+HF4ccsIOXa0x7AeH5IqIwPfDwCFFbi7jLpFdey5rXD5ocMvB+rrS 18 | RmlzzMeaD64sEp0bhmw236PLdxU4tOnesdOkFAV2lfqT2ac0F41ST7Jo9ODs5B6kACKIRAj7uMMW 19 | dwbr3Flkxx2oNXecaf5Adtx/BDVkwR02uS+Z2XKPXIhcHqacC8ZEuMvvbNvvSRzzZA09T1jMLSw/ 20 | 9rBEP431LPq5pgV6PFhBv4btafTIHYTv77E887Ys77Mt7Hiw+6cZ61tj/4fj53p+YV9z/B37DNkS 21 | +9vwBZMkJG7wBQAA 22 | headers: 23 | connection: [keep-alive] 24 | content-encoding: [gzip] 25 | content-type: [application/json; charset=utf-8] 26 | date: ['Thu, 26 Jan 2017 22:04:29 GMT'] 27 | server: [nginx] 28 | set-cookie: [cargo_session=--3Mz45cH3itfVkmY41+nVjWRm9+0=; HttpOnly; Secure; 29 | Path=/] 30 | strict-transport-security: [max-age=31536000] 31 | via: [1.1 vegur] 32 | status: {code: 200, message: OK} 33 | version: 1 34 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_crates.CratesBackendTests.test_get_version: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | Cookie: [cargo_session=--3Mz45cH3itfVkmY41+nVjWRm9+0=] 9 | From: [admin@fedoraproject.org] 10 | User-Agent: [Anitya 0.10.1 at release-monitoring.org] 11 | method: GET 12 | uri: https://crates.io/api/v1/crates/itoa/versions 13 | response: 14 | body: 15 | string: !!binary | 16 | H4sIAAAAAAAAA6WU226DMAyG3yXXQJxwGnkOrjpNKGpSFZUFxKHThHj3GbR2MEFHurs4cWz/X36l 17 | J1ddN3lpGiJee3KsZauJIHlbSuJgqDFWmWxxjwOLXWAuj1IIBXDBkwPmqCKrZHvGBCqrnF4ZnYo0 18 | dKxBweMeo6r8MEUp1Zj+vcR+HF4ccsIOXa0x7AeH5IqIwPfDwCFFbi7jLpFdey5rXD5ocMvB+rrS 19 | RmlzzMeaD64sEp0bhmw236PLdxU4tOnesdOkFAV2lfqT2ac0F41ST7Jo9ODs5B6kACKIRAj7uMMW 20 | dwbr3Flkxx2oNXecaf5Adtx/BDVkwR02uS+Z2XKPXIhcHqacC8ZEuMvvbNvvSRzzZA09T1jMLSw/ 21 | 9rBEP431LPq5pgV6PFhBv4btafTIHYTv77E887Ys77Mt7Hiw+6cZ61tj/4fj53p+YV9z/B37DNkS 22 | +9vwBZMkJG7wBQAA 23 | headers: 24 | connection: [keep-alive] 25 | content-encoding: [gzip] 26 | content-type: [application/json; charset=utf-8] 27 | date: ['Thu, 26 Jan 2017 22:04:30 GMT'] 28 | server: [nginx] 29 | set-cookie: [cargo_session=--3Mz45cH3itfVkmY41+nVjWRm9+0=; HttpOnly; Secure; 30 | Path=/] 31 | strict-transport-security: [max-age=31536000] 32 | via: [1.1 vegur] 33 | status: {code: 200, message: OK} 34 | version: 1 35 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_crates.CratesBackendTests.test_get_version_missing: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | Cookie: [cargo_session=--3Mz45cH3itfVkmY41+nVjWRm9+0=] 9 | From: [admin@fedoraproject.org] 10 | User-Agent: [Anitya 0.10.1 at release-monitoring.org] 11 | method: GET 12 | uri: https://crates.io/api/v1/crates/pleasedontmakethisprojectitllbreakmytests/versions 13 | response: 14 | body: {string: !!python/unicode '{"errors":[{"detail":"Not Found"}]}'} 15 | headers: 16 | connection: [keep-alive] 17 | content-length: ['35'] 18 | content-type: [application/json; charset=utf-8] 19 | date: ['Thu, 26 Jan 2017 22:04:30 GMT'] 20 | server: [nginx] 21 | set-cookie: [cargo_session=--3Mz45cH3itfVkmY41+nVjWRm9+0=; HttpOnly; Secure; 22 | Path=/] 23 | via: [1.1 vegur] 24 | status: {code: 404, message: Not Found} 25 | version: 1 26 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_crates.CratesBackendTests.test_get_versions: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | Cookie: [cargo_session=--3Mz45cH3itfVkmY41+nVjWRm9+0=] 9 | From: [admin@fedoraproject.org] 10 | User-Agent: [Anitya 0.10.1 at release-monitoring.org] 11 | method: GET 12 | uri: https://crates.io/api/v1/crates/itoa/versions 13 | response: 14 | body: 15 | string: !!binary | 16 | H4sIAAAAAAAAA6WU226DMAyG3yXXQJxwGnkOrjpNKGpSFZUFxKHThHj3GbR2MEFHurs4cWz/X36l 17 | J1ddN3lpGiJee3KsZauJIHlbSuJgqDFWmWxxjwOLXWAuj1IIBXDBkwPmqCKrZHvGBCqrnF4ZnYo0 18 | dKxBweMeo6r8MEUp1Zj+vcR+HF4ccsIOXa0x7AeH5IqIwPfDwCFFbi7jLpFdey5rXD5ocMvB+rrS 19 | RmlzzMeaD64sEp0bhmw236PLdxU4tOnesdOkFAV2lfqT2ac0F41ST7Jo9ODs5B6kACKIRAj7uMMW 20 | dwbr3Flkxx2oNXecaf5Adtx/BDVkwR02uS+Z2XKPXIhcHqacC8ZEuMvvbNvvSRzzZA09T1jMLSw/ 21 | 9rBEP431LPq5pgV6PFhBv4btafTIHYTv77E887Ys77Mt7Hiw+6cZ61tj/4fj53p+YV9z/B37DNkS 22 | +9vwBZMkJG7wBQAA 23 | headers: 24 | connection: [keep-alive] 25 | content-encoding: [gzip] 26 | content-type: [application/json; charset=utf-8] 27 | date: ['Thu, 26 Jan 2017 22:04:31 GMT'] 28 | server: [nginx] 29 | set-cookie: [cargo_session=--3Mz45cH3itfVkmY41+nVjWRm9+0=; HttpOnly; Secure; 30 | Path=/] 31 | strict-transport-security: [max-age=31536000] 32 | via: [1.1 vegur] 33 | status: {code: 200, message: OK} 34 | version: 1 35 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_debian.DebianBackendtests.test_get_versions_http_404: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.10.1 at release-monitoring.org] 10 | method: GET 11 | uri: http://ftp.debian.org/debian/pool/main/f/foo/ 12 | response: 13 | body: {string: !!python/unicode '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> 14 | 15 | <html><head> 16 | 17 | <title>404 Not Found 18 | 19 | 20 | 21 |

Not Found

22 | 23 |

The requested URL /debian/pool/main/f/foo/ was not found on this server.

24 | 25 |
26 | 27 |
Apache Server at ftp.debian.org Port 80
28 | 29 | 30 | 31 | '} 32 | headers: 33 | connection: [Keep-Alive] 34 | content-length: ['285'] 35 | content-type: [text/html; charset=iso-8859-1] 36 | date: ['Wed, 11 Jan 2017 21:14:13 GMT'] 37 | keep-alive: ['timeout=5, max=100'] 38 | server: [Apache] 39 | status: {code: 404, message: Not Found} 40 | version: 1 41 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_debian.DebianBackendtests.test_get_versions_no_z_release: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.10.1 at release-monitoring.org] 10 | method: GET 11 | uri: http://ftp.debian.org/debian/pool/main/libg/libgnupg-interface-perl/ 12 | response: 13 | body: 14 | string: !!binary | 15 | H4sIAAAAAAAAA8WYXW+bMBSG7/crzrjYHdiYjy4pYeqSTovWplGbaZqmqXLAAasEkHG7tr9+JiQa 16 | XZuEREjc5EO8Pnmf43N8FHvvR1fD2c/pOXydXV7A9Pvni/EQNB2hH9YQodFsVD2wDAJfeEoThM4n 17 | mv/Oi+Uy8d+BFzMaqnfwJJcJ88dpyB4hWwAK2ZzTFOVZlqAl5SlK+DxavaT3eaTzVDKxoAHTcyYS 18 | D1XLVUC0jujNs/Cp/CHz+KBq7coana9ilx+F78kYHmjCo3SgySzXfI8vIyhEMNAQD7K0QPOEpndG 19 | xBca0EQOtF/j4dVvpUMyLlf7HoVYsMVA+zQcTE6vBiPNn9Al8xB9W3OpNGeaf0ELCcss5AvOwq3i 20 | m0p8w59rAZHyXfcfZEmRUwVgK1uxqKsqSbgPkQYvCKdn1+eT2Wh8XXGGZYh/trbkXfOnVLBUwogL 21 | FshMPK0dV8s/pPMiP918hbUfwaNYaj6ADtWjA1wH2TIXrChYWPeu8vKm6y1lcYsN29FNo2IyJBVG 22 | 9Kz5h6jrmP9xEWyauol14oLZ65snAFuEjkG+HZyA+/Quzf6kbdAXwX7mImiDlHRKekuTpNy9fbQb 23 | XRvEcGIfTtxecRuZ8tGosOvKVsBdt6OtdrBu1Zv0cSf5a/VuelvHPR2bJb3KwfaWtjql393SG00b 24 | pMQwOyRt0tJ1XRvER1V2Wy3t4KYt/VK5F9zSTRsI7uNd4F2d3g7RnQNa+pV6N71bbrs6yhS9bW+n 25 | N7ul39PSa00bpMQ4Yma1RtqopWu6hsRWH+Pte+v0uiN2D6ps97DKVtOaWEBI39zR1wR3Sr+3st0m 26 | ld2ElBhOh6QNK9ttWtlrYmvnqXVMZbc2rEjjYUWaDytHx7ZOTgD3+pbTdFg1/MeM1hcEHg3DMgP+ 27 | WU6DmMENEw9MAJWwkPmmAzMRwTQTEj5i5Xi9QMVYXVd4qLoX+QsB6Ia/VxEAAA== 28 | headers: 29 | connection: [Keep-Alive] 30 | content-encoding: [gzip] 31 | content-length: ['730'] 32 | content-type: [text/html;charset=UTF-8] 33 | date: ['Wed, 11 Jan 2017 21:14:14 GMT'] 34 | keep-alive: ['timeout=5, max=100'] 35 | server: [Apache] 36 | vary: [Accept-Encoding] 37 | x-clacks-overhead: [GNU Terry Pratchett] 38 | status: {code: 200, message: OK} 39 | version: 1 40 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_gitea.GiteaBackendtests.test_get_versions_project_invalid_owner_repo: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - gzip, deflate 9 | Connection: 10 | - keep-alive 11 | From: 12 | - admin@fedoraproject.org 13 | If-modified-since: 14 | - Thu, 01 Jan 1970 00:00:00 GMT 15 | User-Agent: 16 | - Anitya 1.8.1 at release-monitoring.org 17 | method: GET 18 | uri: https://gitea.com/api/v1/repos/project_with_malformed_url/invalid_project/tags 19 | response: 20 | body: 21 | string: '{"errors":["user redirect does not exist [name: project_with_malformed_url]"],"message":"GetUserByName","url":"https://gitea.com/api/swagger"} 22 | 23 | ' 24 | headers: 25 | Cache-Control: 26 | - max-age=0, private, must-revalidate, no-transform 27 | Connection: 28 | - keep-alive 29 | Content-Length: 30 | - '143' 31 | Content-Type: 32 | - application/json;charset=utf-8 33 | Date: 34 | - Mon, 11 Sep 2023 09:25:46 GMT 35 | Server: 36 | - Caddy 37 | Vary: 38 | - Origin 39 | X-Content-Type-Options: 40 | - nosniff 41 | X-Frame-Options: 42 | - SAMEORIGIN 43 | status: 44 | code: 404 45 | message: Not Found 46 | version: 1 47 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_github.GithubBackendtests.test_gargoyle: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: !!python/unicode '{"query": "\n{\n repository(owner: \"garglk\", name: 4 | \"garglk\") {\n refs (refPrefix: \"refs/tags/\", last:50,\n orderBy:{field:TAG_COMMIT_DATE, 5 | direction:ASC}) {\n totalCount\n edges {\n cursor\n node 6 | {\n name\n target {\n commitUrl\n }\n }\n }\n }\n }\n rateLimit 7 | {\n limit\n remaining\n resetAt\n }\n}"}' 8 | headers: 9 | Accept: ['*/*'] 10 | Accept-Encoding: ['gzip, deflate'] 11 | Authorization: [bearer foobar] 12 | Connection: [keep-alive] 13 | Content-Length: ['526'] 14 | Content-Type: [application/json] 15 | From: [admin@fedoraproject.org] 16 | User-Agent: [Anitya 0.13.2 at release-monitoring.org] 17 | method: POST 18 | uri: https://api.github.com/graphql 19 | response: 20 | body: 21 | string: !!binary | 22 | H4sIAAAAAAAAA62TTWsbQQyG/8uc7VjzoflYyCH02gQK7aWlh/nQbJasvWF3TCnG/71aTKHU4Itz 23 | 0oz0SjyIVydRYouiO4mZ3qdlaNP8+/Kryxrb1OL4aToemujsRlDpifM/TiIf52WaRSeevzw+io04 24 | TIXWhkPccxRLi2mkrQKwEKRjQYtzTzyFW6f9fmjf5pF1r629L91u1w/t9ZgeuLTrWTi+/Q0X8c57 25 | byvakKSx3lMkKEUigquOwKHSKZrqpBTn83nzL11/m85LpfBuOk3Byoi+moq5Mk+lmgrabGPVtlgH 26 | QWUFeEX36zZdAP8BdClm3pALiaz2OScrgRwZUjqiK4nAJqOTz//TvTzdpJPwIO/enKeiChksLhhp 27 | kq/RR5WMKz4iplCcskb7Gq7YbruO2dTdbDWgttKjSWDYZxWUDklbdNWCwZRTAMO+9Fdstz0n5Qfs 28 | jUwGysGR4oVBrOSYpbqEOiiDaBH4JqJXK9tPPgkxx0afB7679QLHywMBgCu0j8NhOPSiMyGENbFQ 29 | e2KhUCD9VsJWhq/gO2M7o7+vE/8ACBNMojYEAAA= 30 | headers: 31 | access-control-allow-origin: ['*'] 32 | access-control-expose-headers: ['ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, 33 | X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, 34 | X-Poll-Interval'] 35 | cache-control: [no-cache] 36 | content-encoding: [gzip] 37 | content-security-policy: [default-src 'none'] 38 | content-type: [application/json; charset=utf-8] 39 | date: ['Fri, 19 Oct 2018 08:07:04 GMT'] 40 | referrer-policy: ['origin-when-cross-origin, strict-origin-when-cross-origin'] 41 | server: [GitHub.com] 42 | status: [200 OK] 43 | strict-transport-security: [max-age=31536000; includeSubdomains; preload] 44 | x-accepted-oauth-scopes: [repo] 45 | x-content-type-options: [nosniff] 46 | x-frame-options: [deny] 47 | x-github-media-type: [github.v4; format=json] 48 | x-github-request-id: ['52D0:1EF5:307D4FD:707BA77:5BC990A7'] 49 | x-oauth-scopes: ['repo:status'] 50 | x-ratelimit-limit: ['5000'] 51 | x-ratelimit-remaining: ['5000'] 52 | x-ratelimit-reset: ['1539938803'] 53 | x-xss-protection: [1; mode=block] 54 | status: {code: 200, message: OK} 55 | version: 1 56 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_version_invalid_unknown_project: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: '{"query": "\n{\n repository(owner: \"foobar\", name: \"bar\") {\n refs 4 | (refPrefix: \"refs/tags/\", orderBy: {field: TAG_COMMIT_DATE, direction: ASC}, 5 | last: 50) {\n totalCount\n edges {\n cursor\n node 6 | {\n name target { commitUrl }\n }\n }\n }\n }\n rateLimit 7 | {\n limit\n remaining\n resetAt\n }\n}"}' 8 | headers: 9 | Accept: 10 | - '*/*' 11 | Accept-Encoding: 12 | - gzip, deflate 13 | Authorization: 14 | - bearer foobar 15 | Connection: 16 | - keep-alive 17 | Content-Length: 18 | - '443' 19 | Content-Type: 20 | - application/json 21 | From: 22 | - admin@fedoraproject.org 23 | If-modified-since: 24 | - Thu, 01 Jan 1970 00:00:00 GMT 25 | User-Agent: 26 | - Anitya 0.17.2 at release-monitoring.org 27 | method: POST 28 | uri: https://api.github.com/graphql 29 | response: 30 | body: 31 | string: '{"data":{"repository":null,"rateLimit":{"limit":5000,"remaining":4999,"resetAt":"2019-12-17T17:52:30Z"}},"errors":[{"type":"NOT_FOUND","path":["repository"],"locations":[{"line":3,"column":5}],"message":"Could 32 | not resolve to a Repository with the name ''bar''."}]}' 33 | headers: 34 | Access-Control-Allow-Origin: 35 | - '*' 36 | Access-Control-Expose-Headers: 37 | - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, 38 | X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, 39 | X-GitHub-Media-Type 40 | Cache-Control: 41 | - no-cache 42 | Content-Security-Policy: 43 | - default-src 'none' 44 | Content-Type: 45 | - application/json; charset=utf-8 46 | Date: 47 | - Tue, 17 Dec 2019 16:52:30 GMT 48 | Referrer-Policy: 49 | - origin-when-cross-origin, strict-origin-when-cross-origin 50 | Server: 51 | - GitHub.com 52 | Status: 53 | - 200 OK 54 | Strict-Transport-Security: 55 | - max-age=31536000; includeSubdomains; preload 56 | Transfer-Encoding: 57 | - chunked 58 | X-Accepted-OAuth-Scopes: 59 | - repo 60 | X-Content-Type-Options: 61 | - nosniff 62 | X-Frame-Options: 63 | - deny 64 | X-GitHub-Media-Type: 65 | - github.v4; format=json 66 | X-GitHub-Request-Id: 67 | - 625C:1B2D:76A45:119EF4:5DF907CD 68 | X-OAuth-Scopes: 69 | - '' 70 | X-RateLimit-Limit: 71 | - '5000' 72 | X-RateLimit-Remaining: 73 | - '4999' 74 | X-RateLimit-Reset: 75 | - '1576605150' 76 | X-XSS-Protection: 77 | - 1; mode=block 78 | content-length: 79 | - '262' 80 | status: 81 | code: 200 82 | message: OK 83 | version: 1 84 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_versions_invalid_unknown_project: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: '{"query": "\n{\n repository(owner: \"foobar\", name: \"bar\") {\n refs 4 | (refPrefix: \"refs/tags/\", orderBy: {field: TAG_COMMIT_DATE, direction: ASC}, 5 | last: 50) {\n totalCount\n edges {\n cursor\n node 6 | {\n name target { commitUrl }\n }\n }\n }\n }\n rateLimit 7 | {\n limit\n remaining\n resetAt\n }\n}"}' 8 | headers: 9 | Accept: 10 | - '*/*' 11 | Accept-Encoding: 12 | - gzip, deflate 13 | Authorization: 14 | - bearer foobar 15 | Connection: 16 | - keep-alive 17 | Content-Length: 18 | - '443' 19 | Content-Type: 20 | - application/json 21 | From: 22 | - admin@fedoraproject.org 23 | If-modified-since: 24 | - Thu, 01 Jan 1970 00:00:00 GMT 25 | User-Agent: 26 | - Anitya 0.17.2 at release-monitoring.org 27 | method: POST 28 | uri: https://api.github.com/graphql 29 | response: 30 | body: 31 | string: '{"data":{"repository":null,"rateLimit":{"limit":5000,"remaining":4990,"resetAt":"2019-12-18T15:26:11Z"}},"errors":[{"type":"NOT_FOUND","path":["repository"],"locations":[{"line":3,"column":5}],"message":"Could 32 | not resolve to a Repository with the name ''bar''."}]}' 33 | headers: 34 | Access-Control-Allow-Origin: 35 | - '*' 36 | Access-Control-Expose-Headers: 37 | - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, 38 | X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, 39 | X-GitHub-Media-Type 40 | Cache-Control: 41 | - no-cache 42 | Content-Security-Policy: 43 | - default-src 'none' 44 | Content-Type: 45 | - application/json; charset=utf-8 46 | Date: 47 | - Wed, 18 Dec 2019 14:28:47 GMT 48 | Referrer-Policy: 49 | - origin-when-cross-origin, strict-origin-when-cross-origin 50 | Server: 51 | - GitHub.com 52 | Status: 53 | - 200 OK 54 | Strict-Transport-Security: 55 | - max-age=31536000; includeSubdomains; preload 56 | Transfer-Encoding: 57 | - chunked 58 | Vary: 59 | - Accept-Encoding 60 | X-Accepted-OAuth-Scopes: 61 | - repo 62 | X-Content-Type-Options: 63 | - nosniff 64 | X-Frame-Options: 65 | - deny 66 | X-GitHub-Media-Type: 67 | - github.v4; format=json 68 | X-GitHub-Request-Id: 69 | - 5898:3842D:5BC507:6F58C8:5DFA379E 70 | X-OAuth-Scopes: 71 | - '' 72 | X-RateLimit-Limit: 73 | - '5000' 74 | X-RateLimit-Remaining: 75 | - '4990' 76 | X-RateLimit-Reset: 77 | - '1576682771' 78 | X-XSS-Protection: 79 | - 1; mode=block 80 | content-length: 81 | - '262' 82 | status: 83 | code: 200 84 | message: OK 85 | version: 1 86 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_versions_no_token: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: !!python/unicode '{"query": "\n{\n repository(owner: \"fedora-infra\", 4 | name: \"fedocal\") {\n refs (refPrefix: \"refs/tags/\", last:50,\n orderBy:{field:TAG_COMMIT_DATE, 5 | direction:ASC}) {\n totalCount\n edges {\n cursor\n node 6 | {\n name\n target {\n commitUrl\n }\n }\n }\n }\n }\n rateLimit 7 | {\n limit\n remaining\n resetAt\n }\n}"}' 8 | headers: 9 | Accept: ['*/*'] 10 | Accept-Encoding: ['gzip, deflate'] 11 | Connection: [keep-alive] 12 | Content-Length: ['533'] 13 | Content-Type: [application/json] 14 | From: [admin@fedoraproject.org] 15 | User-Agent: [Anitya 0.12.1 at release-monitoring.org] 16 | method: POST 17 | uri: https://api.github.com/graphql 18 | response: 19 | body: {string: !!python/unicode '{"message":"This endpoint requires you to be 20 | authenticated.","documentation_url":"https://developer.github.com/v3/#authentication"}'} 21 | headers: 22 | access-control-allow-origin: ['*'] 23 | access-control-expose-headers: ['ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, 24 | X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, 25 | X-Poll-Interval'] 26 | content-length: ['131'] 27 | content-security-policy: [default-src 'none'] 28 | content-type: [application/json; charset=utf-8] 29 | date: ['Fri, 24 Aug 2018 11:44:16 GMT'] 30 | referrer-policy: ['origin-when-cross-origin, strict-origin-when-cross-origin'] 31 | server: [GitHub.com] 32 | status: [401 Unauthorized] 33 | strict-transport-security: [max-age=31536000; includeSubdomains; preload] 34 | x-content-type-options: [nosniff] 35 | x-frame-options: [deny] 36 | x-github-media-type: [github.v4; format=json] 37 | x-github-request-id: ['BF56:23A8:3340315:62F7CF2:5B7FEF90'] 38 | x-ratelimit-limit: ['0'] 39 | x-ratelimit-remaining: ['0'] 40 | x-ratelimit-reset: ['1535114656'] 41 | x-runtime-rack: ['0.011707'] 42 | x-xss-protection: [1; mode=block] 43 | status: {code: 401, message: Unauthorized} 44 | version: 1 45 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_versions_no_version_retrieved: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: !!python/unicode '{"query": "\n{\n repository(owner: \"fedora-infra\", 4 | name: \"fpdc\") {\n refs (refPrefix: \"refs/tags/\", last:50,\n orderBy:{field:TAG_COMMIT_DATE, 5 | direction:ASC}) {\n totalCount\n edges {\n cursor\n node 6 | {\n name\n target {\n commitUrl\n }\n }\n }\n }\n }\n rateLimit 7 | {\n limit\n remaining\n resetAt\n }\n}"}' 8 | headers: 9 | Accept: ['*/*'] 10 | Accept-Encoding: ['gzip, deflate'] 11 | Authorization: [bearer e5f0dbdd06ea34113c3839f32088b42c3fff4186] 12 | Connection: [keep-alive] 13 | Content-Length: ['530'] 14 | Content-Type: [application/json] 15 | From: [admin@fedoraproject.org] 16 | User-Agent: [Anitya 0.13.0 at release-monitoring.org] 17 | method: POST 18 | uri: https://api.github.com/graphql 19 | response: 20 | body: 21 | string: !!binary | 22 | H4sIAAAAAAAAAyWKsQrDMAxE/0VzAnJoSu2tdO3YqaGDIGoQJHax1aEY/XsdMt3du1dhJiUIFTJ/ 23 | UhFN+Xesd9lTk9J6S9+oELADnhdufHqZdZBJ+S6btKvCepQRsWmZN5IocYFw8t7voLBemwgDukuP 24 | vh/w4TCgD+78BDP7A+uYFqmJAAAA 25 | headers: 26 | access-control-allow-origin: ['*'] 27 | access-control-expose-headers: ['ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, 28 | X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, 29 | X-Poll-Interval'] 30 | cache-control: [no-cache] 31 | content-encoding: [gzip] 32 | content-security-policy: [default-src 'none'] 33 | content-type: [application/json; charset=utf-8] 34 | date: ['Thu, 20 Sep 2018 09:09:16 GMT'] 35 | referrer-policy: ['origin-when-cross-origin, strict-origin-when-cross-origin'] 36 | server: [GitHub.com] 37 | status: [200 OK] 38 | strict-transport-security: [max-age=31536000; includeSubdomains; preload] 39 | x-accepted-oauth-scopes: [repo] 40 | x-content-type-options: [nosniff] 41 | x-frame-options: [deny] 42 | x-github-media-type: [github.v4; format=json] 43 | x-github-request-id: ['6827:3A75:905D22:166C410:5BA363BC'] 44 | x-oauth-scopes: ['repo:status'] 45 | x-ratelimit-limit: ['5000'] 46 | x-ratelimit-remaining: ['5000'] 47 | x-ratelimit-reset: ['1537438156'] 48 | x-runtime-rack: ['0.107819'] 49 | x-xss-protection: [1; mode=block] 50 | status: {code: 200, message: OK} 51 | version: 1 52 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_github.GithubBackendtests.test_get_versions_unauthorized: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: !!python/unicode '{"query": "\n{\n repository(owner: \"fedora-infra\", 4 | name: \"fedocal\") {\n refs (refPrefix: \"refs/tags/\", last:50,\n orderBy:{field:TAG_COMMIT_DATE, 5 | direction:ASC}) {\n totalCount\n edges {\n cursor\n node 6 | {\n name\n target {\n commitUrl\n }\n }\n }\n }\n }\n rateLimit 7 | {\n limit\n remaining\n resetAt\n }\n}"}' 8 | headers: 9 | Accept: ['*/*'] 10 | Accept-Encoding: ['gzip, deflate'] 11 | Authorization: [bearer Not_found] 12 | Connection: [keep-alive] 13 | Content-Length: ['533'] 14 | Content-Type: [application/json] 15 | From: [admin@fedoraproject.org] 16 | User-Agent: [Anitya 0.12.1 at release-monitoring.org] 17 | method: POST 18 | uri: https://api.github.com/graphql 19 | response: 20 | body: {string: !!python/unicode '{"message":"Bad credentials","documentation_url":"https://developer.github.com/v4"}'} 21 | headers: 22 | access-control-allow-origin: ['*'] 23 | access-control-expose-headers: ['ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, 24 | X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, 25 | X-Poll-Interval'] 26 | content-length: ['83'] 27 | content-security-policy: [default-src 'none'] 28 | content-type: [application/json; charset=utf-8] 29 | date: ['Fri, 24 Aug 2018 11:44:17 GMT'] 30 | referrer-policy: ['origin-when-cross-origin, strict-origin-when-cross-origin'] 31 | server: [GitHub.com] 32 | status: [401 Unauthorized] 33 | strict-transport-security: [max-age=31536000; includeSubdomains; preload] 34 | x-content-type-options: [nosniff] 35 | x-frame-options: [deny] 36 | x-github-media-type: [github.v4; format=json] 37 | x-github-request-id: ['C4A8:23A5:15B7BE2:314B0A8:5B7FEF90'] 38 | x-ratelimit-limit: ['0'] 39 | x-ratelimit-remaining: ['0'] 40 | x-ratelimit-reset: ['1535114656'] 41 | x-runtime-rack: ['0.011671'] 42 | x-xss-protection: [1; mode=block] 43 | status: {code: 401, message: Unauthorized} 44 | version: 1 45 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_gitlab.GitlabBackendtests.test_get_versions_no_version_retrieved: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.13.0 at release-monitoring.org] 10 | method: GET 11 | uri: https://gitlab.com/api/v4/projects/Shukat%2Fproject_1/repository/tags 12 | response: 13 | body: {string: !!python/unicode '[]'} 14 | headers: 15 | cache-control: ['max-age=0, private, must-revalidate'] 16 | content-length: ['2'] 17 | content-type: [application/json] 18 | date: ['Thu, 20 Sep 2018 09:26:00 GMT'] 19 | etag: [W/"d751713988987e9331980363e24189ce"] 20 | link: ['; 21 | rel="first", ; 22 | rel="last"'] 23 | ratelimit-limit: ['600'] 24 | ratelimit-observed: ['1'] 25 | ratelimit-remaining: ['599'] 26 | ratelimit-reset: ['1537435620'] 27 | ratelimit-resettime: ['Fri, 20 Sep 2018 09:27:00 GMT'] 28 | server: [nginx] 29 | strict-transport-security: [max-age=31536000] 30 | vary: [Origin] 31 | x-content-type-options: [nosniff] 32 | x-frame-options: [SAMEORIGIN] 33 | x-next-page: [''] 34 | x-page: ['1'] 35 | x-per-page: ['20'] 36 | x-prev-page: [''] 37 | x-request-id: [a4373198-a859-4195-b7c2-e414f5ab15a2] 38 | x-runtime: ['0.029071'] 39 | x-total: ['0'] 40 | x-total-pages: ['1'] 41 | status: {code: 200, message: OK} 42 | version: 1 43 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_init.BaseBackendTests.test_call_url_last_change: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - gzip, deflate 9 | Connection: 10 | - keep-alive 11 | From: 12 | - admin@fedoraproject.org 13 | If-modified-since: 14 | - Tue, 07 May 2019 12:36:59 GMT 15 | User-Agent: 16 | - Anitya 0.15.1 at release-monitoring.org 17 | method: GET 18 | uri: https://www.example.com/ 19 | response: 20 | body: 21 | string: '' 22 | headers: 23 | Accept-Ranges: 24 | - bytes 25 | Cache-Control: 26 | - max-age=604800 27 | Date: 28 | - Tue, 07 May 2019 12:37:00 GMT 29 | Etag: 30 | - '"1541025663+ident"' 31 | Expires: 32 | - Tue, 14 May 2019 12:37:00 GMT 33 | Last-Modified: 34 | - Fri, 09 Aug 2013 23:54:35 GMT 35 | Server: 36 | - ECS (dcb/7F18) 37 | Vary: 38 | - Accept-Encoding 39 | X-Cache: 40 | - HIT 41 | status: 42 | code: 304 43 | message: Not Modified 44 | version: 1 45 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_maven.MavenBackendTest.test_dots_in_artifact_id: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.11.0 at release-monitoring.org] 10 | method: GET 11 | uri: https://repo1.maven.org/maven2/org/apache/felix/org.apache.felix.gogo.shell/ 12 | response: 13 | body: {string: !!python/unicode "\n\n\n\n\tCentral\ 14 | \ Repository: org/apache/felix/org.apache.felix.gogo.shell\n\t\n\t\ 16 | \n\n\n\n\t\ 17 |
\n\t\t

org/apache/felix/org.apache.felix.gogo.shell

\n\t
\n\ 18 | \t
\n\t
\n\t\t
\n../\n0.10.0/                         \
20 |         \                  2011-06-16 15:50         -      \n0.12.0/                                           2015-10-01\
22 |         \ 08:23         -      \n0.6.0/  \
23 |         \                                          2010-06-07 16:05         -    \
24 |         \  \n0.6.1/                      \
25 |         \                      2010-09-22 20:20         -      \n0.8.0/                                            2011-01-11\
27 |         \ 20:26         -      \n1.0.0/  \
28 |         \                                          2016-10-11 19:24         -    \
29 |         \  \nmaven-metadata.xml\
30 |         \                                2016-10-17 13:57       507      \nmaven-metadata.xml.md5\
32 |         \                            2016-10-17 13:57        32      \nmaven-metadata.xml.sha1\
34 |         \                           2016-10-17 13:57        40      \n\t\t
\n\ 35 | \t
\n\t
\n\n\n"} 36 | headers: 37 | accept-ranges: [bytes] 38 | age: ['0'] 39 | connection: [keep-alive] 40 | content-length: ['1589'] 41 | content-type: [text/html] 42 | date: ['Sun, 11 Mar 2018 07:01:03 GMT'] 43 | etag: ['"3b60ca901b33661fbd4a29553816568e"'] 44 | fastly-debug-digest: [9fc1dd1aeb4578b98d0d1fbea6f59e98c011c203e5459400902ab3d4a95f49ae] 45 | last-modified: ['Thu, 22 Jun 2017 20:30:25 GMT'] 46 | via: [1.1 varnish, 1.1 varnish] 47 | x-cache: ['HIT, MISS'] 48 | x-cache-hits: ['1, 0'] 49 | x-served-by: ['cache-iad2145-IAD, cache-ord1721-ORD'] 50 | x-timer: ['S1520751664.928479,VS0,VE26'] 51 | status: {code: 200, message: OK} 52 | version: 1 53 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_pagure.PagureBackendtests.test_pagure_get_version: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.5.0 at release-monitoring.org] 10 | method: GET 11 | uri: https://pagure.io/api/0/pagure/git/tags 12 | response: 13 | body: {string: !!python/unicode "{\n \"tags\": [\n \"0.1\",\n \"0.1.1\",\n 14 | \ \"0.1.10\",\n \"0.1.11\",\n \"0.1.12\",\n \"0.1.13\",\n \"0.1.14\",\n 15 | \ \"0.1.15\",\n \"0.1.16\",\n \"0.1.2\",\n \"0.1.3\",\n \"0.1.4\",\n 16 | \ \"0.1.5\",\n \"0.1.6\",\n \"0.1.7\",\n \"0.1.8\",\n \"0.1.9\"\n 17 | \ ]\n}"} 18 | headers: 19 | connection: [Keep-Alive] 20 | content-length: ['244'] 21 | content-type: [application/json] 22 | date: ['Mon, 22 Jun 2015 08:21:07 GMT'] 23 | keep-alive: ['timeout=5, max=100'] 24 | server: [Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_wsgi/3.4 25 | Python/2.7.5] 26 | set-cookie: ['pagure=eyJfcGVybWFuZW50Ijp0cnVlfQ.CGlW8w.YnJnvvpvJFr-_8SFRXRpc0LPCkg; 27 | Expires=Thu, 23-Jul-2015 08:21:07 GMT; Secure; HttpOnly; Path=/'] 28 | strict-transport-security: [max-age=15768000; includeSubDomains; preload] 29 | status: {code: 200, message: OK} 30 | version: 1 31 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_pagure.PagureBackendtests.test_pagure_get_versions: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.5.0 at release-monitoring.org] 10 | method: GET 11 | uri: https://pagure.io/api/0/pagure/git/tags 12 | response: 13 | body: {string: !!python/unicode "{\n \"tags\": [\n \"0.1\",\n \"0.1.1\",\n 14 | \ \"0.1.10\",\n \"0.1.11\",\n \"0.1.12\",\n \"0.1.13\",\n \"0.1.14\",\n 15 | \ \"0.1.15\",\n \"0.1.16\",\n \"0.1.2\",\n \"0.1.3\",\n \"0.1.4\",\n 16 | \ \"0.1.5\",\n \"0.1.6\",\n \"0.1.7\",\n \"0.1.8\",\n \"0.1.9\"\n 17 | \ ]\n}"} 18 | headers: 19 | connection: [Keep-Alive] 20 | content-length: ['244'] 21 | content-type: [application/json] 22 | date: ['Mon, 22 Jun 2015 08:21:29 GMT'] 23 | keep-alive: ['timeout=5, max=100'] 24 | server: [Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_wsgi/3.4 25 | Python/2.7.5] 26 | set-cookie: ['pagure=eyJfcGVybWFuZW50Ijp0cnVlfQ.CGlXCQ.hpWuE0kN64m-hzCmOj6h-Lhwd7Q; 27 | Expires=Thu, 23-Jul-2015 08:21:29 GMT; Secure; HttpOnly; Path=/'] 28 | strict-transport-security: [max-age=15768000; includeSubDomains; preload] 29 | status: {code: 200, message: OK} 30 | version: 1 31 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_rubygems.RubygemsBackendtests.test_get_version: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.11.0 at release-monitoring.org] 10 | method: GET 11 | uri: https://rubygems.org/api/v1/versions/bio/latest.json 12 | response: 13 | body: 14 | string: !!binary | 15 | H4sIAKDRpFoAA6pWKkstKs7Mz1OyUjLUM9UzVKoFAAAA//8DAKl3OMQTAAAA 16 | headers: 17 | accept-ranges: [bytes] 18 | age: ['0'] 19 | cache-control: ['max-age=0, private, must-revalidate'] 20 | connection: [keep-alive] 21 | content-encoding: [gzip] 22 | content-security-policy: ['default-src ''self''; script-src ''self'' https://secure.gaug.es; 23 | style-src ''self'' https://fonts.googleapis.com; img-src ''self'' https://secure.gaug.es 24 | https://gravatar.com https://secure.gravatar.com; font-src ''self'' https://fonts.gstatic.com; 25 | connect-src https://s3-us-west-2.amazonaws.com/rubygems-dumps/; frame-src 26 | https://ghbtns.com'] 27 | content-type: [application/json; charset=utf-8] 28 | date: ['Sun, 11 Mar 2018 06:50:08 GMT'] 29 | etag: ['"24c70a65fc5758e148f6f236f9cc374f"'] 30 | server: [RubyGems.org] 31 | strict-transport-security: [max-age=0] 32 | vary: ['Accept-Encoding,Fastly-SSL'] 33 | via: [1.1 varnish] 34 | x-backend: ['F_Rails 54.186.104.15:443'] 35 | x-cache: [MISS] 36 | x-cache-hits: ['0'] 37 | x-content-type-options: [nosniff] 38 | x-frame-options: [SAMEORIGIN] 39 | x-request-id: [a62e7d3b-f95a-4cbb-81da-31d3b457eb08] 40 | x-runtime: ['0.008426'] 41 | x-served-by: [cache-mdw17324-MDW] 42 | x-timer: ['S1520751009.798984,VS0,VE61'] 43 | x-ua-compatible: ['IE=Edge,chrome=1'] 44 | x-xss-protection: [1; mode=block] 45 | status: {code: 200, message: OK} 46 | version: 1 47 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_rubygems.RubygemsBackendtests.test_get_versions: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: ['gzip, deflate'] 7 | Connection: [keep-alive] 8 | From: [admin@fedoraproject.org] 9 | User-Agent: [Anitya 0.11.0 at release-monitoring.org] 10 | method: GET 11 | uri: https://rubygems.org/api/v1/versions/bio/latest.json 12 | response: 13 | body: 14 | string: !!binary | 15 | H4sIAKHRpFoAA6pWKkstKs7Mz1OyUjLUM9UzVKoFAAAA//8DAKl3OMQTAAAA 16 | headers: 17 | accept-ranges: [bytes] 18 | age: ['0'] 19 | cache-control: ['max-age=0, private, must-revalidate'] 20 | connection: [keep-alive] 21 | content-encoding: [gzip] 22 | content-security-policy: ['default-src ''self''; script-src ''self'' https://secure.gaug.es; 23 | style-src ''self'' https://fonts.googleapis.com; img-src ''self'' https://secure.gaug.es 24 | https://gravatar.com https://secure.gravatar.com; font-src ''self'' https://fonts.gstatic.com; 25 | connect-src https://s3-us-west-2.amazonaws.com/rubygems-dumps/; frame-src 26 | https://ghbtns.com'] 27 | content-type: [application/json; charset=utf-8] 28 | date: ['Sun, 11 Mar 2018 06:50:09 GMT'] 29 | etag: ['"4167a607ffbfac075095edd36e6ae6fc"'] 30 | server: [RubyGems.org] 31 | strict-transport-security: [max-age=0] 32 | vary: ['Accept-Encoding,Fastly-SSL'] 33 | via: [1.1 varnish] 34 | x-backend: ['F_Rails 54.186.104.15:443'] 35 | x-cache: [MISS] 36 | x-cache-hits: ['0'] 37 | x-content-type-options: [nosniff] 38 | x-frame-options: [SAMEORIGIN] 39 | x-request-id: [5aec312c-388d-4943-9455-4c9647c92344] 40 | x-runtime: ['0.006702'] 41 | x-served-by: [cache-mdw17328-MDW] 42 | x-timer: ['S1520751009.094779,VS0,VE58'] 43 | x-ua-compatible: ['IE=Edge,chrome=1'] 44 | x-xss-protection: [1; mode=block] 45 | status: {code: 200, message: OK} 46 | version: 1 47 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.lib.backends.test_sourcehut.SourceHutBackendTests.test_get_versions_project_without_any_release: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - gzip, deflate 9 | Connection: 10 | - keep-alive 11 | From: 12 | - admin@fedoraproject.org 13 | If-modified-since: 14 | - Thu, 01 Jan 1970 00:00:00 GMT 15 | User-Agent: 16 | - Anitya 1.5.1 at release-monitoring.org 17 | method: GET 18 | uri: https://git.sr.ht/~sircmpwn/hare/refs/rss.xml 19 | response: 20 | body: 21 | string: ~sircmpwn/hare refshttps://git.sr.ht/~sircmpwn/hare/refsGit 22 | refs for ~sircmpwn/hareen 23 | headers: 24 | Connection: 25 | - keep-alive 26 | Content-Length: 27 | - '205' 28 | Content-Security-Policy: 29 | - default-src 'none'; style-src 'self' 'unsafe-inline'; img-src * data:; script-src 30 | 'self' 'unsafe-inline' 31 | Content-Type: 32 | - application/rss+xml; charset=utf-8 33 | Date: 34 | - Sun, 25 Sep 2022 19:11:55 GMT 35 | Permissions-Policy: 36 | - interest-cohort=() 37 | Server: 38 | - nginx 39 | Strict-Transport-Security: 40 | - max-age=31536000; includeSubDomains; preload 41 | X-Clacks-Overhead: 42 | - GNU Terry Pratchett 43 | status: 44 | code: 200 45 | message: OK 46 | version: 1 47 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.test_flask_api_v2.LiveAuthenticationTests.test_invalid_project_monitoring_request: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: token=8f0e31e7-2f82-4fd1-9287-2916c01c0f32_x4zSXN2dI-5BwSI19BK9lA24GyEWG62h&client_secret=xKz1P6DbhjGjw1VMPPKyNuGjVvK1viSk&client_id=D-05cd2329-1d6e-448b-8880-1a8576caaf4b&token_type_hint=Bearer 4 | headers: 5 | accept-encoding: ['gzip, deflate'] 6 | content-type: [application/x-www-form-urlencoded] 7 | user-agent: [Python-httplib2/0.9.2 (gzip)] 8 | method: POST 9 | uri: https://iddev.fedorainfracloud.org/openidc/TokenInfo 10 | response: 11 | body: {string: '{"username": "ncoghlan", "token_type": "Bearer", "iss": "https://iddev.fedorainfracloud.org/openidc/", 12 | "exp": "1487936918", "client_id": "D-05cd2329-1d6e-448b-8880-1a8576caaf4b", 13 | "iat": "1487851687", "active": true, "scope": "https://release-monitoring.org/oidc/upstream", 14 | "aud": "D-05cd2329-1d6e-448b-8880-1a8576caaf4b", "sub": "ncoghlan"}'} 15 | headers: 16 | AppServer: [iddev.fedorainfracloud.org] 17 | AppTime: [D=12275] 18 | Cache-Control: ['no-cache, no-store, must-revalidate, private'] 19 | Content-Length: ['340'] 20 | Content-Security-Policy: [frame-options 'deny'] 21 | Content-Type: [application/json] 22 | Date: ['Fri, 24 Feb 2017 10:48:39 GMT'] 23 | Pragma: [no-cache] 24 | Server: [Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_auth_gssapi/1.4.0 25 | mod_wsgi/3.4 Python/2.7.5] 26 | Set-Cookie: ['_ipsilon_session_id=6847368024a4e198320eadfe7f31685b1d9cf0e3; 27 | expires=Fri, 24 Feb 2017 11:48:39 GMT; httponly; Path=/; secure'] 28 | X-Frame-Options: [deny] 29 | status: {code: 200, message: OK} 30 | version: 1 31 | -------------------------------------------------------------------------------- /anitya/tests/request-data/anitya.tests.test_flask_api_v2.LiveAuthenticationTests.test_valid_project_monitoring_request: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: client_id=D-05cd2329-1d6e-448b-8880-1a8576caaf4b&client_secret=xKz1P6DbhjGjw1VMPPKyNuGjVvK1viSk&token_type_hint=Bearer&token=510d8899-5164-4e12-85e2-9ada635801ce_1BuudHniy8WVE_LWaBBHL3ZoVwXHFjf7 4 | headers: 5 | accept-encoding: ['gzip, deflate'] 6 | content-type: [application/x-www-form-urlencoded] 7 | user-agent: [Python-httplib2/0.9.2 (gzip)] 8 | method: POST 9 | uri: https://iddev.fedorainfracloud.org/openidc/TokenInfo 10 | response: 11 | body: {string: '{"username": "ncoghlan", "token_type": "Bearer", "iss": "https://iddev.fedorainfracloud.org/openidc/", 12 | "exp": "1487938811", "client_id": "D-05cd2329-1d6e-448b-8880-1a8576caaf4b", 13 | "iat": "1487935197", "active": true, "scope": "openid email https://release-monitoring.org/oidc/upstream", 14 | "aud": "D-05cd2329-1d6e-448b-8880-1a8576caaf4b", "sub": "ncoghlan"}'} 15 | headers: 16 | AppServer: [iddev.fedorainfracloud.org] 17 | AppTime: [D=12270] 18 | Cache-Control: ['no-cache, no-store, must-revalidate, private'] 19 | Content-Length: ['353'] 20 | Content-Security-Policy: [frame-options 'deny'] 21 | Content-Type: [application/json] 22 | Date: ['Fri, 24 Feb 2017 11:20:12 GMT'] 23 | Pragma: [no-cache] 24 | Server: [Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_auth_gssapi/1.4.0 25 | mod_wsgi/3.4 Python/2.7.5] 26 | Set-Cookie: ['_ipsilon_session_id=2ffe5901e7ec69f39f790056fa8378394ef0b5e2; 27 | expires=Fri, 24 Feb 2017 12:20:12 GMT; httponly; Path=/; secure'] 28 | X-Frame-Options: [deny] 29 | status: {code: 200, message: OK} 30 | - request: 31 | body: access_token=510d8899-5164-4e12-85e2-9ada635801ce_1BuudHniy8WVE_LWaBBHL3ZoVwXHFjf7 32 | headers: 33 | accept-encoding: ['gzip, deflate'] 34 | content-type: [application/x-www-form-urlencoded] 35 | user-agent: [Python-httplib2/0.9.2 (gzip)] 36 | method: POST 37 | uri: https://iddev.fedorainfracloud.org/openidc/UserInfo 38 | response: 39 | body: {string: '{"email": "ncoghlan@redhat.com", "sub": "9ab6fa59a070fa1fb52d5c051712b0af5cd99b70bf1ea5d8411f5c8a0cd2c1df"}'} 40 | headers: 41 | AppServer: [iddev.fedorainfracloud.org] 42 | AppTime: [D=11520] 43 | Cache-Control: ['no-cache, no-store, must-revalidate, private'] 44 | Content-Length: ['107'] 45 | Content-Security-Policy: [frame-options 'deny'] 46 | Content-Type: [application/json] 47 | Date: ['Fri, 24 Feb 2017 11:20:13 GMT'] 48 | Pragma: [no-cache] 49 | Server: [Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_auth_gssapi/1.4.0 50 | mod_wsgi/3.4 Python/2.7.5] 51 | Set-Cookie: ['_ipsilon_session_id=61ae136a20d865ab618325a10452b8974db87396; 52 | expires=Fri, 24 Feb 2017 12:20:13 GMT; httponly; Path=/; secure'] 53 | X-Frame-Options: [deny] 54 | status: {code: 200, message: OK} 55 | version: 1 56 | -------------------------------------------------------------------------------- /anitya/tests/test_alembic.py: -------------------------------------------------------------------------------- 1 | # (c) 2017 - Copyright Red Hat Inc 2 | 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # version 2 as published by the Free Software Foundation. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 15 | # 16 | # Authors: 17 | # Pierre-Yves Chibon 18 | """This test module contains tests for the migration system.""" 19 | 20 | import os 21 | import subprocess 22 | import unittest 23 | 24 | REPO_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) 25 | 26 | 27 | class TestAlembic(unittest.TestCase): 28 | """This test class contains tests pertaining to alembic.""" 29 | 30 | def test_alembic_history(self): 31 | """Enforce a linear alembic history. 32 | 33 | This test runs the `alembic history | grep ' (head), '` command, 34 | and ensure it returns only one line. 35 | """ 36 | proc1 = subprocess.Popen( # pylint: disable=R1732 37 | ["alembic", "history"], cwd=REPO_PATH, stdout=subprocess.PIPE 38 | ) 39 | proc2 = subprocess.Popen( # pylint: disable=R1732 40 | ["grep", " (head), "], stdin=proc1.stdout, stdout=subprocess.PIPE 41 | ) 42 | stdout = proc2.communicate()[0] 43 | stdout = stdout.strip().split(b"\n") 44 | self.assertEqual(len(stdout), 1) 45 | proc1.communicate() 46 | -------------------------------------------------------------------------------- /anitya/tests/test_app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of the Anitya project. 4 | # Copyright (C) 2017 Red Hat, Inc. 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | """Tests for the :mod:`anitya.app` module.""" 20 | 21 | import logging 22 | import unittest 23 | 24 | from sqlalchemy.exc import UnboundExecutionError 25 | 26 | from anitya import app 27 | from anitya.config import config as anitya_config 28 | from anitya.db import Session 29 | 30 | 31 | class CreateTests(unittest.TestCase): 32 | """Tests for the :func:`anitya.app.create` function.""" 33 | 34 | def setUp(self): 35 | # Make sure each test starts with a clean session 36 | Session.remove() 37 | Session.configure(bind=None) 38 | 39 | def test_default_config(self): 40 | """Assert when no configuration is provided, :data:`anitya.config.config` is used.""" 41 | application = app.create() 42 | for key, value in anitya_config.items(): 43 | self.assertEqual(value, application.config[key]) 44 | 45 | def test_email_config(self): 46 | """Assert a SMTPHandler is added to the anitya logger when ``EMAIL_ERRORS=True``.""" 47 | config = { 48 | "DB_URL": "sqlite://", 49 | "EMAIL_ERRORS": True, 50 | "SMTP_SERVER": "smtp.example.com", 51 | "ADMIN_EMAIL": "admin@example.com", 52 | } 53 | anitya_logger = logging.getLogger("anitya") 54 | anitya_logger.handlers = [] 55 | 56 | app.create(config) 57 | 58 | self.assertEqual(1, len(anitya_logger.handlers)) 59 | self.assertEqual("smtp.example.com", anitya_logger.handlers[0].mailhost) 60 | self.assertEqual(["admin@example.com"], anitya_logger.handlers[0].toaddrs) 61 | 62 | def test_db_config(self): 63 | """Assert creating the application configures the scoped session.""" 64 | # Assert the scoped session is not bound. 65 | self.assertRaises(UnboundExecutionError, Session.get_bind) 66 | Session.remove() 67 | 68 | app.create({"DB_URL": "sqlite://"}) 69 | self.assertEqual("sqlite://", str(Session().get_bind().url)) 70 | -------------------------------------------------------------------------------- /anitya/tests/test_reverse_proxy.py: -------------------------------------------------------------------------------- 1 | """Test module for testing anitya.reverse_proxy.""" 2 | 3 | import unittest 4 | 5 | import mock 6 | 7 | from anitya.reverse_proxy import ReverseProxied 8 | 9 | 10 | class ReverseProxiedCallTests(unittest.TestCase): 11 | """Unit tests for the :func:anitya.reverse_proxy.ReverseProxied.__call__.""" 12 | 13 | def setUp(self): 14 | """Setup the tests.""" 15 | self.wsgi_app = mock.Mock() 16 | self.config = {} 17 | self.reverse_proxy = ReverseProxied(self.wsgi_app, self.config) 18 | 19 | def test__call__preferred(self): 20 | """ 21 | Test the correct schema is set when provided in config. 22 | """ 23 | self.reverse_proxy.config = {"PREFERRED_URL_SCHEME": "https"} 24 | self.environ = {} 25 | 26 | self.reverse_proxy.__call__(self.environ, None) 27 | 28 | self.assertEqual(self.environ["wsgi.url_scheme"], "https") 29 | 30 | def test__call__forwarded(self): 31 | """ 32 | Test the correct schema is set when in environ. 33 | """ 34 | self.environ = {"HTTP_X_FORWARDED_PROTO": "https"} 35 | 36 | self.reverse_proxy.__call__(self.environ, None) 37 | 38 | self.assertEqual(self.environ["wsgi.url_scheme"], "https") 39 | 40 | def test__call__default(self): 41 | """ 42 | Test that nothing is set when the value is not configured. 43 | """ 44 | self.environ = {} 45 | 46 | self.reverse_proxy.__call__(self.environ, None) 47 | 48 | self.assertNotIn("wsgi.url_scheme", self.environ) 49 | -------------------------------------------------------------------------------- /anitya/tests/test_wsgi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright © 2017 Red Hat, Inc. 4 | # 5 | # This copyrighted material is made available to anyone wishing to use, 6 | # modify, copy, or redistribute it subject to the terms and conditions 7 | # of the GNU General Public License v.2, or (at your option) any later 8 | # version. This program is distributed in the hope that it will be 9 | # useful, but WITHOUT ANY WARRANTY expressed or implied, including the 10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR 11 | # PURPOSE. See the GNU General Public License for more details. You 12 | # should have received a copy of the GNU General Public License along 13 | # with this program; if not, write to the Free Software Foundation, 14 | # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 15 | # 16 | # Any Red Hat trademarks that are incorporated in the source 17 | # code or documentation are not subject to the GNU General Public 18 | # License and may only be used or replicated with the express permission 19 | # of Red Hat, Inc. 20 | """Test wsgi""" 21 | import unittest 22 | 23 | import flask 24 | 25 | from anitya import wsgi 26 | 27 | 28 | class WsgiTests(unittest.TestCase): 29 | """Tests for the :mod:`wsgi` module.""" 30 | 31 | def test_application_object(self): 32 | """Test application object""" 33 | self.assertTrue(isinstance(wsgi.application, flask.Flask)) 34 | -------------------------------------------------------------------------------- /anitya/wsgi.py: -------------------------------------------------------------------------------- 1 | # This file is part of the Anitya project. 2 | # Copyright (C) 2017 Red Hat, Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | """wsgi""" 18 | from .app import create 19 | 20 | application = create() 21 | -------------------------------------------------------------------------------- /container-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2.0" 2 | 3 | services: 4 | anitya-web: 5 | image: anitya-base 6 | build: 7 | context: . 8 | dockerfile: Containerfile.dev 9 | args: 10 | FEDORA_VERSION: 40 11 | container_name: "anitya-web" 12 | ports: 13 | - "127.0.0.1:5000:5000" 14 | volumes: 15 | - .:/app:z 16 | restart: unless-stopped 17 | environment: 18 | - ANITYA_WEB_CONFIG=anitya.toml 19 | - FLASK_APP=anitya.wsgi 20 | - FLASK_DEBUG=1 21 | - START_COMMAND=flask run -h 0.0.0.0 22 | healthcheck: 23 | test: [ "CMD", "nc", "-z", "localhost", "5000" ] 24 | interval: 3s 25 | timeout: 3s 26 | retries: 30 27 | depends_on: 28 | postgres: 29 | condition: service_started 30 | rabbitmq: 31 | condition: service_started 32 | 33 | anitya-check-service: 34 | image: anitya-base 35 | container_name: "anitya-check-service" 36 | volumes: 37 | - .:/app:z 38 | restart: unless-stopped 39 | environment: 40 | - ANITYA_WEB_CONFIG=anitya.toml 41 | - START_COMMAND=check_service 42 | depends_on: 43 | postgres: 44 | condition: service_started 45 | rabbitmq: 46 | condition: service_started 47 | 48 | rabbitmq: 49 | image: docker.io/library/rabbitmq:3.8.16-management-alpine 50 | container_name: "rabbitmq" 51 | restart: unless-stopped 52 | ports: 53 | - "15672:15672" 54 | healthcheck: 55 | test: [ "CMD", "nc", "-z", "localhost", "5672" ] 56 | interval: 3s 57 | timeout: 3s 58 | retries: 30 59 | environment: 60 | - RABBITMQ_DEFAULT_USER=anitya 61 | - RABBITMQ_DEFAULT_PASS=anitya 62 | 63 | postgres: 64 | image: docker.io/library/postgres:13.4 65 | container_name: "postgres" 66 | user: postgres 67 | volumes: 68 | - .container/dump:/dump:z 69 | healthcheck: 70 | test: ["CMD-SHELL", "pg_isready"] 71 | interval: 3s 72 | timeout: 3s 73 | retries: 30 74 | environment: 75 | - POSTGRES_USER=postgres 76 | - POSTGRES_PASSWORD=anypasswordworkslocally 77 | - POSTGRES_DB=anitya 78 | 79 | -------------------------------------------------------------------------------- /createdb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """createdb""" 4 | import os 5 | from argparse import ArgumentParser 6 | from pathlib import Path 7 | 8 | from alembic import command 9 | from alembic.config import Config 10 | 11 | from anitya.app import create 12 | from anitya.config import load 13 | from anitya.db import Base, Session 14 | 15 | parser = ArgumentParser() 16 | 17 | script_dir = Path(__file__).parent.absolute() 18 | 19 | parser.add_argument("--debug", action="store_true", default=False, dest="verbose") 20 | parser.add_argument("--alembic-config", default=None) 21 | parser.add_argument( 22 | "--db-uri", 23 | default=None, 24 | help="This will override the URLs specified in alembic and anitya", 25 | ) 26 | 27 | args = parser.parse_args() 28 | 29 | alembic_config = args.alembic_config 30 | anitya_config = load() 31 | 32 | anitya_config["SQL_DEBUG"] = args.verbose 33 | 34 | if args.db_uri: 35 | anitya_config["DB_URL"] = args.db_uri 36 | 37 | # An app object is required for social_auth tables to be created properly 38 | anitya_app = create(config=anitya_config) 39 | engine = Session.get_bind() 40 | 41 | Base.metadata.create_all(engine) 42 | 43 | # Set the alembic_version based on the current migrations available. 44 | # This presupposes the models haven't changed outside of a migration. 45 | # 46 | # Default to the side-by-side alembic.ini. 47 | if alembic_config is None: 48 | alembic_config = os.path.join(script_dir, "alembic.ini") 49 | if args.verbose and os.path.isfile(alembic_config): 50 | print(f"No alembic config specified, defaulting to: {alembic_config}") 51 | 52 | if alembic_config and os.path.isfile(alembic_config): 53 | alembic_cfg = Config(alembic_config) 54 | alembic_cfg.set_main_option("sqlalchemy.url", anitya_config["DB_URL"]) 55 | command.stamp(alembic_cfg, "head") 56 | -------------------------------------------------------------------------------- /docs/_templates/layout.html: -------------------------------------------------------------------------------- 1 | 7 | {% extends "!layout.html" %} 8 | {%- block footer %} 9 | 18 | {%- endblock %} 19 | 20 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | API Documentation 3 | ================= 4 | 5 | Anitya provides several APIs for users. 6 | 7 | .. _http-api-v2: 8 | 9 | HTTP API v2 10 | =========== 11 | 12 | The token for API could be obtained from `settings`_ page in 13 | Anitya web interface. This token needs to be provided in 14 | ``Authorization`` header of the request. See request examples 15 | bellow. 16 | 17 | .. autoflask:: anitya.app:create() 18 | :undoc-static: 19 | :modules: anitya.api_v2 20 | :order: path 21 | 22 | 23 | HTTP API v1 24 | =========== 25 | 26 | .. autoflask:: anitya.app:create() 27 | :undoc-static: 28 | :modules: anitya.api 29 | :order: path 30 | 31 | 32 | Python APIs 33 | =========== 34 | 35 | Exceptions 36 | ---------- 37 | 38 | .. automodule:: anitya.lib.exceptions 39 | :members: 40 | :undoc-members: 41 | :show-inheritance: 42 | 43 | Database API 44 | ------------ 45 | 46 | .. automodule:: anitya.db 47 | :members: 48 | :undoc-members: 49 | :show-inheritance: 50 | 51 | anitya.db.meta 52 | ^^^^^^^^^^^^^^ 53 | 54 | .. automodule:: anitya.db.meta 55 | :members: 56 | :undoc-members: 57 | :show-inheritance: 58 | 59 | anitya.db.events 60 | ^^^^^^^^^^^^^^^^ 61 | 62 | .. automodule:: anitya.db.events 63 | :members: 64 | :undoc-members: 65 | :show-inheritance: 66 | 67 | anitya.db.models 68 | ^^^^^^^^^^^^^^^^ 69 | 70 | .. automodule:: anitya.db.models 71 | :noindex: 72 | :members: 73 | :undoc-members: 74 | :show-inheritance: 75 | 76 | Backend API 77 | ----------- 78 | 79 | .. automodule:: anitya.lib.backends 80 | :noindex: 81 | :members: 82 | :undoc-members: 83 | :show-inheritance: 84 | 85 | 86 | Plugin API 87 | ---------- 88 | 89 | .. automodule:: anitya.lib.plugins 90 | :members: 91 | :undoc-members: 92 | :show-inheritance: 93 | 94 | Ecosystem API 95 | ------------- 96 | 97 | .. automodule:: anitya.lib.ecosystems 98 | :noindex: 99 | :members: 100 | :undoc-members: 101 | :show-inheritance: 102 | 103 | Versions API 104 | ------------ 105 | 106 | .. automodule:: anitya.lib.versions.base 107 | :members: 108 | :undoc-members: 109 | :show-inheritance: 110 | 111 | .. _settings: https://release-monitoring.org/settings 112 | -------------------------------------------------------------------------------- /docs/database.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | Database models 3 | =============== 4 | 5 | Click below to explore Anitya's database models: 6 | 7 | .. autosummary:: 8 | :toctree: docblocks 9 | 10 | anitya.db.models 11 | 12 | 13 | Database Schema 14 | =============== 15 | 16 | The Anitya database schema can be seen below. 17 | 18 | .. figure:: images/database.png 19 | :align: center 20 | 21 | Database schema. 22 | -------------------------------------------------------------------------------- /docs/generate_db_schema: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Generate an image of the database schema 4 | """ 5 | from sqlalchemy_schemadisplay import create_schema_graph 6 | 7 | from anitya.config import config 8 | from anitya.db import meta 9 | 10 | 11 | def write_graph(filename): 12 | """ 13 | Creates graph from database classes using 14 | `sqlalchemy_schemadisplay` library. 15 | 16 | Args: 17 | filename (str): Output file where image will be written 18 | """ 19 | graph = create_schema_graph( 20 | engine=meta.initialize(config), 21 | metadata=meta.metadata, 22 | show_datatypes=False, 23 | show_indexes=False, 24 | rankdir="LR", 25 | concentrate=False, 26 | ) 27 | graph.write_png(filename) 28 | 29 | 30 | if __name__ == "__main__": 31 | write_graph("images/database.png") 32 | -------------------------------------------------------------------------------- /docs/glossary.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Glossary 3 | ======== 4 | 5 | .. glossary:: 6 | 7 | ecosystem 8 | Many programming languages now provide a package manager and public 9 | collection of packages for that language. Examples include the Python 10 | Package Index (PyPI), Rust's Cargo, or JavaScript's NPM. 11 | 12 | backend 13 | A backend in Anitya defines how to retrieve versions in a particular 14 | way. For example, some backends might use an API, while others use 15 | regular expressions to extract the versions from a web page. 16 | -------------------------------------------------------------------------------- /docs/images/database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/anitya/1ea6ac8b6a7a4f3d36caf12e36449adf02e4f4df/docs/images/database.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Anitya documentation master file, created by 2 | sphinx-quickstart on Tue Nov 22 14:46:31 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | ====== 7 | Anitya 8 | ====== 9 | 10 | Anitya is a release monitoring project. 11 | 12 | Its goal is to regularly check if a project has made a new release. 13 | When Anitya discovers a new release for a project, it publishes a RabbitMQ message via 14 | `fedora messaging `_. 15 | This makes it easy to integrate with Anitya and perform 16 | actions when a new release is created for a project. For example, the Fedora project 17 | runs a service called `the-new-hotness `_ 18 | which files a Bugzilla bug against a package when the upstream project makes a new release. 19 | 20 | Anitya provides a user-friendly interface to add, edit, or browse projects. 21 | 22 | :Github page: https://github.com/fedora-infra/anitya 23 | 24 | User Guide 25 | ========== 26 | 27 | .. toctree:: 28 | :maxdepth: 2 29 | 30 | user-guide 31 | admin-user-guide 32 | release-notes 33 | glossary 34 | 35 | Admin Guide 36 | =========== 37 | 38 | .. toctree:: 39 | :maxdepth: 2 40 | 41 | admin-guide 42 | integrating-with-anitya 43 | 44 | Developer Guide 45 | =============== 46 | 47 | .. toctree:: 48 | :maxdepth: 2 49 | 50 | api 51 | contributing 52 | database 53 | -------------------------------------------------------------------------------- /docs/integrating-with-anitya.rst: -------------------------------------------------------------------------------- 1 | ======================= 2 | Integrating with Anitya 3 | ======================= 4 | 5 | This chapter describes ways how you can integrate Anitya with your solution. 6 | 7 | 8 | Fedora messaging 9 | ================ 10 | 11 | `Fedora messaging`_ is a message bus. In other words it is a 12 | system that allows for the sending and receiving of notifications between 13 | applications. For anitya, every action made on the application is 14 | announced/broadcasted on this bus, allowing anyone listening to it to act 15 | immediately instead of (for example) polling hourly all the data, looking for 16 | changes, and acting then. 17 | 18 | To start receiving `Fedora messaging`_ messages from anitya, 19 | it is as simple as: 20 | 21 | * install ``Fedora messaging`` the way you want: 22 | 23 | On Fedora :: 24 | 25 | dnf install fedora-messaging 26 | 27 | Via pip :: 28 | 29 | pip install fedora-messaging 30 | 31 | For how to start a local broker for `fedora messaging`. See 32 | `Fedora messaging documentation `_. 33 | 34 | List of fedora messaging topics 35 | ------------------------------- 36 | 37 | This section will list all the `Fedora messaging`_ topics Anitya is 38 | sending and in what situation. 39 | 40 | * *anitya.distro.add* is sent when a new distribution is created. 41 | * *anitya.distro.edit* is sent when an existing distribution is edited. 42 | * *anitya.distro.remove* is sent when an existing distribution is deleted. 43 | * *anitya.project.add* is sent when a new project is added. 44 | * *anitya.project.edit* is sent when an existing project is edited. 45 | * *anitya.project.remove* is sent when an existing project is deleted. 46 | * *anitya.project.flag* is sent when a new flag is created on project. 47 | * *anitya.project.flag.set* is sent when a status of flag is changed. 48 | * *anitya.project.map.new* is sent when a new mapping to distribution 49 | is created on project. 50 | * *anitya.project.map.update* is sent when an existing mapping on project 51 | is edited. 52 | * *anitya.project.map.remove* is sent when an existing mapping on project 53 | is deleted. 54 | * *anitya.project.version.update* is sent when a new release is found for 55 | the project. This topic is now deprecated. 56 | * *anitya.project.version.update.v2* is sent when a new release is found 57 | for the project. This message differentiate between stable and 58 | not stable releases. 59 | * *anitya.project.version.remove* is sent when an existing release is 60 | deleted from project. This topic is now deprecated. 61 | * *anitya.project.version.remove.v2* is sent when an existing release is 62 | deleted from project. Adds support for batch deletion. 63 | 64 | You can found out more about Anitya messages in `Fedora messaging schema`_. 65 | The schema should be used by every consumer that consumes Anitya messages. 66 | 67 | .. _Fedora messaging: https://fedora-messaging.readthedocs.io/en/latest 68 | .. _Fedora messaging schema: https://pypi.org/project/anitya-schema/ 69 | -------------------------------------------------------------------------------- /files/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # path to migration scripts 5 | script_location = /usr/share/anitya/alembic 6 | 7 | # template used to generate migration files 8 | # file_template = %%(rev)s_%%(slug)s 9 | 10 | # max length of characters to apply to the 11 | # "slug" field 12 | #truncate_slug_length = 40 13 | 14 | # set to 'true' to run the environment during 15 | # the 'revision' command, regardless of autogenerate 16 | # revision_environment = false 17 | 18 | # set to 'true' to allow .pyc and .pyo files without 19 | # a source .py file to be detected as revisions in the 20 | # versions/ directory 21 | # sourceless = false 22 | 23 | #sqlalchemy.url = driver://user:pass@localhost/dbname 24 | 25 | 26 | # Logging configuration 27 | [loggers] 28 | keys = root,sqlalchemy,alembic 29 | 30 | [handlers] 31 | keys = console 32 | 33 | [formatters] 34 | keys = generic 35 | 36 | [logger_root] 37 | level = WARN 38 | handlers = console 39 | qualname = 40 | 41 | [logger_sqlalchemy] 42 | level = WARN 43 | handlers = 44 | qualname = sqlalchemy.engine 45 | [logger_alembic] 46 | level = INFO 47 | handlers = 48 | qualname = alembic 49 | 50 | [handler_console] 51 | class = StreamHandler 52 | args = (sys.stderr,) 53 | level = NOTSET 54 | formatter = generic 55 | 56 | [formatter_generic] 57 | format = %(levelname)-5.5s [%(name)s] %(message)s 58 | datefmt = %H:%M:%S 59 | -------------------------------------------------------------------------------- /files/config.toml.sample: -------------------------------------------------------------------------------- 1 | # A sample configuration for fedora-messaging. This file is in the TOML format. 2 | # For complete details on all configuration options, see the documentation. 3 | # https://fedora-messaging.readthedocs.io/en/latest/configuration.html 4 | 5 | amqp_url = "amqp://" 6 | 7 | [tls] 8 | ca_cert = "/etc/pki/tls/certs/ca-bundle.crt" 9 | keyfile = "/my/client/key.pem" 10 | certfile = "/my/client/cert.pem" -------------------------------------------------------------------------------- /mypy.cfg: -------------------------------------------------------------------------------- 1 | # Global options: 2 | 3 | [mypy] 4 | python_version = 3.8 5 | warn_return_any = True 6 | warn_unused_configs = True 7 | ignore_missing_imports = True 8 | strict_optional = True 9 | -------------------------------------------------------------------------------- /news/PR1891.other: -------------------------------------------------------------------------------- 1 | Update documentation for APIv2 2 | -------------------------------------------------------------------------------- /news/_template.rst: -------------------------------------------------------------------------------- 1 | {% macro issue_url(value) -%} 2 | {%- if value.startswith("PR") -%} 3 | `PR#{{ value[2:] }} `_ 4 | {%- elif value.startswith("C") -%} 5 | `{{ value[1:] }} `_ 6 | {%- else -%} 7 | `#{{ value }} `_ 8 | {%- endif -%} 9 | {%- endmacro -%} 10 | 11 | {% for section, _ in sections.items() %} 12 | {% set underline = underlines[0] %}{% if section %}{{section}} 13 | {{ underline * section|length }}{% set underline = underlines[1] %} 14 | 15 | {% endif %} 16 | 17 | {% if sections[section] %} 18 | {% for category, val in definitions.items() if category in sections[section] and category != "author" %} 19 | {{ definitions[category]['name'] }} 20 | {{ underline * definitions[category]['name']|length }} 21 | 22 | {% if definitions[category]['showcontent'] %} 23 | {% for text, values in sections[section][category].items() %} 24 | * {{ text }} 25 | ({% for value in values -%} 26 | {{ issue_url(value) }} 27 | {%- if not loop.last %}, {% endif -%} 28 | {%- endfor %}) 29 | 30 | {% endfor %} 31 | {% else %} 32 | * {{ sections[section][category]['']|sort|join(', ') }} 33 | 34 | {% endif %} 35 | {% if sections[section][category]|length == 0 %} 36 | No significant changes. 37 | 38 | {% else %} 39 | {% endif %} 40 | 41 | {% endfor %} 42 | {% if sections[section]["author"] %} 43 | {{definitions['author']["name"]}} 44 | {{ underline * definitions['author']['name']|length }} 45 | Many thanks to the contributors of bug reports, pull requests, and pull request 46 | reviews for this release: 47 | 48 | {% for text, values in sections[section]["author"].items() %} 49 | * {{ text }} 50 | {% endfor %} 51 | {% endif %} 52 | 53 | {% else %} 54 | No significant changes. 55 | 56 | 57 | {% endif %} 58 | {% endfor %} 59 | -------------------------------------------------------------------------------- /news/get-authors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | This script is browsing through git commit history (starting at latest tag), 5 | collects all authors of commits and creates fragment for `towncrier`_ python module. 6 | 7 | It's meant to be run before creating a final documentation before new release. 8 | 9 | Example: 10 | $ python get_authors.py 11 | 12 | .. _towncrier: 13 | https://github.com/hawkowl/towncrier 14 | 15 | (c) 2018 - Copyright Red Hat Inc 16 | 17 | Authors: 18 | Aurelien Bompard 19 | Michal Konecny 20 | """ 21 | 22 | 23 | from subprocess import check_output 24 | 25 | last_tag = check_output(["git", "describe", "--abbrev=0"], universal_newlines=True) 26 | authors = {} 27 | log_range = last_tag.strip() + "..HEAD" 28 | output = check_output( 29 | ["git", "log", log_range, "--format=%ae\t%an"], universal_newlines=True 30 | ) 31 | for line in output.splitlines(): 32 | email, fullname = line.split("\t") 33 | email = email.split("@")[0].replace(".", "") 34 | if email in authors: 35 | continue 36 | authors[email] = fullname 37 | 38 | for nick, fullname in authors.items(): 39 | with open(f"{nick}.author", mode="w", encoding="UTF-8") as f: 40 | f.write(fullname) 41 | f.write("\n") 42 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base", 5 | "group:allNonMajor", 6 | "schedule:weekdays", 7 | ":preserveSemverRanges" 8 | ], 9 | "docker": { 10 | "enabled": false 11 | }, 12 | "packageRules": [ 13 | { 14 | "matchPackageNames": ["Werkzeug"], 15 | "groupName": null 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /runserver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | An instance of the Flask application. 5 | 6 | This is intended to be run with the Flask CLI for development purposes only:: 7 | 8 | $ export ANITYA_WEB_CONFIG= 9 | $ export FLASK_DEBUG=1 10 | $ export FLASK_APP= 11 | $ flask run --host 0.0.0.0 --port 5000 12 | """ 13 | 14 | from anitya.app import create 15 | 16 | app = create() 17 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = mypy,py310,py311,py312,diff-cover,lint,format,bandit,docs,pre-commit 3 | isolated_build = True 4 | 5 | [testenv] 6 | deps = poetry 7 | allowlist_externals = 8 | rm 9 | pre-commit 10 | commands = 11 | poetry install 12 | rm -rf htmlcov coverage.xml 13 | py.test -vv --cov-config .coveragerc --cov=anitya \ 14 | --cov-report term --cov-report xml --cov-report html {posargs} 15 | 16 | [testenv:diff-cover] 17 | commands = 18 | poetry install 19 | diff-cover coverage.xml --compare-branch=origin/master \ 20 | --exclude debug.py --fail-under=100 21 | 22 | [testenv:docs] 23 | changedir = docs 24 | allowlist_externals = 25 | mkdir 26 | rm 27 | commands= 28 | poetry install 29 | mkdir -p _static 30 | rm -rf _build 31 | mkdir -p images 32 | ./generate_db_schema 33 | sphinx-build -W -b html -d {envtmpdir}/doctrees . _build/html 34 | 35 | [testenv:lint] 36 | commands = 37 | poetry install 38 | python -m flake8 anitya/ {posargs} 39 | 40 | [testenv:format] 41 | commands = 42 | poetry install 43 | python -m black --check --diff {posargs:.} 44 | 45 | [testenv:bandit] 46 | commands = 47 | poetry install 48 | bandit -r anitya/ -x anitya/db/migrations/* -ll 49 | 50 | [testenv:mypy] 51 | commands = 52 | poetry install 53 | mypy --config-file {toxinidir}/mypy.cfg anitya 54 | 55 | [testenv:pre-commit] 56 | commands = 57 | poetry install 58 | pre-commit run --all-files 59 | 60 | [flake8] 61 | show-source = True 62 | max-line-length = 100 63 | ignore = E203,W503 64 | exclude = .git,.tox,dist,*egg,build,files 65 | --------------------------------------------------------------------------------