From a07c1ac63d6d4a86b9db3e13fe096bb3fb48a2cc Mon Sep 17 00:00:00 2001 From: Alexander Todorov Date: Wed, 7 Oct 2020 12:48:56 +0300 Subject: [PATCH 001/106] Add CI job to install on Windows. Refs #134 will fail with: running install running build Didn't find rst2man or rst2man.py ERROR: Command errored out with exit status 1: ... --- .github/workflows/build.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e300acb9..3478536f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,3 +72,24 @@ jobs: ./setup.py rpm - run: mock --root epel-8-x86_64 *.src.rpm + + # Build and install on Windows + windows: + runs-on: windows-latest + strategy: + matrix: + python-version: [3.8] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Build tarball & install + run: | + python setup.py sdist + + pip install --find-links dist python-bugzilla From 301ae4e61f1a180a6350869fa41ede48045e3bd2 Mon Sep 17 00:00:00 2001 From: Alexander Todorov Date: Wed, 7 Oct 2020 20:07:45 +0300 Subject: [PATCH 002/106] Use shutil.which() instead of find_executable(). Refs #134 unfortunately this doesn't seem to work --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 50f2b522..e7e4ca22 100755 --- a/setup.py +++ b/setup.py @@ -2,6 +2,7 @@ import glob import os +import shutil import subprocess import sys @@ -77,10 +78,9 @@ def run(self): class BuildCommand(distutils.command.build.build): def _make_man_pages(self): - from distutils.spawn import find_executable - rstbin = find_executable("rst2man") + rstbin = shutil.which("rst2man") if not rstbin: - rstbin = find_executable("rst2man.py") + rstbin = shutil.which("rst2man.py") if not rstbin: sys.exit("Didn't find rst2man or rst2man.py") From b3767190cd79d9ff25ed123e027322e83e46e1c0 Mon Sep 17 00:00:00 2001 From: Alexander Todorov Date: Wed, 7 Oct 2020 13:58:30 +0300 Subject: [PATCH 003/106] Install without man pages if rst2man not found. Fixes #134 Prior to 68a1dc628ac21a24018710ac3fdba2709aa80441 the man page generation was skipped if it failed, which worked fine for users on both Windows and Linux where rst2man was missing. This commit brings back the same behavior. --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e7e4ca22..9ba486bb 100755 --- a/setup.py +++ b/setup.py @@ -5,6 +5,7 @@ import shutil import subprocess import sys +import warnings import distutils.command.build from distutils.core import Command @@ -82,7 +83,8 @@ def _make_man_pages(self): if not rstbin: rstbin = shutil.which("rst2man.py") if not rstbin: - sys.exit("Didn't find rst2man or rst2man.py") + warnings.warn("Didn't find rst2man or rst2man.py. Installing without man pages") + return for path in glob.glob("man/*.rst"): base = os.path.basename(path) From 119be9df3f04c6ea62eaf7d4b5ce348cc900f60f Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 7 Oct 2020 13:28:39 -0400 Subject: [PATCH 004/106] Prep for release 3.0.1 Signed-off-by: Cole Robinson --- NEWS.md | 3 +++ bugzilla/apiversion.py | 2 +- python-bugzilla.spec | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 90f5ba77..cfd01b0b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # python-bugzilla release news +## Release 3.0.1 (October 07, 2020) +- Skip man page generation to fix build on Windows (Alexander Todorov) + ## Release 3.0.0 (October 03, 2020) - Drop python2 support - New option `bugzilla modify --minor-update option` diff --git a/bugzilla/apiversion.py b/bugzilla/apiversion.py index 34b7f6f0..f5e5ba64 100644 --- a/bugzilla/apiversion.py +++ b/bugzilla/apiversion.py @@ -4,5 +4,5 @@ # This work is licensed under the GNU GPLv2 or later. # See the COPYING file in the top-level directory. -version = "3.0.0" +version = "3.0.1" __version__ = version diff --git a/python-bugzilla.spec b/python-bugzilla.spec index cb79ddfd..f3f36de6 100644 --- a/python-bugzilla.spec +++ b/python-bugzilla.spec @@ -1,5 +1,5 @@ Name: python-bugzilla -Version: 3.0.0 +Version: 3.0.1 Release: 1%{?dist} Summary: Python library for interacting with Bugzilla From 29cc511e455e08927c810e0ae9883f23a6c48c56 Mon Sep 17 00:00:00 2001 From: Jiri Popelka Date: Mon, 12 Oct 2020 14:24:38 +0200 Subject: [PATCH 005/106] Packit config file (.packit.yml) [Packit](https://packit.dev) is a [Github App](https://github.com/marketplace/packit-as-a-service) which helps you integrate this project/package downstream (Fedora). Just add a config (i.e. merge this PR) and [install the Github App](https://packit.dev/packit-as-a-service) in this repo. There are two [jobs](https://packit.dev/docs/configuration/#supported-jobs) in the config file, which tell the service what to do: - `[copr_build]` anytime a PR is created/updated, Packit builds RPM packages in [copr](https://copr.fedorainfracloud.org) and lets you know whether everything's still ok - `[tests]` run tests in [Testing Farm](https://packit.dev/testing-farm), by default a simple installation test is performed For more job types or configuration options, see [documentation](https://packit.dev/docs/configuration/#supported-jobs). --- .packit.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .packit.yml diff --git a/.packit.yml b/.packit.yml new file mode 100644 index 00000000..4140e4a9 --- /dev/null +++ b/.packit.yml @@ -0,0 +1,14 @@ +--- +upstream_project_url: https://github.com/python-bugzilla/python-bugzilla + +jobs: + - job: copr_build + trigger: pull_request + metadata: + targets: + - fedora-all + - job: tests + trigger: pull_request + metadata: + targets: + - fedora-all From 12f314fe9df78e88ea48859a37bc0e2f224a1191 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Mon, 12 Oct 2020 09:13:55 -0400 Subject: [PATCH 006/106] packit: build on epel-8 too Signed-off-by: Cole Robinson --- .packit.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.packit.yml b/.packit.yml index 4140e4a9..af3a2e0f 100644 --- a/.packit.yml +++ b/.packit.yml @@ -7,8 +7,10 @@ jobs: metadata: targets: - fedora-all + - epel-8-x86_64 - job: tests trigger: pull_request metadata: targets: - fedora-all + - epel-8-x86_64 From 06dcf606d8f6b8d0c5faf51afe8b8d1d26a445ad Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Mon, 12 Oct 2020 09:15:04 -0400 Subject: [PATCH 007/106] ci: Drop github RPM build step, packit has us covered now Signed-off-by: Cole Robinson --- .github/workflows/build.yml | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3478536f..f4a643ec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,35 +44,6 @@ jobs: file: ./coverage.xml flags: unittests - # Build the RPM on latest fedora, centos7 and centos8 - rpm: - runs-on: ubuntu-latest - - container: - image: fedora:latest - # All this is needed to ensure 'mock' works in docker - options: --cap-add=SYS_ADMIN --security-opt label:disable --security-opt seccomp=unconfined --security-opt apparmor:unconfined - - steps: - - uses: actions/checkout@v2 - - - name: Install deps - run: | - # glibc-langpacks-en needed to work around python locale issues - dnf install -y \ - python3-pip \ - rpm-build \ - mock \ - dnf-plugins-core \ - glibc-langpack-en - dnf builddep -y ./*.spec - - - name: Build RPM + SRPM - run: | - ./setup.py rpm - - - run: mock --root epel-8-x86_64 *.src.rpm - # Build and install on Windows windows: runs-on: windows-latest From 4189f04c6c2aa1dc49146251491a99c00755a54b Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Mon, 12 Oct 2020 09:40:12 -0400 Subject: [PATCH 008/106] packit: enable RPM tests on commits too Signed-off-by: Cole Robinson --- .packit.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.packit.yml b/.packit.yml index af3a2e0f..52ee1416 100644 --- a/.packit.yml +++ b/.packit.yml @@ -3,7 +3,9 @@ upstream_project_url: https://github.com/python-bugzilla/python-bugzilla jobs: - job: copr_build - trigger: pull_request + trigger: + - pull_request + - commit metadata: targets: - fedora-all From c4476b79022919e1e92fc112b411ad890d6f92c6 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Mon, 12 Oct 2020 09:53:58 -0400 Subject: [PATCH 009/106] packit: fix syntax for running against 'commit' Signed-off-by: Cole Robinson --- .packit.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.packit.yml b/.packit.yml index 52ee1416..7d6b64c9 100644 --- a/.packit.yml +++ b/.packit.yml @@ -3,9 +3,13 @@ upstream_project_url: https://github.com/python-bugzilla/python-bugzilla jobs: - job: copr_build - trigger: - - pull_request - - commit + trigger: pull_request + metadata: + targets: + - fedora-all + - epel-8-x86_64 + - job: copr_build + trigger: commit metadata: targets: - fedora-all From efa0990a2e89c8c97a83803487fafb2052e94e8a Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 3 Nov 2020 11:00:33 -0500 Subject: [PATCH 010/106] Re-add man/bugzilla.1 We will explicitly regenerate this at release time. Dealing with it at build time is a pain Signed-off-by: Cole Robinson --- .gitignore | 1 - man/bugzilla.1 | 621 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 621 insertions(+), 1 deletion(-) create mode 100644 man/bugzilla.1 diff --git a/.gitignore b/.gitignore index 1f832a6b..a3f16c2f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,3 @@ build .coverage .tox .pytest_cache -man/bugzilla.1 diff --git a/man/bugzilla.1 b/man/bugzilla.1 new file mode 100644 index 00000000..fd83d516 --- /dev/null +++ b/man/bugzilla.1 @@ -0,0 +1,621 @@ +.\" Man page generated from reStructuredText. +. +.TH BUGZILLA 1 "" "" "User Commands" +.SH NAME +bugzilla \- command line tool for interacting with Bugzilla +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBbugzilla\fP [\fIoptions\fP] [\fIcommand\fP] [\fIcommand\-options\fP] +.SH DESCRIPTION +.sp +\fBbugzilla\fP is a command line tool for interacting with a Bugzilla +instance over REST or XMLRPC. +.nf + +\fIcommand\fP is one of: +* login \- log into the given bugzilla instance +* new \- create a new bug +* query \- search for bugs matching given criteria +* modify \- modify existing bugs +* attach \- attach files to existing bugs, or get attachments +* info \- get info about the given bugzilla instance +.fi +.sp +.SH GLOBAL OPTIONS +.SS \fB\-\-help, \-h\fP +.sp +\fBSyntax:\fP \fB\-h\fP +.sp +show this help message and exit +.SS \fB\-\-bugzilla\fP +.sp +\fBSyntax:\fP \fB\-\-bugzilla\fP BUGZILLA +.sp +The bugzilla URL. Full API URLs are typically like: +.nf + +* \fI\%https://bugzilla.example.com/xmlrpc.cgi\fP # XMLRPC API +* \fI\%https://bugzilla.example.com/rest/\fP # REST API + +.fi +.sp +.sp +If a non\-specific URL is passed, like \(aqbugzilla.redhat.com\(aq, \fBbugzilla\fP +will try to probe whether the expected XMLRPC or REST path is available, +preferring XMLRPC for backwards compatibility. +.sp +The default URL \fI\%https://bugzilla.redhat.com\fP +.SS \fB\-\-nosslverify\fP +.sp +\fBSyntax:\fP \fB\-\-nosslverify\fP +.sp +Don\(aqt error on invalid bugzilla SSL certificate +.SS \fB\-\-cert\fP +.sp +\fBSyntax:\fP \fB\-\-cert\fP CERTFILE +.sp +client side certificate file needed by the webserver. +.SS \fB\-\-login\fP +.sp +\fBSyntax:\fP \fB\-\-login\fP +.sp +Run interactive "login" before performing the specified command. +.SS \fB\-\-username\fP +.sp +\fBSyntax:\fP \fB\-\-username\fP USERNAME +.sp +Log in with this username +.SS \fB\-\-password\fP +.sp +\fBSyntax:\fP \fB\-\-password\fP PASSWORD +.sp +Log in with this password +.SS \fB\-\-restrict\-login\fP +.sp +\fBSyntax:\fP \fB\-\-restrict\-login\fP +.sp +The session (login token) will be restricted to the current IP +address. +.SS \fB\-\-ensure\-logged\-in\fP +.sp +\fBSyntax:\fP \fB\-\-ensure\-logged\-in\fP +.sp +Raise an error if we aren\(aqt logged in to bugzilla. Consider using +this if you are depending on cached credentials, to ensure that when +they expire the tool errors, rather than subtly change output. +.SS \fB\-\-no\-cache\-credentials\fP +.sp +\fBSyntax:\fP \fB\-\-no\-cache\-credentials\fP +.sp +Don\(aqt save any bugzilla cookies or tokens to disk, and don\(aqt use any +pre\-existing credentials. +.SS \fB\-\-cookiefile\fP +.sp +\fBSyntax:\fP \fB\-\-cookiefile\fP COOKIEFILE +.sp +cookie file to use for bugzilla authentication +.SS \fB\-\-tokenfile\fP +.sp +\fBSyntax:\fP \fB\-\-tokenfile\fP TOKENFILE +.sp +token file to use for bugzilla authentication +.SS \fB\-\-verbose\fP +.sp +\fBSyntax:\fP \fB\-\-verbose\fP +.sp +give more info about what\(aqs going on +.SS \fB\-\-debug\fP +.sp +\fBSyntax:\fP \fB\-\-debug\fP +.sp +output bunches of debugging info +.SS \fB\-\-version\fP +.sp +\fBSyntax:\fP \fB\-\-version\fP +.sp +show program\(aqs version number and exit +.SH STANDARD BUGZILLA OPTIONS +.sp +These options are shared by some combination of the \(aqnew\(aq, \(aqquery\(aq, and +\(aqmodify\(aq sub commands. Not every option works for each command though. +.SS \fB\-p, \-\-product\fP +.sp +\fBSyntax:\fP \fB\-\-product\fP PRODUCT +.sp +Product name +.SS \fB\-v, \-\-version\fP +.sp +\fBSyntax:\fP \fB\-\-version\fP VERSION +.sp +Product version +.SS \fB\-c, \-\-component\fP +.sp +\fBSyntax:\fP \fB\-\-component\fP COMPONENT +.sp +Component name +.SS \fB\-s, \-\-summary\fP +.sp +\fBSyntax:\fP \fB\-\-summary\fP SUMMARY +.sp +Bug summary +.SS \fB\-l, \-\-comment\fP +.sp +\fBSyntax:\fP \fB\-\-comment\fP DESCRIPTION +.sp +Set initial bug comment/description +.SS \fB\-\-comment\-tag\fP +.sp +\fBSyntax:\fP \fB\-\-comment\-tag\fP TAG +.sp +Comment tag for the new comment +.SS \fB\-\-sub\-component\fP +.sp +\fBSyntax:\fP \fB\-\-sub\-component\fP SUB_COMPONENT +.sp +RHBZ sub component name +.SS \fB\-o, \-\-os\fP +.sp +\fBSyntax:\fP \fB\-\-os\fP OS +.sp +Operating system +.SS \fB\-\-arch\fP +.sp +\fBSyntax:\fP \fB\-\-arch\fP ARCH +.sp +Arch this bug occurs on +.SS \fB\-x, \-\-severity\fP +.sp +\fBSyntax:\fP \fB\-\-severity\fP SEVERITY +.sp +Bug severity +.SS \fB\-z, \-\-priority\fP +.sp +\fBSyntax:\fP \fB\-\-priority\fP PRIORITY +.sp +Bug priority +.SS \fB\-\-alias\fP +.sp +\fBSyntax:\fP \fB\-\-alias\fP ALIAS +.sp +Bug alias (name) +.SS \fB\-s, \-\-status\fP +.sp +\fBSyntax:\fP \fB\-\-status\fP STATUS +.sp +Bug status (NEW, ASSIGNED, etc.) +.SS \fB\-u, \-\-url\fP +.sp +\fBSyntax:\fP \fB\-\-url\fP URL +.sp +URL for further bug info +.SS \fB\-m \-\-target_milestone\fP +.sp +\fBSyntax:\fP \fB\-\-target_milestone\fP TARGET_MILESTONE +.sp +Target milestone +.SS \fB\-\-target_release\fP +.sp +\fBSyntax:\fP \fB\-\-target_release\fP TARGET_RELEASE +.sp +RHBZ Target release +.SS \fB\-\-blocked\fP +.sp +\fBSyntax:\fP \fB\&...]\fP +.sp +Bug IDs that this bug blocks +.SS \fB\-\-dependson\fP +.sp +\fBSyntax:\fP \fB\&...]\fP +.sp +Bug IDs that this bug depends on +.SS \fB\-\-keywords\fP +.sp +\fBSyntax:\fP \fB\&...]\fP +.sp +Bug keywords +.SS \fB\-\-groups\fP +.sp +\fBSyntax:\fP \fB\&...]\fP +.sp +Which user groups can view this bug +.SS \fB\-\-cc\fP +.sp +\fBSyntax:\fP \fB\&...]\fP +.sp +CC list +.SS \fB\-a, \-\-assignee, \-\-assigned_to\fP +.sp +\fBSyntax:\fP \fB\-\-assigned_to\fP ASSIGNED_TO +.sp +Bug assignee +.SS \fB\-q, \-\-qa_contact\fP +.sp +\fBSyntax:\fP \fB\-\-qa_contact\fP QA_CONTACT +.sp +QA contact +.SS \fB\-\-flag\fP +.sp +\fBSyntax:\fP \fB\-\-flag\fP FLAG +.sp +Set or unset a flag. For example, to set a flag named devel_ack, do +\-\-flag devel_ack+ Unset a flag with the \(aqX\(aq value, like \-\-flag +needinfoX +.SS \fB\-\-tags\fP +.sp +\fBSyntax:\fP \fB\-\-tags\fP TAG +.sp +Set (personal) tags field +.SS \fB\-w, \-\-whiteboard\fP +.sp +\fBSyntax:\fP \fB\-\-whiteboard\fP WHITEBOARD +.sp +Whiteboard field +.SS \fB\-\-devel_whiteboard\fP +.sp +\fBSyntax:\fP \fB\-\-devel_whiteboard\fP DEVEL_WHITEBOARD +.sp +RHBZ devel whiteboard field +.SS \fB\-\-internal_whiteboard\fP +.sp +\fBSyntax:\fP \fB\-\-internal_whiteboard\fP INTERNAL_WHITEBOARD +.sp +RHBZ internal whiteboard field +.SS \fB\-\-qa_whiteboard\fP +.sp +\fBSyntax:\fP \fB\-\-qa_whiteboard\fP QA_WHITEBOARD +.sp +RHBZ QA whiteboard field +.SS \fB\-F, \-\-fixed_in\fP +.sp +\fBSyntax:\fP \fB\-\-fixed_in\fP FIXED_IN +.sp +RHBZ \(aqFixed in version\(aq field +.SS \fB\-\-field\fP +.sp +\fBSyntax:\fP \fB\-\-field\fP FIELD\(ga\(ga VALUE +.sp +Manually specify a bugzilla API field. FIELD is the raw name used +by the bugzilla instance. For example if your bugzilla instance has a +custom field cf_my_field, do: \-\-field cf_my_field=VALUE +.SH OUTPUT OPTIONS +.sp +These options are shared by several commands, for tweaking the text +output of the command results. +.SS \fB\-f, \-\-full\fP +.sp +\fBSyntax:\fP \fB\-\-full\fP +.sp +output detailed bug info +.SS \fB\-i, \-\-ids\fP +.sp +\fBSyntax:\fP \fB\-\-ids\fP +.sp +output only bug IDs +.SS \fB\-e, \-\-extra\fP +.sp +\fBSyntax:\fP \fB\-\-extra\fP +.sp +output additional bug information (keywords, Whiteboards, etc.) +.SS \fB\-\-oneline\fP +.sp +\fBSyntax:\fP \fB\-\-oneline\fP +.sp +one line summary of the bug (useful for scripts) +.SS \fB\-\-json\fP +.sp +\fBSyntax:\fP \fB\-\-json\fP +.sp +output bug contents in JSON format +.SS \fB\-\-includefield\fP +.sp +\fBSyntax:\fP \fB\-\-includefield\fP +.sp +Pass the field name to bugzilla include_fields list. +Only the fields passed to include_fields are returned +by the bugzilla server. +This can be specified multiple times. +.SS \fB\-\-extrafield\fP +.sp +\fBSyntax:\fP \fB\-\-extrafield\fP +.sp +Pass the field name to bugzilla extra_fields list. +When used with \-\-json this can be used to request +bugzilla to return values for non\-default fields. +This can be specified multiple times. +.SS \fB\-\-excludefield\fP +.sp +\fBSyntax:\fP \fB\-\-excludefield\fP +.sp +Pass the field name to bugzilla exclude_fields list. +When used with \-\-json this can be used to request +bugzilla to not return values for a field. +This can be specified multiple times. +.SS \fB\-\-raw\fP +.sp +\fBSyntax:\fP \fB\-\-raw\fP +.sp +raw output of the bugzilla contents. This format is unstable and +difficult to parse. Please use the \fB\-\-json\fP instead if you want +maximum output from the \fIbugzilla\fP +.SS \fB\-\-outputformat\fP +.sp +\fBSyntax:\fP \fB\-\-outputformat\fP OUTPUTFORMAT +.sp +Print output in the form given. You can use RPM\-style tags that match +bug fields, e.g.: \(aq%{id}: %{summary}\(aq. +.sp +The output of the bugzilla tool should NEVER BE PARSED unless you are +using a custom \-\-outputformat. For everything else, just don\(aqt parse it, +the formats are not stable and are subject to change. +.sp +\-\-outputformat allows printing arbitrary bug data in a user preferred +format. For example, to print a returned bug ID, component, and product, +separated with ::, do: +.sp +\-\-outputformat "%{id}::%{component}::%{product}" +.sp +The fields (like \(aqid\(aq, \(aqcomponent\(aq, etc.) are the names of the values +returned by bugzilla\(aqs API. To see a list of all fields, +check the API documentation in the \(aqSEE ALSO\(aq section. Alternatively, +run a \(aqbugzilla \-\-debug query ...\(aq and look at the key names returned in +the query results. Also, in most cases, using the name of the associated +command line switch should work, like \-\-bug_status becomes +%{bug_status}, etc. +.SH ‘QUERY’ SPECIFIC OPTIONS +.sp +Certain options can accept a comma separated list to query multiple +values, including \-\-status, \-\-component, \-\-product, \-\-version, \-\-id. +.sp +Note: querying via explicit command line options will only get you so +far. See the \-\-from\-url option for a way to use powerful Web UI queries +from the command line. +.SS \fB\-b, \-\-bug_id, \-\-id\fP +.sp +\fBSyntax:\fP \fB\-\-id\fP ID +.sp +specify individual bugs by IDs, separated with commas +.SS \fB\-r, \-\-reporter\fP +.sp +\fBSyntax:\fP \fB\-\-reporter\fP REPORTER +.sp +Email: search reporter email for given address +.SS \fB\-\-quicksearch\fP +.sp +\fBSyntax:\fP \fB\-\-quicksearch\fP QUICKSEARCH +.sp +Search using bugzilla\(aqs quicksearch functionality. +.SS \fB\-\-savedsearch\fP +.sp +\fBSyntax:\fP \fB\-\-savedsearch\fP SAVEDSEARCH +.sp +Name of a bugzilla saved search. If you don\(aqt own this saved search, +you must passed \-\-savedsearch_sharer_id. +.SS \fB\-\-savedsearch\-sharer\-id\fP +.sp +\fBSyntax:\fP \fB\-\-savedsearch\-sharer\-id\fP SAVEDSEARCH_SHARER_ID +.sp +Owner ID of the \-\-savedsearch. You can get this ID from the URL +bugzilla generates when running the saved search from the web UI. +.SS \fB\-\-from\-url\fP +.sp +\fBSyntax:\fP \fB\-\-from\-url\fP WEB_QUERY_URL +.sp +Make a working query via bugzilla\(aqs \(aqAdvanced search\(aq web UI, grab +the url from your browser (the string with query.cgi or buglist.cgi +in it), and \-\-from\-url will run it via the bugzilla API. Don\(aqt forget +to quote the string! This only works for Bugzilla 5 and Red Hat +bugzilla +.SH ‘MODIFY’ SPECIFIC OPTIONS +.sp +Fields that take multiple values have a special input format. +.nf +Append: \fI\%\-\-cc=foo@example.com\fP +Overwrite: \fI\%\-\-cc==foo@example.com\fP +Remove: \fI\%\-\-cc=\-foo@example.com\fP +.fi +.sp +.sp +Options that accept this format: \-\-cc, \-\-blocked, \-\-dependson, \-\-groups, +\-\-tags, whiteboard fields. +.SS \fB\-k, \-\-close RESOLUTION\fP +.sp +\fBSyntax:\fP \fBRESOLUTION\fP +.sp +Close with the given resolution (WONTFIX, NOTABUG, etc.) +.SS \fB\-d, \-\-dupeid\fP +.sp +\fBSyntax:\fP \fB\-\-dupeid\fP ORIGINAL +.sp +ID of original bug. Implies \-\-close DUPLICATE +.SS \fB\-\-private\fP +.sp +\fBSyntax:\fP \fB\-\-private\fP +.sp +Mark new comment as private +.SS \fB\-\-reset\-assignee\fP +.sp +\fBSyntax:\fP \fB\-\-reset\-assignee\fP +.sp +Reset assignee to component default +.SS \fB\-\-reset\-qa\-contact\fP +.sp +\fBSyntax:\fP \fB\-\-reset\-qa\-contact\fP +.sp +Reset QA contact to component default +.SS \fB\-\-minor\-update\fP +.sp +\fBSyntax:\fP \fB\-\-minor\-update\fP +.sp +Request bugzilla to not send any email about this change +.SH ‘NEW’ SPECIFIC OPTIONS +.SS \fB\-\-private\fP +.sp +\fBSyntax:\fP \fB\-\-private\fP +.sp +Mark new comment as private +.SH ‘ATTACH’ OPTIONS +.SS \fB\-f, \-\-file\fP +.sp +\fBSyntax:\fP \fB\-\-file\fP FILENAME +.sp +File to attach, or filename for data provided on stdin +.SS \fB\-d, \-\-description\fP +.sp +\fBSyntax:\fP \fB\-\-description\fP DESCRIPTION +.sp +A short description of the file being attached +.SS \fB\-t, \-\-type\fP +.sp +\fBSyntax:\fP \fB\-\-type\fP MIMETYPE +.sp +Mime\-type for the file being attached +.SS \fB\-g, \-\-get\fP +.sp +\fBSyntax:\fP \fB\-\-get\fP ATTACHID +.sp +Download the attachment with the given ID +.SS \fB\-\-getall\fP +.sp +\fBSyntax:\fP \fB\-\-getall\fP BUGID +.sp +Download all attachments on the given bug +.SS \fB\-\-ignore\-obsolete\fP +.sp +\fBSyntax:\fP \fB\-\-ignore\-obsolete\fP +.sp +Do not download attachments marked as obsolete. +.SS \fB\-l, \-\-comment\fP +.sp +\fBSyntax:\fP \fB\-\-comment\fP COMMENT +.sp +Add comment with attachment +.SH ‘INFO’ OPTIONS +.SS \fB\-p, \-\-products\fP +.sp +\fBSyntax:\fP \fB\-\-products\fP +.sp +Get a list of products +.SS \fB\-c, \-\-components\fP +.sp +\fBSyntax:\fP \fB\-\-components\fP PRODUCT +.sp +List the components in the given product +.SS \fB\-o, \-\-component_owners\fP +.sp +\fBSyntax:\fP \fB\-\-component_owners\fP PRODUCT +.sp +List components (and their owners) +.SS \fB\-v, \-\-versions\fP +.sp +\fBSyntax:\fP \fB\-\-versions\fP PRODUCT +.sp +List the versions for the given product +.SS \fB\-\-active\-components\fP +.sp +\fBSyntax:\fP \fB\-\-active\-components\fP +.sp +Only show active components. Combine with \-\-components* +.SH AUTHENTICATION CACHE AND API KEYS +.sp +Some command usage will require an active login to the bugzilla +instance. For example, if the bugzilla instance has some private bugs, +those bugs will be missing from \(aqquery\(aq output if you do not have an +active login. +.sp +If you are connecting to a bugzilla 5.0 or later instance, the best +option is to use bugzilla API keys. From the bugzilla web UI, log in, +navigate to Preferences\->API Keys, and generate a key (it will be a long +string of characters and numbers). Then create a +~/.config/python\-bugzilla/bugzillarc like this: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +$ cat ~/.config/python\-bugzilla/bugzillarc + +[bugzilla.example.com] +api_key=YOUR_API_KEY +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Replace \(aqbugzilla.example.com\(aq with your bugzilla host name, and +YOUR_API_KEY with the generated API Key from the Web UI. +.sp +Alternatively, you can use \(aqbugzilla login \-\-api\-key\(aq, which will ask +for the API key, and save it to bugzillarc for you. +.sp +For older bugzilla instances, you will need to cache a login cookie or +token with the "login" subcommand or the "\-\-login" argument. +.sp +Additionally, the \-\-no\-cache\-credentials option will tell the bugzilla +tool to \fInot\fP save or use any authentication cache, including the +bugzillarc file. +.SH EXAMPLES +.nf +bugzilla query \-\-bug_id 62037 + +bugzilla query \-\-version 15 \-\-component python\-bugzilla + +bugzilla login + +bugzilla new \-p Fedora \-v rawhide \-c python\-bugzilla \e +.in +2 +\-\-summary "python\-bugzilla causes headaches" \e +\-\-comment "python\-bugzilla made my brain hurt when I used it." + +.in -2 +bugzilla attach \-\-file ~/Pictures/cam1.jpg \-\-desc "me, in pain" +$BUGID + +bugzilla attach \-\-getall $BUGID + +bugzilla modify \-\-close NOTABUG \-\-comment "Actually, you\(aqre +hungover." $BUGID +.fi +.sp +.SH EXIT STATUS +.sp +\fBbugzilla\fP normally returns 0 if the requested command was successful. +Otherwise, exit status is 1 if \fBbugzilla\fP is interrupted by the user +(or a login attempt fails), 2 if a socket error occurs (e.g. TCP +connection timeout), and 3 if the Bugzilla server throws an error. +.SH BUGS +.sp +Please report any bugs as github issues at +\fI\%https://github.com/python\-bugzilla/python\-bugzilla\fP +.SH SEE ALSO +.sp +\fI\%https://bugzilla.readthedocs.io/en/latest/api/index.html\fP +\fI\%https://bugzilla.redhat.com/docs/en/html/api/Bugzilla/WebService/Bug.html\fP +.\" Generated by docutils manpage writer. +. From 948f020a796c88c0b5e5dfdd435ce75a51c98960 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 3 Nov 2020 11:01:46 -0500 Subject: [PATCH 011/106] Don't generate man page at build time Instead add an explicit 'regenerate_manpages' command that I will do before release. Dealing with docutils both at build time, and as part of the setuptools dep chain has proven to be a pain in a variety of ways. Signed-off-by: Cole Robinson --- python-bugzilla.spec | 1 - requirements.txt | 1 - setup.py | 20 ++++++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/python-bugzilla.spec b/python-bugzilla.spec index f3f36de6..51a7d585 100644 --- a/python-bugzilla.spec +++ b/python-bugzilla.spec @@ -9,7 +9,6 @@ Source0: https://github.com/python-bugzilla/python-bugzilla/archive/v%{ve BuildArch: noarch BuildRequires: python3-devel -BuildRequires: python3-docutils BuildRequires: python3-requests BuildRequires: python3-setuptools BuildRequires: python3-pytest diff --git a/requirements.txt b/requirements.txt index f6bdb7bc..f2293605 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ requests -docutils diff --git a/setup.py b/setup.py index 9ba486bb..e32c1c9f 100755 --- a/setup.py +++ b/setup.py @@ -5,9 +5,7 @@ import shutil import subprocess import sys -import warnings -import distutils.command.build from distutils.core import Command from setuptools import setup @@ -77,14 +75,21 @@ def run(self): subprocess.check_call(cmd) -class BuildCommand(distutils.command.build.build): +class ManCommand(Command): + description = ("Regenerate manpages from rst") + user_options = [] + + def initialize_options(self): + pass + def finalize_options(self): + pass + def _make_man_pages(self): rstbin = shutil.which("rst2man") if not rstbin: rstbin = shutil.which("rst2man.py") if not rstbin: - warnings.warn("Didn't find rst2man or rst2man.py. Installing without man pages") - return + raise RuntimeError("Didn't find rst2man or rst2man.py") for path in glob.glob("man/*.rst"): base = os.path.basename(path) @@ -101,7 +106,6 @@ def _make_man_pages(self): def run(self): self._make_man_pages() - distutils.command.build.build.run(self) def _parse_requirements(fname): @@ -135,14 +139,14 @@ def _parse_requirements(fname): 'Programming Language :: Python :: 3.9', ], packages=['bugzilla'], - data_files=[], + data_files=[('share/man/man1', ['man/bugzilla.1'])], entry_points={'console_scripts': ['bugzilla = bugzilla._cli:cli']}, install_requires=_parse_requirements("requirements.txt"), tests_require=_parse_requirements("test-requirements.txt"), cmdclass={ - "build": BuildCommand, + "regenerate_manpages": ManCommand, "pylint": PylintCommand, "rpm": RPMCommand, }, From 4c5a66b3ad34507eeb8c6a1fb16125c300054eb8 Mon Sep 17 00:00:00 2001 From: Ken Dreyer Date: Thu, 5 Nov 2020 13:12:43 -0700 Subject: [PATCH 012/106] bug: remove copied comment from base.py This comment was an accidental copy & paste from commit 72ba03a3dc3a69d4d09db51237f71aa6e6917b3e. --- bugzilla/bug.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bugzilla/bug.py b/bugzilla/bug.py index 5faa28b8..e6c457fc 100644 --- a/bugzilla/bug.py +++ b/bugzilla/bug.py @@ -1,5 +1,3 @@ -# base.py - the base classes etc. for a Python interface to bugzilla -# # Copyright (C) 2007, 2008, 2009, 2010 Red Hat Inc. # Author: Will Woods # From 4fc3636e4a1cfa27510d0f0e1a2833c4f8d57c31 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 12 Nov 2020 11:08:00 -0500 Subject: [PATCH 013/106] xmlrpc: Drop explicit UTF-8 encoding setup I don't think this is required anymore Signed-off-by: Cole Robinson --- bugzilla/_backendxmlrpc.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/bugzilla/_backendxmlrpc.py b/bugzilla/_backendxmlrpc.py index db905541..523a9243 100644 --- a/bugzilla/_backendxmlrpc.py +++ b/bugzilla/_backendxmlrpc.py @@ -44,9 +44,6 @@ def __request_helper(self, url, request_body): response = self.__bugzillasession.request( "POST", url, data=request_body) - # We expect utf-8 from the server - response.encoding = 'UTF-8' - # update/set any cookies self.__bugzillasession.set_response_cookies(response) From 260e4e3ea8d8931d58c2ab10dad832e4207553e4 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 12 Nov 2020 11:08:32 -0500 Subject: [PATCH 014/106] xmlrpc: Only set cookies if request didn't error There shouldn't be anything to set in that case Signed-off-by: Cole Robinson --- bugzilla/_backendxmlrpc.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bugzilla/_backendxmlrpc.py b/bugzilla/_backendxmlrpc.py index 523a9243..af430aa8 100644 --- a/bugzilla/_backendxmlrpc.py +++ b/bugzilla/_backendxmlrpc.py @@ -43,11 +43,9 @@ def __request_helper(self, url, request_body): try: response = self.__bugzillasession.request( "POST", url, data=request_body) + response.raise_for_status() - # update/set any cookies self.__bugzillasession.set_response_cookies(response) - - response.raise_for_status() return self.parse_response(response) except RequestException as e: if not response: From 3f974a6402bc11e0d6d31fc25f17d10c61aa999d Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 12 Nov 2020 12:11:19 -0500 Subject: [PATCH 015/106] session: Centralize xmlrpc vs rest session setup Move it all to the session class, because we need more differences than we currently have Signed-off-by: Cole Robinson --- bugzilla/_backendrest.py | 9 +++------ bugzilla/_backendxmlrpc.py | 4 +--- bugzilla/_session.py | 21 ++++++++++++++++++--- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/bugzilla/_backendrest.py b/bugzilla/_backendrest.py index 43a503f6..275c40da 100644 --- a/bugzilla/_backendrest.py +++ b/bugzilla/_backendrest.py @@ -26,17 +26,14 @@ class _BackendREST(_BackendBase): """ def __init__(self, url, bugzillasession): _BackendBase.__init__(self, url, bugzillasession) - self._bugzillasession.set_content_type("application/json") + self._bugzillasession.set_rest_defaults() ######################### # Internal REST helpers # ######################### - def _handle_response(self, response): - response.raise_for_status() - text = response.text - + def _handle_response(self, text): try: ret = dict(json.loads(text)) except Exception: @@ -60,7 +57,7 @@ def _op(self, method, apiurl, paramdict=None): response = self._bugzillasession.request(method, fullurl, data=data, params=params) - return self._handle_response(response) + return self._handle_response(response.text) def _get(self, *args, **kwargs): return self._op("GET", *args, **kwargs) diff --git a/bugzilla/_backendxmlrpc.py b/bugzilla/_backendxmlrpc.py index af430aa8..500a99c6 100644 --- a/bugzilla/_backendxmlrpc.py +++ b/bugzilla/_backendxmlrpc.py @@ -22,7 +22,7 @@ def __init__(self, bugzillasession): Transport.__init__(self, use_datetime=False) self.__bugzillasession = bugzillasession - self.__bugzillasession.set_content_type("text/xml") + self.__bugzillasession.set_xmlrpc_defaults() self.__seen_valid_xml = False # Override Transport.user_agent @@ -43,9 +43,7 @@ def __request_helper(self, url, request_body): try: response = self.__bugzillasession.request( "POST", url, data=request_body) - response.raise_for_status() - self.__bugzillasession.set_response_cookies(response) return self.parse_response(response) except RequestException as e: if not response: diff --git a/bugzilla/_session.py b/bugzilla/_session.py index 26ff8684..8b76ac16 100644 --- a/bugzilla/_session.py +++ b/bugzilla/_session.py @@ -25,6 +25,7 @@ def __init__(self, url, user_agent, self._cookiecache = cookiecache self._tokencache = tokencache self._api_key = api_key + self._is_xmlrpc = False if self._scheme not in ["http", "https"]: raise Exception("Invalid URL scheme: %s (%s)" % ( @@ -52,6 +53,12 @@ def _get_timeout(self): envtimeout = os.environ.get("PYTHONBUGZILLA_REQUESTS_TIMEOUT") return float(envtimeout or DEFAULT_TIMEOUT) + def set_rest_defaults(self): + self._session.headers["Content-Type"] = "application/json" + def set_xmlrpc_defaults(self): + self._is_xmlrpc = True + self._session.headers["Content-Type"] = "text/xml" + def get_user_agent(self): return self._user_agent def get_scheme(self): @@ -63,8 +70,6 @@ def get_token_value(self): def set_token_value(self, value): self._tokencache.set_value(self._url, value) self._set_tokencache_param() - def set_content_type(self, value): - self._session.headers["Content-Type"] = value def _set_tokencache_param(self): if self._api_key: @@ -89,4 +94,14 @@ def request(self, *args, **kwargs): timeout = self._get_timeout() if "timeout" not in kwargs: kwargs["timeout"] = timeout - return self._session.request(*args, **kwargs) + response = self._session.request(*args, **kwargs) + + if self._is_xmlrpc: + # Yes this still appears to matter for properly decoding unicode + # code points in bugzilla.redhat.com content + response.encoding = "UTF-8" + # Set response cookies + self.set_response_cookies(response) + + response.raise_for_status() + return response From 13b8a058400a6413042eeaf0e1081e7835c421e2 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 12 Nov 2020 12:31:37 -0500 Subject: [PATCH 016/106] session: Only set api_key query param for REST XMLRPC sends it in the POST payload. This makes it less likely that the API key will leak into error messages: https://bugzilla.redhat.com/show_bug.cgi?id=1896791 Signed-off-by: Cole Robinson --- bugzilla/_session.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bugzilla/_session.py b/bugzilla/_session.py index 8b76ac16..c9daa318 100644 --- a/bugzilla/_session.py +++ b/bugzilla/_session.py @@ -42,7 +42,6 @@ def __init__(self, url, user_agent, if sslverify is False: self._session.verify = False self._session.headers["User-Agent"] = self._user_agent - self._session.params["Bugzilla_api_key"] = self._api_key self._set_tokencache_param() def _get_timeout(self): @@ -55,6 +54,10 @@ def _get_timeout(self): def set_rest_defaults(self): self._session.headers["Content-Type"] = "application/json" + # Bugzilla 5.0 only supports api_key as a query parameter. + # Bugzilla 5.1+ takes it as a X-BUGZILLA-API-KEY header as well, + # with query param taking preference. + self._session.params["Bugzilla_api_key"] = self._api_key def set_xmlrpc_defaults(self): self._is_xmlrpc = True self._session.headers["Content-Type"] = "text/xml" From 209ef83486316957793698e66339c07999828012 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 12 Nov 2020 12:59:30 -0500 Subject: [PATCH 017/106] packit: Remove the pull_request trigger On PRs, these jobs fail by default unless I explicitly trigger it with `/packit build`, which is mildly annoying. It's not really important for us to test RPM build + install on the occasional PR, so just do it at commit time. Signed-off-by: Cole Robinson --- .packit.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.packit.yml b/.packit.yml index 7d6b64c9..aefa13b9 100644 --- a/.packit.yml +++ b/.packit.yml @@ -2,12 +2,6 @@ upstream_project_url: https://github.com/python-bugzilla/python-bugzilla jobs: - - job: copr_build - trigger: pull_request - metadata: - targets: - - fedora-all - - epel-8-x86_64 - job: copr_build trigger: commit metadata: @@ -15,7 +9,7 @@ jobs: - fedora-all - epel-8-x86_64 - job: tests - trigger: pull_request + trigger: commit metadata: targets: - fedora-all From f4e980577d05855acbbf48c30dbcf0af8bc26be2 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 12 Nov 2020 19:18:02 -0500 Subject: [PATCH 018/106] session: Scrape API key out of requests exceptions Via the params dictionary this can leak into requests errors, depending on the way it fails. Perform a straight string replacement on the exception string and re-raise the exception https://bugzilla.redhat.com/show_bug.cgi?id=1896791 Signed-off-by: Cole Robinson --- bugzilla/_session.py | 11 ++++++++++- tests/test_ro_functional.py | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/bugzilla/_session.py b/bugzilla/_session.py index c9daa318..d8529200 100644 --- a/bugzilla/_session.py +++ b/bugzilla/_session.py @@ -4,6 +4,8 @@ from logging import getLogger import os +import sys + import requests from ._compatimports import urlparse @@ -97,6 +99,7 @@ def request(self, *args, **kwargs): timeout = self._get_timeout() if "timeout" not in kwargs: kwargs["timeout"] = timeout + response = self._session.request(*args, **kwargs) if self._is_xmlrpc: @@ -106,5 +109,11 @@ def request(self, *args, **kwargs): # Set response cookies self.set_response_cookies(response) - response.raise_for_status() + try: + response.raise_for_status() + except Exception as e: + # Scrape the api key out of the returned exception string + message = str(e).replace(self._api_key or "", "") + raise type(e)(message).with_traceback(sys.exc_info()[2]) + return response diff --git a/tests/test_ro_functional.py b/tests/test_ro_functional.py index 4c3ec476..4c153396 100644 --- a/tests/test_ro_functional.py +++ b/tests/test_ro_functional.py @@ -8,6 +8,7 @@ """ Unit tests that do readonly functional tests against real bugzilla instances. """ +import pytest import bugzilla import tests @@ -63,6 +64,22 @@ def test_rest_xmlrpc_detection(): assert bz.is_xmlrpc() +def test_apikey_error_scraping(): + # Ensure the API key does not leak into any requests exceptions + fakekey = "FOOBARMYKEY" + with pytest.raises(Exception) as e: + _open_bz("https://httpstat.us/502&foo", + force_xmlrpc=True, api_key=fakekey) + assert "400 Client Error" in str(e.value) + assert fakekey not in str(e.value) + + with pytest.raises(Exception) as e: + _open_bz("https://httpstat.us/502&foo", + force_rest=True, api_key=fakekey) + assert "400 Client Error" in str(e.value) + assert fakekey not in str(e.value) + + ################### # mozilla testing # ################### From 6cfe8c5b6fd069a96f9a0f5e405bd40a0bef734c Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 12 Nov 2020 19:39:05 -0500 Subject: [PATCH 019/106] Remove compatimports file We don't need this after dropping python2 support Signed-off-by: Cole Robinson --- bugzilla/_authfiles.py | 19 ++++++++++--------- bugzilla/_backendxmlrpc.py | 4 ++-- bugzilla/_cli.py | 9 +++++---- bugzilla/_compatimports.py | 11 ----------- bugzilla/_session.py | 5 ++--- bugzilla/base.py | 15 +++++++++------ tests/test_cli_misc.py | 6 +++--- 7 files changed, 31 insertions(+), 38 deletions(-) delete mode 100644 bugzilla/_compatimports.py diff --git a/bugzilla/_authfiles.py b/bugzilla/_authfiles.py index 7d746c3f..609b9651 100644 --- a/bugzilla/_authfiles.py +++ b/bugzilla/_authfiles.py @@ -1,11 +1,12 @@ # This work is licensed under the GNU GPLv2 or later. # See the COPYING file in the top-level directory. +import configparser +import http.cookiejar import os from logging import getLogger +import urllib.parse -from ._compatimports import (ConfigParser, LoadError, - MozillaCookieJar, urlparse) from .exceptions import BugzillaError from ._util import listify @@ -15,7 +16,7 @@ def _parse_hostname(url): # If http://example.com is passed, netloc=example.com path="" # If just example.com is passed, netloc="" path=example.com - parsedbits = urlparse(url) + parsedbits = urllib.parse.urlparse(url) return parsedbits.netloc or parsedbits.path @@ -60,7 +61,7 @@ def set_configpaths(self, configpaths): configpaths = [os.path.expanduser(p) for p in listify(configpaths or [])] - cfg = ConfigParser() + cfg = configparser.ConfigParser() read_files = cfg.read(configpaths) if read_files: log.info("Found bugzillarc files: %s", read_files) @@ -118,7 +119,7 @@ def save_api_key(self, url, api_key): config_filename = configpaths[-1] section = _parse_hostname(url) - cfg = ConfigParser() + cfg = configparser.ConfigParser() cfg.read(config_filename) if section not in cfg.sections(): @@ -146,7 +147,7 @@ def __init__(self): self._cfg = None def _get_domain(self, url): - domain = urlparse(url)[1] + domain = urllib.parse.urlparse(url)[1] if domain and domain not in self._cfg.sections(): self._cfg.add_section(domain) return domain @@ -178,7 +179,7 @@ def get_filename(self): def set_filename(self, filename): log.debug("Using tokenfile=%s", filename) - cfg = ConfigParser() + cfg = configparser.ConfigParser() if filename: cfg.read(filename) self._filename = filename @@ -194,7 +195,7 @@ def __init__(self): self._cookiejar = None def _build_cookiejar(self, cookiefile): - cj = MozillaCookieJar(cookiefile) + cj = http.cookiejar.MozillaCookieJar(cookiefile) if (cookiefile is None or not os.path.exists(cookiefile)): return cj @@ -202,7 +203,7 @@ def _build_cookiejar(self, cookiefile): try: cj.load() return cj - except LoadError: + except http.cookiejar.LoadError: msg = "cookiefile=%s not in Mozilla format" % cookiefile raise BugzillaError(msg) from None diff --git a/bugzilla/_backendxmlrpc.py b/bugzilla/_backendxmlrpc.py index 500a99c6..029dee52 100644 --- a/bugzilla/_backendxmlrpc.py +++ b/bugzilla/_backendxmlrpc.py @@ -3,12 +3,12 @@ from logging import getLogger import sys +from xmlrpc.client import (Binary, Fault, ProtocolError, + ServerProxy, Transport) from requests import RequestException from ._backendbase import _BackendBase -from ._compatimports import (Binary, Fault, ProtocolError, - ServerProxy, Transport) from .exceptions import BugzillaError from ._util import listify diff --git a/bugzilla/_cli.py b/bugzilla/_cli.py index 84d516be..22e05ea6 100755 --- a/bugzilla/_cli.py +++ b/bugzilla/_cli.py @@ -21,11 +21,12 @@ import socket import sys import tempfile +import urllib.parse +import xmlrpc.client import requests.exceptions import bugzilla -from bugzilla._compatimports import Fault, ProtocolError, urlparse DEFAULT_BZ = 'https://bugzilla.redhat.com' @@ -1212,7 +1213,7 @@ def _handle_login(opt, action, bz): print("You already have an API key configured for %s" % bz.url) print("There is no need to cache a login token. Exiting.") sys.exit(0) - print("Logging into %s" % urlparse(bz.url)[1]) + print("Logging into %s" % urllib.parse.urlparse(bz.url)[1]) bz.interactive_login(username, password, restrict_login=opt.restrict_login) except bugzilla.BugzillaError as e: @@ -1293,7 +1294,7 @@ def main(unittest_bz_instance=None): except KeyboardInterrupt: print("\nExited at user request.") sys.exit(1) - except (Fault, bugzilla.BugzillaError) as e: + except (xmlrpc.client.Fault, bugzilla.BugzillaError) as e: print("\nServer error: %s" % str(e)) sys.exit(3) except requests.exceptions.SSLError as e: @@ -1307,7 +1308,7 @@ def main(unittest_bz_instance=None): requests.exceptions.HTTPError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL, - ProtocolError) as e: + xmlrpc.client.ProtocolError) as e: print("\nConnection lost/failed: %s" % str(e)) sys.exit(2) diff --git a/bugzilla/_compatimports.py b/bugzilla/_compatimports.py deleted file mode 100644 index 47fcaf79..00000000 --- a/bugzilla/_compatimports.py +++ /dev/null @@ -1,11 +0,0 @@ -# This work is licensed under the GNU GPLv2 or later. -# See the COPYING file in the top-level directory. - -# pylint: disable=unused-import - -from collections.abc import Mapping -from configparser import ConfigParser -from http.cookiejar import LoadError, MozillaCookieJar -from urllib.parse import urlparse, urlunparse, parse_qsl -from xmlrpc.client import (Binary, DateTime, Fault, ProtocolError, - ServerProxy, Transport) diff --git a/bugzilla/_session.py b/bugzilla/_session.py index d8529200..9bc56835 100644 --- a/bugzilla/_session.py +++ b/bugzilla/_session.py @@ -5,11 +5,10 @@ import os import sys +import urllib.parse import requests -from ._compatimports import urlparse - log = getLogger(__name__) @@ -23,7 +22,7 @@ def __init__(self, url, user_agent, tokencache, api_key, requests_session=None): self._url = url self._user_agent = user_agent - self._scheme = urlparse(url)[0] + self._scheme = urllib.parse.urlparse(url)[0] self._cookiecache = cookiecache self._tokencache = tokencache self._api_key = api_key diff --git a/bugzilla/base.py b/bugzilla/base.py index 924b230a..5d469ca8 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -6,12 +6,14 @@ # This work is licensed under the GNU GPLv2 or later. # See the COPYING file in the top-level directory. +import collections import getpass import locale from logging import getLogger import mimetypes import os import sys +import urllib.parse from io import BytesIO @@ -20,7 +22,6 @@ from .apiversion import __version__ from ._backendrest import _BackendREST from ._backendxmlrpc import _BackendXMLRPC -from ._compatimports import Mapping, urlparse, urlunparse, parse_qsl from .bug import Bug, Group, User from .exceptions import BugzillaError from ._rhconverters import _RHBugzillaConverters @@ -34,7 +35,7 @@ def _nested_update(d, u): # Helper for nested dict update() for k, v in list(u.items()): - if isinstance(v, Mapping): + if isinstance(v, collections.abc.Mapping): d[k] = _nested_update(d.get(k, {}), v) else: d[k] = v @@ -110,13 +111,13 @@ def url_to_query(url): # pylint: disable=unpacking-non-sequence (ignore1, ignore2, path, - ignore, query, ignore3) = urlparse(url) + ignore, query, ignore3) = urllib.parse.urlparse(url) base = os.path.basename(path) if base not in ('buglist.cgi', 'query.cgi'): return {} - for (k, v) in parse_qsl(query): + for (k, v) in urllib.parse.parse_qsl(query): if k not in q: q[k] = v elif isinstance(q[k], list): @@ -141,7 +142,8 @@ def fix_url(url, force_rest=False): :param force_rest: If True, generate a REST API url """ - scheme, netloc, path, params, query, fragment = urlparse(url) + (scheme, netloc, path, + params, query, fragment) = urllib.parse.urlparse(url) if not scheme: scheme = 'https' @@ -154,7 +156,8 @@ def fix_url(url, force_rest=False): if force_rest: path = "rest/" - newurl = urlunparse((scheme, netloc, path, params, query, fragment)) + newurl = urllib.parse.urlunparse( + (scheme, netloc, path, params, query, fragment)) return newurl @staticmethod diff --git a/tests/test_cli_misc.py b/tests/test_cli_misc.py index 2955a82e..70f49bf6 100644 --- a/tests/test_cli_misc.py +++ b/tests/test_cli_misc.py @@ -12,12 +12,12 @@ import base64 import datetime import json +import xmlrpc.client import pytest import requests import bugzilla -from bugzilla._compatimports import Binary, DateTime import tests import tests.mockbackend @@ -111,8 +111,8 @@ def test_json_xmlrpc(run_cli): bugid = 1165434 data = {"bugs": [{ 'id': bugid, - 'timetest': DateTime(dateobj), - 'binarytest': Binary(attachdata), + 'timetest': xmlrpc.client.DateTime(dateobj), + 'binarytest': xmlrpc.client.Binary(attachdata), }]} fakebz = tests.mockbackend.make_bz( From 1852b7712f84fb9d499df389c1f06c12ffa69e3b Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 12 Nov 2020 20:28:20 -0500 Subject: [PATCH 020/106] Prep for release 3.0.2 Signed-off-by: Cole Robinson --- NEWS.md | 3 +++ bugzilla/apiversion.py | 2 +- python-bugzilla.spec | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index cfd01b0b..8f070b90 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # python-bugzilla release news +## Release 3.0.2 (November 12, 2020) +- Fix API key leaking into requests exceptions + ## Release 3.0.1 (October 07, 2020) - Skip man page generation to fix build on Windows (Alexander Todorov) diff --git a/bugzilla/apiversion.py b/bugzilla/apiversion.py index f5e5ba64..26bfecb5 100644 --- a/bugzilla/apiversion.py +++ b/bugzilla/apiversion.py @@ -4,5 +4,5 @@ # This work is licensed under the GNU GPLv2 or later. # See the COPYING file in the top-level directory. -version = "3.0.1" +version = "3.0.2" __version__ = version diff --git a/python-bugzilla.spec b/python-bugzilla.spec index 51a7d585..236fb9f4 100644 --- a/python-bugzilla.spec +++ b/python-bugzilla.spec @@ -1,5 +1,5 @@ Name: python-bugzilla -Version: 3.0.1 +Version: 3.0.2 Release: 1%{?dist} Summary: Python library for interacting with Bugzilla From 051b2a0e3b7a7a7cae71e136c378cb93d6818248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Mon, 8 Feb 2021 12:29:34 +0100 Subject: [PATCH 021/106] Reference the manual page from the token deprecation message Related to https://github.com/python-bugzilla/python-bugzilla/issues/93#issuecomment-774097863 --- bugzilla/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bugzilla/base.py b/bugzilla/base.py index 5d469ca8..76d9b4e5 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -678,7 +678,8 @@ def interactive_login(self, user=None, password=None, force=False, msg += " Token cache saved to %s" % self.tokenfile if self._get_version() >= 5.0: msg += "\nToken usage is deprecated. " - msg += "Consider using bugzilla API keys instead." + msg += "Consider using bugzilla API keys instead. " + msg += "See `man bugzilla` for more details." print(msg) def logout(self): From d4b1861156b06074ae50c7b249a9e91052443547 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 6 Apr 2021 12:46:27 -0400 Subject: [PATCH 022/106] pylint: Exclude use-a-generator Signed-off-by: Cole Robinson --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 23122933..db2f2c95 100644 --- a/.pylintrc +++ b/.pylintrc @@ -7,7 +7,7 @@ persistent=no # can either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). -disable=Design,Format,Similarities,invalid-name,missing-docstring,locally-disabled,unnecessary-lambda,star-args,fixme,global-statement,broad-except,no-self-use,bare-except,locally-enabled,wrong-import-position,consider-using-ternary,len-as-condition,no-else-return,useless-object-inheritance,inconsistent-return-statements,consider-using-dict-comprehension,import-outside-toplevel +disable=Design,Format,Similarities,invalid-name,missing-docstring,locally-disabled,unnecessary-lambda,star-args,fixme,global-statement,broad-except,no-self-use,bare-except,locally-enabled,wrong-import-position,consider-using-ternary,len-as-condition,no-else-return,useless-object-inheritance,inconsistent-return-statements,consider-using-dict-comprehension,import-outside-toplevel,use-a-generator enable=fixme From 370eddbe175bb24a4abb37d2fd45fe75219b6c44 Mon Sep 17 00:00:00 2001 From: Ivan Lausuch Date: Mon, 22 Feb 2021 16:57:33 +0100 Subject: [PATCH 023/106] Add limit as option to build_query Problem: In queries where the number of bugs are too large, usually we get a timeout Solution: bugzilla API provides a limit option in queries. This commit adds this option to the build_query method --- bugzilla/base.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bugzilla/base.py b/bugzilla/base.py index 76d9b4e5..43d7a323 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -1197,7 +1197,8 @@ def build_query(self, sub_component=None, tags=None, exclude_fields=None, - extra_fields=None): + extra_fields=None, + limit=None): """ Build a query string from passed arguments. Will handle query parameter differences between various bugzilla versions. @@ -1230,6 +1231,7 @@ def build_query(self, "quicksearch": quicksearch, "savedsearch": savedsearch, "sharer_id": savedsearch_sharer_id, + "limit": limit, # RH extensions... don't add any more. See comment below "sub_components": listify(sub_component), From 4bb6f156288d82c9dc6d8d56e330705f4806f176 Mon Sep 17 00:00:00 2001 From: Nikolas Komonen <32624665+NikolasKomonen@users.noreply.github.com> Date: Thu, 13 May 2021 18:35:05 -0400 Subject: [PATCH 024/106] Update Documentation Links for External Bugs --- bugzilla/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bugzilla/base.py b/bugzilla/base.py index 43d7a323..8c1ea590 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -1990,7 +1990,7 @@ def add_external_tracker(self, bug_ids, ext_bz_bug_id, ext_type_id=None, ExternalBugs::WebService::add_external_bug method. This is documented at - https://bugzilla.redhat.com/docs/en/html/api/extensions/ExternalBugs/lib/WebService.html#add_external_bug + https://bugzilla.redhat.com/docs/en/html/integrating/api/Bugzilla/Extension/ExternalBugs/WebService.html#add-external-bug bug_ids: A single bug id or list of bug ids to have external trackers added. @@ -2033,7 +2033,7 @@ def update_external_tracker(self, ids=None, ext_type_id=None, ExternalBugs::WebService::update_external_bug method. This is documented at - https://bugzilla.redhat.com/docs/en/html/api/extensions/ExternalBugs/lib/WebService.html#update_external_bug + https://bugzilla.redhat.com/docs/en/html/integrating/api/Bugzilla/Extension/ExternalBugs/WebService.html#update-external-bug ids: A single external tracker bug id or list of external tracker bug ids. @@ -2078,7 +2078,7 @@ def remove_external_tracker(self, ids=None, ext_type_id=None, ExternalBugs::WebService::remove_external_bug method. This is documented at - https://bugzilla.redhat.com/docs/en/html/api/extensions/ExternalBugs/lib/WebService.html#remove_external_bug + https://bugzilla.redhat.com/docs/en/html/integrating/api/Bugzilla/Extension/ExternalBugs/WebService.html#remove-external-bug ids: A single external tracker bug id or list of external tracker bug ids. From d2803aa680735ff3df8cf8ac2a48098b61387aed Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sun, 23 May 2021 16:46:43 -0400 Subject: [PATCH 025/106] setup: Drop distutils direct usage Signed-off-by: Cole Robinson --- setup.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index e32c1c9f..38ce974d 100755 --- a/setup.py +++ b/setup.py @@ -6,8 +6,7 @@ import subprocess import sys -from distutils.core import Command -from setuptools import setup +import setuptools def get_version(): @@ -17,7 +16,7 @@ def get_version(): return eval(line.split('=')[-1]) # pylint: disable=eval-used -class PylintCommand(Command): +class PylintCommand(setuptools.Command): user_options = [] def initialize_options(self): @@ -52,7 +51,7 @@ def run(self): pylint.lint.Run(files + pylint_opts) -class RPMCommand(Command): +class RPMCommand(setuptools.Command): description = ("Build src and binary rpms and output them " "in the source directory") user_options = [] @@ -75,7 +74,7 @@ def run(self): subprocess.check_call(cmd) -class ManCommand(Command): +class ManCommand(setuptools.Command): description = ("Regenerate manpages from rst") user_options = [] @@ -117,7 +116,7 @@ def _parse_requirements(fname): return ret -setup( +setuptools.setup( name='python-bugzilla', version=get_version(), description='Library and command line tool for interacting with Bugzilla', From 77e9094e6cb7f5b2de01b2de3a82b7e561000a81 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sun, 23 May 2021 16:47:00 -0400 Subject: [PATCH 026/106] pylint: Disable consider-using-with Signed-off-by: Cole Robinson --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index db2f2c95..182a9a87 100644 --- a/.pylintrc +++ b/.pylintrc @@ -7,7 +7,7 @@ persistent=no # can either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). -disable=Design,Format,Similarities,invalid-name,missing-docstring,locally-disabled,unnecessary-lambda,star-args,fixme,global-statement,broad-except,no-self-use,bare-except,locally-enabled,wrong-import-position,consider-using-ternary,len-as-condition,no-else-return,useless-object-inheritance,inconsistent-return-statements,consider-using-dict-comprehension,import-outside-toplevel,use-a-generator +disable=Design,Format,Similarities,invalid-name,missing-docstring,locally-disabled,unnecessary-lambda,star-args,fixme,global-statement,broad-except,no-self-use,bare-except,locally-enabled,wrong-import-position,consider-using-ternary,len-as-condition,no-else-return,useless-object-inheritance,inconsistent-return-statements,consider-using-dict-comprehension,import-outside-toplevel,use-a-generator,consider-using-with enable=fixme From 3fd0566a395dc9f33bad6498c9d84bb95034266a Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 22 Jun 2021 19:47:28 -0400 Subject: [PATCH 027/106] spec: Remove shebang fixup redhat-rpm-config brp-mangle-shebangs added in 2018 will do /usr/bin/env python3 -> /usr/bin/python3 automatically for us Signed-off-by: Cole Robinson --- python-bugzilla.spec | 8 -------- 1 file changed, 8 deletions(-) diff --git a/python-bugzilla.spec b/python-bugzilla.spec index 236fb9f4..88fc6a76 100644 --- a/python-bugzilla.spec +++ b/python-bugzilla.spec @@ -48,14 +48,6 @@ This package includes the 'bugzilla' command-line tool for interacting with bugz %install %{__python3} setup.py install -O1 --root %{buildroot} -# Replace '#!/usr/bin/env python' with '#!/usr/bin/python2' -# The format is ideal for upstream, but not a distro. See: -# https://fedoraproject.org/wiki/Features/SystemPythonExecutablesUseSystemPython -%global python_env_path %{__python3} -for f in $(find %{buildroot} -type f -executable -print); do - sed -i "1 s|^#!/usr/bin/.*|#!%{python_env_path}|" $f || : -done - %check From 9ed2bea7c74f9fc9262b8979e85432807510c901 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 20 Jul 2021 09:20:42 -0400 Subject: [PATCH 028/106] base: Relax redhat bugzilla detection Internall RH hosts staging versions of bugzilla at URLs that don't match bugzilla.redhat.com, so loosen the restriction Signed-off-by: Cole Robinson --- bugzilla/base.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bugzilla/base.py b/bugzilla/base.py index 8c1ea590..97ce8a3b 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -261,8 +261,9 @@ def _detect_is_redhat_bugzilla(self): if self._is_redhat_bugzilla: return True - if "bugzilla.redhat.com" in self.url: - log.info("Using RHBugzilla for URL containing bugzilla.redhat.com") + match = ".redhat.com" + if match in self.url: + log.info("Using RHBugzilla for URL containing %s", match) return True return False From 250b6dbfd09b2ac2209f3fb38f585fbb9284ad9a Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 20 Jul 2021 09:24:14 -0400 Subject: [PATCH 029/106] Remove partner-bugzilla.redhat.com references Replace with bugzilla.stage.redhat.com, the new URL Signed-off-by: Cole Robinson --- CONTRIBUTING.md | 6 +++--- examples/bug_autorefresh.py | 2 +- examples/create.py | 4 ++-- examples/getbug.py | 4 ++-- examples/getbug_restapi.py | 2 +- examples/query.py | 6 +++--- examples/update.py | 6 +++--- tests/conftest.py | 2 +- tests/data/authfiles/output-cookies.txt | 4 ++-- tests/data/cookies-lwp.txt | 4 ++-- tests/data/cookies-moz.txt | 4 ++-- tests/test_rw_functional.py | 2 +- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8f8f24f0..c3943334 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,17 +24,17 @@ login account is required. Run them with: ## Read/Write Functional Tests. -Read/Write functional tests use partner-bugzilla.redhat.com, which is a +Read/Write functional tests use bugzilla.stage.redhat.com, which is a bugzilla instance specifically for this type of testing. Data is occasionally hard synced with regular bugzilla.redhat.com, and all local edits are removed. Login accounts are also synced. If you want access to -partner-bugzilla.redhat.com, sign up for a regular bugzilla.redhat.com login +bugzilla.stage.redhat.com, sign up for a regular bugzilla.redhat.com login and wait for the next sync period. Before running these tests, you'll need to cache login credentials. Example: - ./bugzilla-cli --bugzilla=partner-bugzilla.redhat.com --username=$USER login + ./bugzilla-cli --bugzilla=bugzilla.stage.redhat.com --username=$USER login pytest --rw-functional ## Testing across python versions diff --git a/examples/bug_autorefresh.py b/examples/bug_autorefresh.py index 09c1c533..a8aa6728 100644 --- a/examples/bug_autorefresh.py +++ b/examples/bug_autorefresh.py @@ -9,7 +9,7 @@ import bugzilla # public test instance of bugzilla.redhat.com. It's okay to make changes -URL = "partner-bugzilla.redhat.com" +URL = "bugzilla.stage.redhat.com" bzapi = bugzilla.Bugzilla(URL) # The Bugzilla.bug_autorefresh setting controls whether bugs will diff --git a/examples/create.py b/examples/create.py index be1c75dc..124a93b0 100644 --- a/examples/create.py +++ b/examples/create.py @@ -12,8 +12,8 @@ # public test instance of bugzilla.redhat.com. # # Don't worry, changing things here is fine, and won't send any email to -# users or anything. It's what partner-bugzilla.redhat.com is for! -URL = "partner-bugzilla.redhat.com" +# users or anything. It's what bugzilla.stage.redhat.com is for! +URL = "bugzilla.stage.redhat.com" bzapi = bugzilla.Bugzilla(URL) if not bzapi.logged_in: print("This example requires cached login credentials for %s" % URL) diff --git a/examples/getbug.py b/examples/getbug.py index 866b0d6d..faf4c30f 100644 --- a/examples/getbug.py +++ b/examples/getbug.py @@ -11,14 +11,14 @@ import bugzilla # public test instance of bugzilla.redhat.com. It's okay to make changes -URL = "partner-bugzilla.redhat.com" +URL = "bugzilla.stage.redhat.com" bzapi = bugzilla.Bugzilla(URL) # getbug() is just a simple wrapper around getbugs(), which takes a list # IDs, if you need to fetch multiple # -# Example bug: https://partner-bugzilla.redhat.com/show_bug.cgi?id=427301 +# Example bug: https://bugzilla.stage.redhat.com/show_bug.cgi?id=427301 bug = bzapi.getbug(427301) print("Fetched bug #%s:" % bug.id) print(" Product = %s" % bug.product) diff --git a/examples/getbug_restapi.py b/examples/getbug_restapi.py index 94723884..1cb4e797 100644 --- a/examples/getbug_restapi.py +++ b/examples/getbug_restapi.py @@ -10,7 +10,7 @@ import bugzilla # public test instance of bugzilla.redhat.com. It's okay to make changes -URL = "partner-bugzilla.redhat.com" +URL = "bugzilla.stage.redhat.com" # By default, if plain Bugzilla(URL) is invoked, the Bugzilla class will # attempt to determine if XMLRPC or REST API is available, with a preference diff --git a/examples/query.py b/examples/query.py index ac285118..05ad7026 100644 --- a/examples/query.py +++ b/examples/query.py @@ -10,7 +10,7 @@ import bugzilla # public test instance of bugzilla.redhat.com. It's okay to make changes -URL = "partner-bugzilla.redhat.com" +URL = "bugzilla.stage.redhat.com" bzapi = bugzilla.Bugzilla(URL) @@ -55,7 +55,7 @@ # bugzilla.redhat.com, and bugzilla >= 5.0 support queries using the same # format as is used for 'advanced' search URLs via the Web UI. For example, -# I go to partner-bugzilla.redhat.com -> Search -> Advanced Search, select +# I go to bugzilla.stage.redhat.com -> Search -> Advanced Search, select # Classification=Fedora # Product=Fedora # Component=python-bugzilla @@ -65,7 +65,7 @@ # # Run that, copy the URL and bring it here, pass it to url_to_query to # convert it to a dict(), and query as usual -query = bzapi.url_to_query("https://partner-bugzilla.redhat.com/" +query = bzapi.url_to_query("https://bugzilla.stage.redhat.com/" "buglist.cgi?classification=Fedora&component=python-bugzilla&" "f1=creation_ts&o1=lessthaneq&order=Importance&product=Fedora&" "query_format=advanced&v1=2010-01-01") diff --git a/examples/update.py b/examples/update.py index ff13bdf3..cd76992e 100644 --- a/examples/update.py +++ b/examples/update.py @@ -10,7 +10,7 @@ import bugzilla # public test instance of bugzilla.redhat.com. It's okay to make changes -URL = "partner-bugzilla.redhat.com" +URL = "bugzilla.stage.redhat.com" bzapi = bugzilla.Bugzilla(URL) if not bzapi.logged_in: print("This example requires cached login credentials for %s" % URL) @@ -23,9 +23,9 @@ # The param names map to those accepted by Bugzilla Bug.update: # https://bugzilla.readthedocs.io/en/latest/api/core/v1/bug.html#update-bug # -# Example bug: https://partner-bugzilla.redhat.com/show_bug.cgi?id=427301 +# Example bug: https://bugzilla.stage.redhat.com/show_bug.cgi?id=427301 # Don't worry, changing things here is fine, and won't send any email to -# users or anything. It's what partner-bugzilla.redhat.com is for! +# users or anything. It's what bugzilla.stage.redhat.com is for! bug = bzapi.getbug(427301) print("Bug id=%s original summary=%s" % (bug.id, bug.summary)) diff --git a/tests/conftest.py b/tests/conftest.py index 0ea42d39..a90dfbb9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,7 +23,7 @@ def pytest_addoption(parser): parser.addoption("--rw-functional", action="store_true", default=False, help=("Run read/write functional tests against actual bugzilla " "instances. As of now this only runs against " - "partner-bugzilla.redhat.com, which requires an RH " + "bugzilla.stage.redhat.com, which requires an RH " "bugzilla account with cached login creds. This will " "also be very slow.")) parser.addoption("--redhat-url", diff --git a/tests/data/authfiles/output-cookies.txt b/tests/data/authfiles/output-cookies.txt index 193bd5d5..e4970061 100644 --- a/tests/data/authfiles/output-cookies.txt +++ b/tests/data/authfiles/output-cookies.txt @@ -2,5 +2,5 @@ # http://curl.haxx.se/rfc/cookie_spec.html # This is a generated file! Do not edit. -.partner-bugzilla.redhat.com TRUE / FALSE 2145916800 Bugzilla_login notacookie -.partner-bugzilla.redhat.com TRUE / FALSE 2145916800 Bugzilla_logincookie notacookie +.bugzilla.stage.redhat.com TRUE / FALSE 2145916800 Bugzilla_login notacookie +.bugzilla.stage.redhat.com TRUE / FALSE 2145916800 Bugzilla_logincookie notacookie diff --git a/tests/data/cookies-lwp.txt b/tests/data/cookies-lwp.txt index b8818ef3..d3795485 100644 --- a/tests/data/cookies-lwp.txt +++ b/tests/data/cookies-lwp.txt @@ -1,3 +1,3 @@ #LWP-Cookies-2.0 -Set-Cookie3: Bugzilla_login=notacookie; path="/"; domain=".partner-bugzilla.redhat.com"; domain_dot; expires="2038-01-01 00:00:00Z"; version=0 -Set-Cookie3: Bugzilla_logincookie=notacookie; path="/"; domain=".partner-bugzilla.redhat.com"; domain_dot; expires="2038-01-01 00:00:00Z"; version=0 +Set-Cookie3: Bugzilla_login=notacookie; path="/"; domain=".bugzilla.stage.redhat.com"; domain_dot; expires="2038-01-01 00:00:00Z"; version=0 +Set-Cookie3: Bugzilla_logincookie=notacookie; path="/"; domain=".bugzilla.stage.redhat.com"; domain_dot; expires="2038-01-01 00:00:00Z"; version=0 diff --git a/tests/data/cookies-moz.txt b/tests/data/cookies-moz.txt index 6a16c9db..316e4d96 100644 --- a/tests/data/cookies-moz.txt +++ b/tests/data/cookies-moz.txt @@ -2,5 +2,5 @@ # http://www.netscape.com/newsref/std/cookie_spec.html # This is a generated file! Do not edit. -.partner-bugzilla.redhat.com TRUE / FALSE 2145916800 Bugzilla_login notacookie -.partner-bugzilla.redhat.com TRUE / FALSE 2145916800 Bugzilla_logincookie notacookie +.bugzilla.stage.redhat.com TRUE / FALSE 2145916800 Bugzilla_login notacookie +.bugzilla.stage.redhat.com TRUE / FALSE 2145916800 Bugzilla_logincookie notacookie diff --git a/tests/test_rw_functional.py b/tests/test_rw_functional.py index 6bd62d31..b3d211bc 100644 --- a/tests/test_rw_functional.py +++ b/tests/test_rw_functional.py @@ -22,7 +22,7 @@ import tests.utils -RHURL = tests.CLICONFIG.REDHAT_URL or "partner-bugzilla.redhat.com" +RHURL = tests.CLICONFIG.REDHAT_URL or "bugzilla.stage.redhat.com" def _split_int(s): From 55e55cc1e5f6a702e84f8c91da3d18f4496c4f18 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 27 Jul 2021 13:24:39 -0400 Subject: [PATCH 030/106] tox: Add py310 Signed-off-by: Cole Robinson --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 1a6ced78..0d51036b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py34,py35,py36,py37,py38,py39 +envlist = py34,py35,py36,py37,py38,py39,py310 [testenv] deps = From e7d1d595af72213b9448267bafc7395ad65f09c8 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 27 Jul 2021 13:29:56 -0400 Subject: [PATCH 031/106] Prep for release 3.1.0 Signed-off-by: Cole Robinson --- NEWS.md | 4 ++++ bugzilla/apiversion.py | 2 +- python-bugzilla.spec | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 8f070b90..fba19ea3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # python-bugzilla release news +## Release 3.1.0 (July 27, 2021) +- Detect bugzilla.stage.redhat.com as RHBugzilla +- Add limit as option to build_query (Ivan Lausuch) + ## Release 3.0.2 (November 12, 2020) - Fix API key leaking into requests exceptions diff --git a/bugzilla/apiversion.py b/bugzilla/apiversion.py index 26bfecb5..38fdb64b 100644 --- a/bugzilla/apiversion.py +++ b/bugzilla/apiversion.py @@ -4,5 +4,5 @@ # This work is licensed under the GNU GPLv2 or later. # See the COPYING file in the top-level directory. -version = "3.0.2" +version = "3.1.0" __version__ = version diff --git a/python-bugzilla.spec b/python-bugzilla.spec index 88fc6a76..660f8109 100644 --- a/python-bugzilla.spec +++ b/python-bugzilla.spec @@ -1,5 +1,5 @@ Name: python-bugzilla -Version: 3.0.2 +Version: 3.1.0 Release: 1%{?dist} Summary: Python library for interacting with Bugzilla From 17ad10afd5055990f8437afddc9ebb141f5eb084 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Fri, 30 Jul 2021 12:04:19 -0400 Subject: [PATCH 032/106] README.md: some tweaks --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5943aa3e..be52d437 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,11 @@ This package provides two bits: -* 'bugzilla' python module for talking to a [Bugzilla](https://www.bugzilla.org/) instance over XMLRPC or REST -* /usr/bin/bugzilla command line tool for performing actions from the command line: create or edit bugs, various queries, etc. +* `bugzilla` python module for talking to a [Bugzilla](https://www.bugzilla.org/) instance over XMLRPC or REST +* `/usr/bin/bugzilla` command line tool for performing actions from the command line: create or edit bugs, various queries, etc. -This was originally written specifically for Red Hat's Bugzilla instance -and is used heavily at Red Hat and in Fedora, but it should still be +This was originally written specifically for [Red Hat's Bugzilla instance](https://bugzilla.redhat.com) +and is used heavily at Red Hat and in Fedora, but it should be generically useful. You can find some code examples in the [examples](examples) directory. From be67e6df822a82501be3577e1a4eef0e5ecf14e0 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Mon, 4 Oct 2021 12:56:50 -0400 Subject: [PATCH 033/106] tests: Fix after rhbz query limits Signed-off-by: Cole Robinson --- tests/test_ro_functional.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_ro_functional.py b/tests/test_ro_functional.py index 4c153396..577e8203 100644 --- a/tests/test_ro_functional.py +++ b/tests/test_ro_functional.py @@ -235,7 +235,7 @@ def testQueryURL(run_cli, backends): qurl = ("/buglist.cgi?f1=creation_ts" "&list_id=973582&o1=greaterthaneq&classification=Fedora&" "o2=lessthaneq&query_format=advanced&f2=creation_ts" - "&v1=2010-01-01&component=python-bugzilla&v2=2011-01-01" + "&v1=2010-01-01&component=python-bugzilla&v2=2010-06-01" "&product=Fedora") url = REDHAT_URL @@ -244,7 +244,7 @@ def testQueryURL(run_cli, backends): else: url += qurl out = run_cli("bugzilla query --from-url \"%s\"" % url, bz) - _check(out, 22, "#553878 CLOSED") + _check(out, 10, "#553878 CLOSED") def testQueryFixedIn(run_cli, backends): From 1e42224b4b83037ea78a4a293d346b17b22f1ddf Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Mon, 4 Oct 2021 15:34:30 -0400 Subject: [PATCH 034/106] Fix some pylint Signed-off-by: Cole Robinson --- .pylintrc | 2 +- tests/test_cli_attach.py | 2 +- tests/test_ro_functional.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pylintrc b/.pylintrc index 182a9a87..efdb0d1f 100644 --- a/.pylintrc +++ b/.pylintrc @@ -7,7 +7,7 @@ persistent=no # can either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). -disable=Design,Format,Similarities,invalid-name,missing-docstring,locally-disabled,unnecessary-lambda,star-args,fixme,global-statement,broad-except,no-self-use,bare-except,locally-enabled,wrong-import-position,consider-using-ternary,len-as-condition,no-else-return,useless-object-inheritance,inconsistent-return-statements,consider-using-dict-comprehension,import-outside-toplevel,use-a-generator,consider-using-with +disable=Design,Format,Similarities,invalid-name,missing-docstring,locally-disabled,unnecessary-lambda,star-args,fixme,global-statement,broad-except,no-self-use,bare-except,locally-enabled,wrong-import-position,consider-using-ternary,len-as-condition,no-else-return,useless-object-inheritance,inconsistent-return-statements,consider-using-dict-comprehension,import-outside-toplevel,use-a-generator,consider-using-with,consider-using-f-string,unspecified-encoding enable=fixme diff --git a/tests/test_cli_attach.py b/tests/test_cli_attach.py index 2975d106..d287bc54 100644 --- a/tests/test_cli_attach.py +++ b/tests/test_cli_attach.py @@ -58,7 +58,7 @@ def _test_attach_get(run_cli): assert "not used for" in out # Basic --get ATTID usage - filename = u"Klíč memorial test file.txt" + filename = "Klíč memorial test file.txt" cmd = "bugzilla attach --get 112233" fakebz = tests.mockbackend.make_bz( bug_attachment_get_args="data/mockargs/test_attach_get1.txt", diff --git a/tests/test_ro_functional.py b/tests/test_ro_functional.py index 577e8203..c2dd75c1 100644 --- a/tests/test_ro_functional.py +++ b/tests/test_ro_functional.py @@ -226,7 +226,7 @@ def testQueryFormat(run_cli, backends): # Unicode in this bug's summary args = "--bug_id 522796 --outputformat \"%{summary}\"" out = run_cli("bugzilla query %s" % args, bz) - assert u"V34 — system" in out + assert "V34 — system" in out def testQueryURL(run_cli, backends): From 017a9311783fce4529a320b293c89fa2002cfc30 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 5 Oct 2021 12:34:21 -0400 Subject: [PATCH 035/106] cli; Drop unnecessary u"" usage Signed-off-by: Cole Robinson --- bugzilla/_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bugzilla/_cli.py b/bugzilla/_cli.py index 22e05ea6..5fc28652 100755 --- a/bugzilla/_cli.py +++ b/bugzilla/_cli.py @@ -662,7 +662,7 @@ def _filter_components(compdetails): elif opt.component_owners: details = bz.getcomponentsdetails(productname) for c in sorted(_filter_components(details)): - print(u"%s: %s" % (c, details[c]['default_assigned_to'])) + print("%s: %s" % (c, details[c]['default_assigned_to'])) def _convert_to_outputformat(output): From 380fc34c9db049f5cf66098f9b705e7e293243df Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 5 Oct 2021 12:29:04 -0400 Subject: [PATCH 036/106] Remove cookie auth support Bugzilla dropped cookie auth support in 4.4 version, released in 1. I think that's long enough to drop support here. If anyone still needs this feature, they can continue to use old python-bugzilla versions Signed-off-by: Cole Robinson --- bugzilla/_authfiles.py | 63 ++----------------------- bugzilla/_cli.py | 3 +- bugzilla/_session.py | 13 +---- bugzilla/base.py | 32 ++++--------- examples/apikey.py | 2 +- man/bugzilla.rst | 14 ++---- tests/data/authfiles/output-cookies.txt | 6 --- tests/data/cookies-bad.txt | 1 - tests/data/cookies-lwp.txt | 3 -- tests/data/cookies-moz.txt | 6 --- tests/test_api_authfiles.py | 63 ++----------------------- tests/test_cli_misc.py | 7 +++ 12 files changed, 30 insertions(+), 183 deletions(-) delete mode 100644 tests/data/authfiles/output-cookies.txt delete mode 100644 tests/data/cookies-bad.txt delete mode 100644 tests/data/cookies-lwp.txt delete mode 100644 tests/data/cookies-moz.txt diff --git a/bugzilla/_authfiles.py b/bugzilla/_authfiles.py index 609b9651..ae31797d 100644 --- a/bugzilla/_authfiles.py +++ b/bugzilla/_authfiles.py @@ -2,12 +2,10 @@ # See the COPYING file in the top-level directory. import configparser -import http.cookiejar import os from logging import getLogger import urllib.parse -from .exceptions import BugzillaError from ._util import listify log = getLogger(__name__) @@ -26,17 +24,12 @@ def _makedirs(path): os.makedirs(os.path.dirname(path), 0o700) -def _default_location(filename, kind): +def _default_cache_location(filename): """ - Determine default location for passed filename and xdg kind, - example: ~/.cache/python-bugzilla/bugzillacookies + Determine default location for passed xdg filename. + example: ~/.cache/python-bugzilla/bugzillarc """ - xdgpath = os.path.expanduser("~/.%s/python-bugzilla/%s" % (kind, filename)) - return xdgpath - - -def _default_cache_location(filename): - return _default_location(filename, 'cache') + return os.path.expanduser("~/.cache/python-bugzilla/%s" % filename) class _BugzillaRCFile(object): @@ -184,51 +177,3 @@ def set_filename(self, filename): cfg.read(filename) self._filename = filename self._cfg = cfg - - -class _BugzillaCookieCache(object): - @staticmethod - def get_default_path(): - return _default_cache_location("bugzillacookies") - - def __init__(self): - self._cookiejar = None - - def _build_cookiejar(self, cookiefile): - cj = http.cookiejar.MozillaCookieJar(cookiefile) - if (cookiefile is None or - not os.path.exists(cookiefile)): - return cj - - try: - cj.load() - return cj - except http.cookiejar.LoadError: - msg = "cookiefile=%s not in Mozilla format" % cookiefile - raise BugzillaError(msg) from None - - def set_filename(self, cookiefile): - log.debug("Using cookiefile=%s", cookiefile) - self._cookiejar = self._build_cookiejar(cookiefile) - - def get_filename(self): - return self._cookiejar.filename - - def get_cookiejar(self): - return self._cookiejar - - def set_cookies(self, cookies): - for cookie in cookies: - self._cookiejar.set_cookie(cookie) - - cookiefile = self._cookiejar.filename - if not cookiefile: - return - - if not os.path.exists(cookiefile): - _makedirs(cookiefile) - # Make sure a new file has correct permissions - open(cookiefile, 'a').close() - os.chmod(cookiefile, 0o600) - - self._cookiejar.save() diff --git a/bugzilla/_cli.py b/bugzilla/_cli.py index 5fc28652..d5035cc2 100755 --- a/bugzilla/_cli.py +++ b/bugzilla/_cli.py @@ -125,8 +125,7 @@ def _setup_root_parser(): help="Don't save any bugzilla cookies or tokens to disk, and " "don't use any pre-existing credentials.") - p.add_argument('--cookiefile', default=None, - help="cookie file to use for bugzilla authentication") + p.add_argument('--cookiefile', default=None, help=argparse.SUPPRESS) p.add_argument('--tokenfile', default=None, help="token file to use for bugzilla authentication") diff --git a/bugzilla/_session.py b/bugzilla/_session.py index 9bc56835..9288ec51 100644 --- a/bugzilla/_session.py +++ b/bugzilla/_session.py @@ -18,12 +18,11 @@ class _BugzillaSession(object): Class to handle the backend agnostic 'requests' setup """ def __init__(self, url, user_agent, - cookiecache, sslverify, cert, + sslverify, cert, tokencache, api_key, requests_session=None): self._url = url self._user_agent = user_agent self._scheme = urllib.parse.urlparse(url)[0] - self._cookiecache = cookiecache self._tokencache = tokencache self._api_key = api_key self._is_xmlrpc = False @@ -38,8 +37,6 @@ def __init__(self, url, user_agent, if cert: self._session.cert = cert - if self._cookiecache: - self._session.cookies = self._cookiecache.get_cookiejar() if sslverify is False: self._session.verify = False self._session.headers["User-Agent"] = self._user_agent @@ -85,12 +82,6 @@ def _set_tokencache_param(self): token = self.get_token_value() self._session.params["Bugzilla_token"] = token - def set_response_cookies(self, response): - """ - Save any cookies received from the passed requests response - """ - self._cookiecache.set_cookies(response.cookies) - def get_requests_session(self): return self._session @@ -105,8 +96,6 @@ def request(self, *args, **kwargs): # Yes this still appears to matter for properly decoding unicode # code points in bugzilla.redhat.com content response.encoding = "UTF-8" - # Set response cookies - self.set_response_cookies(response) try: response.raise_for_status() diff --git a/bugzilla/base.py b/bugzilla/base.py index 97ce8a3b..997ea3c8 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -17,8 +17,7 @@ from io import BytesIO -from ._authfiles import (_BugzillaRCFile, - _BugzillaCookieCache, _BugzillaTokenCache) +from ._authfiles import _BugzillaRCFile, _BugzillaTokenCache from .apiversion import __version__ from ._backendrest import _BackendREST from ._backendxmlrpc import _BackendXMLRPC @@ -87,7 +86,7 @@ class Bugzilla(object): bzapi = Bugzilla("http://bugzilla.example.com") If you have previously logged into that URL, and have cached login - cookies/tokens, you will automatically be logged in. Otherwise to + tokens, you will automatically be logged in. Otherwise to log in, you can either pass auth options to __init__, or call a login helper like interactive_login(). @@ -185,18 +184,14 @@ def __init__(self, url=-1, user=None, password=None, cookiefile=-1, :param password: optional password for the connecting user :param cert: optional certificate file for client side certificate authentication - :param cookiefile: Location to cache the login session cookies so you - don't have to keep specifying username/password. Bugzilla 5+ will - use tokens instead of cookies. - If -1, use the default path. If None, don't use or save - any cookiefile. + :param cookiefile: Deprecated, raises an error if not -1 or None :param sslverify: Set this to False to skip SSL hostname and CA validation checks, like out of date certificate :param tokenfile: Location to cache the API login token so youi don't have to keep specifying username/password. If -1, use the default path. If None, don't use or save any tokenfile. - :param use_creds: If False, this disables cookiefile, tokenfile, + :param use_creds: If False, this disables tokenfile and configpaths by default. This is a convenience option to unset those values at init time. If those values are later changed, they may be used for future operations. @@ -232,25 +227,23 @@ def __init__(self, url=-1, user=None, password=None, cookiefile=-1, self._is_redhat_bugzilla = False self._rcfile = _BugzillaRCFile() - self._cookiecache = _BugzillaCookieCache() self._tokencache = _BugzillaTokenCache() self._force_rest = force_rest self._force_xmlrpc = force_xmlrpc + if cookiefile not in [None, -1]: + raise TypeError("cookiefile is deprecated, don't pass any value.") + if not use_creds: - cookiefile = None tokenfile = None configpaths = [] - if cookiefile == -1: - cookiefile = self._cookiecache.get_default_path() if tokenfile == -1: tokenfile = self._tokencache.get_default_path() if configpaths == -1: configpaths = _BugzillaRCFile.get_default_configpaths() - self._setcookiefile(cookiefile) self._settokenfile(tokenfile) self._setconfigpath(configpaths) @@ -368,12 +361,8 @@ def _get_api_aliases(self): ################# def _getcookiefile(self): - return self._cookiecache.get_filename() - def _delcookiefile(self): - self._setcookiefile(None) - def _setcookiefile(self, cookiefile): - self._cookiecache.set_filename(cookiefile) - cookiefile = property(_getcookiefile, _setcookiefile, _delcookiefile) + return None + cookiefile = property(_getcookiefile) def _gettokenfile(self): return self._tokencache.get_filename() @@ -516,7 +505,6 @@ def connect(self, url=None): self.readconfig(overwrite=False) self._session = _BugzillaSession(self.url, self.user_agent, - cookiecache=self._cookiecache, sslverify=self._sslverify, cert=self.cert, tokencache=self._tokencache, @@ -686,7 +674,7 @@ def interactive_login(self, user=None, password=None, force=False, def logout(self): """ Log out of bugzilla. Drops server connection and user info, and - destroys authentication cookies. + destroys authentication cache """ self._backend.user_logout() self.disconnect() diff --git a/examples/apikey.py b/examples/apikey.py index 8b2c90b7..021d70fb 100644 --- a/examples/apikey.py +++ b/examples/apikey.py @@ -20,6 +20,6 @@ # API key usage assumes the API caller is storing the API key; if you would # like to use one of the login options that stores credentials on-disk for -# command-line usage, use tokens or cookies. +# command-line usage, use login tokens. bzapi = bugzilla.Bugzilla(URL, api_key=api_key) assert bzapi.logged_in diff --git a/man/bugzilla.rst b/man/bugzilla.rst index 08c14dab..5d790eca 100644 --- a/man/bugzilla.rst +++ b/man/bugzilla.rst @@ -127,18 +127,10 @@ they expire the tool errors, rather than subtly change output. **Syntax:** ``--no-cache-credentials`` -Don't save any bugzilla cookies or tokens to disk, and don't use any +Don't save any bugzilla tokens to disk, and don't use any pre-existing credentials. -``--cookiefile`` -^^^^^^^^^^^^^^^^ - -**Syntax:** ``--cookiefile`` COOKIEFILE - -cookie file to use for bugzilla authentication - - ``--tokenfile`` ^^^^^^^^^^^^^^^ @@ -817,8 +809,8 @@ YOUR_API_KEY with the generated API Key from the Web UI. Alternatively, you can use 'bugzilla login --api-key', which will ask for the API key, and save it to bugzillarc for you. -For older bugzilla instances, you will need to cache a login cookie or -token with the "login" subcommand or the "--login" argument. +For older bugzilla instances, you will need to cache a login token +with the "login" subcommand or the "--login" argument. Additionally, the --no-cache-credentials option will tell the bugzilla tool to *not* save or use any authentication cache, including the diff --git a/tests/data/authfiles/output-cookies.txt b/tests/data/authfiles/output-cookies.txt deleted file mode 100644 index e4970061..00000000 --- a/tests/data/authfiles/output-cookies.txt +++ /dev/null @@ -1,6 +0,0 @@ -# Netscape HTTP Cookie File -# http://curl.haxx.se/rfc/cookie_spec.html -# This is a generated file! Do not edit. - -.bugzilla.stage.redhat.com TRUE / FALSE 2145916800 Bugzilla_login notacookie -.bugzilla.stage.redhat.com TRUE / FALSE 2145916800 Bugzilla_logincookie notacookie diff --git a/tests/data/cookies-bad.txt b/tests/data/cookies-bad.txt deleted file mode 100644 index 0928f036..00000000 --- a/tests/data/cookies-bad.txt +++ /dev/null @@ -1 +0,0 @@ -foo this is invalid cookies diff --git a/tests/data/cookies-lwp.txt b/tests/data/cookies-lwp.txt deleted file mode 100644 index d3795485..00000000 --- a/tests/data/cookies-lwp.txt +++ /dev/null @@ -1,3 +0,0 @@ -#LWP-Cookies-2.0 -Set-Cookie3: Bugzilla_login=notacookie; path="/"; domain=".bugzilla.stage.redhat.com"; domain_dot; expires="2038-01-01 00:00:00Z"; version=0 -Set-Cookie3: Bugzilla_logincookie=notacookie; path="/"; domain=".bugzilla.stage.redhat.com"; domain_dot; expires="2038-01-01 00:00:00Z"; version=0 diff --git a/tests/data/cookies-moz.txt b/tests/data/cookies-moz.txt deleted file mode 100644 index 316e4d96..00000000 --- a/tests/data/cookies-moz.txt +++ /dev/null @@ -1,6 +0,0 @@ -# Netscape HTTP Cookie File -# http://www.netscape.com/newsref/std/cookie_spec.html -# This is a generated file! Do not edit. - -.bugzilla.stage.redhat.com TRUE / FALSE 2145916800 Bugzilla_login notacookie -.bugzilla.stage.redhat.com TRUE / FALSE 2145916800 Bugzilla_logincookie notacookie diff --git a/tests/test_api_authfiles.py b/tests/test_api_authfiles.py index 00318613..1452f6af 100644 --- a/tests/test_api_authfiles.py +++ b/tests/test_api_authfiles.py @@ -13,48 +13,22 @@ import shutil import tempfile -import pytest import requests -import bugzilla - import tests import tests.mockbackend import tests.utils -def testCookies(monkeypatch): - monkeypatch.setitem(os.environ, "HOME", - os.path.dirname(__file__) + "/data/homedir") - +def test_tokenfile(monkeypatch): dirname = os.path.dirname(__file__) - cookiesbad = dirname + "/data/cookies-bad.txt" - cookieslwp = dirname + "/data/cookies-lwp.txt" - cookiesmoz = dirname + "/data/cookies-moz.txt" - - # We used to convert LWP cookies, but it shouldn't matter anymore, - # so verify they fail at least - with pytest.raises(bugzilla.BugzillaError): - tests.mockbackend.make_bz(version="3.0.0", - bz_kwargs={"cookiefile": cookieslwp, "use_creds": True}) + monkeypatch.setitem(os.environ, "HOME", dirname + "/data/homedir") - with pytest.raises(bugzilla.BugzillaError): - tests.mockbackend.make_bz(version="3.0.0", - bz_kwargs={"cookiefile": cookiesbad, "use_creds": True}) - - # Mozilla should 'just work' - bz = tests.mockbackend.make_bz(version="3.0.0", - bz_kwargs={"cookiefile": cookiesmoz, "use_creds": True}) - - # cookie/token property magic bz = tests.mockbackend.make_bz(bz_kwargs={"use_creds": True}) token = dirname + "/data/homedir/.cache/python-bugzilla/bugzillatoken" - cookie = dirname + "/data/homedir/.cache/python-bugzilla/bugzillacookies" assert token == bz.tokenfile - assert cookie == bz.cookiefile del(bz.tokenfile) - del(bz.cookiefile) assert bz.tokenfile is None assert bz.cookiefile is None @@ -130,14 +104,6 @@ def _write(c): _check(None, None, None, None) -def _get_cookiejar(): - cookiefile = os.path.dirname(__file__) + "/data/cookies-moz.txt" - inputbz = tests.mockbackend.make_bz( - bz_kwargs={"use_creds": True, "cookiefile": cookiefile}) - cookiecache = inputbz._cookiecache # pylint: disable=protected-access - return cookiecache.get_cookiejar() - - def test_authfiles_saving(monkeypatch): tmpdir = tempfile.mkdtemp() try: @@ -151,9 +117,6 @@ def test_authfiles_saving(monkeypatch): backend = bzapi._backend # pylint: disable=protected-access bsession = backend._bugzillasession # pylint: disable=protected-access - response = requests.Response() - response.cookies = _get_cookiejar() - # token testing, with repetitions to hit various code paths bsession.set_token_value(None) bsession.set_token_value("MY-FAKE-TOKEN") @@ -161,31 +124,15 @@ def test_authfiles_saving(monkeypatch): bsession.set_token_value(None) bsession.set_token_value("MY-FAKE-TOKEN") - # cookie testing - bsession.set_response_cookies(response) - dirname = os.path.dirname(__file__) + "/data/authfiles/" output_token = dirname + "output-token.txt" - output_cookies = dirname + "output-cookies.txt" tests.utils.diff_compare(open(bzapi.tokenfile).read(), output_token) - # On RHEL7 the cookie comment header is different. Strip off leading - # comments - def strip_comments(f): - return "".join([ - line for line in open(f).readlines() if - not line.startswith("#")]) - - tests.utils.diff_compare(strip_comments(bzapi.cookiefile), - None, expect_out=strip_comments(output_cookies)) - # Make sure file can re-read them and not error bzapi = tests.mockbackend.make_bz( bz_kwargs={"use_creds": True, - "cookiefile": output_cookies, "tokenfile": output_token}) assert bzapi.tokenfile == output_token - assert bzapi.cookiefile == output_cookies # Test rcfile writing for api_key rcfile = bzapi._rcfile # pylint: disable=protected-access @@ -205,16 +152,12 @@ def strip_comments(f): def test_authfiles_nowrite(): - # Set values when n when cookiefile is None, should be fine + # Setting values tokenfile is None, should be fine bzapi = tests.mockbackend.make_bz(bz_kwargs={"use_creds": False}) bzapi.connect("https://example.com/foo") backend = bzapi._backend # pylint: disable=protected-access bsession = backend._bugzillasession # pylint: disable=protected-access rcfile = bzapi._rcfile # pylint: disable=protected-access - response = requests.Response() - response.cookies = _get_cookiejar() - bsession.set_token_value("NEW-TOKEN-VALUE") - bsession.set_response_cookies(response) assert rcfile.save_api_key(bzapi.url, "fookey") is None diff --git a/tests/test_cli_misc.py b/tests/test_cli_misc.py index 70f49bf6..5a898bef 100644 --- a/tests/test_cli_misc.py +++ b/tests/test_cli_misc.py @@ -38,6 +38,13 @@ def testVersion(run_cli): assert out.strip() == bugzilla.__version__ +def testCookiefileDeprecated(run_cli): + with pytest.raises(TypeError) as e: + run_cli("bugzilla --cookiefile foobar login", + None, expectfail=True) + assert "cookiefile is deprecated" in str(e) + + def testPositionalArgs(run_cli): # Make sure cli correctly rejects ambiguous positional args out = run_cli("bugzilla login --xbadarg foo", From 6573d90abefe26234120f603d1ce27874c882b94 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 5 Oct 2021 14:18:06 -0400 Subject: [PATCH 037/106] session: Rework how auth params are passed to backends REST backend relies on session to set the params, but XMLRPC does it in the backend. Switch it so session hands a param dictionary off to the backend. This will help with upcoming changes that make param values more conditional, and a possible future where bugzilla doesn't accept auth via URL params Signed-off-by: Cole Robinson --- bugzilla/_backendrest.py | 6 +++--- bugzilla/_backendxmlrpc.py | 14 ++++---------- bugzilla/_session.py | 33 +++++++++++++++------------------ 3 files changed, 22 insertions(+), 31 deletions(-) diff --git a/bugzilla/_backendrest.py b/bugzilla/_backendrest.py index 275c40da..f27fa12a 100644 --- a/bugzilla/_backendrest.py +++ b/bugzilla/_backendrest.py @@ -49,14 +49,14 @@ def _op(self, method, apiurl, paramdict=None): log.debug("Bugzilla REST %s %s params=%s", method, fullurl, paramdict) data = None - params = None + authparams = self._bugzillasession.get_auth_params() if method == "GET": - params = paramdict + authparams.update(paramdict or {}) else: data = json.dumps(paramdict or {}) response = self._bugzillasession.request(method, fullurl, data=data, - params=params) + params=authparams) return self._handle_response(response.text) def _get(self, *args, **kwargs): diff --git a/bugzilla/_backendxmlrpc.py b/bugzilla/_backendxmlrpc.py index 029dee52..0d47694f 100644 --- a/bugzilla/_backendxmlrpc.py +++ b/bugzilla/_backendxmlrpc.py @@ -118,18 +118,12 @@ def _ServerProxy__request(self, methodname, params): newparams = params and params[0].copy() or {} log.debug("XMLRPC call: %s(%s)", methodname, newparams) - api_key = self.__bugzillasession.get_api_key() - token_value = self.__bugzillasession.get_token_value() - - if api_key is not None: - if 'Bugzilla_api_key' not in newparams: - newparams['Bugzilla_api_key'] = api_key - elif token_value is not None: - if 'Bugzilla_token' not in newparams: - newparams['Bugzilla_token'] = token_value + authparams = self.__bugzillasession.get_auth_params() + authparams.update(newparams) # pylint: disable=no-member - ret = ServerProxy._ServerProxy__request(self, methodname, (newparams,)) + ret = ServerProxy._ServerProxy__request( + self, methodname, (authparams,)) # pylint: enable=no-member if isinstance(ret, dict) and 'token' in ret.keys(): diff --git a/bugzilla/_session.py b/bugzilla/_session.py index 9288ec51..1960ba50 100644 --- a/bugzilla/_session.py +++ b/bugzilla/_session.py @@ -40,7 +40,6 @@ def __init__(self, url, user_agent, if sslverify is False: self._session.verify = False self._session.headers["User-Agent"] = self._user_agent - self._set_tokencache_param() def _get_timeout(self): # Default to 5 minutes. This is longer than bugzilla.redhat.com's @@ -52,10 +51,6 @@ def _get_timeout(self): def set_rest_defaults(self): self._session.headers["Content-Type"] = "application/json" - # Bugzilla 5.0 only supports api_key as a query parameter. - # Bugzilla 5.1+ takes it as a X-BUGZILLA-API-KEY header as well, - # with query param taking preference. - self._session.params["Bugzilla_api_key"] = self._api_key def set_xmlrpc_defaults(self): self._is_xmlrpc = True self._session.headers["Content-Type"] = "text/xml" @@ -64,23 +59,25 @@ def get_user_agent(self): return self._user_agent def get_scheme(self): return self._scheme - def get_api_key(self): - return self._api_key - def get_token_value(self): - return self._tokencache.get_value(self._url) def set_token_value(self, value): self._tokencache.set_value(self._url, value) - self._set_tokencache_param() - def _set_tokencache_param(self): + def get_auth_params(self): + # Don't add a token to the params list if an API key is set. + # Keeping API key solo means bugzilla will definitely fail + # if the key expires. Passing in a token could hide that + # fact, which could make it confusing to pinpoint the issue. if self._api_key: - # Don't add a token to the params list if an API key is set. - # Keeping API key solo means bugzilla will definitely fail - # if the key expires. Passing in a token could hide that - # fact, which could make it confusing to pinpoint the issue. - return - token = self.get_token_value() - self._session.params["Bugzilla_token"] = token + # Bugzilla 5.0 only supports api_key as a query parameter. + # Bugzilla 5.1+ takes it as a X-BUGZILLA-API-KEY header as well, + # with query param taking preference. + return {"Bugzilla_api_key": self._api_key} + + token = self._tokencache.get_value(self._url) + if token: + return {"Bugzilla_token": token} + + return {} def get_requests_session(self): return self._session From ddfbc168f647690b37bcef9e8bc9343b806ef8b2 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 5 Oct 2021 14:45:07 -0400 Subject: [PATCH 038/106] session: Use Authorization header for RH bugzilla See https://bugzilla.redhat.com/show_bug.cgi?id=1833585 bugzilla.redhat.com has added support for non-standard 'Authorization: Bearer $APIKEY' header for authenticating. Other auth methods may eventually be removed. So let's start using this for bugzilla.redhat.com One caveat is that we need to stop sending token/apikey values as query parameters when this header is used Signed-off-by: Cole Robinson --- bugzilla/_session.py | 16 ++++++++++++++-- bugzilla/base.py | 5 ++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/bugzilla/_session.py b/bugzilla/_session.py index 1960ba50..c83fcf15 100644 --- a/bugzilla/_session.py +++ b/bugzilla/_session.py @@ -18,14 +18,16 @@ class _BugzillaSession(object): Class to handle the backend agnostic 'requests' setup """ def __init__(self, url, user_agent, - sslverify, cert, - tokencache, api_key, requests_session=None): + sslverify, cert, tokencache, api_key, + is_redhat_bugzilla, + requests_session=None): self._url = url self._user_agent = user_agent self._scheme = urllib.parse.urlparse(url)[0] self._tokencache = tokencache self._api_key = api_key self._is_xmlrpc = False + self._use_auth_bearer = False if self._scheme not in ["http", "https"]: raise Exception("Invalid URL scheme: %s (%s)" % ( @@ -41,6 +43,11 @@ def __init__(self, url, user_agent, self._session.verify = False self._session.headers["User-Agent"] = self._user_agent + if is_redhat_bugzilla and self._api_key: + self._use_auth_bearer = True + self._session.headers["Authorization"] = ( + "Bearer %s" % self._api_key) + def _get_timeout(self): # Default to 5 minutes. This is longer than bugzilla.redhat.com's # apparent 3 minute timeout so shouldn't affect legitimate usage, @@ -63,6 +70,11 @@ def set_token_value(self, value): self._tokencache.set_value(self._url, value) def get_auth_params(self): + # bugzilla.redhat.com will error if there's auth bits in params + # when Authorization header is used + if self._use_auth_bearer: + return {} + # Don't add a token to the params list if an API key is set. # Keeping API key solo means bugzilla will definitely fail # if the key expires. Passing in a token could hide that diff --git a/bugzilla/base.py b/bugzilla/base.py index 997ea3c8..6eb70eb6 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -504,11 +504,15 @@ def connect(self, url=None): # we've changed URLs - reload config self.readconfig(overwrite=False) + # Detect if connecting to redhat bugzilla + self._init_class_from_url() + self._session = _BugzillaSession(self.url, self.user_agent, sslverify=self._sslverify, cert=self.cert, tokencache=self._tokencache, api_key=self.api_key, + is_redhat_bugzilla=self._is_redhat_bugzilla, requests_session=self._user_requests_session) self._backend = backendclass(self.url, self._session) @@ -522,7 +526,6 @@ def connect(self, url=None): version = self._backend.bugzilla_version()["version"] log.debug("Bugzilla version string: %s", version) self._set_bz_version(version) - self._init_class_from_url() @property From 50acd2e97e8561766313ae894cb6771906940045 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 5 Oct 2021 15:25:32 -0400 Subject: [PATCH 039/106] tests: Fill out some more backend test coverage Signed-off-by: Cole Robinson --- bugzilla/_backendrest.py | 12 +++---- bugzilla/_backendxmlrpc.py | 4 +-- tests/test_ro_functional.py | 31 ++++++++++++++-- tests/test_rw_functional.py | 72 +++++++++++++++++++++++-------------- tests/utils.py | 7 ---- 5 files changed, 82 insertions(+), 44 deletions(-) diff --git a/bugzilla/_backendrest.py b/bugzilla/_backendrest.py index f27fa12a..3abe49c7 100644 --- a/bugzilla/_backendrest.py +++ b/bugzilla/_backendrest.py @@ -36,7 +36,7 @@ def __init__(self, url, bugzillasession): def _handle_response(self, text): try: ret = dict(json.loads(text)) - except Exception: + except Exception: # pragma: no cover log.debug("Failed to parse REST response. Output is:\n%s", text) raise @@ -148,19 +148,19 @@ def component_create(self, paramdict): return self._post("/component", paramdict) def component_update(self, paramdict): if "ids" in paramdict: - apiurl = str(listify(paramdict["ids"])[0]) + apiurl = str(listify(paramdict["ids"])[0]) # pragma: no cover if "names" in paramdict: apiurl = ("%(product)s/%(component)s" % listify(paramdict["names"])[0]) return self._put("/component/%s" % apiurl, paramdict) - def externalbugs_add(self, paramdict): + def externalbugs_add(self, paramdict): # pragma: no cover raise BugzillaError( "No REST API available yet for externalbugs_add") - def externalbugs_remove(self, paramdict): + def externalbugs_remove(self, paramdict): # pragma: no cover raise BugzillaError( "No REST API available yet for externalbugs_remove") - def externalbugs_update(self, paramdict): + def externalbugs_update(self, paramdict): # pragma: no cover raise BugzillaError( "No REST API available yet for externalbugs_update") @@ -187,7 +187,7 @@ def user_logout(self): def user_update(self, paramdict): urlid = None if "ids" in paramdict: - urlid = listify(paramdict["ids"])[0] + urlid = listify(paramdict["ids"])[0] # pragma: no cover if "names" in paramdict: urlid = listify(paramdict["names"])[0] return self._put("/user/%s" % urlid, paramdict) diff --git a/bugzilla/_backendxmlrpc.py b/bugzilla/_backendxmlrpc.py index 0d47694f..feb7b2f9 100644 --- a/bugzilla/_backendxmlrpc.py +++ b/bugzilla/_backendxmlrpc.py @@ -48,7 +48,7 @@ def __request_helper(self, url, request_body): except RequestException as e: if not response: raise - raise ProtocolError( + raise ProtocolError( # pragma: no cover url, response.status_code, str(e), response.headers) except Fault: raise @@ -75,7 +75,7 @@ def parse_response(self, response): msg = response.text.encode('utf-8') try: parser.feed(msg) - except Exception: + except Exception: # pragma: no cover log.debug("Failed to parse this XMLRPC response:\n%s", msg) raise diff --git a/tests/test_ro_functional.py b/tests/test_ro_functional.py index c2dd75c1..9e0cf9a0 100644 --- a/tests/test_ro_functional.py +++ b/tests/test_ro_functional.py @@ -58,10 +58,14 @@ def test_rest_xmlrpc_detection(): # See /rest in the URL, so use REST bz = _open_bz("bugzilla.redhat.com/rest") assert bz.is_rest() + with pytest.raises(bugzilla.BugzillaError) as e: + dummy = bz._proxy # pylint: disable=protected-access + assert "raw XMLRPC access is not provided" in str(e) # See /xmlrpc.cgi in the URL, so use XMLRPC bz = _open_bz("bugzilla.redhat.com/xmlrpc.cgi") assert bz.is_xmlrpc() + assert bz._proxy # pylint: disable=protected-access def test_apikey_error_scraping(): @@ -80,6 +84,12 @@ def test_apikey_error_scraping(): assert fakekey not in str(e.value) +def test_xmlrpc_bad_url(): + with pytest.raises(bugzilla.BugzillaError) as e: + _open_bz("https://example.com/#xmlrpc") + assert "URL may not be an XMLRPC URL" in str(e) + + ################### # mozilla testing # ################### @@ -288,8 +298,6 @@ def testGetBugAlias(backends): def testQuerySubComponent(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) - tests.utils.skip_if_rest(bz, "Not working on REST, not sure why yet") - # Test special error wrappers in bugzilla/_cli.py out = run_cli("bugzilla query --product 'Red Hat Enterprise Linux 7' " "--component lvm2 --sub-component 'Thin Provisioning'", bz) @@ -306,6 +314,13 @@ def testBugFields(backends): assert set(bz.bugfields) == set(["product", "bug_status"]) +def testProductGetMisc(backends): + bz = _open_bz(REDHAT_URL, **backends) + + assert bz.product_get(ptype="enterable", include_fields=["id"]) + assert bz.product_get(ptype="selectable", include_fields=["name"]) + + def testBugAutoRefresh(backends): bz = _open_bz(REDHAT_URL, **backends) @@ -381,6 +396,18 @@ def testFaults(run_cli, backends): assert "--nosslverify" in out +def test_login_stubs(backends): + bz = _open_bz(REDHAT_URL, **backends) + + # Failed login, verifies our backends are calling the correct API + with pytest.raises(bugzilla.BugzillaError) as e: + bz.login("foo", "bar") + assert "Login failed" in str(e) + + # Works fine when not logged in + bz.logout() + + def test_redhat_version(backends): bzversion = (5, 0) bz = _open_bz(REDHAT_URL, **backends) diff --git a/tests/test_rw_functional.py b/tests/test_rw_functional.py index b3d211bc..b7d9cfff 100644 --- a/tests/test_rw_functional.py +++ b/tests/test_rw_functional.py @@ -747,6 +747,15 @@ def test11UserUpdate(backends): user.refresh() assert user.groupnames == origgroups + # Try user create + try: + name = "pythonbugzilla-%s" % datetime.datetime.today() + bz.createuser(name + "@example.com", name, name) + except Exception as e: + if have_admin: + raise + assert "Sorry, you aren't a member" in str(e) + def test11ComponentEditing(backends): bz = _open_bz(**backends) @@ -798,10 +807,6 @@ def compare(data, newid): # bugzilla 5 error string ("You are not allowed" in str(e))) - # bugzilla.redhat.com doesn't have REST editcomponent yet - tests.utils.skip_if_rest( - bz, "editcomponent not supported for redhat REST API") - # Edit component data = basedata.copy() data.update({ @@ -817,11 +822,15 @@ def compare(data, newid): if newid is not None: compare(data, newid) except Exception as e: - if have_admin: + if bz.is_rest(): + # redhat REST does not support component editing + assert "A REST API resource was not found" in str(e) + elif have_admin: raise - assert (("Sorry, you aren't a member" in str(e)) or - # bugzilla 5 error string - ("You are not allowed" in str(e))) + else: + assert (("Sorry, you aren't a member" in str(e)) or + # bugzilla 5 error string + ("You are not allowed" in str(e))) def test13SubComponents(backends): @@ -843,14 +852,10 @@ def test13SubComponents(backends): "Default / Unclassified (RHEL5)"]} -def test14ExternalTrackersAddUpdateRemoveQuery(backends): - bz = _open_bz(**backends) +def _testExternalTrackers(bz): bugid = 461686 ext_bug_id = 380489 - tests.utils.skip_if_rest( - bz, "unknown if REST API has externaltrackers support") - # Delete any existing external trackers to get to a known state ids = [bug['id'] for bug in bz.getbug(bugid).external_bugs] if ids != []: @@ -895,6 +900,16 @@ def test14ExternalTrackersAddUpdateRemoveQuery(backends): assert len(ids) == 0 +def test14ExternalTrackersAddUpdateRemoveQuery(backends): + bz = _open_bz(**backends) + try: + _testExternalTrackers(bz) + except Exception as e: + if not bz.is_rest(): + raise + assert "No REST API available" in str(e) + + def test15EnsureLoggedIn(run_cli, backends): bz = _open_bz(**backends) comm = "bugzilla --ensure-logged-in query --bug_id 979546" @@ -913,24 +928,27 @@ def test16ModifyTags(run_cli, backends): bz = _open_bz(**backends) bug = bz.getbug(bugid) - tests.utils.skip_if_rest(bz, "update_tags not supported for REST API") + try: + if bug.tags: + bz.update_tags(bug.id, tags_remove=bug.tags) + bug.refresh() + assert bug.tags == [] - if bug.tags: - bz.update_tags(bug.id, tags_remove=bug.tags) + run_cli(cmd + "--tags foo --tags +bar --tags baz", bz) bug.refresh() - assert bug.tags == [] - - run_cli(cmd + "--tags foo --tags +bar --tags baz", bz) - bug.refresh() - assert bug.tags == ["foo", "bar", "baz"] + assert bug.tags == ["foo", "bar", "baz"] - run_cli(cmd + "--tags=-bar", bz) - bug.refresh() - assert bug.tags == ["foo", "baz"] + run_cli(cmd + "--tags=-bar", bz) + bug.refresh() + assert bug.tags == ["foo", "baz"] - bz.update_tags(bug.id, tags_remove=bug.tags) - bug.refresh() - assert bug.tags == [] + bz.update_tags(bug.id, tags_remove=bug.tags) + bug.refresh() + assert bug.tags == [] + except Exception as e: + if not bz.is_rest(): + raise + assert "No REST API available" in str(e) def test17LoginAPIKey(backends): diff --git a/tests/utils.py b/tests/utils.py index a9dd98dc..cfa1b424 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -10,8 +10,6 @@ import shlex import sys -import pytest - import bugzilla._cli import tests @@ -52,11 +50,6 @@ def open_functional_bz(bzclass, url, kwargs): return bz -def skip_if_rest(bz, msg): - if bz.is_rest(): - pytest.skip(msg) - - def diff_compare(inputdata, filename, expect_out=None): """Compare passed string output to contents of filename""" def _process(data): From ac853a5f9fd815dc91ad5b33aff1e66bb3d1c2e5 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 5 Oct 2021 16:12:14 -0400 Subject: [PATCH 040/106] session: Move tokencache updating into Bugzilla class This is wired deep into the backend layer, but it really should just be done after a successful login() call. Move the tokencache.set_value() call to Bugzilla.login() and adjust everything to match. Fill in some more descriptive interactive_login text while we are here Signed-off-by: Cole Robinson --- bugzilla/_authfiles.py | 2 +- bugzilla/_backendxmlrpc.py | 2 -- bugzilla/_session.py | 2 -- bugzilla/base.py | 11 +++++++++-- tests/data/clioutput/tokenfile.txt | 3 +++ tests/test_api_authfiles.py | 21 +++++++++++---------- tests/test_cli_login.py | 18 +++++++++++++++++- 7 files changed, 41 insertions(+), 18 deletions(-) create mode 100644 tests/data/clioutput/tokenfile.txt diff --git a/bugzilla/_authfiles.py b/bugzilla/_authfiles.py index ae31797d..bdb977e7 100644 --- a/bugzilla/_authfiles.py +++ b/bugzilla/_authfiles.py @@ -147,7 +147,7 @@ def _get_domain(self, url): def get_value(self, url): domain = self._get_domain(url) - if self._cfg.has_option(domain, 'token'): + if domain and self._cfg.has_option(domain, 'token'): return self._cfg.get(domain, 'token') return None diff --git a/bugzilla/_backendxmlrpc.py b/bugzilla/_backendxmlrpc.py index feb7b2f9..0558b350 100644 --- a/bugzilla/_backendxmlrpc.py +++ b/bugzilla/_backendxmlrpc.py @@ -126,8 +126,6 @@ def _ServerProxy__request(self, methodname, params): self, methodname, (authparams,)) # pylint: enable=no-member - if isinstance(ret, dict) and 'token' in ret.keys(): - self.__bugzillasession.set_token_value(ret.get('token')) return ret diff --git a/bugzilla/_session.py b/bugzilla/_session.py index c83fcf15..ce030514 100644 --- a/bugzilla/_session.py +++ b/bugzilla/_session.py @@ -66,8 +66,6 @@ def get_user_agent(self): return self._user_agent def get_scheme(self): return self._scheme - def set_token_value(self, value): - self._tokencache.set_value(self._url, value) def get_auth_params(self): # bugzilla.redhat.com will error if there's auth bits in params diff --git a/bugzilla/base.py b/bugzilla/base.py index 6eb70eb6..68b36833 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -609,6 +609,8 @@ def login(self, user=None, password=None, restrict_login=None): ret = self._backend.user_login(payload) self.password = '' log.info("login succeeded for user=%s", self.user) + if "token" in ret: + self._tokencache.set_value(self.url, ret["token"]) return ret except Exception as e: log.debug("Login exception: %s", str(e), exc_info=True) @@ -666,8 +668,13 @@ def interactive_login(self, user=None, password=None, force=False, log.info('Logging in... ') out = self.login(user, password, restrict_login) msg = "Login successful." - if "token" in out and self.tokenfile: - msg += " Token cache saved to %s" % self.tokenfile + if "token" not in out: + msg += " However no token was returned." + else: + if not self.tokenfile: + msg += " Token not saved to disk." + else: + msg += " Token cache saved to %s" % self.tokenfile if self._get_version() >= 5.0: msg += "\nToken usage is deprecated. " msg += "Consider using bugzilla API keys instead. " diff --git a/tests/data/clioutput/tokenfile.txt b/tests/data/clioutput/tokenfile.txt new file mode 100644 index 00000000..3f4ff578 --- /dev/null +++ b/tests/data/clioutput/tokenfile.txt @@ -0,0 +1,3 @@ +[example.com] +token = my-fake-token + diff --git a/tests/test_api_authfiles.py b/tests/test_api_authfiles.py index 1452f6af..6717385f 100644 --- a/tests/test_api_authfiles.py +++ b/tests/test_api_authfiles.py @@ -13,8 +13,6 @@ import shutil import tempfile -import requests - import tests import tests.mockbackend import tests.utils @@ -116,13 +114,17 @@ def test_authfiles_saving(monkeypatch): bzapi.cert = "foo-fake-path" backend = bzapi._backend # pylint: disable=protected-access bsession = backend._bugzillasession # pylint: disable=protected-access + btokencache = bzapi._tokencache # pylint: disable=protected-access # token testing, with repetitions to hit various code paths - bsession.set_token_value(None) - bsession.set_token_value("MY-FAKE-TOKEN") - bsession.set_token_value("MY-FAKE-TOKEN") - bsession.set_token_value(None) - bsession.set_token_value("MY-FAKE-TOKEN") + btokencache.set_value(bzapi.url, None) + assert "Bugzilla_token" not in bsession.get_auth_params() + btokencache.set_value(bzapi.url, "MY-FAKE-TOKEN") + assert bsession.get_auth_params()["Bugzilla_token"] == "MY-FAKE-TOKEN" + btokencache.set_value(bzapi.url, "MY-FAKE-TOKEN") + btokencache.set_value(bzapi.url, None) + assert "Bugzilla_token" not in bsession.get_auth_params() + btokencache.set_value(bzapi.url, "MY-FAKE-TOKEN") dirname = os.path.dirname(__file__) + "/data/authfiles/" output_token = dirname + "output-token.txt" @@ -155,9 +157,8 @@ def test_authfiles_nowrite(): # Setting values tokenfile is None, should be fine bzapi = tests.mockbackend.make_bz(bz_kwargs={"use_creds": False}) bzapi.connect("https://example.com/foo") - backend = bzapi._backend # pylint: disable=protected-access - bsession = backend._bugzillasession # pylint: disable=protected-access + btokencache = bzapi._tokencache # pylint: disable=protected-access rcfile = bzapi._rcfile # pylint: disable=protected-access - bsession.set_token_value("NEW-TOKEN-VALUE") + btokencache.set_value(bzapi.url, "NEW-TOKEN-VALUE") assert rcfile.save_api_key(bzapi.url, "fookey") is None diff --git a/tests/test_cli_login.py b/tests/test_cli_login.py index 967a92c2..fad2b7dd 100644 --- a/tests/test_cli_login.py +++ b/tests/test_cli_login.py @@ -61,16 +61,32 @@ def test_login(run_cli): # Returns success for logged_in check and hits a tokenfile line cmd = "bugzilla --ensure-logged-in " cmd += "login FOO BAR" + tmp = tempfile.NamedTemporaryFile() fakebz = tests.mockbackend.make_bz( - bz_kwargs={"use_creds": True}, + bz_kwargs={"use_creds": True, "tokenfile": tmp.name}, user_login_args="data/mockargs/test_login.txt", user_login_return={'id': 1234, 'token': 'my-fake-token'}, user_get_args=None, user_get_return={}) + fakebz.connect("https://example.com") out = run_cli(cmd, fakebz) assert "Token cache saved" in out assert fakebz.tokenfile in out assert "Consider using bugzilla API" in out + tests.utils.diff_compare(open(tmp.name).read(), + "data/clioutput/tokenfile.txt") + + # Returns success for logged_in check and hits another tokenfile line + cmd = "bugzilla --ensure-logged-in " + cmd += "login FOO BAR" + fakebz = tests.mockbackend.make_bz( + bz_kwargs={"use_creds": True, "tokenfile": None}, + user_login_args="data/mockargs/test_login.txt", + user_login_return={'id': 1234, 'token': 'my-fake-token'}, + user_get_args=None, + user_get_return={}) + out = run_cli(cmd, fakebz) + assert "Token not saved" in out def test_interactive_login(monkeypatch, run_cli): From acd5e43e09ea79b0476612e06a617d65ec55a6f7 Mon Sep 17 00:00:00 2001 From: Jan Macku Date: Thu, 7 Oct 2021 18:23:36 +0200 Subject: [PATCH 041/106] codecov: Add badge to show current test coverage --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index be52d437..bd2d6d82 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ [![CI](https://github.com/python-bugzilla/python-bugzilla/workflows/CI/badge.svg)](https://github.com/python-bugzilla/python-bugzilla/actions?query=workflow%3ACI) +[![codecov](https://codecov.io/gh/python-bugzilla/python-bugzilla/branch/master/graph/badge.svg?token=4R3FR4RVH4)](https://codecov.io/gh/python-bugzilla/python-bugzilla) [![PyPI](https://img.shields.io/pypi/v/python-bugzilla)](https://pypi.org/project/python-bugzilla/) # python-bugzilla From 79585d67c9824203b6388c9c661f9ff6a0f12ec7 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 12 Jan 2022 13:06:13 -0500 Subject: [PATCH 042/106] tests: Fix API key scraping test Error code changed so we need to relax our string check Signed-off-by: Cole Robinson --- tests/test_ro_functional.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_ro_functional.py b/tests/test_ro_functional.py index 9e0cf9a0..a533863a 100644 --- a/tests/test_ro_functional.py +++ b/tests/test_ro_functional.py @@ -74,13 +74,13 @@ def test_apikey_error_scraping(): with pytest.raises(Exception) as e: _open_bz("https://httpstat.us/502&foo", force_xmlrpc=True, api_key=fakekey) - assert "400 Client Error" in str(e.value) + assert "Client Error" in str(e.value) assert fakekey not in str(e.value) with pytest.raises(Exception) as e: _open_bz("https://httpstat.us/502&foo", force_rest=True, api_key=fakekey) - assert "400 Client Error" in str(e.value) + assert "Client Error" in str(e.value) assert fakekey not in str(e.value) From cd701f6879e7868c0b6b4b37857994b01b88eda1 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 12 Jan 2022 13:09:08 -0500 Subject: [PATCH 043/106] Prep for release 3.2.0 Signed-off-by: Cole Robinson --- NEWS.md | 4 ++++ bugzilla/apiversion.py | 2 +- python-bugzilla.spec | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index fba19ea3..e642fbf1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # python-bugzilla release news +## Release 3.2.0 (January 12, 2022) +- Use soon-to-be-required Authorization header for RH bugzilla +- Remove cookie auth support + ## Release 3.1.0 (July 27, 2021) - Detect bugzilla.stage.redhat.com as RHBugzilla - Add limit as option to build_query (Ivan Lausuch) diff --git a/bugzilla/apiversion.py b/bugzilla/apiversion.py index 38fdb64b..3a6d3e83 100644 --- a/bugzilla/apiversion.py +++ b/bugzilla/apiversion.py @@ -4,5 +4,5 @@ # This work is licensed under the GNU GPLv2 or later. # See the COPYING file in the top-level directory. -version = "3.1.0" +version = "3.2.0" __version__ = version diff --git a/python-bugzilla.spec b/python-bugzilla.spec index 660f8109..fe4e459f 100644 --- a/python-bugzilla.spec +++ b/python-bugzilla.spec @@ -1,5 +1,5 @@ Name: python-bugzilla -Version: 3.1.0 +Version: 3.2.0 Release: 1%{?dist} Summary: Python library for interacting with Bugzilla From a7c324041175a4157823bc2332a046cc2a54d105 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 27 Jan 2022 11:22:28 -0500 Subject: [PATCH 044/106] README.md: Rename 'master' branch to 'main' Signed-off-by: Cole Robinson --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd2d6d82..4c40be66 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![CI](https://github.com/python-bugzilla/python-bugzilla/workflows/CI/badge.svg)](https://github.com/python-bugzilla/python-bugzilla/actions?query=workflow%3ACI) -[![codecov](https://codecov.io/gh/python-bugzilla/python-bugzilla/branch/master/graph/badge.svg?token=4R3FR4RVH4)](https://codecov.io/gh/python-bugzilla/python-bugzilla) +[![codecov](https://codecov.io/gh/python-bugzilla/python-bugzilla/branch/main/graph/badge.svg?token=4R3FR4RVH4)](https://codecov.io/gh/python-bugzilla/python-bugzilla) [![PyPI](https://img.shields.io/pypi/v/python-bugzilla)](https://pypi.org/project/python-bugzilla/) # python-bugzilla From 6de112f4851b2fe0ff93aee39ee4f16a1c8fc983 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 9 Aug 2023 10:48:04 -0400 Subject: [PATCH 045/106] ci: modernize a bit - Use newer github action versions - Build against latest python 3.x version - Pin ubuntu-20.04 so we can get python 3.6 testing for rhel/centos8 - Reduce the testing matrix a bit - Drop redundant codecov params Signed-off-by: Cole Robinson --- .github/workflows/build.yml | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f4a643ec..2af21f8c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,16 +8,19 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-latest + # We stick with 20.04 to get access to python 3.6 + # https://github.com/actions/setup-python/issues/544 + runs-on: ubuntu-20.04 strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8] + # python 3.6 is for rhel/centos8 compat + python-version: ["3.6", "3.x"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} @@ -39,23 +42,21 @@ jobs: pytest --cov --cov-report=xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 - with: - file: ./coverage.xml - flags: unittests + uses: codecov/codecov-action@v3 + # Build and install on Windows windows: runs-on: windows-latest strategy: matrix: - python-version: [3.8] + python-version: ["3.x"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} From 138caf8aa72757329aa92c1acc66b4302486ac35 Mon Sep 17 00:00:00 2001 From: Stanislav Levin Date: Fri, 21 Oct 2022 15:38:51 +0300 Subject: [PATCH 046/106] rest api: Post process bugzilla code on HTTP error Bugzilla REST API map result codes to HTTP status codes: https://github.com/bugzilla/bugzilla/blob/7581e08f9136ec32219af6c3192e42ff1c8e9691/Bugzilla/WebService/Constants.pm#L262-L287 But python-bugzilla don't propagate those Bugzilla codes. Fixes: https://github.com/python-bugzilla/python-bugzilla/issues/171 Signed-off-by: Stanislav Levin --- bugzilla/_backendrest.py | 28 +++++++++++++++++++++++++--- bugzilla/_session.py | 8 ++++++-- bugzilla/exceptions.py | 5 +++++ tests/test_rw_functional.py | 4 ++-- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/bugzilla/_backendrest.py b/bugzilla/_backendrest.py index 3abe49c7..d90da35f 100644 --- a/bugzilla/_backendrest.py +++ b/bugzilla/_backendrest.py @@ -7,7 +7,7 @@ import os from ._backendbase import _BackendBase -from .exceptions import BugzillaError +from .exceptions import BugzillaError, BugzillaHTTPError from ._util import listify @@ -32,6 +32,23 @@ def __init__(self, url, bugzillasession): ######################### # Internal REST helpers # ######################### + def _handle_error(self, e): + response = getattr(e, "response", None) + if response is None: + raise e + + if response.status_code in [400, 401, 404]: + self._handle_error_response(response.text) + raise e + + def _handle_error_response(self, text): + try: + result = json.loads(text) + except json.JSONDecodeError: + return + + if result.get("error"): + raise BugzillaError(result["message"], code=result["code"]) def _handle_response(self, text): try: @@ -55,8 +72,13 @@ def _op(self, method, apiurl, paramdict=None): else: data = json.dumps(paramdict or {}) - response = self._bugzillasession.request(method, fullurl, data=data, - params=authparams) + try: + response = self._bugzillasession.request( + method, fullurl, data=data, params=authparams + ) + except BugzillaHTTPError as e: + self._handle_error(e) + return self._handle_response(response.text) def _get(self, *args, **kwargs): diff --git a/bugzilla/_session.py b/bugzilla/_session.py index ce030514..8006bbde 100644 --- a/bugzilla/_session.py +++ b/bugzilla/_session.py @@ -9,6 +9,7 @@ import requests +from .exceptions import BugzillaHTTPError log = getLogger(__name__) @@ -106,9 +107,12 @@ def request(self, *args, **kwargs): try: response.raise_for_status() - except Exception as e: + except requests.HTTPError as e: # Scrape the api key out of the returned exception string message = str(e).replace(self._api_key or "", "") - raise type(e)(message).with_traceback(sys.exc_info()[2]) + response = getattr(e, "response", None) + raise BugzillaHTTPError(message, response=response).with_traceback( + sys.exc_info()[2] + ) return response diff --git a/bugzilla/exceptions.py b/bugzilla/exceptions.py index d884df0a..2562dc45 100644 --- a/bugzilla/exceptions.py +++ b/bugzilla/exceptions.py @@ -1,5 +1,6 @@ # This work is licensed under the GNU GPLv2 or later. # See the COPYING file in the top-level directory. +from requests import HTTPError class BugzillaError(Exception): @@ -36,3 +37,7 @@ def __init__(self, message, code=None): if self.code: message += " (code=%s)" % self.code Exception.__init__(self, message) + + +class BugzillaHTTPError(HTTPError): + """Error raised in the Bugzilla session""" diff --git a/tests/test_rw_functional.py b/tests/test_rw_functional.py index b7d9cfff..ce01ac37 100644 --- a/tests/test_rw_functional.py +++ b/tests/test_rw_functional.py @@ -50,8 +50,8 @@ def _check_have_admin(bz): return ret -def test0LoggedInNoCreds(): - bz = _open_bz(use_creds=False) +def test0LoggedInNoCreds(backends): + bz = _open_bz(**backends, use_creds=False) assert not bz.logged_in From 0135f97336d24300d47b89c38caeff1f4f2f58f7 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sun, 20 Aug 2023 07:38:42 -0400 Subject: [PATCH 047/106] tests: Fix updateperms test This had no chance of working since 4d6c31e7, but I've never bothered getting admin permissions back from RHBZ admins to confirm :/ Signed-off-by: Cole Robinson --- tests/test_rw_functional.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_rw_functional.py b/tests/test_rw_functional.py index ce01ac37..4e600393 100644 --- a/tests/test_rw_functional.py +++ b/tests/test_rw_functional.py @@ -696,8 +696,8 @@ def test11UserUpdate(backends): # Test group_get try: - group = bz.getgroup("fedora_contrib") - group.refresh() + groupobj = bz.getgroup(group) + groupobj.refresh() except Exception as e: if have_admin: raise From f3c019c4920a4f3206915e6c8a98fa951a861dd2 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sun, 20 Aug 2023 09:03:07 -0400 Subject: [PATCH 048/106] Use timeout=10 for probe() URL detection method Signed-off-by: Cole Robinson --- bugzilla/_backendbase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bugzilla/_backendbase.py b/bugzilla/_backendbase.py index b81e1082..8fd9a80e 100644 --- a/bugzilla/_backendbase.py +++ b/bugzilla/_backendbase.py @@ -22,7 +22,7 @@ def __init__(self, url, bugzillasession): @staticmethod def probe(url): try: - requests.head(url).raise_for_status() + requests.head(url, timeout=10).raise_for_status() return True # pragma: no cover except Exception as e: log.debug("Failed to probe url=%s : %s", url, str(e)) From 5e8b2c86cc3d5dfe949b34195336ecf24f61afb6 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sun, 20 Aug 2023 09:04:54 -0400 Subject: [PATCH 049/106] backendrest: Add coverage annotations Signed-off-by: Cole Robinson --- bugzilla/_backendrest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bugzilla/_backendrest.py b/bugzilla/_backendrest.py index d90da35f..a14c4c03 100644 --- a/bugzilla/_backendrest.py +++ b/bugzilla/_backendrest.py @@ -35,7 +35,7 @@ def __init__(self, url, bugzillasession): def _handle_error(self, e): response = getattr(e, "response", None) if response is None: - raise e + raise e # pragma: no cover if response.status_code in [400, 401, 404]: self._handle_error_response(response.text) @@ -57,7 +57,7 @@ def _handle_response(self, text): log.debug("Failed to parse REST response. Output is:\n%s", text) raise - if ret.get("error", False): + if ret.get("error", False): # pragma: no cover raise BugzillaError(ret["message"], code=ret["code"]) return ret From 51d6fd0728338e98502bfb2e6faccfbed3afb991 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sun, 20 Aug 2023 09:07:26 -0400 Subject: [PATCH 050/106] Fix pylint and pep8 issues Signed-off-by: Cole Robinson --- .pylintrc | 2 +- bugzilla/_cli.py | 2 +- bugzilla/_session.py | 2 +- bugzilla/base.py | 4 ++-- bugzilla/bug.py | 2 +- tests/test_api_authfiles.py | 4 ++-- tests/test_api_bug.py | 2 +- tests/test_api_misc.py | 1 + tests/test_rw_functional.py | 1 + 9 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.pylintrc b/.pylintrc index efdb0d1f..43ac9c2c 100644 --- a/.pylintrc +++ b/.pylintrc @@ -7,7 +7,7 @@ persistent=no # can either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). -disable=Design,Format,Similarities,invalid-name,missing-docstring,locally-disabled,unnecessary-lambda,star-args,fixme,global-statement,broad-except,no-self-use,bare-except,locally-enabled,wrong-import-position,consider-using-ternary,len-as-condition,no-else-return,useless-object-inheritance,inconsistent-return-statements,consider-using-dict-comprehension,import-outside-toplevel,use-a-generator,consider-using-with,consider-using-f-string,unspecified-encoding +disable=Design,Format,Similarities,invalid-name,missing-docstring,locally-disabled,unnecessary-lambda,fixme,global-statement,broad-except,bare-except,wrong-import-position,consider-using-ternary,len-as-condition,no-else-return,useless-object-inheritance,inconsistent-return-statements,consider-using-dict-comprehension,import-outside-toplevel,use-a-generator,consider-using-with,consider-using-f-string,unspecified-encoding enable=fixme diff --git a/bugzilla/_cli.py b/bugzilla/_cli.py index d5035cc2..d0688809 100755 --- a/bugzilla/_cli.py +++ b/bugzilla/_cli.py @@ -1047,7 +1047,7 @@ def _do_modify(bz, parser, opt): for k, v in wbmap.copy().items(): if not v[0] and not v[1]: - del(wbmap[k]) + del wbmap[k] if opt.fields: _merge_field_opts(update, opt.fields, parser) diff --git a/bugzilla/_session.py b/bugzilla/_session.py index 8006bbde..98064671 100644 --- a/bugzilla/_session.py +++ b/bugzilla/_session.py @@ -31,7 +31,7 @@ def __init__(self, url, user_agent, self._use_auth_bearer = False if self._scheme not in ["http", "https"]: - raise Exception("Invalid URL scheme: %s (%s)" % ( + raise ValueError("Invalid URL scheme: %s (%s)" % ( self._scheme, url)) self._session = requests_session diff --git a/bugzilla/base.py b/bugzilla/base.py index 68b36833..ca638f53 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -1302,7 +1302,7 @@ def add_email(key, value, count): # Strip out None elements in the dict for k, v in query.copy().items(): if v is None: - del(query[k]) + del query[k] self.pre_translation(query) return query @@ -1799,7 +1799,7 @@ def _validate_createbug(self, *args, **kwargs): # Back compat handling for check_args if "check_args" in data: - del(data["check_args"]) + del data["check_args"] return data diff --git a/bugzilla/bug.py b/bugzilla/bug.py index e6c457fc..282e6052 100644 --- a/bugzilla/bug.py +++ b/bugzilla/bug.py @@ -136,7 +136,7 @@ def _translate_dict(self, newdict): "d[%s]=%s and d[%s]=%s , dropping the value " "d[%s]", newname, newdict[newname], oldname, newdict[oldname], oldname) - del(newdict[oldname]) + del newdict[oldname] def _update_dict(self, newdict): diff --git a/tests/test_api_authfiles.py b/tests/test_api_authfiles.py index 6717385f..fcd6fbd8 100644 --- a/tests/test_api_authfiles.py +++ b/tests/test_api_authfiles.py @@ -26,7 +26,7 @@ def test_tokenfile(monkeypatch): token = dirname + "/data/homedir/.cache/python-bugzilla/bugzillatoken" assert token == bz.tokenfile - del(bz.tokenfile) + del bz.tokenfile assert bz.tokenfile is None assert bz.cookiefile is None @@ -96,7 +96,7 @@ def _write(c): # Test confipath overwrite assert [temp.name] == bzapi.configpath - del(bzapi.configpath) + del bzapi.configpath assert [] == bzapi.configpath bzapi.readconfig() _check(None, None, None, None) diff --git a/tests/test_api_bug.py b/tests/test_api_bug.py index 2f762453..e4f88b35 100644 --- a/tests/test_api_bug.py +++ b/tests/test_api_bug.py @@ -106,7 +106,7 @@ def test_bug_getattr(): bug.autorefresh = True summary = bug.summary - del(bug.__dict__["summary"]) + del bug.__dict__["summary"] # Trigger autorefresh assert bug.summary == summary diff --git a/tests/test_api_misc.py b/tests/test_api_misc.py index 75814e35..dfb0e923 100644 --- a/tests/test_api_misc.py +++ b/tests/test_api_misc.py @@ -164,6 +164,7 @@ def testStandardQuery(): } assert bz4.url_to_query(url) == query + # pylint: disable=use-implicit-booleaness-not-comparison # Test with unknown URL assert bz4.url_to_query("https://example.com") == {} diff --git a/tests/test_rw_functional.py b/tests/test_rw_functional.py index 4e600393..5e40b004 100644 --- a/tests/test_rw_functional.py +++ b/tests/test_rw_functional.py @@ -363,6 +363,7 @@ def cleardict_new(b): bz.update_flags(bug2.id, cleardict_new(bug2)) bug2.refresh() + # pylint: disable=use-implicit-booleaness-not-comparison assert cleardict_old(bug1) == {} assert cleardict_old(bug2) == {} From b591b4ef90ed06b550b87503b98d19094fb8c1de Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sun, 20 Aug 2023 09:44:49 -0400 Subject: [PATCH 051/106] codecov: Exclude _session.py, which won't be covered by github CI Signed-off-by: Cole Robinson --- codecov.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/codecov.yml b/codecov.yml index fbd9242a..4aa11c0f 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,4 +1,6 @@ -# The files aren't interesting for the unit tests run in CI +# These files will only get full coverage from running the functional +# tests, but those aren't run from CI ignore: - "bugzilla/_backendrest.py" - "bugzilla/_backendxmlrpc.py" + - "bugzilla/_session.py" From c6bfae507c79a66a062fca65a0b5e770a568803f Mon Sep 17 00:00:00 2001 From: "Danilo C. L. de Paula" Date: Thu, 2 Jun 2022 10:01:53 -0400 Subject: [PATCH 052/106] docs: fixing wrong documentation link This solves the problem found in #161 The old link referred to the deprecated APIs. --- man/bugzilla.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/bugzilla.rst b/man/bugzilla.rst index 5d790eca..38f484bb 100644 --- a/man/bugzilla.rst +++ b/man/bugzilla.rst @@ -859,4 +859,4 @@ SEE ALSO ======== https://bugzilla.readthedocs.io/en/latest/api/index.html -https://bugzilla.redhat.com/docs/en/html/api/Bugzilla/WebService/Bug.html +https://bugzilla.redhat.com/docs/en/html/api/core/v1/bug.html From 7798f8176b6575575ec00172ec34c87aa577ad4f Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Mon, 3 Jul 2023 16:51:53 +0200 Subject: [PATCH 053/106] Set `Bug.weburl` that is compatible with the REST API (fixes #178) Instead of replacing the substring 'xmlrpc.cgi' in the Bugzilla URL, the URL is now constructed by explicitly using the `netloc` of the Bugzilla URL. --- bugzilla/bug.py | 13 +++++++++++-- tests/data/clioutput/test_query2.txt | 2 +- tests/test_api_bug.py | 9 +++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/bugzilla/bug.py b/bugzilla/bug.py index 282e6052..ec0e9c00 100644 --- a/bugzilla/bug.py +++ b/bugzilla/bug.py @@ -6,6 +6,7 @@ import copy from logging import getLogger +from urllib.parse import urlparse, urlunparse log = getLogger(__name__) @@ -39,8 +40,16 @@ def __init__(self, bugzilla, bug_id=None, dict=None, autorefresh=False): dict["id"] = bug_id self._update_dict(dict) - self.weburl = bugzilla.url.replace('xmlrpc.cgi', - 'show_bug.cgi?id=%i' % self.bug_id) + self.weburl = self._generate_weburl() + + def _generate_weburl(self): + """ + Generate the URL to the bug in the web UI + """ + parsed = urlparse(self.bugzilla.url) + return urlunparse((parsed.scheme, parsed.netloc, + 'show_bug.cgi', '', 'id=%s' % self.bug_id, + '')) def __str__(self): """ diff --git a/tests/data/clioutput/test_query2.txt b/tests/data/clioutput/test_query2.txt index f7a3b723..d959145f 100644 --- a/tests/data/clioutput/test_query2.txt +++ b/tests/data/clioutput/test_query2.txt @@ -57,7 +57,7 @@ ATTRIBUTE[target_milestone]: rc ATTRIBUTE[target_release]: ['---'] ATTRIBUTE[url]: ATTRIBUTE[version]: ['5.8'] -ATTRIBUTE[weburl]: https:///TESTSUITEMOCK +ATTRIBUTE[weburl]: https:///show_bug.cgi?id=1165434 ATTRIBUTE[whiteboard]: genericwhiteboard diff --git a/tests/test_api_bug.py b/tests/test_api_bug.py index e4f88b35..47391d46 100644 --- a/tests/test_api_bug.py +++ b/tests/test_api_bug.py @@ -184,3 +184,12 @@ def _get_fake_bug(apiname): attachments = bug.get_attachments() bug.attachments = attachments assert [469147, 470041, 502352] == bug.get_attachment_ids() + + +def test_bug_weburl(): + fakebz = tests.mockbackend.make_bz( + bug_get_args=None, + bug_get_return="data/mockreturn/test_getbug_rhel.txt") + bug_id = 1165434 + bug = fakebz.getbug(bug_id) + assert bug.weburl == f"https:///show_bug.cgi?id={bug_id}" From 9363b2dce55077bcac4aef560ffc9bb15805445d Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 6 Sep 2023 10:46:21 -0400 Subject: [PATCH 054/106] tests: Add functional test for bug.weburl generation Signed-off-by: Cole Robinson --- tests/test_ro_functional.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_ro_functional.py b/tests/test_ro_functional.py index a533863a..4bc31dff 100644 --- a/tests/test_ro_functional.py +++ b/tests/test_ro_functional.py @@ -414,3 +414,12 @@ def test_redhat_version(backends): if not tests.CLICONFIG.REDHAT_URL: _test_version(bz, bzversion) + + +def test_bug_misc(backends): + bz = _open_bz(REDHAT_URL, **backends) + + # Ensure weburl is generated consistently whether + # we are using XMLRPC or REST + bug = bz.getbug(720773) + assert bug.weburl == "https://bugzilla.redhat.com/show_bug.cgi?id=720773" From 55279176be97f6041af4ee86c5346ebf4a1ebdbc Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sun, 20 Aug 2023 09:44:49 -0400 Subject: [PATCH 055/106] codecov: Exclude _session.py, which won't be covered by github CI Signed-off-by: Cole Robinson --- tests/test_rw_functional.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/test_rw_functional.py b/tests/test_rw_functional.py index 5e40b004..af240e76 100644 --- a/tests/test_rw_functional.py +++ b/tests/test_rw_functional.py @@ -116,18 +116,19 @@ def test04NewBugAllFields(run_cli, backends): blocked = "461686,461687" dependson = "427301" comment = "Test bug from python-bugzilla test suite" - sub_component = "Command-line tools (RHEL6)" + component = "Extensions" + sub_component = "AgileTools" alias = "pybz-%s" % datetime.datetime.today().strftime("%s") newout = run_cli("bugzilla new " - "--product 'Red Hat Enterprise Linux 6' --version 6.0 " - "--component lvm2 --sub-component '%s' " + "--product 'Bugzilla' --version 5.0 " + "--component %s --sub-component '%s' " "--summary \"%s\" " "--comment \"%s\" " "--url %s --severity Urgent --priority Low --os %s " "--arch ppc --cc %s --blocked %s --dependson %s " "--alias %s " "--outputformat \"%%{bug_id}\"" % - (sub_component, summary, comment, url, + (component, sub_component, summary, comment, url, osval, cc, blocked, dependson, alias), bz) assert len(newout.splitlines()) == 1 @@ -139,11 +140,12 @@ def test04NewBugAllFields(run_cli, backends): assert bug.summary == summary assert bug.bug_file_loc == url assert bug.op_sys == osval - assert bug.blocks == _split_int(blocked) - assert bug.depends_on == _split_int(dependson) + # Using a non-RH account seems to fail to set these at bug create time + # assert bug.blocks == _split_int(blocked) + # assert bug.depends_on == _split_int(dependson) assert all([e in bug.cc for e in cc.split(",")]) assert bug.longdescs[0]["text"] == comment - assert bug.sub_components == {"lvm2": [sub_component]} + assert bug.sub_components == {component: [sub_component]} assert bug.alias == [alias] # Close the bug From 4b521037c2ab7258e9c1e7d1df7d755c3e596fa5 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 7 Sep 2023 11:34:28 -0400 Subject: [PATCH 056/106] tests: rw: Make things pass without rhbz dev permissions All these tests were written with implicit Fedora and RHEL developer permissions, since that's what my bugzilla.redhat.com account has. This is a big rework to make things pass and get maximum test coverage with an unprivileged account. The main bit is we need to use more self created bugs, vs pre-existing bugs. This necessitates a bunch of messy changes scattered everywhere Signed-off-by: Cole Robinson --- tests/test_rw_functional.py | 509 +++++++++++++++++++++--------------- 1 file changed, 301 insertions(+), 208 deletions(-) diff --git a/tests/test_rw_functional.py b/tests/test_rw_functional.py index af240e76..3d49688e 100644 --- a/tests/test_rw_functional.py +++ b/tests/test_rw_functional.py @@ -25,6 +25,10 @@ RHURL = tests.CLICONFIG.REDHAT_URL or "bugzilla.stage.redhat.com" +################## +# helper methods # +################## + def _split_int(s): return [int(i) for i in s.split(",")] @@ -50,30 +54,41 @@ def _check_have_admin(bz): return ret -def test0LoggedInNoCreds(backends): - bz = _open_bz(**backends, use_creds=False) - assert not bz.logged_in +def _set_have_dev(bug, assigned_to): + # This will only take effect if the logged in user has fedora dev perms + have_dev = bug.assigned_to == assigned_to + bug._testsuite_have_dev = have_dev # pylint: disable=protected-access -def test0ClassDetection(): - bz = bugzilla.Bugzilla(RHURL, use_creds=False) - assert bz.__class__ is bugzilla.RHBugzilla +def _bug_close(run_cli, bug): + # Pre-close it + bz = bug.bugzilla + run_cli("bugzilla modify --close NOTABUG %s --minor-update" % bug.id, bz) + bug.refresh() + assert bug.status == "CLOSED" + assert bug.resolution == "NOTABUG" def _makebug(run_cli, bz): + """ + Make a basic bug that the logged in user can maximally manipulate + """ + product = "Fedora" component = "python-bugzilla" version = "rawhide" + assigned_to = "triage@lists.fedoraproject.org" summary = ("python-bugzilla test basic bug %s" % datetime.datetime.today()) newout = run_cli("bugzilla new " - "--product Fedora --component %s --version %s " - "--summary \"%s\" " + f"--product '{product}' " + f"--component '{component}' " + f"--version '{version}' " + f"--assigned_to '{assigned_to}' " + f"--summary \"{summary}\" " "--comment \"Test bug from the python-bugzilla test suite\" " - "--outputformat \"%%{bug_id}\"" % - (component, version, summary), bz) + "--outputformat \"%{bug_id}\"", bz) - assert len(newout.splitlines()) == 1 - bugid = int(newout.splitlines()[0]) + bugid = int(newout.splitlines()[-1]) bug = bz.getbug(bugid) print("\nCreated bugid: %s" % bug.id) @@ -81,86 +96,129 @@ def _makebug(run_cli, bz): assert bug.version == version assert bug.summary == summary + _set_have_dev(bug, assigned_to) + _bug_close(run_cli, bug) + return bug -def test03NewBugBasic(run_cli, backends): - """ - Create a bug with minimal amount of fields, then close it - """ - bz = _open_bz(**backends) - bug = _makebug(run_cli, bz) +def _check_have_dev(bug): + funcname = inspect.stack()[1][3] + have_dev = bug._testsuite_have_dev # pylint: disable=protected-access - # Verify hasattr works - assert hasattr(bug, "id") - assert hasattr(bug, "bug_id") + if not have_dev: + print("\nNo dev privs, reduced testing of %s" % funcname) + return have_dev - # Close the bug - run_cli("bugzilla modify --close NOTABUG %s --minor-update" % bug.id, bz) - bug.refresh() - assert bug.status == "CLOSED" - assert bug.resolution == "NOTABUG" +class _BugCache: + cache = {} -def test04NewBugAllFields(run_cli, backends): + @classmethod + def get_bug(cls, run_cli, bz): + key = bz.is_xmlrpc() and "xmlrpc" or "rest" + if key not in cls.cache: + cls.cache[key] = _makebug(run_cli, bz) + return cls.cache[key] + + +def _make_subcomponent_bug(run_cli, bz): """ - Create a bug using all 'new' fields, check some values, close it + Helper for creating a bug that can handle rhbz sub components """ - bz = _open_bz(**backends) - summary = ("python-bugzilla test manyfields bug %s" % datetime.datetime.today()) + assigned_to = "triage@lists.fedoraproject.org" url = "http://example.com" osval = "Windows" cc = "triage@lists.fedoraproject.org" + assigned_to = "triage@lists.fedoraproject.org" blocked = "461686,461687" dependson = "427301" comment = "Test bug from python-bugzilla test suite" + # We use this product+component to test sub_component + product = "Bugzilla" component = "Extensions" + version = "5.0" sub_component = "AgileTools" alias = "pybz-%s" % datetime.datetime.today().strftime("%s") newout = run_cli("bugzilla new " - "--product 'Bugzilla' --version 5.0 " - "--component %s --sub-component '%s' " - "--summary \"%s\" " - "--comment \"%s\" " - "--url %s --severity Urgent --priority Low --os %s " - "--arch ppc --cc %s --blocked %s --dependson %s " - "--alias %s " - "--outputformat \"%%{bug_id}\"" % - (component, sub_component, summary, comment, url, - osval, cc, blocked, dependson, alias), bz) - - assert len(newout.splitlines()) == 1 - - bugid = int(newout.splitlines()[0]) + f"--product '{product}' " + f"--version '{version}' " + f"--component '{component}' " + f"--sub-component '{sub_component}' " + f"--summary \"{summary}\" " + f"--comment \"{comment}\" " + f"--url {url} " + f"--os {osval} " + f"--cc {cc} " + f"--assigned_to {assigned_to} " + f"--blocked {blocked} " + f"--dependson {dependson} " + f"--alias {alias} " + "--arch ppc --severity Urgent --priority Low " + "--outputformat \"%{bug_id}\"", bz) + + bugid = int(newout.splitlines()[-1]) bug = bz.getbug(bugid, extra_fields=["sub_components"]) print("\nCreated bugid: %s" % bugid) + _set_have_dev(bug, assigned_to) + have_dev = _check_have_dev(bug) + assert bug.summary == summary assert bug.bug_file_loc == url assert bug.op_sys == osval - # Using a non-RH account seems to fail to set these at bug create time - # assert bug.blocks == _split_int(blocked) - # assert bug.depends_on == _split_int(dependson) assert all([e in bug.cc for e in cc.split(",")]) assert bug.longdescs[0]["text"] == comment assert bug.sub_components == {component: [sub_component]} assert bug.alias == [alias] - # Close the bug + if have_dev: + assert bug.blocks == _split_int(blocked) + assert bug.depends_on == _split_int(dependson) + else: + # Using a non-dev account seems to fail to set these at bug create time + assert bug.blocks == [] + assert bug.depends_on == [] + + _bug_close(run_cli, bug) + + return bug + + +############## +# test cases # +############## + +def test0LoggedInNoCreds(backends): + bz = _open_bz(**backends, use_creds=False) + assert not bz.logged_in - # RHBZ makes it difficult to provide consistent semantics for - # 'alias' update: - # https://bugzilla.redhat.com/show_bug.cgi?id=1173114 - # alias += "-closed" + +def test0ClassDetection(): + bz = bugzilla.Bugzilla(RHURL, use_creds=False) + assert bz.__class__ is bugzilla.RHBugzilla + + +def test04NewBugAllFields(run_cli, backends): + """ + Create a bug using all 'new' fields, check some values, close it + """ + bz = _open_bz(**backends) + bug = _make_subcomponent_bug(run_cli, bz) + + # Verify hasattr works + assert hasattr(bug, "id") + assert hasattr(bug, "bug_id") + + # Close the bug run_cli("bugzilla modify " "--close WONTFIX %s " % - bugid, bz) + bug.id, bz) bug.refresh() assert bug.status == "CLOSED" assert bug.resolution == "WONTFIX" - assert bug.alias == [alias] # Check bug's minimal history ret = bug.get_history_raw() @@ -173,44 +231,53 @@ def test05ModifyStatus(run_cli, backends): Modify status and comment fields for an existing bug """ bz = _open_bz(**backends) - bugid = "663674" - cmd = "bugzilla modify %s " % bugid - - bug = bz.getbug(bugid) - - # We want to start with an open bug, so fix things - if bug.status == "CLOSED": - run_cli(cmd + "--status ASSIGNED", bz) - bug.refresh() - assert bug.status == "ASSIGNED" + bug = _BugCache.get_bug(run_cli, bz) + have_dev = _check_have_dev(bug) + cmd = "bugzilla modify %s " % bug.id origstatus = bug.status + perm_error = "not allowed to (un)mark comments" # Set to ON_QA with a private comment - status = "ON_QA" - comment = ("changing status to %s at %s" % - (status, datetime.datetime.today())) - run_cli(cmd + - "--status %s --comment \"%s\" --private" % (status, comment), bz) + try: + status = "ON_QA" + comment = ("changing status to %s at %s" % + (status, datetime.datetime.today())) + run_cli(cmd + + "--status %s --comment \"%s\" --private" % (status, comment), bz) - bug.refresh() - assert bug.status == status - assert bug.longdescs[-1]["is_private"] == 1 - assert bug.longdescs[-1]["text"] == comment + bug.refresh() + assert bug.status == status + assert bug.longdescs[-1]["is_private"] == 1 + assert bug.longdescs[-1]["text"] == comment + except RuntimeError as e: + if have_dev: + raise + assert perm_error in str(e) # Close bug as DEFERRED with a private comment - resolution = "DEFERRED" - comment = ("changing status to CLOSED=%s at %s" % - (resolution, datetime.datetime.today())) - run_cli(cmd + - "--close %s --comment \"%s\" --private" % - (resolution, comment), bz) + try: + resolution = "DEFERRED" + comment = ("changing status to CLOSED=%s at %s" % + (resolution, datetime.datetime.today())) + run_cli(cmd + + "--close %s --comment \"%s\" --private" % + (resolution, comment), bz) + bug.refresh() + assert bug.status == "CLOSED" + assert bug.resolution == resolution + assert bug.comments[-1]["is_private"] == 1 + assert bug.comments[-1]["text"] == comment + except RuntimeError as e: + if have_dev: + raise + assert perm_error in str(e) + + # Set to assigned + run_cli(cmd + "--status ASSIGNED", bz) bug.refresh() - assert bug.status == "CLOSED" - assert bug.resolution == resolution - assert bug.comments[-1]["is_private"] == 1 - assert bug.comments[-1]["text"] == comment + assert bug.status == "ASSIGNED" # Close bug as dup with no comment dupeid = "461686" @@ -224,12 +291,17 @@ def test05ModifyStatus(run_cli, backends): assert "marked as a duplicate" in bug.longdescs[-1]["text"] # bz.setstatus test - comment = ("adding lone comment at %s" % datetime.datetime.today()) - bug.setstatus("POST", comment=comment, private=True) - bug.refresh() - assert bug.longdescs[-1]["is_private"] == 1 - assert bug.longdescs[-1]["text"] == comment - assert bug.status == "POST" + try: + comment = ("adding lone comment at %s" % datetime.datetime.today()) + bug.setstatus("POST", comment=comment, private=True) + bug.refresh() + assert bug.longdescs[-1]["is_private"] == 1 + assert bug.longdescs[-1]["text"] == comment + assert bug.status == "POST" + except Exception as e: + if have_dev: + raise + assert perm_error in str(e) # bz.close test fixed_in = str(datetime.datetime.today()) @@ -260,56 +332,69 @@ def test06ModifyEmails(run_cli, backends): Modify cc, assignee, qa_contact for existing bug """ bz = _open_bz(**backends) - bugid = "663674" - cmd = "bugzilla modify %s " % bugid - - bug = bz.getbug(bugid) + bug = _BugCache.get_bug(run_cli, bz) + user = bug.creator + have_dev = _check_have_dev(bug) - origcc = bug.cc + cmd = "bugzilla modify %s " % bug.id # Test CC list and reset it email1 = "triage@lists.fedoraproject.org" - email2 = "crobinso@redhat.com" - bug.deletecc(origcc) - run_cli(cmd + "--cc %s --cc %s" % (email1, email2), bz) - bug.addcc(email1) - + run_cli(cmd + "--cc %s --cc %s" % (email1, user), bz) bug.refresh() assert email1 in bug.cc - assert email2 in bug.cc - assert len(bug.cc) == 2 + assert user in bug.cc - run_cli(cmd + "--cc=-%s" % email1, bz) + # Remove CC via command line + # Unprivileged user can only add/remove their own CC value + run_cli(cmd + "--cc=-%s" % user, bz) bug.refresh() - assert email1 not in bug.cc + assert user not in bug.cc - # Test assigned target - run_cli(cmd + "--assignee %s" % email1, bz) + # Re-add CC via API + bug.addcc(user) bug.refresh() - assert bug.assigned_to == email1 + assert user in bug.cc - # Test QA target - run_cli(cmd + "--qa_contact %s" % email1, bz) + # Remove it again, via API + bug.deletecc(user) bug.refresh() - assert bug.qa_contact == email1 + assert user not in bug.cc + assert bug.cc - # Reset values - bug.deletecc(bug.cc) - run_cli(cmd + "--reset-qa-contact --reset-assignee", bz) + perm_error = "required permissions may change that field" - bug.refresh() - assert bug.cc == [] - assert bug.assigned_to == "crobinso@redhat.com" - assert bug.qa_contact == "extras-qa@fedoraproject.org" + # Test assigned and QA target + try: + run_cli(cmd + "--assignee %s --qa_contact %s" % (email1, email1), bz) + bug.refresh() + assert bug.assigned_to == email1 + assert bug.qa_contact == email1 + except RuntimeError as e: + if have_dev: + raise + assert perm_error in str(e) + + + # Test --reset options + try: + run_cli(cmd + "--reset-qa-contact --reset-assignee", bz) + bug.refresh() + assert bug.assigned_to != email1 + assert bug.qa_contact != email1 + except RuntimeError as e: + if have_dev: + raise + assert perm_error in str(e) -def test07ModifyMultiFlags(run_cli, backends): +def test070ModifyMultiFlags(run_cli, backends): """ Modify flags and fixed_in for 2 bugs """ bz = _open_bz(**backends) - bugid1 = "461686" - bugid2 = "461687" + bugid1 = _BugCache.get_bug(run_cli, bz).id + bugid2 = _makebug(run_cli, bz).id cmd = "bugzilla modify %s %s " % (bugid1, bugid2) def flagstr(b): @@ -345,7 +430,7 @@ def cleardict_new(b): # Set flags and confirm - setflags = "needinfo? requires_doc_text-" + setflags = "fedora_prioritized_bug? needinfo+" run_cli(cmd + " ".join([(" --flag " + f) for f in setflags.split()]), bz) @@ -354,8 +439,8 @@ def cleardict_new(b): assert flagstr(bug1) == setflags assert flagstr(bug2) == setflags - assert bug1.get_flags("needinfo")[0]["status"] == "?" - assert bug1.get_flag_status("requires_doc_text") == "-" + assert bug1.get_flags("needinfo")[0]["status"] == "+" + assert bug1.get_flag_status("fedora_prioritized_bug") == "?" # Clear flags if cleardict_new(bug1): @@ -377,7 +462,7 @@ def cleardict_new(b): if newfix == origfix2: newfix = origfix2 + "-2" - run_cli(cmd + "--fixed_in=%s" % newfix, bz) + run_cli(cmd + "--fixed_in '%s'" % newfix, bz) bug1.refresh() bug2.refresh() @@ -385,7 +470,7 @@ def cleardict_new(b): assert bug2.fixed_in == newfix # Reset fixed_in - run_cli(cmd + "--fixed_in=\"-\"", bz) + run_cli(cmd + "--fixed_in \"-\"", bz) bug1.refresh() bug2.refresh() @@ -393,11 +478,11 @@ def cleardict_new(b): assert bug2.fixed_in == "-" -def test07ModifyMisc(run_cli, backends): - bugid = "461686" - cmd = "bugzilla modify %s " % bugid +def test071ModifyMisc(run_cli, backends): bz = _open_bz(**backends) - bug = bz.getbug(bugid) + bug = _BugCache.get_bug(run_cli, bz) + have_dev = _check_have_dev(bug) + cmd = "bugzilla modify %s " % bug.id # modify --dependson run_cli(cmd + "--dependson 123456", bz) @@ -419,40 +504,52 @@ def test07ModifyMisc(run_cli, backends): assert [] == bug.blocks # modify --keywords + origkw = bug.keywords run_cli(cmd + "--keywords +Documentation --keywords EasyFix", bz) bug.refresh() - assert ["Documentation", "EasyFix"] == bug.keywords - run_cli(cmd + "--keywords=-EasyFix --keywords=-Documentation", - bz) + assert set(["Documentation", "EasyFix"] + origkw) == set(bug.keywords) + run_cli(cmd + "--keywords=-EasyFix --keywords=-Documentation", bz) bug.refresh() - assert [] == bug.keywords - - # modify --target_release - # modify --target_milestone - targetbugid = 492463 - targetbug = bz.getbug(targetbugid) - targetcmd = "bugzilla modify %s " % targetbugid - run_cli(targetcmd + - "--target_milestone beta --target_release 6.2", bz) - targetbug.refresh() - assert targetbug.target_milestone == "beta" - assert targetbug.target_release == ["6.2"] - run_cli(targetcmd + - "--target_milestone rc --target_release 6.10", bz) - targetbug.refresh() - assert targetbug.target_milestone == "rc" - assert targetbug.target_release == ["6.10"] - - # modify --priority - # modify --severity - run_cli(cmd + "--priority low --severity high", bz) - bug.refresh() - assert bug.priority == "low" - assert bug.severity == "high" - run_cli(cmd + "--priority medium --severity medium", bz) - bug.refresh() - assert bug.priority == "medium" - assert bug.severity == "medium" + assert origkw == bug.keywords + + perm_error = "user with the required permissions" + + try: + # modify --target_release + # modify --target_milestone + targetbugid = 492463 + targetbug = bz.getbug(targetbugid) + targetcmd = "bugzilla modify %s " % targetbugid + run_cli(targetcmd + + "--target_milestone beta --target_release 6.2", bz) + targetbug.refresh() + assert targetbug.target_milestone == "beta" + assert targetbug.target_release == ["6.2"] + run_cli(targetcmd + + "--target_milestone rc --target_release 6.10", bz) + targetbug.refresh() + assert targetbug.target_milestone == "rc" + assert targetbug.target_release == ["6.10"] + except RuntimeError as e: + if have_dev: + raise + assert perm_error in str(e) + + try: + # modify --priority + # modify --severity + run_cli(cmd + "--priority low --severity high", bz) + bug.refresh() + assert bug.priority == "low" + assert bug.severity == "high" + run_cli(cmd + "--priority medium --severity medium", bz) + bug.refresh() + assert bug.priority == "medium" + assert bug.severity == "medium" + except RuntimeError as e: + if have_dev: + raise + assert perm_error in str(e) # modify --os # modify --platform @@ -504,7 +601,7 @@ def _test8Attachments(run_cli, backends): testfile = "../tests/data/bz-attach-get1.txt" # Add attachment as CLI option - setbug = _makebug(run_cli, bz) + setbug = _BugCache.get_bug(run_cli, bz) setbug = bz.getbug(setbug.id, extra_fields=["attachments"]) orignumattach = len(setbug.attachments) @@ -598,45 +695,42 @@ def _test8Attachments(run_cli, backends): def test09Whiteboards(run_cli, backends): bz = _open_bz(**backends) - bug_id = "663674" - cmd = "bugzilla modify %s " % bug_id - bug = bz.getbug(bug_id) + bug = _BugCache.get_bug(run_cli, bz) + have_dev = _check_have_dev(bug) + cmd = "bugzilla modify %s " % bug.id # Set all whiteboards initval = str(random.randint(1, 1024)) - run_cli(cmd + - "--whiteboard =%sstatus " - "--devel_whiteboard =%sdevel " - "--internal_whiteboard '=%sinternal, security, foo security1' " - "--qa_whiteboard =%sqa " % - (initval, initval, initval, initval), bz) + statusstr = initval + "foo, bar, baz bar1" + devstr = initval + "devel" + internalstr = initval + "internal" + qastr = initval + "qa" + run_cmd = (cmd + f"--whiteboard '{statusstr}' ") + if have_dev: + run_cmd += ( + f"--devel_whiteboard '{devstr}' " + f"--internal_whiteboard '{internalstr}' " + f"--qa_whiteboard '{qastr}' ") + run_cli(run_cmd, bz) bug.refresh() - assert bug.whiteboard == (initval + "status") - assert bug.qa_whiteboard == (initval + "qa") - assert bug.devel_whiteboard == (initval + "devel") - assert (bug.internal_whiteboard == - (initval + "internal, security, foo security1")) + assert bug.whiteboard == statusstr - # Modify whiteboards - run_cli(cmd + - "--whiteboard =foobar " - "--qa_whiteboard _app " - "--devel_whiteboard =pre-%s" % bug.devel_whiteboard, bz) + if have_dev: + assert bug.qa_whiteboard == qastr + assert bug.devel_whiteboard == devstr + assert bug.internal_whiteboard == internalstr + # Remove a tag + run_cli(cmd + "--whiteboard=-bar, ", bz) bug.refresh() - assert bug.qa_whiteboard == (initval + "qa" + " _app") - assert bug.devel_whiteboard == ("pre-" + initval + "devel") - assert bug.status_whiteboard == "foobar" + statusstr = statusstr.replace("bar, ", "") + assert bug.status_whiteboard == statusstr - # Verify that tag manipulation is smart about separator - run_cli(cmd + - "--qa_whiteboard=-_app " - "--internal_whiteboard=-security,", bz) + run_cli(cmd + "--whiteboard NEWBIT", bz) bug.refresh() - - assert bug.qa_whiteboard == (initval + "qa") - assert bug.internal_whiteboard == (initval + "internal, foo security1") + statusstr += " NEWBIT" + assert bug.whiteboard == statusstr # Clear whiteboards update = bz.build_update( @@ -646,9 +740,10 @@ def test09Whiteboards(run_cli, backends): bug.refresh() assert bug.whiteboard == "" - assert bug.qa_whiteboard == "" - assert bug.devel_whiteboard == "" - assert bug.internal_whiteboard == "" + if have_dev: + assert bug.qa_whiteboard == "" + assert bug.devel_whiteboard == "" + assert bug.internal_whiteboard == "" def test10Login(run_cli, monkeypatch): @@ -836,27 +931,26 @@ def compare(data, newid): ("You are not allowed" in str(e))) -def test13SubComponents(backends): +def test13SubComponents(run_cli, backends): bz = _open_bz(**backends) - # Long closed RHEL5 lvm2 bug. This component has sub_components - bug = bz.getbug("185526") + bug = _make_subcomponent_bug(run_cli, bz) + bug.autorefresh = True - assert bug.component == "lvm2" + assert bug.component == "Extensions" bz.update_bugs(bug.id, bz.build_update( - component="lvm2", sub_component="Command-line tools (RHEL5)")) + component="Extensions", sub_component="RedHat")) bug.refresh() - assert bug.sub_components == {"lvm2": ["Command-line tools (RHEL5)"]} + assert bug.sub_components == {"Extensions": ["RedHat"]} bz.update_bugs(bug.id, bz.build_update( - component="lvm2", sub_component="Default / Unclassified (RHEL5)")) + component="Extensions", sub_component="AgileTools")) bug.refresh() - assert bug.sub_components == {"lvm2": [ - "Default / Unclassified (RHEL5)"]} + assert bug.sub_components == {"Extensions": ["AgileTools"]} -def _testExternalTrackers(bz): - bugid = 461686 +def _testExternalTrackers(run_cli, bz): + bugid = _BugCache.get_bug(run_cli, bz).id ext_bug_id = 380489 # Delete any existing external trackers to get to a known state @@ -903,10 +997,10 @@ def _testExternalTrackers(bz): assert len(ids) == 0 -def test14ExternalTrackersAddUpdateRemoveQuery(backends): +def test14ExternalTrackersAddUpdateRemoveQuery(run_cli, backends): bz = _open_bz(**backends) try: - _testExternalTrackers(bz) + _testExternalTrackers(run_cli, bz) except Exception as e: if not bz.is_rest(): raise @@ -926,10 +1020,9 @@ def test15EnsureLoggedIn(run_cli, backends): def test16ModifyTags(run_cli, backends): - bugid = "461686" - cmd = "bugzilla modify %s " % bugid bz = _open_bz(**backends) - bug = bz.getbug(bugid) + bug = _BugCache.get_bug(run_cli, bz) + cmd = "bugzilla modify %s " % bug.id try: if bug.tags: From a890ad06e63fb92ce16dbfcf9acca694ee3dd900 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Fri, 8 Sep 2023 11:58:54 -0400 Subject: [PATCH 057/106] Add a pylint exclusion Signed-off-by: Cole Robinson --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 43ac9c2c..747933e0 100644 --- a/.pylintrc +++ b/.pylintrc @@ -7,7 +7,7 @@ persistent=no # can either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). -disable=Design,Format,Similarities,invalid-name,missing-docstring,locally-disabled,unnecessary-lambda,fixme,global-statement,broad-except,bare-except,wrong-import-position,consider-using-ternary,len-as-condition,no-else-return,useless-object-inheritance,inconsistent-return-statements,consider-using-dict-comprehension,import-outside-toplevel,use-a-generator,consider-using-with,consider-using-f-string,unspecified-encoding +disable=Design,Format,Similarities,invalid-name,missing-docstring,locally-disabled,unnecessary-lambda,fixme,global-statement,broad-except,bare-except,wrong-import-position,consider-using-ternary,len-as-condition,no-else-return,useless-object-inheritance,inconsistent-return-statements,consider-using-dict-comprehension,import-outside-toplevel,use-a-generator,consider-using-with,consider-using-f-string,unspecified-encoding,use-implicit-booleaness-not-comparison enable=fixme From 5ffa1b216cd791175e2c7d46880d5277832ce9a1 Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Fri, 22 Sep 2023 14:17:30 -0700 Subject: [PATCH 058/106] build_update: don't convert 'blocks' or 'depends' to int This is not necessary - Bugzilla, at least the current version on bugzilla.redhat.com, is happy to accept IDs as a string. It also causes a problem: this prevents you from using aliases when setting blocks or depends, which is often very convenient when bugs have predictable aliases (like "F40Changes" or "F39BetaBlocker"). Without this change, you have to do an extra query just to find the ID of the bug. Signed-off-by: Adam Williamson --- bugzilla/base.py | 18 +++++------------- tests/data/mockargs/test_modify2.txt | 2 +- tests/data/mockargs/test_modify5.txt | 4 ++-- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/bugzilla/base.py b/bugzilla/base.py index ca638f53..15ccc4f8 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -1493,19 +1493,13 @@ def add_dict(key, add, remove, _set=None, convert=None): if add is remove is _set is None: return - def c(val): - val = listify(val) - if convert: - val = [convert(v) for v in val] - return val - newdict = {} if add is not None: - newdict["add"] = c(add) + newdict["add"] = listify(add) if remove is not None: - newdict["remove"] = c(remove) + newdict["remove"] = listify(remove) if _set is not None: - newdict["set"] = c(_set) + newdict["set"] = listify(_set) ret[key] = newdict @@ -1539,10 +1533,8 @@ def c(val): s("comment_tags", comment_tags, listify) s("minor_update", minor_update, bool) - add_dict("blocks", blocks_add, blocks_remove, blocks_set, - convert=int) - add_dict("depends_on", depends_on_add, depends_on_remove, - depends_on_set, convert=int) + add_dict("blocks", blocks_add, blocks_remove, blocks_set) + add_dict("depends_on", depends_on_add, depends_on_remove, depends_on_set) add_dict("cc", cc_add, cc_remove) add_dict("groups", groups_add, groups_remove) add_dict("keywords", keywords_add, keywords_remove, keywords_set) diff --git a/tests/data/mockargs/test_modify2.txt b/tests/data/mockargs/test_modify2.txt index 5ee27d14..3c0315af 100644 --- a/tests/data/mockargs/test_modify2.txt +++ b/tests/data/mockargs/test_modify2.txt @@ -1,5 +1,5 @@ (['123456'], - {'blocks': {'set': [123456, 445566]}, + {'blocks': {'set': ['123456', '445566']}, 'comment': {'comment': 'some example comment', 'is_private': True}, 'component': 'NEWCOMP', 'dupe_of': 555666, diff --git a/tests/data/mockargs/test_modify5.txt b/tests/data/mockargs/test_modify5.txt index 972c2765..5b9b14a3 100644 --- a/tests/data/mockargs/test_modify5.txt +++ b/tests/data/mockargs/test_modify5.txt @@ -2,13 +2,13 @@ {'alias': 'fooalias', 'assigned_to': 'foo@example.com', 'bar': 'foo', - 'blocks': {'add': [1234], 'remove': [1235], 'set': []}, + 'blocks': {'add': ['1234'], 'remove': ['1235'], 'set': []}, 'cc': {'add': ['+bar@example.com'], 'remove': ['steve@example.com']}, 'cf_devel_whiteboard': 'DEVBOARD', 'cf_internal_whiteboard': 'INTBOARD', 'cf_qa_whiteboard': 'QABOARD', 'comment_tags': ['FOOTAG'], - 'depends_on': {'add': [2234], 'remove': [2235], 'set': []}, + 'depends_on': {'add': ['2234'], 'remove': ['2235'], 'set': []}, 'groups': {'add': ['foogroup']}, 'keywords': {'add': ['newkeyword'], 'remove': ['byekeyword'], 'set': []}, 'minor_update': True, From 343f15ebfbe8a942ee028e2c10f6ea132b44b776 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Tue, 12 Sep 2023 20:31:39 +0200 Subject: [PATCH 059/106] ci: add dependabot config for GitHub Actions Enable dependabot for the "main" branch, letting it scan for outdated GitHub Actions used in workflows on a weekly base. --- .github/dependabot.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..13e4e05b --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + target-branch: "main" + commit-message: + prefix: "ci" From 2b1281f04f536d4c42c0c2cf3256831cbc0560ef Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Tue, 24 Oct 2023 14:54:34 +0200 Subject: [PATCH 060/106] Use proper REST API route for getting a single bug (fixes #174) (#183) This avoids an `IndexError` in Bugzilla._getbug` and ensures that a `BugzillaError` gets raised e.g. if the bug ID does not exist or the client is not authorized. closes #174 --- bugzilla/_backendrest.py | 17 +++++++++++++++-- tests/test_backend_rest.py | 35 +++++++++++++++++++++++++++++++++++ tests/test_ro_functional.py | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 tests/test_backend_rest.py diff --git a/bugzilla/_backendrest.py b/bugzilla/_backendrest.py index a14c4c03..00b5563a 100644 --- a/bugzilla/_backendrest.py +++ b/bugzilla/_backendrest.py @@ -107,9 +107,22 @@ def bug_create(self, paramdict): def bug_fields(self, paramdict): return self._get("/field/bug", paramdict) def bug_get(self, bug_ids, aliases, paramdict): + bug_list = listify(bug_ids) + alias_list = listify(aliases) data = paramdict.copy() - data["id"] = listify(bug_ids) - data["alias"] = listify(aliases) + + # FYI: The high-level API expects the backends to raise an exception + # when retrieval of a single bug fails (default behavior of the XMLRPC + # API), but the REST API simply returns an empty search result set. + # To ensure compliant behavior, the REST backend needs to use the + # explicit URL to get a single bug. + if len(bug_list or []) + len(alias_list or []) == 1: + for id_list in (bug_list, alias_list): + if id_list: + return self._get("/bug/%s" % id_list[0], data) + + data["id"] = bug_list + data["alias"] = alias_list ret = self._get("/bug", data) return ret diff --git a/tests/test_backend_rest.py b/tests/test_backend_rest.py new file mode 100644 index 00000000..fdfbd05b --- /dev/null +++ b/tests/test_backend_rest.py @@ -0,0 +1,35 @@ +from types import MethodType + +from bugzilla._backendrest import _BackendREST +from bugzilla._session import _BugzillaSession + + +def test_getbug(): + session = _BugzillaSession(url="http://example.com", + user_agent="py-bugzilla-test", + sslverify=False, + cert=None, + tokencache={}, + api_key="", + is_redhat_bugzilla=False) + backend = _BackendREST(url="http://example.com", + bugzillasession=session) + + def _assertion(self, *args): + self.assertion_called = True + assert args and args[0] == url + + setattr(backend, "_get", MethodType(_assertion, backend)) + + for _ids, aliases, url in ( + (1, None, "/bug/1"), + ([1], [], "/bug/1"), + (None, "CVE-1999-0001", "/bug/CVE-1999-0001"), + ([], ["CVE-1999-0001"], "/bug/CVE-1999-0001"), + (1, "CVE-1999-0001", "/bug"), + ): + backend.assertion_called = False + + backend.bug_get(_ids, aliases, {}) + + assert backend.assertion_called is True diff --git a/tests/test_ro_functional.py b/tests/test_ro_functional.py index 4bc31dff..24ffcf8b 100644 --- a/tests/test_ro_functional.py +++ b/tests/test_ro_functional.py @@ -8,9 +8,12 @@ """ Unit tests that do readonly functional tests against real bugzilla instances. """ +from xmlrpc.client import Fault + import pytest import bugzilla +from bugzilla.exceptions import BugzillaError import tests @@ -295,6 +298,38 @@ def testGetBugAlias(backends): assert bug.bug_id == 720773 +def testGetBug404(backends): + """ + getbug() is expected to raise an error, if a bug ID or alias does not exist + """ + bz = _open_bz(REDHAT_URL, **backends) + + try: + bz.getbug(100000000) + except Fault as error: # XMLRPC API + assert error.faultCode == 101 + except BugzillaError as error: # REST API + assert error.code == 101 + else: + raise AssertionError("No exception raised") + + +def testGetBugAlias404(backends): + """ + getbug() is expected to raise an error, if a bug ID or alias does not exist + """ + bz = _open_bz(REDHAT_URL, **backends) + + try: + bz.getbug("CVE-1234-4321") + except Fault as error: # XMLRPC API + assert error.faultCode == 100 + except BugzillaError as error: # REST API + assert error.code == 100 + else: + raise AssertionError("No exception raised") + + def testQuerySubComponent(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) From 0001f6e4e328c80dabd61116dbfddcdcc12cd413 Mon Sep 17 00:00:00 2001 From: Ricardo Branco Date: Mon, 25 Sep 2023 21:24:02 +0200 Subject: [PATCH 061/106] Avoid duplicate entries when one id is 0 --- bugzilla/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bugzilla/base.py b/bugzilla/base.py index 15ccc4f8..4ea036ed 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -1099,7 +1099,7 @@ def _alias_or_int(_v): for idval in idlist: idint, alias = _alias_or_int(idval) for bugdict in r["bugs"]: - if idint and idint != bugdict.get("id", None): + if idint is not None and idint != bugdict.get("id", None): continue aliaslist = listify(bugdict.get("alias", None) or []) if alias and alias not in aliaslist: From 0a0bddb3aa542d8b548d32974c60d476dfd47e4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Oct 2023 12:23:08 +0000 Subject: [PATCH 062/106] ci: bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2af21f8c..f51c690a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: python-version: ["3.6", "3.x"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 @@ -53,7 +53,7 @@ jobs: python-version: ["3.x"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 From 80a0316a643d47fe5f769d57c738795e0c2de90b Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Tue, 24 Oct 2023 15:15:33 +0200 Subject: [PATCH 063/106] Removed unused argument from `Bugzilla.add_dict` --- bugzilla/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bugzilla/base.py b/bugzilla/base.py index 4ea036ed..b310242a 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -1489,7 +1489,7 @@ def s(key, val, convert=None): val = convert(val) ret[key] = val - def add_dict(key, add, remove, _set=None, convert=None): + def add_dict(key, add, remove, _set=None): if add is remove is _set is None: return From 182e0b0ba05c393f2a6ab81e02a1de8c2e04b7a3 Mon Sep 17 00:00:00 2001 From: Ricardo Branco Date: Wed, 20 Sep 2023 18:07:54 +0200 Subject: [PATCH 064/106] Fix API key leak --- bugzilla/_session.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bugzilla/_session.py b/bugzilla/_session.py index 98064671..017c6d91 100644 --- a/bugzilla/_session.py +++ b/bugzilla/_session.py @@ -98,14 +98,14 @@ def request(self, *args, **kwargs): if "timeout" not in kwargs: kwargs["timeout"] = timeout - response = self._session.request(*args, **kwargs) + try: + response = self._session.request(*args, **kwargs) - if self._is_xmlrpc: - # Yes this still appears to matter for properly decoding unicode - # code points in bugzilla.redhat.com content - response.encoding = "UTF-8" + if self._is_xmlrpc: + # This still appears to matter for properly decoding unicode + # code points in bugzilla.redhat.com content + response.encoding = "UTF-8" - try: response.raise_for_status() except requests.HTTPError as e: # Scrape the api key out of the returned exception string From c756f55593f9e37f1a7150ac884884fdeccbd8c2 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 25 Oct 2023 05:56:32 -0400 Subject: [PATCH 065/106] ci: Fix packit RPM build locale errors (#196) Be more thorough trying to find a UTF-8 locale in minimal fedora buildroot Lifted from https://gitlab.com/libosinfo/osinfo-db Signed-off-by: Cole Robinson --- tests/conftest.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index a90dfbb9..cfac4671 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -52,12 +52,16 @@ def pytest_ignore_collect(path, config): def pytest_configure(config): - try: - # Needed for test reproducibility on systems not using a UTF-8 locale - locale.setlocale(locale.LC_ALL, 'C') - locale.setlocale(locale.LC_CTYPE, 'en_US.UTF-8') - except Exception as e: - print("Error setting locale: %s" % str(e)) + # Needed for test reproducibility on any system not using a UTF-8 locale + locale.setlocale(locale.LC_ALL, "C") + for loc in ["C.UTF-8", "C.utf8", "UTF-8", "en_US.UTF-8"]: + try: + locale.setlocale(locale.LC_CTYPE, loc) + break + except locale.Error: + pass + else: + raise locale.Error("No UTF-8 locale found") if config.getoption("--redhat-url"): tests.CLICONFIG.REDHAT_URL = config.getoption("--redhat-url") From def2d28782286c668d473da478f51a263f01f784 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Dec 2023 08:45:03 +0100 Subject: [PATCH 066/106] ci: bump actions/setup-python from 4 to 5 (#198) Bumps actions/setup-python from 4 to 5. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f51c690a..e671b6ab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} @@ -56,7 +56,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} From 629248f036b7624fd59064192afd864fe5ab6499 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 6 Feb 2024 02:58:10 -0500 Subject: [PATCH 067/106] A couple doc fixes (#201) Fix a couple doc issues, as reported here: https://github.com/python-bugzilla/python-bugzilla/issues/176 --------- Signed-off-by: Cole Robinson --- man/bugzilla.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/man/bugzilla.rst b/man/bugzilla.rst index 38f484bb..54e3b10f 100644 --- a/man/bugzilla.rst +++ b/man/bugzilla.rst @@ -355,8 +355,8 @@ Bug assignee QA contact -``--flag`` -^^^^^^^^^^ +``-f, --flag`` +^^^^^^^^^^^^^^ **Syntax:** ``--flag`` FLAG @@ -431,8 +431,8 @@ These options are shared by several commands, for tweaking the text output of the command results. -``-f, --full`` -^^^^^^^^^^^^^^ +``--full`` +^^^^^^^^^^ **Syntax:** ``--full`` @@ -859,4 +859,5 @@ SEE ALSO ======== https://bugzilla.readthedocs.io/en/latest/api/index.html + https://bugzilla.redhat.com/docs/en/html/api/core/v1/bug.html From e5f9d93e57792056a91590603972142f5a176600 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 09:03:02 +0100 Subject: [PATCH 068/106] ci: bump codecov/codecov-action from 3 to 4 (#199) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4.
Release notes

Sourced from codecov/codecov-action's releases.

v4.0.0

v4 of the Codecov Action uses the CLI as the underlying upload. The CLI has helped to power new features including local upload, the global upload token, and new upcoming features.

Breaking Changes

  • The Codecov Action runs as a node20 action due to node16 deprecation. See this post from GitHub on how to migrate.
  • Tokenless uploading is unsupported. However, PRs made from forks to the upstream public repos will support tokenless (e.g. contributors to OS projects do not need the upstream repo's Codecov token). This doc shows instructions on how to add the Codecov token.
  • OS platforms have been added, though some may not be automatically detected. To see a list of platforms, see our CLI download page
  • Various arguments to the Action have been changed. Please be aware that the arguments match with the CLI's needs

v3 versions and below will not have access to CLI features (e.g. global upload token, ATS).

What's Changed

... (truncated)

Changelog

Sourced from codecov/codecov-action's changelog.

4.0.0-beta.2

Fixes

  • #1085 not adding -n if empty to do-upload command

4.0.0-beta.1

v4 represents a move from the universal uploader to the Codecov CLI. Although this will unlock new features for our users, the CLI is not yet at feature parity with the universal uploader.

Breaking Changes

  • No current support for aarch64 and alpine architectures.
  • Tokenless uploading is unsuported
  • Various arguments to the Action have been removed

3.1.4

Fixes

  • #967 Fix typo in README.md
  • #971 fix: add back in working dir
  • #969 fix: CLI option names for uploader

Dependencies

  • #970 build(deps-dev): bump @​types/node from 18.15.12 to 18.16.3
  • #979 build(deps-dev): bump @​types/node from 20.1.0 to 20.1.2
  • #981 build(deps-dev): bump @​types/node from 20.1.2 to 20.1.4

3.1.3

Fixes

  • #960 fix: allow for aarch64 build

Dependencies

  • #957 build(deps-dev): bump jest-junit from 15.0.0 to 16.0.0
  • #958 build(deps): bump openpgp from 5.7.0 to 5.8.0
  • #959 build(deps-dev): bump @​types/node from 18.15.10 to 18.15.12

3.1.2

Fixes

  • #718 Update README.md
  • #851 Remove unsupported path_to_write_report argument
  • #898 codeql-analysis.yml
  • #901 Update README to contain correct information - inputs and negate feature
  • #955 fix: add in all the extra arguments for uploader

Dependencies

  • #819 build(deps): bump openpgp from 5.4.0 to 5.5.0
  • #835 build(deps): bump node-fetch from 3.2.4 to 3.2.10
  • #840 build(deps): bump ossf/scorecard-action from 1.1.1 to 2.0.4
  • #841 build(deps): bump @​actions/core from 1.9.1 to 1.10.0
  • #843 build(deps): bump @​actions/github from 5.0.3 to 5.1.1
  • #869 build(deps): bump node-fetch from 3.2.10 to 3.3.0
  • #872 build(deps-dev): bump jest-junit from 13.2.0 to 15.0.0
  • #879 build(deps): bump decode-uri-component from 0.2.0 to 0.2.2

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=codecov/codecov-action&package-manager=github_actions&previous-version=3&new-version=4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e671b6ab..cd5c13bd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: pytest --cov --cov-report=xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 # Build and install on Windows From e15878007438b9df818beadd3f32c2d5f6130044 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 6 Feb 2024 10:17:23 -0500 Subject: [PATCH 069/106] codecov: workflow v4 requires an upload token (#202) Signed-off-by: Cole Robinson --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cd5c13bd..0b617085 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,6 +43,8 @@ jobs: - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} # Build and install on Windows From 0955d2e423f1f31c693c97b18a1278422e89df85 Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Tue, 6 Feb 2024 16:29:20 +0100 Subject: [PATCH 070/106] Include `alias` in `include_fields` (closes #170) (#186) Include `alias` in `include_fields`, when the parameter for `getbug` is an alias (closes #170) Because the `_getbugs` method tries to return bug data in the same order as IDs and aliases are provided, the `alias` needs to be explicitly added to `include_fields`. --- bugzilla/base.py | 5 +++++ tests/test_api_bug.py | 23 +++++++++++++++++++++++ tests/test_ro_functional.py | 7 +++++++ 3 files changed, 35 insertions(+) diff --git a/bugzilla/base.py b/bugzilla/base.py index b310242a..8d6af295 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -1081,6 +1081,11 @@ def _alias_or_int(_v): else: ids.append(idstr) + if (include_fields is not None and aliases + and "alias" not in include_fields): + # Extra field to prevent sorting (see below) from causing an error + include_fields.append("alias") + extra_fields = listify(extra_fields or []) extra_fields += self._getbug_extra_fields() diff --git a/tests/test_api_bug.py b/tests/test_api_bug.py index 47391d46..61572fc5 100644 --- a/tests/test_api_bug.py +++ b/tests/test_api_bug.py @@ -94,6 +94,29 @@ def test_api_getbugs(): assert fakebz.getbugs(["123456", "CVE-1234-FAKE"]) == [] +def test_getbug_alias(): + """ + Test that `getbug()` includes the alias in `include_fields` + """ + fakebz = tests.mockbackend.make_bz( + bug_get_args=None, + bug_get_return="data/mockreturn/test_query_cve_getbug.txt") + bug = fakebz.getbug("CVE-1234-5678", include_fields=["id"]) + assert bug.alias == ["CVE-1234-5678"] + assert bug.id == 123456 + + def mock_bug_get(bug_ids, aliases, paramdict): + assert bug_ids == [] + assert aliases == ["CVE-1234-5678"] + assert "alias" in paramdict.get("include_fields", []) + return {"bugs": [bug.get_raw_data()]} + + backend = getattr(fakebz, "_backend") + setattr(backend, "bug_get", mock_bug_get) + + fakebz.getbug("CVE-1234-5678", include_fields=["id"]) + + def test_bug_getattr(): fakebz = tests.mockbackend.make_bz( bug_get_args=None, diff --git a/tests/test_ro_functional.py b/tests/test_ro_functional.py index 24ffcf8b..9c6f75d0 100644 --- a/tests/test_ro_functional.py +++ b/tests/test_ro_functional.py @@ -330,6 +330,13 @@ def testGetBugAlias404(backends): raise AssertionError("No exception raised") +def testGetBugAliasIncludedField(backends): + bz = _open_bz(REDHAT_URL, **backends) + + bug = bz.getbug("CVE-2011-2527", include_fields=["id"]) + assert bug.bug_id == 720773 + + def testQuerySubComponent(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) From 473da017066ab960d4a87d1c25092e2c40c626c1 Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Thu, 8 Feb 2024 17:44:36 +0100 Subject: [PATCH 071/106] Run pylint as separate GH action (#193) ... and allow lines to be 100 characters long. This closes python-bugzilla#185 --- .github/workflows/build.yml | 20 ++++++++++++++++++++ .pylintrc | 2 +- test-requirements.txt | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0b617085..6369f0be 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,26 @@ name: CI on: [push, pull_request] jobs: + linter: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.x"] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt -r test-requirements.txt setuptools + - name: Lint + run: | + pylint --output-format colorized --rcfile .pylintrc \ + bugzilla-cli setup.py bugzilla examples tests + build: # We stick with 20.04 to get access to python 3.6 # https://github.com/actions/setup-python/issues/544 diff --git a/.pylintrc b/.pylintrc index 747933e0..378199f1 100644 --- a/.pylintrc +++ b/.pylintrc @@ -19,7 +19,7 @@ score=no [FORMAT] # Maximum number of characters on a single line. -max-line-length=80 +max-line-length=100 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). diff --git a/test-requirements.txt b/test-requirements.txt index c588a62a..6abde2bb 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,2 +1,4 @@ # additional packages needed for testing pytest +pylint<3.1 +pycodestyle<2.12 From 5f0727b33fac1f0428ef1c6f568a9702bcd9ba07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 17:46:56 +0000 Subject: [PATCH 072/106] ci: bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6369f0be..8c8d6cf8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: matrix: python-version: ["3.x"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: From 41f357030cbbc923580de086133d6eebe44f48fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 17:47:00 +0000 Subject: [PATCH 073/106] ci: bump actions/setup-python from 4 to 5 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8c8d6cf8..cd92d9d8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From f95ff31d76463fbb05d49fe624f63d3b27c3ef72 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Mon, 5 Feb 2024 16:04:15 -0500 Subject: [PATCH 074/106] cli: Add `--field-json=JSONSTRING` This is similar to the `--field` option for interacting with custom bugzilla fields, but it takes a full JSON string as input. This lets users set complex values like arrays, dict mappings, that otherwise were not settable with plain `--field` Fixes: https://github.com/python-bugzilla/python-bugzilla/issues/163 Signed-off-by: Cole Robinson --- bugzilla/_cli.py | 32 ++++++++++++++++++++-------- man/bugzilla.rst | 11 +++++++++- tests/data/mockargs/test_modify5.txt | 2 ++ tests/data/mockargs/test_new2.txt | 2 ++ tests/data/mockargs/test_query6.txt | 2 ++ tests/test_cli_modify.py | 1 + tests/test_cli_new.py | 1 + tests/test_cli_query.py | 9 +++++++- 8 files changed, 49 insertions(+), 11 deletions(-) diff --git a/bugzilla/_cli.py b/bugzilla/_cli.py index d0688809..e14c1df4 100755 --- a/bugzilla/_cli.py +++ b/bugzilla/_cli.py @@ -263,6 +263,10 @@ def _parser_add_bz_fields(rootp, command): "the raw name used by the bugzilla instance. For example, if your " "bugzilla instance has a custom field cf_my_field, do:\n" " --field cf_my_field=VALUE") + p.add_argument('--field-json', + metavar="JSONSTRING", action="append", dest="field_jsons", + help="Specify --field data as a JSON string. Example: --field-json " + '\'{"cf_my_field": "VALUE", "cf_array_field": [1, 2]}\'') if not cmd_modify: _parser_add_output_options(rootp) @@ -436,15 +440,28 @@ def setup_parser(): # Command routines # #################### -def _merge_field_opts(query, fields, parser): +def _merge_field_opts(query, fields, field_jsons, parser): + values = {} + # Add any custom fields if specified - for f in fields: + for f in (fields or []): try: f, v = f.split('=', 1) - query[f] = v + values[f] = v except Exception: parser.error("Invalid field argument provided: %s" % (f)) + for j in (field_jsons or []): + try: + jvalues = json.loads(j) + values.update(jvalues) + except Exception as e: + parser.error("Invalid field-json value=%s: %s" % (j, e)) + + if values: + log.debug("parsed --field* values: %s", values) + query.update(values) + def _do_query(bz, opt, parser): q = {} @@ -599,8 +616,7 @@ def _do_query(bz, opt, parser): kwopts["tags"] = opt.tags built_query = bz.build_query(**kwopts) - if opt.fields: - _merge_field_opts(built_query, opt.fields, parser) + _merge_field_opts(built_query, opt.fields, opt.field_jsons, parser) built_query.update(q) q = built_query @@ -907,8 +923,7 @@ def parse_multi(val): kwopts["comment_private"] = opt.private ret = bz.build_createbug(**kwopts) - if opt.fields: - _merge_field_opts(ret, opt.fields, parser) + _merge_field_opts(ret, opt.fields, opt.field_jsons, parser) b = bz.createbug(ret) b.refresh() @@ -1049,8 +1064,7 @@ def _do_modify(bz, parser, opt): if not v[0] and not v[1]: del wbmap[k] - if opt.fields: - _merge_field_opts(update, opt.fields, parser) + _merge_field_opts(update, opt.fields, opt.field_jsons, parser) log.debug("update bug dict=%s", update) log.debug("update whiteboard dict=%s", wbmap) diff --git a/man/bugzilla.rst b/man/bugzilla.rst index 54e3b10f..c814e519 100644 --- a/man/bugzilla.rst +++ b/man/bugzilla.rst @@ -416,13 +416,22 @@ RHBZ 'Fixed in version' field ``--field`` ^^^^^^^^^^^ -**Syntax:** ``--field`` FIELD`` VALUE +**Syntax:** ``--field`` FIELD=VALUE Manually specify a bugzilla API field. FIELD is the raw name used by the bugzilla instance. For example if your bugzilla instance has a custom field cf_my_field, do: --field cf_my_field=VALUE +``--field-json`` +^^^^^^^^^^^^^^^^ + +**Syntax:** ``--field-json`` JSONSTRING + +Specify --field data as a JSON string. Example: +--field-json '{"cf_my_field": "VALUE", "cf_array_field": [1, 2]}' + + Output options ============== diff --git a/tests/data/mockargs/test_modify5.txt b/tests/data/mockargs/test_modify5.txt index 5b9b14a3..b21a6ee2 100644 --- a/tests/data/mockargs/test_modify5.txt +++ b/tests/data/mockargs/test_modify5.txt @@ -4,9 +4,11 @@ 'bar': 'foo', 'blocks': {'add': ['1234'], 'remove': ['1235'], 'set': []}, 'cc': {'add': ['+bar@example.com'], 'remove': ['steve@example.com']}, + 'cf_blah': {'1': 2}, 'cf_devel_whiteboard': 'DEVBOARD', 'cf_internal_whiteboard': 'INTBOARD', 'cf_qa_whiteboard': 'QABOARD', + 'cf_verified': ['Tested'], 'comment_tags': ['FOOTAG'], 'depends_on': {'add': ['2234'], 'remove': ['2235'], 'set': []}, 'groups': {'add': ['foogroup']}, diff --git a/tests/data/mockargs/test_new2.txt b/tests/data/mockargs/test_new2.txt index 5bd147e2..a3636ead 100644 --- a/tests/data/mockargs/test_new2.txt +++ b/tests/data/mockargs/test_new2.txt @@ -2,6 +2,8 @@ 'assigned_to': 'foo@example.com', 'blocks': ['12345', '6789'], 'cc': ['foo@example.com', 'bar@example.com'], + 'cf_blah': {'1': 2}, + 'cf_verified': ['Tested'], 'comment_is_private': True, 'comment_tags': ['FOO'], 'component': 'FOOCOMP', diff --git a/tests/data/mockargs/test_query6.txt b/tests/data/mockargs/test_query6.txt index 78b18f48..b3a21252 100644 --- a/tests/data/mockargs/test_query6.txt +++ b/tests/data/mockargs/test_query6.txt @@ -1,6 +1,8 @@ {'BAR': 'WIBBLE', 'FOO': '1', 'bug_status': ['VERIFIED', 'RELEASE_PENDING', 'CLOSED'], + 'cf_blah': {'1': 2}, + 'cf_verified': ['Tested'], 'include_fields': ['assigned_to', 'blocks', 'component', diff --git a/tests/test_cli_modify.py b/tests/test_cli_modify.py index ed5519c3..e3731451 100644 --- a/tests/test_cli_modify.py +++ b/tests/test_cli_modify.py @@ -85,6 +85,7 @@ def test_modify(run_cli): cmd += "--devel_whiteboard =DEVBOARD --internal_whiteboard =INTBOARD " cmd += "--qa_whiteboard =QABOARD " cmd += "--comment-tag FOOTAG --field bar=foo " + cmd += '--field-json \'{"cf_verified": ["Tested"], "cf_blah": {"1": 2}}\' ' cmd += "--minor-update " fakebz = tests.mockbackend.make_bz(rhbz=True, bug_update_args="data/mockargs/test_modify5.txt", diff --git a/tests/test_cli_new.py b/tests/test_cli_new.py index 5e4742be..86e153ab 100644 --- a/tests/test_cli_new.py +++ b/tests/test_cli_new.py @@ -41,6 +41,7 @@ def test_new(run_cli): cmd += "--assignee foo@example.com --qa_contact qa@example.com " cmd += "--comment-tag FOO " cmd += "--field foo=bar " + cmd += '--field-json \'{"cf_verified": ["Tested"], "cf_blah": {"1": 2}}\' ' fakebz = tests.mockbackend.make_bz( bug_create_args="data/mockargs/test_new2.txt", diff --git a/tests/test_cli_query.py b/tests/test_cli_query.py index 98c6028b..5a751a53 100644 --- a/tests/test_cli_query.py +++ b/tests/test_cli_query.py @@ -15,12 +15,18 @@ ################################# def test_query(run_cli): - # bad field option + # bad --field option fakebz = tests.mockbackend.make_bz() cmd = "bugzilla query --field FOO" out = run_cli(cmd, fakebz, expectfail=True) assert "Invalid field argument" in out + # bad --field-json option + fakebz = tests.mockbackend.make_bz() + cmd = "bugzilla query --field-json='{1: 2}'" + out = run_cli(cmd, fakebz, expectfail=True) + assert "Invalid field-json" in out + # Simple query with some comma opts cmd = "bugzilla query " cmd += "--product foo --component foo,bar --bug_id 1234,2480" @@ -104,6 +110,7 @@ def test_query(run_cli): # Test --status EOL and --oneline, and some --field usage cmd = "bugzilla query --status EOL --oneline " cmd += "--field FOO=1 --field=BAR=WIBBLE " + cmd += '--field-json \'{"cf_verified": ["Tested"], "cf_blah": {"1": 2}}\' ' fakebz = tests.mockbackend.make_bz( bug_search_args="data/mockargs/test_query6.txt", bug_search_return="data/mockreturn/test_getbug_rhel.txt", From b9eaa598a82d1b00ff041af3119855a0dbdd1264 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 24 Oct 2023 10:47:32 -0400 Subject: [PATCH 075/106] session: Fix API leak pt2 Between the time 182e0b0ba0 was written, 138caf8aa72 was committed which inadvertently loosened up the api_key scraping. Go back to catching `Exception` so we try harder to scrape api_key from error messages. Signed-off-by: Cole Robinson --- bugzilla/_session.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bugzilla/_session.py b/bugzilla/_session.py index 017c6d91..ff00f3f5 100644 --- a/bugzilla/_session.py +++ b/bugzilla/_session.py @@ -107,12 +107,15 @@ def request(self, *args, **kwargs): response.encoding = "UTF-8" response.raise_for_status() - except requests.HTTPError as e: + except Exception as e: # Scrape the api key out of the returned exception string message = str(e).replace(self._api_key or "", "") - response = getattr(e, "response", None) - raise BugzillaHTTPError(message, response=response).with_traceback( - sys.exc_info()[2] - ) + if isinstance(e, requests.HTTPError): + response = getattr(e, "response", None) + raise BugzillaHTTPError( + message, response=response).with_traceback( + sys.exc_info()[2]) + raise type(e)(message).with_traceback(sys.exc_info()[2]) + return response From 70c6ad300b9e87dbed5be40837a14a0eff2e9791 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 24 Oct 2023 10:26:29 -0400 Subject: [PATCH 076/106] tests: confirm API key doesn't leak on connection error Signed-off-by: Cole Robinson --- tests/test_ro_functional.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_ro_functional.py b/tests/test_ro_functional.py index 9c6f75d0..df7f9e5d 100644 --- a/tests/test_ro_functional.py +++ b/tests/test_ro_functional.py @@ -74,6 +74,17 @@ def test_rest_xmlrpc_detection(): def test_apikey_error_scraping(): # Ensure the API key does not leak into any requests exceptions fakekey = "FOOBARMYKEY" + + with pytest.raises(Exception) as e: + _open_bz("https://bugzilla.redhat.nopedontexist", + force_rest=True, api_key=fakekey) + assert fakekey not in str(e.value) + + with pytest.raises(Exception) as e: + _open_bz("https://bugzilla.redhat.nopedontexist", + force_xmlrpc=True, api_key=fakekey) + assert fakekey not in str(e.value) + with pytest.raises(Exception) as e: _open_bz("https://httpstat.us/502&foo", force_xmlrpc=True, api_key=fakekey) From 7359b339c4395b5e8c6e7a63e189d60f595e89ab Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 14 Feb 2024 11:13:24 -0500 Subject: [PATCH 077/106] base: Tweak `query()` docs - Drop outdated references - point to `build_query` Signed-off-by: Cole Robinson --- bugzilla/base.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bugzilla/base.py b/bugzilla/base.py index 8d6af295..23cdc7ba 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -1314,11 +1314,11 @@ def add_email(key, value, count): def query(self, query): """ - Query bugzilla and return a list of matching bugs. - query must be a dict with fields like those in in querydata['fields']. - Returns a list of Bug objects. - Also see the _query() method for details about the underlying - implementation. + Pass search terms to bugzilla and and return a list of matching + Bug objects. + + See `build_query` for more details about constructing the + `query` dict parameter. """ try: r = self._backend.bug_search(query) From 794865f9c45d53b4e92252ca2777ad5dd83b7d48 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 14 Feb 2024 10:46:41 -0500 Subject: [PATCH 078/106] base: Add query_return_extra This is like `query()`, but the return value is altered to be (buglist, values), where `values` is raw dictionary output from the API call, excluding the bug content. For example this may include a `limit` value if the bugzilla instance puts an implied limit on returned result numbers. bugzilla.redhat.com also has a custom extension to report `total_matches` for a query, which lets user know if the returned results are complete or not Signed-off-by: Cole Robinson --- bugzilla/base.py | 33 +++++++++++++++++++-------- tests/data/mockreturn/test_query1.txt | 2 +- tests/test_api_misc.py | 9 ++++++++ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/bugzilla/base.py b/bugzilla/base.py index 23cdc7ba..a30ec2cb 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -1312,13 +1312,14 @@ def add_email(key, value, count): self.pre_translation(query) return query - def query(self, query): - """ - Pass search terms to bugzilla and and return a list of matching - Bug objects. - See `build_query` for more details about constructing the - `query` dict parameter. + def query_return_extra(self, query): + """ + Same as `query()`, but the return value is altered to be + (buglist, values), where `values` is raw dictionary output from + the API call, excluding the bug content. For example this may + include a `limit` value if the bugzilla instance puts an implied + limit on returned result numbers. """ try: r = self._backend.bug_search(query) @@ -1334,9 +1335,23 @@ def query(self, query): "appear to support API queries derived from bugzilla " "web URL queries." % e) from None - log.debug("Query returned %s bugs", len(r['bugs'])) - return [Bug(self, dict=b, - autorefresh=self.bug_autorefresh) for b in r['bugs']] + rawbugs = r.pop("bugs") + log.debug("Query returned %s bugs", len(rawbugs)) + bugs = [Bug(self, dict=b, + autorefresh=self.bug_autorefresh) for b in rawbugs] + + return bugs, r + + def query(self, query): + """ + Pass search terms to bugzilla and and return a list of matching + Bug objects. + + See `build_query` for more details about constructing the + `query` dict parameter. + """ + bugs, dummy = self.query_return_extra(query) + return bugs def pre_translation(self, query): """ diff --git a/tests/data/mockreturn/test_query1.txt b/tests/data/mockreturn/test_query1.txt index d1bdac5c..4f7019b2 100644 --- a/tests/data/mockreturn/test_query1.txt +++ b/tests/data/mockreturn/test_query1.txt @@ -1 +1 @@ -{'bugs': [{'assigned_to_detail': {'real_name': 'Libvirt Maintainers', 'email': 'libvirt-maint', 'name': 'libvirt-maint', 'id': 311982}, 'summary': 'RFE: qemu: Support a managed autoconnect mode for host USB devices', 'status': 'NEW', 'assigned_to': 'Libvirt Maintainers', 'id': 508645}, {'assigned_to_detail': {'real_name': 'Cole Robinson', 'email': 'crobinso', 'name': 'crobinso', 'id': 199727}, 'summary': 'RFE: warn users at guest start if networks/storage pools are inactive', 'status': 'NEW', 'assigned_to': 'Cole Robinson', 'id': 668543}]} +{'bugs': [{'assigned_to_detail': {'real_name': 'Libvirt Maintainers', 'email': 'libvirt-maint', 'name': 'libvirt-maint', 'id': 311982}, 'summary': 'RFE: qemu: Support a managed autoconnect mode for host USB devices', 'status': 'NEW', 'assigned_to': 'Libvirt Maintainers', 'id': 508645}, {'assigned_to_detail': {'real_name': 'Cole Robinson', 'email': 'crobinso', 'name': 'crobinso', 'id': 199727}, 'summary': 'RFE: warn users at guest start if networks/storage pools are inactive', 'status': 'NEW', 'assigned_to': 'Cole Robinson', 'id': 668543}], 'limit': 0, 'FOOFAKEVALUE': 'hello'} diff --git a/tests/test_api_misc.py b/tests/test_api_misc.py index dfb0e923..ea1f2e47 100644 --- a/tests/test_api_misc.py +++ b/tests/test_api_misc.py @@ -305,3 +305,12 @@ def test_query_url_fail(): bz.query(query) except Exception as e: assert checkstr not in str(e) + + +def test_query_return_extra(): + bz = tests.mockbackend.make_bz(version="5.1.0", + bug_search_args=None, + bug_search_return="data/mockreturn/test_query1.txt") + dummy, extra = bz.query_return_extra({}) + assert extra['limit'] == 0 + assert extra['FOOFAKEVALUE'] == "hello" From 46f6cc38ecbd541c887accd571d074d41086351b Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 13 Feb 2024 16:01:22 -0500 Subject: [PATCH 079/106] examples: add redhat_query_all.py In late 2021, bugzilla.redhat.com changed query() results to default returning only 20 bugs. If the user passes in limit=0, that number changes to 1000, but is still capped if the query would return more than that. There's a discussion here with multiple proposed workarounds: https://github.com/python-bugzilla/python-bugzilla/issues/149 This demonstrates the one that takes the least amount of code IMO. It uses `ids_only=True`, which is a custom bugzilla.redhat.com query feature to bypass the query limit by only returning matching bug IDs. Signed-off-by: Cole Robinson --- examples/redhat_query_all.py | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 examples/redhat_query_all.py diff --git a/examples/redhat_query_all.py b/examples/redhat_query_all.py new file mode 100644 index 00000000..9ec5f26f --- /dev/null +++ b/examples/redhat_query_all.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# This work is licensed under the GNU GPLv2 or later. +# See the COPYING file in the top-level directory. + +# redhat_query_all.py: Perform a few varieties of queries + +import bugzilla + +# public test instance of bugzilla.redhat.com. It's okay to make changes +URL = "bugzilla.stage.redhat.com" + +bzapi = bugzilla.Bugzilla(URL) + + +# In late 2021, bugzilla.redhat.com changed query() results to default to +# returning only 20 bugs. If the user passes in limit=0, that number changes +# to 1000, but is still capped if the query would return more than that. +# +# There's a discussion here with multiple proposed ways to work around it: +# https://github.com/python-bugzilla/python-bugzilla/issues/149 +# +# This method uses ids_only=True, which is a custom bugzilla.redhat.com +# query feature to bypass the query limit by only returning matching bug IDs. +# rhbz feature bug: https://bugzilla.redhat.com/show_bug.cgi?id=2005153 + + +# As of Feb 2024 this 1300+ bugs, which would have hit the query limit of 1000 +query = bzapi.build_query( + product="Fedora", + component="virt-manager") +# Request the bugzilla.redhat.com extension ids_only=True to bypass limit +query["ids_only"] = True + +queried_bugs = bzapi.query(query) +ids = [bug.id for bug in queried_bugs] +print(f"Queried {len(ids)} ids") + + +# Use getbugs to fetch the full list. getbugs is not affected by +# default RHBZ limits. However, requesting too much data via getbugs +# will timeout. This paginates the lookup to query 1000 bugs at a time. +# +# We also limit the returned data to just give us the `summary`. +# You should always limit your queries with include_fields` to only return +# the data you need. +count = 0 +pagesize = 1000 +include_fields = ["summary"] +while count < len(ids): + idslice = ids[count:(count + pagesize)] + print(f"Fetching data for bugs {count}-{count+len(idslice)-1}") + bugs = bzapi.getbugs(idslice, include_fields=include_fields) + print(f"Fetched {len(bugs)} bugs") + count += pagesize From c2b4fedda606ef91202941de70916fb1a32d8add Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sun, 25 Feb 2024 15:44:41 -0500 Subject: [PATCH 080/106] cli: Support `--field` and `--field-json` for `bugzilla attach` (#206) Enables option passthrough for adding attachments too Resolves: https://github.com/python-bugzilla/python-bugzilla/issues/169 --------- Signed-off-by: Cole Robinson Co-authored-by: Andreas Hasenkopf --- bugzilla/_cli.py | 29 +++++++++++++++++----------- tests/data/mockargs/test_attach3.txt | 9 +++++++++ tests/test_cli_attach.py | 12 ++++++++++++ 3 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 tests/data/mockargs/test_attach3.txt diff --git a/bugzilla/_cli.py b/bugzilla/_cli.py index e14c1df4..02d6a367 100755 --- a/bugzilla/_cli.py +++ b/bugzilla/_cli.py @@ -185,6 +185,19 @@ def _parser_add_output_options(p): "section 'Output options' for more details.") +def _parser_add_field_passthrough_opts(p): + p.add_argument('--field', + metavar="FIELD=VALUE", action="append", dest="fields", + help="Manually specify a bugzilla API field. FIELD is " + "the raw name used by the bugzilla instance. For example, if your " + "bugzilla instance has a custom field cf_my_field, do:\n" + " --field cf_my_field=VALUE") + p.add_argument('--field-json', + metavar="JSONSTRING", action="append", dest="field_jsons", + help="Specify --field data as a JSON string. Example: --field-json " + '\'{"cf_my_field": "VALUE", "cf_array_field": [1, 2]}\'') + + def _parser_add_bz_fields(rootp, command): cmd_new = (command == "new") cmd_query = (command == "query") @@ -256,17 +269,7 @@ def _parser_add_bz_fields(rootp, command): p.add_argument('-F', '--fixed_in', help="RHBZ 'Fixed in version' field") - # Put this at the end, so it sticks out more - p.add_argument('--field', - metavar="FIELD=VALUE", action="append", dest="fields", - help="Manually specify a bugzilla API field. FIELD is " - "the raw name used by the bugzilla instance. For example, if your " - "bugzilla instance has a custom field cf_my_field, do:\n" - " --field cf_my_field=VALUE") - p.add_argument('--field-json', - metavar="JSONSTRING", action="append", dest="field_jsons", - help="Specify --field data as a JSON string. Example: --field-json " - '\'{"cf_my_field": "VALUE", "cf_array_field": [1, 2]}\'') + _parser_add_field_passthrough_opts(p) if not cmd_modify: _parser_add_output_options(rootp) @@ -405,6 +408,8 @@ def _setup_action_attach_parser(subparsers): p.add_argument('--private', action='store_true', default=False, help='Mark new comment as private') + _parser_add_field_passthrough_opts(p) + def _setup_action_login_parser(subparsers): usage = 'bugzilla login [--api-key] [username [password]]' @@ -1172,6 +1177,8 @@ def _do_set_attach(bz, opt, parser): kwargs["is_private"] = True desc = opt.desc or os.path.basename(fileobj.name) + _merge_field_opts(kwargs, opt.fields, opt.field_jsons, parser) + # Upload attachments for bugid in opt.ids: attid = bz.attachfile(bugid, fileobj, desc, **kwargs) diff --git a/tests/data/mockargs/test_attach3.txt b/tests/data/mockargs/test_attach3.txt new file mode 100644 index 00000000..48da7d7a --- /dev/null +++ b/tests/data/mockargs/test_attach3.txt @@ -0,0 +1,9 @@ +(['123456'], + 'STRIPPED-BY-TESTSUITE', + {'content_type': 'text/plain', + 'file_name': 'bz-attach-get1.txt', + 'flags': [{'name': 'review', + 'requestee': 'crobinso@redhat.com', + 'status': '-'}], + 'is_obsolete': '1', + 'summary': 'bz-attach-get1.txt'}) diff --git a/tests/test_cli_attach.py b/tests/test_cli_attach.py index d287bc54..584858f1 100644 --- a/tests/test_cli_attach.py +++ b/tests/test_cli_attach.py @@ -49,6 +49,18 @@ def test_attach(run_cli): out = run_cli(cmd, fakebz, stdin=attachcontent) assert "Created attachment 1557949 on bug 123456" in out + # Test --field passthrough + cmd = "bugzilla attach 123456 --file=%s " % attachfile + cmd += "--field=is_obsolete=1 " + cmd += "--field-json " + cmd += ('\'{"flags": [{"name": "review"' + ', "requestee": "crobinso@redhat.com", "status": "-"}]}\'') + fakebz = tests.mockbackend.make_bz( + bug_attachment_create_args="data/mockargs/test_attach3.txt", + bug_attachment_create_return={'ids': [1557949]}) + out = run_cli(cmd, fakebz) + assert "Created attachment 1557949 on bug 123456" in out + def _test_attach_get(run_cli): # Hit error when using ids with --get* From 9e9d39bf8f76079887cfee6a5336555c3578eb00 Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Thu, 13 Jun 2024 14:16:20 +0200 Subject: [PATCH 081/106] Allow bug creation with an explicitly empty list of groups (closes #210) This allows the caller of this method to create a bug with no groups assigned, even if server-side default group assignments are configured. --- bugzilla/base.py | 2 +- tests/test_base.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/test_base.py diff --git a/bugzilla/base.py b/bugzilla/base.py index a30ec2cb..eef84aba 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -1761,7 +1761,7 @@ def build_createbug(self, localdict["cc"] = listify(cc) if depends_on: localdict["depends_on"] = listify(depends_on) - if groups: + if groups is not None: localdict["groups"] = listify(groups) if keywords: localdict["keywords"] = listify(keywords) diff --git a/tests/test_base.py b/tests/test_base.py new file mode 100644 index 00000000..d62428e5 --- /dev/null +++ b/tests/test_base.py @@ -0,0 +1,20 @@ +from bugzilla.base import Bugzilla + + +def test_build_createbug(): + bz = Bugzilla(url=None) + + args = {"product": "Ubuntu 33⅓", "summary": "Hello World", "alias": "CVE-2024-0000"} + result = bz.build_createbug(**args) + assert result == args + + result = bz.build_createbug(groups=None, **args) + assert result == args + + args["groups"] = [] + result = bz.build_createbug(**args) + assert result == args + + args["groups"] += ["the-group"] + result = bz.build_createbug(**args) + assert result == args From efd2c174bd03dc2e1cecb889b0c2bf4f10ce7f02 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 7 Feb 2024 08:59:16 -0500 Subject: [PATCH 082/106] man: Add section about `bugzillarc` Fixes: https://github.com/python-bugzilla/python-bugzilla/issues/175 Signed-off-by: Cole Robinson --- man/bugzilla.rst | 59 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/man/bugzilla.rst b/man/bugzilla.rst index c814e519..09ff4132 100644 --- a/man/bugzilla.rst +++ b/man/bugzilla.rst @@ -791,6 +791,46 @@ List the versions for the given product Only show active components. Combine with --components* +``bugzillarc`` CONFIG FILE +========================== + +Both ``bugzilla`` and the python-bugzilla library will read +a ``bugzillarc`` config file if it is present in the following +locations: + +- /etc/bugzillarc +- ~/.bugzillarc +- ~/.config/python-bugzilla/bugzillarc + +The contents of the files are processed and merged together +in the order they are listed above. + +The main usage for ``bugzillarc`` is to store API keys for your +bugzilla URLs: + +:: + + [bugzilla.example.com] + api_key=INSERT-YOUR-API-KEY-HERE + + [bugzilla.redhat.com] + api_key=MY-REDHAT-API-KEY-BLAH + + +The sections must be hostnames. Other values that can be +set per hostname section are + +- ``user``: default auth username +- ``password``: default auth password +- ``cert``: default client side certificate + + +A ``[DEFAULTS]`` section is also accepted, which takes the following +values: + +- ``url``: default bugzilla URL + + AUTHENTICATION CACHE AND API KEYS ================================= @@ -802,28 +842,17 @@ active login. If you are connecting to a bugzilla 5.0 or later instance, the best option is to use bugzilla API keys. From the bugzilla web UI, log in, navigate to Preferences->API Keys, and generate a key (it will be a long -string of characters and numbers). Then create a -~/.config/python-bugzilla/bugzillarc like this: - -:: - - $ cat ~/.config/python-bugzilla/bugzillarc - - [bugzilla.example.com] - api_key=YOUR_API_KEY - -Replace 'bugzilla.example.com' with your bugzilla host name, and -YOUR_API_KEY with the generated API Key from the Web UI. +string of characters and numbers). -Alternatively, you can use 'bugzilla login --api-key', which will ask -for the API key, and save it to bugzillarc for you. +Then use 'bugzilla --bugzilla URL login --api-key', which will ask +for the API key, and save it to ``bugzillarc`` for you. For older bugzilla instances, you will need to cache a login token with the "login" subcommand or the "--login" argument. Additionally, the --no-cache-credentials option will tell the bugzilla tool to *not* save or use any authentication cache, including the -bugzillarc file. +``bugzillarc`` file. EXAMPLES From 178fb6fc7fd05877520d8594630317cebc60532b Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 4 Sep 2024 14:14:27 -0400 Subject: [PATCH 083/106] man: Regenerate bugzilla.1 Signed-off-by: Cole Robinson --- man/bugzilla.1 | 116 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 40 deletions(-) diff --git a/man/bugzilla.1 b/man/bugzilla.1 index fd83d516..fba29568 100644 --- a/man/bugzilla.1 +++ b/man/bugzilla.1 @@ -1,8 +1,5 @@ .\" Man page generated from reStructuredText. . -.TH BUGZILLA 1 "" "" "User Commands" -.SH NAME -bugzilla \- command line tool for interacting with Bugzilla . .nr rst2man-indent-level 0 . @@ -30,6 +27,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. +.TH "BUGZILLA" 1 "" "" "User Commands" +.SH NAME +bugzilla \- command line tool for interacting with Bugzilla .SH SYNOPSIS .sp \fBbugzilla\fP [\fIoptions\fP] [\fIcommand\fP] [\fIcommand\-options\fP] @@ -86,7 +86,7 @@ client side certificate file needed by the webserver. .sp \fBSyntax:\fP \fB\-\-login\fP .sp -Run interactive "login" before performing the specified command. +Run interactive \(dqlogin\(dq before performing the specified command. .SS \fB\-\-username\fP .sp \fBSyntax:\fP \fB\-\-username\fP USERNAME @@ -114,13 +114,8 @@ they expire the tool errors, rather than subtly change output. .sp \fBSyntax:\fP \fB\-\-no\-cache\-credentials\fP .sp -Don\(aqt save any bugzilla cookies or tokens to disk, and don\(aqt use any +Don\(aqt save any bugzilla tokens to disk, and don\(aqt use any pre\-existing credentials. -.SS \fB\-\-cookiefile\fP -.sp -\fBSyntax:\fP \fB\-\-cookiefile\fP COOKIEFILE -.sp -cookie file to use for bugzilla authentication .SS \fB\-\-tokenfile\fP .sp \fBSyntax:\fP \fB\-\-tokenfile\fP TOKENFILE @@ -260,7 +255,7 @@ Bug assignee \fBSyntax:\fP \fB\-\-qa_contact\fP QA_CONTACT .sp QA contact -.SS \fB\-\-flag\fP +.SS \fB\-f, \-\-flag\fP .sp \fBSyntax:\fP \fB\-\-flag\fP FLAG .sp @@ -299,16 +294,22 @@ RHBZ QA whiteboard field RHBZ \(aqFixed in version\(aq field .SS \fB\-\-field\fP .sp -\fBSyntax:\fP \fB\-\-field\fP FIELD\(ga\(ga VALUE +\fBSyntax:\fP \fB\-\-field\fP FIELD=VALUE .sp Manually specify a bugzilla API field. FIELD is the raw name used by the bugzilla instance. For example if your bugzilla instance has a custom field cf_my_field, do: \-\-field cf_my_field=VALUE +.SS \fB\-\-field\-json\fP +.sp +\fBSyntax:\fP \fB\-\-field\-json\fP JSONSTRING +.sp +Specify \-\-field data as a JSON string. Example: +\-\-field\-json \(aq{\(dqcf_my_field\(dq: \(dqVALUE\(dq, \(dqcf_array_field\(dq: [1, 2]}\(aq .SH OUTPUT OPTIONS .sp These options are shared by several commands, for tweaking the text output of the command results. -.SS \fB\-f, \-\-full\fP +.SS \fB\-\-full\fP .sp \fBSyntax:\fP \fB\-\-full\fP .sp @@ -379,7 +380,7 @@ the formats are not stable and are subject to change. format. For example, to print a returned bug ID, component, and product, separated with ::, do: .sp -\-\-outputformat "%{id}::%{component}::%{product}" +\-\-outputformat \(dq%{id}::%{component}::%{product}\(dq .sp The fields (like \(aqid\(aq, \(aqcomponent\(aq, etc.) are the names of the values returned by bugzilla\(aqs API. To see a list of all fields, @@ -542,44 +543,78 @@ List the versions for the given product \fBSyntax:\fP \fB\-\-active\-components\fP .sp Only show active components. Combine with \-\-components* -.SH AUTHENTICATION CACHE AND API KEYS +.SH BUGZILLARC CONFIG FILE .sp -Some command usage will require an active login to the bugzilla -instance. For example, if the bugzilla instance has some private bugs, -those bugs will be missing from \(aqquery\(aq output if you do not have an -active login. +Both \fBbugzilla\fP and the python\-bugzilla library will read +a \fBbugzillarc\fP config file if it is present in the following +locations: +.INDENT 0.0 +.IP \(bu 2 +/etc/bugzillarc +.IP \(bu 2 +~/.bugzillarc +.IP \(bu 2 +~/.config/python\-bugzilla/bugzillarc +.UNINDENT .sp -If you are connecting to a bugzilla 5.0 or later instance, the best -option is to use bugzilla API keys. From the bugzilla web UI, log in, -navigate to Preferences\->API Keys, and generate a key (it will be a long -string of characters and numbers). Then create a -~/.config/python\-bugzilla/bugzillarc like this: +The contents of the files are processed and merged together +in the order they are listed above. +.sp +The main usage for \fBbugzillarc\fP is to store API keys for your +bugzilla URLs: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C -$ cat ~/.config/python\-bugzilla/bugzillarc - [bugzilla.example.com] -api_key=YOUR_API_KEY +api_key=INSERT\-YOUR\-API\-KEY\-HERE + +[bugzilla.redhat.com] +api_key=MY\-REDHAT\-API\-KEY\-BLAH .ft P .fi .UNINDENT .UNINDENT .sp -Replace \(aqbugzilla.example.com\(aq with your bugzilla host name, and -YOUR_API_KEY with the generated API Key from the Web UI. +The sections must be hostnames. Other values that can be +set per hostname section are +.INDENT 0.0 +.IP \(bu 2 +\fBuser\fP: default auth username +.IP \(bu 2 +\fBpassword\fP: default auth password +.IP \(bu 2 +\fBcert\fP: default client side certificate +.UNINDENT +.sp +A \fB[DEFAULTS]\fP section is also accepted, which takes the following +values: +.INDENT 0.0 +.IP \(bu 2 +\fBurl\fP: default bugzilla URL +.UNINDENT +.SH AUTHENTICATION CACHE AND API KEYS +.sp +Some command usage will require an active login to the bugzilla +instance. For example, if the bugzilla instance has some private bugs, +those bugs will be missing from \(aqquery\(aq output if you do not have an +active login. .sp -Alternatively, you can use \(aqbugzilla login \-\-api\-key\(aq, which will ask -for the API key, and save it to bugzillarc for you. +If you are connecting to a bugzilla 5.0 or later instance, the best +option is to use bugzilla API keys. From the bugzilla web UI, log in, +navigate to Preferences\->API Keys, and generate a key (it will be a long +string of characters and numbers). .sp -For older bugzilla instances, you will need to cache a login cookie or -token with the "login" subcommand or the "\-\-login" argument. +Then use \(aqbugzilla \-\-bugzilla URL login \-\-api\-key\(aq, which will ask +for the API key, and save it to \fBbugzillarc\fP for you. +.sp +For older bugzilla instances, you will need to cache a login token +with the \(dqlogin\(dq subcommand or the \(dq\-\-login\(dq argument. .sp Additionally, the \-\-no\-cache\-credentials option will tell the bugzilla tool to \fInot\fP save or use any authentication cache, including the -bugzillarc file. +\fBbugzillarc\fP file. .SH EXAMPLES .nf bugzilla query \-\-bug_id 62037 @@ -590,17 +625,17 @@ bugzilla login bugzilla new \-p Fedora \-v rawhide \-c python\-bugzilla \e .in +2 -\-\-summary "python\-bugzilla causes headaches" \e -\-\-comment "python\-bugzilla made my brain hurt when I used it." +\-\-summary \(dqpython\-bugzilla causes headaches\(dq \e +\-\-comment \(dqpython\-bugzilla made my brain hurt when I used it.\(dq .in -2 -bugzilla attach \-\-file ~/Pictures/cam1.jpg \-\-desc "me, in pain" +bugzilla attach \-\-file ~/Pictures/cam1.jpg \-\-desc \(dqme, in pain\(dq $BUGID bugzilla attach \-\-getall $BUGID -bugzilla modify \-\-close NOTABUG \-\-comment "Actually, you\(aqre -hungover." $BUGID +bugzilla modify \-\-close NOTABUG \-\-comment \(dqActually, you\(aqre +hungover.\(dq $BUGID .fi .sp .SH EXIT STATUS @@ -616,6 +651,7 @@ Please report any bugs as github issues at .SH SEE ALSO .sp \fI\%https://bugzilla.readthedocs.io/en/latest/api/index.html\fP -\fI\%https://bugzilla.redhat.com/docs/en/html/api/Bugzilla/WebService/Bug.html\fP +.sp +\fI\%https://bugzilla.redhat.com/docs/en/html/api/core/v1/bug.html\fP .\" Generated by docutils manpage writer. . From 3dac0fa9d580b9bb24f6310799054e91795fcff4 Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Wed, 20 Sep 2023 16:10:09 +0200 Subject: [PATCH 084/106] Run functional RO tests in GitHub actions * Run MariaDB and Bugzilla in service containers * Populate the DB with a defined dump * Include all files to build the Bugzilla image and prepare the environment * Implemented integration tests in new test module * Added a new fixture for request mocking --- .github/workflows/build.yml | 43 + test-requirements.txt | 1 + tests/conftest.py | 54 + tests/integration/__init__.py | 9 + tests/integration/ro_api_test.py | 101 ++ tests/integration/ro_cli_test.py | 47 + tests/services/Dockerfile | 28 + tests/services/README.md | 58 + tests/services/bugs.sql | 2270 ++++++++++++++++++++++++++++++ tests/services/bugzilla.conf | 9 + tests/services/bugzillarc | 2 + tests/services/localconfig | 19 + tests/services/params.json | 104 ++ tests/utils.py | 5 + 14 files changed, 2750 insertions(+) create mode 100644 tests/integration/__init__.py create mode 100644 tests/integration/ro_api_test.py create mode 100644 tests/integration/ro_cli_test.py create mode 100644 tests/services/Dockerfile create mode 100644 tests/services/README.md create mode 100644 tests/services/bugs.sql create mode 100644 tests/services/bugzilla.conf create mode 100644 tests/services/bugzillarc create mode 100644 tests/services/localconfig create mode 100644 tests/services/params.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cd92d9d8..771d9b72 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,6 +66,49 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} + # Run functional tests + integrationRO: + runs-on: ubuntu-latest + services: + mariadb: + image: mariadb:latest + env: + MARIADB_USER: bugs + MARIADB_DATABASE: bugs + MARIADB_PASSWORD: secret + MARIADB_ROOT_PASSWORD: supersecret + ports: + - 3306:3306 + bugzilla: + image: ghcr.io/crazyscientist/bugzilla:test + ports: + - 80:80 + strategy: + matrix: + python-version: ["3.x"] + steps: + - uses: actions/checkout@v3 + - name: Install MariaDB utils + run: sudo apt install --no-install-recommends -q -y mariadb-client + - name: Restore DB dump + run: mariadb -h 127.0.0.1 -P 3306 --password=secret -u bugs bugs < tests/services/bugs.sql + - name: Store API key + run: | + mkdir -p ~/.config/python-bugzilla/ + cp tests/services/bugzillarc ~/.config/python-bugzilla/ + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest pytest-cov + pip install -r requirements.txt -r test-requirements.txt + - name: Test with pytest + run: pytest --ro-integration + env: + BUGZILLA_URL: http://localhost # Build and install on Windows windows: diff --git a/test-requirements.txt b/test-requirements.txt index 6abde2bb..6e80f2c7 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -2,3 +2,4 @@ pytest pylint<3.1 pycodestyle<2.12 +responses diff --git a/tests/conftest.py b/tests/conftest.py index cfac4671..938932b6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,8 +4,10 @@ import locale import logging import os +import re import pytest +import responses import tests import tests.utils @@ -17,6 +19,8 @@ # https://docs.pytest.org/en/latest/writing_plugins.html def pytest_addoption(parser): + parser.addoption("--ro-integration", action="store_true", default=False, + help="Run readonly tests against local Bugzilla instance.") parser.addoption("--ro-functional", action="store_true", default=False, help=("Run readonly functional tests against actual " "bugzilla instances. This will be very slow.")) @@ -40,11 +44,17 @@ def pytest_addoption(parser): def pytest_ignore_collect(path, config): has_ro = config.getoption("--ro-functional") + has_ro_i = config.getoption("--ro-integration") has_rw = config.getoption("--rw-functional") base = os.path.basename(str(path)) is_ro = base == "test_ro_functional.py" + is_ro_i = "tests/integration/ro" in str(path) is_rw = base == "test_rw_functional.py" + + if is_ro_i and not has_ro_i: + return True + if is_ro and not has_ro: return True if is_rw and not has_rw: @@ -107,3 +117,47 @@ def run_cli(capsys, monkeypatch): def _do_run(*args, **kwargs): return tests.utils.do_run_cli(capsys, monkeypatch, *args, **kwargs) return _do_run + + +@pytest.fixture +def mocked_responses(): + """ + Mock responses + + * Quickly return error responses + * Pass through requests to live instances + * Provide an incorrect XMLRPC response + """ + passthrough = () + status_pattern = re.compile(r"https://httpstat.us/(?P\d+).*") + + def status_callback(request): + match = status_pattern.match(request.url) + status_code = 400 + if match: + status_code = int(match.group("status")) + + return status_code, {}, "

Lorem ipsum

" + + test_url = os.getenv("BUGZILLA_URL") + if test_url: + passthrough += (test_url, ) + with responses.RequestsMock(passthru_prefixes=passthrough, + assert_all_requests_are_fired=False) as mock: + mock.add_callback( + method=responses.GET, + url=status_pattern, + callback=status_callback + ) + mock.add_callback( + method=responses.POST, + url=status_pattern, + callback=status_callback + ) + mock.add( + method=responses.POST, + url="https://example.com/#xmlrpc", + status=200, + body="This is no XML" + ) + yield mock diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 00000000..908a1f54 --- /dev/null +++ b/tests/integration/__init__.py @@ -0,0 +1,9 @@ +import os + + +TEST_URL = os.getenv("BUGZILLA_URL", "http://localhost") +TEST_OWNER = "andreas@hasenkopf.xyz" +TEST_PRODUCTS = {"Red Hat Enterprise Linux 9", + "SUSE Linux Enterprise Server 15 SP6", + "TestProduct"} +TEST_SUSE_COMPONENTS = {"Containers", "Kernel"} diff --git a/tests/integration/ro_api_test.py b/tests/integration/ro_api_test.py new file mode 100644 index 00000000..3f096587 --- /dev/null +++ b/tests/integration/ro_api_test.py @@ -0,0 +1,101 @@ +# Ignoring pytest-related warnings: +# pylint: disable=redefined-outer-name,unused-argument +import pytest + +from bugzilla import BugzillaError + +from ..utils import open_bz +from . import TEST_URL, TEST_PRODUCTS, TEST_SUSE_COMPONENTS, TEST_OWNER + + +def test_rest_xmlrpc_detection(mocked_responses): + # The default: use XMLRPC + bz = open_bz(url=TEST_URL) + assert bz.is_xmlrpc() + assert "/xmlrpc.cgi" in bz.url + + # See /rest in the URL, so use REST + bz = open_bz(url=TEST_URL + "/rest") + assert bz.is_rest() + with pytest.raises(BugzillaError) as e: + dummy = bz._proxy # pylint: disable=protected-access + assert "raw XMLRPC access is not provided" in str(e) + + # See /xmlrpc.cgi in the URL, so use XMLRPC + bz = open_bz(url=TEST_URL + "/xmlrpc.cgi") + assert "/xmlrpc.cgi" in bz.url + assert bz.is_xmlrpc() + assert bz._proxy # pylint: disable=protected-access + + +def test_apikey_error_scraping(mocked_responses): + # Ensure the API key does not leak into any requests exceptions + fakekey = "FOOBARMYKEY" + with pytest.raises(Exception) as e: + open_bz("https://httpstat.us/400&foo", + force_xmlrpc=True, api_key=fakekey) + assert "Client Error" in str(e.value) + assert fakekey not in str(e.value) + + with pytest.raises(Exception) as e: + open_bz("https://httpstat.us/400&foo", + force_rest=True, api_key=fakekey) + assert "Client Error" in str(e.value) + assert fakekey not in str(e.value) + + +def test_xmlrpc_bad_url(mocked_responses): + with pytest.raises(BugzillaError) as e: + open_bz(url="https://example.com/#xmlrpc", force_xmlrpc=True) + assert "URL may not be an XMLRPC URL" in str(e) + + +def test_get_products(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + + assert len(bz.products) == 3 + assert {p["name"] for p in bz.products} == TEST_PRODUCTS + + rhel = next(p for p in bz.products if p["id"] == 2) + assert {v["name"] for v in rhel["versions"]} == {"9.0", "9.1", "unspecified"} + + +def test_get_components(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + components = bz.getcomponents(product="SUSE Linux Enterprise Server 15 SP6") + assert len(components) == 2 + assert set(components) == TEST_SUSE_COMPONENTS + + +def test_get_component_detail(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + component = bz.getcomponentdetails(product="Red Hat Enterprise Linux 9", + component="python-bugzilla") + assert component["id"] == 2 + assert component["default_assigned_to"] == TEST_OWNER + + +def test_query(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + query = bz.build_query(product="Red Hat Enterprise Linux 9", component="python-bugzilla") + bugs = bz.query(query=query) + + assert len(bugs) == 1 + assert bugs[0].id == 2 + assert bugs[0].summary == "Expect the Spanish inquisition" + + bz = open_bz(url=TEST_URL, **backends) + query = bz.build_query(product="SUSE Linux Enterprise Server 15 SP6") + bugs = bz.query(query=query) + + assert len(bugs) == 1 + assert bugs[0].id == 1 + assert bugs[0].whiteboard == "AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:L" + + +def test_get_bug_alias(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + bug = bz.getbug("FOO-1") + + assert bug.id == 1 + assert bug.summary == "ZeroDivisionError in function foo_bar()" diff --git a/tests/integration/ro_cli_test.py b/tests/integration/ro_cli_test.py new file mode 100644 index 00000000..f4d308d9 --- /dev/null +++ b/tests/integration/ro_cli_test.py @@ -0,0 +1,47 @@ +# Ignoring pytest-related warnings: +# pylint: disable=unused-argument +from ..utils import open_bz +from . import TEST_URL, TEST_PRODUCTS, TEST_SUSE_COMPONENTS, TEST_OWNER + + +def test_get_products(mocked_responses, run_cli, backends): + bz = open_bz(url=TEST_URL, **backends) + out = run_cli("bugzilla info --products", bzinstance=bz) + assert len(out.strip().split("\n")) == 3 + + for product in TEST_PRODUCTS: + assert product in out + + +def test_get_components(mocked_responses, run_cli, backends): + bz = open_bz(url=TEST_URL, **backends) + out = run_cli("bugzilla info --components 'SUSE Linux Enterprise Server 15 SP6'", bzinstance=bz) + assert len(out.strip().split("\n")) == 2 + for comp in TEST_SUSE_COMPONENTS: + assert comp in out + + +def test_get_component_owners(mocked_responses, run_cli, backends): + bz = open_bz(url=TEST_URL, **backends) + out = run_cli("bugzilla info --component_owners 'SUSE Linux Enterprise Server 15 SP6'", + bzinstance=bz) + assert TEST_OWNER in out + + +def test_get_versions(mocked_responses, run_cli, backends): + bz = open_bz(url=TEST_URL, **backends) + out = run_cli("bugzilla info --versions 'Red Hat Enterprise Linux 9'", bzinstance=bz) + versions = set(out.strip().split("\n")) + + assert versions == {"unspecified", "9.0", "9.1"} + + +def test_query(mocked_responses, run_cli, backends): + bz = open_bz(url=TEST_URL, **backends) + out = run_cli("bugzilla query --product 'Red Hat Enterprise Linux 9' " + "--component 'python-bugzilla'", bzinstance=bz) + lines = out.strip().splitlines() + + assert len(lines) == 1 + assert lines[0].startswith("#2") + assert "Expect the Spanish inquisition" in lines[0] diff --git a/tests/services/Dockerfile b/tests/services/Dockerfile new file mode 100644 index 00000000..83c1e209 --- /dev/null +++ b/tests/services/Dockerfile @@ -0,0 +1,28 @@ +FROM ubuntu:22.04 +LABEL description="Bugzilla image for testing purposes" +ARG DEBIAN_FRONTEND=noninteractive +ENV TZ="Etc/UTC" +RUN apt update && \ + apt install --no-install-recommends -q -y \ + tzdata wget apache2 libcgi-pm-perl libdatetime-perl libdatetime-timezone-perl libdbi-perl \ + libdbix-connector-perl libdigest-sha-perl libemail-address-perl libemail-mime-perl \ + libemail-sender-perl libjson-xs-perl liblist-moreutils-perl libmath-random-isaac-perl \ + libtemplate-perl libtimedate-perl liburi-perl libmariadb-dev-compat libdbd-mysql-perl \ + libxmlrpc-lite-perl libsoap-lite-perl libapache2-mod-perl2 libtest-taint-perl \ + libjson-rpc-perl && \ + apt clean +RUN mkdir -p /var/www/webapps && \ + wget https://ftp.mozilla.org/pub/mozilla.org/webtools/bugzilla-5.0.6.tar.gz \ + -O /tmp/bugzilla-5.0.6.tar.gz&& \ + tar xvzf /tmp/bugzilla-5.0.6.tar.gz && \ + rm /tmp/bugzilla-5.0.6.tar.gz && \ + mv /bugzilla-5.0.6/ /var/www/webapps/bugzilla/ && \ + mkdir /var/www/webapps/bugzilla/data/ +COPY bugzilla.conf /etc/apache2/sites-available/ +COPY localconfig /var/www/webapps/bugzilla/ +COPY params.json /var/www/webapps/bugzilla/data/ +RUN a2dissite 000-default && \ + a2ensite bugzilla && \ + a2enmod cgi headers expires rewrite perl && \ + /var/www/webapps/bugzilla/checksetup.pl +CMD apachectl -D FOREGROUND diff --git a/tests/services/README.md b/tests/services/README.md new file mode 100644 index 00000000..029241b8 --- /dev/null +++ b/tests/services/README.md @@ -0,0 +1,58 @@ +# Working with the containerized Bugzilla instance + +This document describes the steps for building a Bugzilla container image that can be used in the +GitHub Actions as a service and generating a database dump. + +In the following examples, the use of `docker` is assumed. Commands for `podman` should be +identical. + +## Build + +```shell +$ docker network create --driver bridge local-bridge +$ docker run --rm -itd \ + --env MARIADB_USER=bugs \ + --env MARIADB_DATABASE=bugs \ + --env MARIADB_PASSWORD=secret \ + --env MARIADB_ROOT_PASSWORD=supersecret \ + -p 3306:3306 \ + --network local-bridge \ + --name mariadb \ + mariadb:latest +$ mariadb -u bugs -h 127.0.0.1 -P 3306 --password=secret bugs < bugs.sql +$ docker build --network local-bridge . -t ghcr.io/crazyscientist/bugzilla:test +``` + +For those, who can spot the _chicken and egg problem_: The first version of `bugs.sql` was +created after running the Bugzilla installer inside the container. + +## Usage + +Once built, you can follow the above instructions; instead of building +the image, you can run it: + +```shell +docker run --rm -itd \ + -p 8000:80 \ + --network local-bridge \ + ghcr.io/crazyscientist/bugzilla:test +``` + +## Test data + +The test data used by the Bugzilla service in the integration test suite is stored in `bugs.sql`. + +One can edit this file manually or follow the above instructions to start both a MariaDB and +Bugzilla container and edit the data in Bugzilla. Once done, one needs to dump the changed data into +the file again: + +```shell +$ mariadb-dump -u bugs -h 127.0.0.1 -P 3306 --password=secret bugs > bugs.qql +``` + +## Testing +And now, you can run the integration tests against this instance: + +```shell +BUGZILLA_URL=http://localhost:8000 pytest --ro-integration +``` diff --git a/tests/services/bugs.sql b/tests/services/bugs.sql new file mode 100644 index 00000000..c0ddf4ba --- /dev/null +++ b/tests/services/bugs.sql @@ -0,0 +1,2270 @@ +-- MariaDB dump 10.19 Distrib 10.6.12-MariaDB, for debian-linux-gnu (x86_64) +-- +-- Host: 127.0.0.1 Database: bugs +-- ------------------------------------------------------ +-- Server version 11.1.2-MariaDB-1:11.1.2+maria~ubu2204 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `attach_data` +-- + +DROP TABLE IF EXISTS `attach_data`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `attach_data` ( + `id` mediumint(9) NOT NULL, + `thedata` longblob NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_attach_data_id_attachments_attach_id` FOREIGN KEY (`id`) REFERENCES `attachments` (`attach_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci MAX_ROWS=100000 AVG_ROW_LENGTH=1000000; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `attach_data` +-- + +LOCK TABLES `attach_data` WRITE; +/*!40000 ALTER TABLE `attach_data` DISABLE KEYS */; +/*!40000 ALTER TABLE `attach_data` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `attachments` +-- + +DROP TABLE IF EXISTS `attachments`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `attachments` ( + `attach_id` mediumint(9) NOT NULL AUTO_INCREMENT, + `bug_id` mediumint(9) NOT NULL, + `creation_ts` datetime NOT NULL, + `modification_time` datetime NOT NULL, + `description` tinytext NOT NULL, + `mimetype` tinytext NOT NULL, + `ispatch` tinyint(4) NOT NULL DEFAULT 0, + `filename` varchar(255) NOT NULL, + `submitter_id` mediumint(9) NOT NULL, + `isobsolete` tinyint(4) NOT NULL DEFAULT 0, + `isprivate` tinyint(4) NOT NULL DEFAULT 0, + PRIMARY KEY (`attach_id`), + KEY `attachments_bug_id_idx` (`bug_id`), + KEY `attachments_creation_ts_idx` (`creation_ts`), + KEY `attachments_modification_time_idx` (`modification_time`), + KEY `attachments_submitter_id_idx` (`submitter_id`,`bug_id`), + CONSTRAINT `fk_attachments_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_attachments_submitter_id_profiles_userid` FOREIGN KEY (`submitter_id`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `attachments` +-- + +LOCK TABLES `attachments` WRITE; +/*!40000 ALTER TABLE `attachments` DISABLE KEYS */; +/*!40000 ALTER TABLE `attachments` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `audit_log` +-- + +DROP TABLE IF EXISTS `audit_log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `audit_log` ( + `user_id` mediumint(9) DEFAULT NULL, + `class` varchar(255) NOT NULL, + `object_id` int(11) NOT NULL, + `field` varchar(64) NOT NULL, + `removed` mediumtext DEFAULT NULL, + `added` mediumtext DEFAULT NULL, + `at_time` datetime NOT NULL, + KEY `audit_log_class_idx` (`class`,`at_time`), + KEY `fk_audit_log_user_id_profiles_userid` (`user_id`), + CONSTRAINT `fk_audit_log_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE SET NULL ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `audit_log` +-- + +LOCK TABLES `audit_log` WRITE; +/*!40000 ALTER TABLE `audit_log` DISABLE KEYS */; +INSERT INTO `audit_log` VALUES (NULL,'Bugzilla::Field',1,'__create__',NULL,'bug_id','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',2,'__create__',NULL,'short_desc','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',3,'__create__',NULL,'classification','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',4,'__create__',NULL,'product','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',5,'__create__',NULL,'version','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',6,'__create__',NULL,'rep_platform','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',7,'__create__',NULL,'bug_file_loc','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',8,'__create__',NULL,'op_sys','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',9,'__create__',NULL,'bug_status','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',10,'__create__',NULL,'status_whiteboard','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',11,'__create__',NULL,'keywords','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',12,'__create__',NULL,'resolution','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',13,'__create__',NULL,'bug_severity','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',14,'__create__',NULL,'priority','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',15,'__create__',NULL,'component','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',16,'__create__',NULL,'assigned_to','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',17,'__create__',NULL,'reporter','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',18,'__create__',NULL,'qa_contact','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',19,'__create__',NULL,'assigned_to_realname','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',20,'__create__',NULL,'reporter_realname','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',21,'__create__',NULL,'qa_contact_realname','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',22,'__create__',NULL,'cc','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',23,'__create__',NULL,'dependson','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',24,'__create__',NULL,'blocked','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',25,'__create__',NULL,'attachments.description','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',26,'__create__',NULL,'attachments.filename','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',27,'__create__',NULL,'attachments.mimetype','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',28,'__create__',NULL,'attachments.ispatch','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',29,'__create__',NULL,'attachments.isobsolete','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',30,'__create__',NULL,'attachments.isprivate','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',31,'__create__',NULL,'attachments.submitter','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',32,'__create__',NULL,'target_milestone','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',33,'__create__',NULL,'creation_ts','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',34,'__create__',NULL,'delta_ts','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',35,'__create__',NULL,'longdesc','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',36,'__create__',NULL,'longdescs.isprivate','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',37,'__create__',NULL,'longdescs.count','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',38,'__create__',NULL,'alias','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',39,'__create__',NULL,'everconfirmed','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',40,'__create__',NULL,'reporter_accessible','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',41,'__create__',NULL,'cclist_accessible','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',42,'__create__',NULL,'bug_group','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',43,'__create__',NULL,'estimated_time','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',44,'__create__',NULL,'remaining_time','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',45,'__create__',NULL,'deadline','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',46,'__create__',NULL,'commenter','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',47,'__create__',NULL,'flagtypes.name','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',48,'__create__',NULL,'requestees.login_name','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',49,'__create__',NULL,'setters.login_name','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',50,'__create__',NULL,'work_time','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',51,'__create__',NULL,'percentage_complete','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',52,'__create__',NULL,'content','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',53,'__create__',NULL,'attach_data.thedata','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',54,'__create__',NULL,'owner_idle_time','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',55,'__create__',NULL,'see_also','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',56,'__create__',NULL,'tag','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',57,'__create__',NULL,'last_visit_ts','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',58,'__create__',NULL,'comment_tag','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',59,'__create__',NULL,'days_elapsed','2023-09-20 13:12:35'),(NULL,'Bugzilla::Classification',1,'__create__',NULL,'Unclassified','2023-09-20 13:12:35'),(NULL,'Bugzilla::Group',1,'__create__',NULL,'admin','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',2,'__create__',NULL,'tweakparams','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',3,'__create__',NULL,'editusers','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',4,'__create__',NULL,'creategroups','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',5,'__create__',NULL,'editclassifications','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',6,'__create__',NULL,'editcomponents','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',7,'__create__',NULL,'editkeywords','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',8,'__create__',NULL,'editbugs','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',9,'__create__',NULL,'canconfirm','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',10,'__create__',NULL,'bz_canusewhineatothers','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',11,'__create__',NULL,'bz_canusewhines','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',12,'__create__',NULL,'bz_sudoers','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',13,'__create__',NULL,'bz_sudo_protect','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',14,'__create__',NULL,'bz_quip_moderators','2023-09-20 13:12:40'),(NULL,'Bugzilla::User',1,'__create__',NULL,'andreas@hasenkopf.xyz','2023-09-20 13:12:55'),(NULL,'Bugzilla::Product',1,'__create__',NULL,'TestProduct','2023-09-20 13:12:55'),(NULL,'Bugzilla::Version',1,'__create__',NULL,'unspecified','2023-09-20 13:12:55'),(NULL,'Bugzilla::Milestone',1,'__create__',NULL,'---','2023-09-20 13:12:55'),(NULL,'Bugzilla::Component',1,'__create__',NULL,'TestComponent','2023-09-20 13:12:55'),(1,'Bugzilla::Product',2,'__create__',NULL,'Red Hat Enterprise Linux 9','2023-11-27 12:25:54'),(1,'Bugzilla::Version',2,'__create__',NULL,'unspecified','2023-11-27 12:25:54'),(1,'Bugzilla::Milestone',2,'__create__',NULL,'---','2023-11-27 12:25:54'),(1,'Bugzilla::Component',2,'__create__',NULL,'python-bugzilla','2023-11-27 12:25:54'),(1,'Bugzilla::Version',3,'__create__',NULL,'9.0','2023-11-27 12:26:06'),(1,'Bugzilla::Version',4,'__create__',NULL,'9.1','2023-11-27 12:26:14'),(1,'Bugzilla::Product',3,'__create__',NULL,'SUSE Linux Enterprise Server 15 SP6','2023-11-27 12:29:18'),(1,'Bugzilla::Version',5,'__create__',NULL,'unspecified','2023-11-27 12:29:18'),(1,'Bugzilla::Milestone',3,'__create__',NULL,'---','2023-11-27 12:29:18'),(1,'Bugzilla::Component',3,'__create__',NULL,'Kernel','2023-11-27 12:29:18'),(1,'Bugzilla::Component',4,'__create__',NULL,'Containers','2023-11-27 12:29:46'); +/*!40000 ALTER TABLE `audit_log` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bug_group_map` +-- + +DROP TABLE IF EXISTS `bug_group_map`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bug_group_map` ( + `bug_id` mediumint(9) NOT NULL, + `group_id` mediumint(9) NOT NULL, + UNIQUE KEY `bug_group_map_bug_id_idx` (`bug_id`,`group_id`), + KEY `bug_group_map_group_id_idx` (`group_id`), + CONSTRAINT `fk_bug_group_map_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_bug_group_map_group_id_groups_id` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bug_group_map` +-- + +LOCK TABLES `bug_group_map` WRITE; +/*!40000 ALTER TABLE `bug_group_map` DISABLE KEYS */; +/*!40000 ALTER TABLE `bug_group_map` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bug_see_also` +-- + +DROP TABLE IF EXISTS `bug_see_also`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bug_see_also` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `bug_id` mediumint(9) NOT NULL, + `value` varchar(255) NOT NULL, + `class` varchar(255) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY `bug_see_also_bug_id_idx` (`bug_id`,`value`), + CONSTRAINT `fk_bug_see_also_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bug_see_also` +-- + +LOCK TABLES `bug_see_also` WRITE; +/*!40000 ALTER TABLE `bug_see_also` DISABLE KEYS */; +/*!40000 ALTER TABLE `bug_see_also` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bug_severity` +-- + +DROP TABLE IF EXISTS `bug_severity`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bug_severity` ( + `id` smallint(6) NOT NULL AUTO_INCREMENT, + `value` varchar(64) NOT NULL, + `sortkey` smallint(6) NOT NULL DEFAULT 0, + `isactive` tinyint(4) NOT NULL DEFAULT 1, + `visibility_value_id` smallint(6) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `bug_severity_value_idx` (`value`), + KEY `bug_severity_sortkey_idx` (`sortkey`,`value`), + KEY `bug_severity_visibility_value_id_idx` (`visibility_value_id`) +) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bug_severity` +-- + +LOCK TABLES `bug_severity` WRITE; +/*!40000 ALTER TABLE `bug_severity` DISABLE KEYS */; +INSERT INTO `bug_severity` VALUES (1,'blocker',100,1,NULL),(2,'critical',200,1,NULL),(3,'major',300,1,NULL),(4,'normal',400,1,NULL),(5,'minor',500,1,NULL),(6,'trivial',600,1,NULL),(7,'enhancement',700,1,NULL); +/*!40000 ALTER TABLE `bug_severity` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bug_status` +-- + +DROP TABLE IF EXISTS `bug_status`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bug_status` ( + `id` smallint(6) NOT NULL AUTO_INCREMENT, + `value` varchar(64) NOT NULL, + `sortkey` smallint(6) NOT NULL DEFAULT 0, + `isactive` tinyint(4) NOT NULL DEFAULT 1, + `visibility_value_id` smallint(6) DEFAULT NULL, + `is_open` tinyint(4) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + UNIQUE KEY `bug_status_value_idx` (`value`), + KEY `bug_status_sortkey_idx` (`sortkey`,`value`), + KEY `bug_status_visibility_value_id_idx` (`visibility_value_id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bug_status` +-- + +LOCK TABLES `bug_status` WRITE; +/*!40000 ALTER TABLE `bug_status` DISABLE KEYS */; +INSERT INTO `bug_status` VALUES (1,'UNCONFIRMED',100,1,NULL,1),(2,'CONFIRMED',200,1,NULL,1),(3,'IN_PROGRESS',300,1,NULL,1),(4,'RESOLVED',400,1,NULL,0),(5,'VERIFIED',500,1,NULL,0); +/*!40000 ALTER TABLE `bug_status` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bug_tag` +-- + +DROP TABLE IF EXISTS `bug_tag`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bug_tag` ( + `bug_id` mediumint(9) NOT NULL, + `tag_id` mediumint(9) NOT NULL, + UNIQUE KEY `bug_tag_bug_id_idx` (`bug_id`,`tag_id`), + KEY `fk_bug_tag_tag_id_tag_id` (`tag_id`), + CONSTRAINT `fk_bug_tag_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_bug_tag_tag_id_tag_id` FOREIGN KEY (`tag_id`) REFERENCES `tag` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bug_tag` +-- + +LOCK TABLES `bug_tag` WRITE; +/*!40000 ALTER TABLE `bug_tag` DISABLE KEYS */; +/*!40000 ALTER TABLE `bug_tag` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bug_user_last_visit` +-- + +DROP TABLE IF EXISTS `bug_user_last_visit`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bug_user_last_visit` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` mediumint(9) NOT NULL, + `bug_id` mediumint(9) NOT NULL, + `last_visit_ts` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `bug_user_last_visit_idx` (`user_id`,`bug_id`), + KEY `bug_user_last_visit_last_visit_ts_idx` (`last_visit_ts`), + KEY `fk_bug_user_last_visit_bug_id_bugs_bug_id` (`bug_id`), + CONSTRAINT `fk_bug_user_last_visit_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_bug_user_last_visit_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bug_user_last_visit` +-- + +LOCK TABLES `bug_user_last_visit` WRITE; +/*!40000 ALTER TABLE `bug_user_last_visit` DISABLE KEYS */; +INSERT INTO `bug_user_last_visit` VALUES (1,1,1,'2023-11-27 15:53:08'),(2,1,2,'2023-11-27 15:38:47'); +/*!40000 ALTER TABLE `bug_user_last_visit` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bugs` +-- + +DROP TABLE IF EXISTS `bugs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bugs` ( + `bug_id` mediumint(9) NOT NULL AUTO_INCREMENT, + `assigned_to` mediumint(9) NOT NULL, + `bug_file_loc` mediumtext NOT NULL DEFAULT '', + `bug_severity` varchar(64) NOT NULL, + `bug_status` varchar(64) NOT NULL, + `creation_ts` datetime DEFAULT NULL, + `delta_ts` datetime NOT NULL, + `short_desc` varchar(255) NOT NULL, + `op_sys` varchar(64) NOT NULL, + `priority` varchar(64) NOT NULL, + `product_id` smallint(6) NOT NULL, + `rep_platform` varchar(64) NOT NULL, + `reporter` mediumint(9) NOT NULL, + `version` varchar(64) NOT NULL, + `component_id` mediumint(9) NOT NULL, + `resolution` varchar(64) NOT NULL DEFAULT '', + `target_milestone` varchar(64) NOT NULL DEFAULT '---', + `qa_contact` mediumint(9) DEFAULT NULL, + `status_whiteboard` mediumtext NOT NULL DEFAULT '', + `lastdiffed` datetime DEFAULT NULL, + `everconfirmed` tinyint(4) NOT NULL, + `reporter_accessible` tinyint(4) NOT NULL DEFAULT 1, + `cclist_accessible` tinyint(4) NOT NULL DEFAULT 1, + `estimated_time` decimal(7,2) NOT NULL DEFAULT 0.00, + `remaining_time` decimal(7,2) NOT NULL DEFAULT 0.00, + `deadline` datetime DEFAULT NULL, + PRIMARY KEY (`bug_id`), + KEY `bugs_assigned_to_idx` (`assigned_to`), + KEY `bugs_creation_ts_idx` (`creation_ts`), + KEY `bugs_delta_ts_idx` (`delta_ts`), + KEY `bugs_bug_severity_idx` (`bug_severity`), + KEY `bugs_bug_status_idx` (`bug_status`), + KEY `bugs_op_sys_idx` (`op_sys`), + KEY `bugs_priority_idx` (`priority`), + KEY `bugs_product_id_idx` (`product_id`), + KEY `bugs_reporter_idx` (`reporter`), + KEY `bugs_version_idx` (`version`), + KEY `bugs_component_id_idx` (`component_id`), + KEY `bugs_resolution_idx` (`resolution`), + KEY `bugs_target_milestone_idx` (`target_milestone`), + KEY `bugs_qa_contact_idx` (`qa_contact`), + CONSTRAINT `fk_bugs_assigned_to_profiles_userid` FOREIGN KEY (`assigned_to`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE, + CONSTRAINT `fk_bugs_component_id_components_id` FOREIGN KEY (`component_id`) REFERENCES `components` (`id`) ON UPDATE CASCADE, + CONSTRAINT `fk_bugs_product_id_products_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON UPDATE CASCADE, + CONSTRAINT `fk_bugs_qa_contact_profiles_userid` FOREIGN KEY (`qa_contact`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE, + CONSTRAINT `fk_bugs_reporter_profiles_userid` FOREIGN KEY (`reporter`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bugs` +-- + +LOCK TABLES `bugs` WRITE; +/*!40000 ALTER TABLE `bugs` DISABLE KEYS */; +INSERT INTO `bugs` VALUES (1,1,'','major','IN_PROGRESS','2023-11-27 15:35:33','2023-11-27 15:53:04','ZeroDivisionError in function foo_bar()','Linux','---',3,'PC',1,'unspecified',4,'','---',NULL,'AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:L','2023-11-27 15:53:04',1,1,1,0.00,0.00,NULL),(2,1,'','enhancement','CONFIRMED','2023-11-27 15:38:45','2023-11-27 15:38:45','Expect the Spanish inquisition','Linux','---',2,'PC',1,'9.1',2,'','---',NULL,'','2023-11-27 15:38:45',1,1,1,0.00,0.00,NULL); +/*!40000 ALTER TABLE `bugs` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bugs_activity` +-- + +DROP TABLE IF EXISTS `bugs_activity`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bugs_activity` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `bug_id` mediumint(9) NOT NULL, + `attach_id` mediumint(9) DEFAULT NULL, + `who` mediumint(9) NOT NULL, + `bug_when` datetime NOT NULL, + `fieldid` mediumint(9) NOT NULL, + `added` varchar(255) DEFAULT NULL, + `removed` varchar(255) DEFAULT NULL, + `comment_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `bugs_activity_bug_id_idx` (`bug_id`), + KEY `bugs_activity_who_idx` (`who`), + KEY `bugs_activity_bug_when_idx` (`bug_when`), + KEY `bugs_activity_fieldid_idx` (`fieldid`), + KEY `bugs_activity_added_idx` (`added`), + KEY `bugs_activity_removed_idx` (`removed`), + KEY `fk_bugs_activity_attach_id_attachments_attach_id` (`attach_id`), + KEY `fk_bugs_activity_comment_id_longdescs_comment_id` (`comment_id`), + CONSTRAINT `fk_bugs_activity_attach_id_attachments_attach_id` FOREIGN KEY (`attach_id`) REFERENCES `attachments` (`attach_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_bugs_activity_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_bugs_activity_comment_id_longdescs_comment_id` FOREIGN KEY (`comment_id`) REFERENCES `longdescs` (`comment_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_bugs_activity_fieldid_fielddefs_id` FOREIGN KEY (`fieldid`) REFERENCES `fielddefs` (`id`) ON UPDATE CASCADE, + CONSTRAINT `fk_bugs_activity_who_profiles_userid` FOREIGN KEY (`who`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bugs_activity` +-- + +LOCK TABLES `bugs_activity` WRITE; +/*!40000 ALTER TABLE `bugs_activity` DISABLE KEYS */; +INSERT INTO `bugs_activity` VALUES (1,1,NULL,1,'2023-11-27 15:45:09',9,'IN_PROGRESS','CONFIRMED',NULL),(2,1,NULL,1,'2023-11-27 15:47:58',10,'AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:L','',NULL),(3,1,NULL,1,'2023-11-27 15:53:04',38,'FOO-1','',NULL); +/*!40000 ALTER TABLE `bugs_activity` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bugs_aliases` +-- + +DROP TABLE IF EXISTS `bugs_aliases`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bugs_aliases` ( + `alias` varchar(40) NOT NULL, + `bug_id` mediumint(9) DEFAULT NULL, + UNIQUE KEY `bugs_aliases_alias_idx` (`alias`), + KEY `bugs_aliases_bug_id_idx` (`bug_id`), + CONSTRAINT `fk_bugs_aliases_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bugs_aliases` +-- + +LOCK TABLES `bugs_aliases` WRITE; +/*!40000 ALTER TABLE `bugs_aliases` DISABLE KEYS */; +INSERT INTO `bugs_aliases` VALUES ('FOO-1',1); +/*!40000 ALTER TABLE `bugs_aliases` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bugs_fulltext` +-- + +DROP TABLE IF EXISTS `bugs_fulltext`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bugs_fulltext` ( + `bug_id` mediumint(9) NOT NULL, + `short_desc` varchar(255) NOT NULL, + `comments` mediumtext DEFAULT NULL, + `comments_noprivate` mediumtext DEFAULT NULL, + PRIMARY KEY (`bug_id`), + FULLTEXT KEY `bugs_fulltext_short_desc_idx` (`short_desc`), + FULLTEXT KEY `bugs_fulltext_comments_idx` (`comments`), + FULLTEXT KEY `bugs_fulltext_comments_noprivate_idx` (`comments_noprivate`), + CONSTRAINT `fk_bugs_fulltext_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bugs_fulltext` +-- + +LOCK TABLES `bugs_fulltext` WRITE; +/*!40000 ALTER TABLE `bugs_fulltext` DISABLE KEYS */; +INSERT INTO `bugs_fulltext` VALUES (1,'ZeroDivisionError in function foo_bar()','Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\nAt vero eos et accusam et justo duo dolores et ea rebum.\nStet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.','Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\nAt vero eos et accusam et justo duo dolores et ea rebum.\nStet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.'),(2,'Expect the Spanish inquisition','Nobody expects the Spanish Inquisition! \n\nOur chief weapon is surprise, surprise and fear, fear and surprise. \n\nOur two weapons are fear and surprise, and ruthless efficiency. \n\nOur three weapons are fear and surprise and ruthless efficiency and an almost fanatical dedication to the pope.','Nobody expects the Spanish Inquisition! \n\nOur chief weapon is surprise, surprise and fear, fear and surprise. \n\nOur two weapons are fear and surprise, and ruthless efficiency. \n\nOur three weapons are fear and surprise and ruthless efficiency and an almost fanatical dedication to the pope.'); +/*!40000 ALTER TABLE `bugs_fulltext` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `bz_schema` +-- + +DROP TABLE IF EXISTS `bz_schema`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `bz_schema` ( + `schema_data` longblob NOT NULL, + `version` decimal(3,2) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `bz_schema` +-- + +LOCK TABLES `bz_schema` WRITE; +/*!40000 ALTER TABLE `bz_schema` DISABLE KEYS */; +INSERT INTO `bz_schema` VALUES ('$VAR1 = {\n \'attach_data\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'attach_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'attachments\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'thedata\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'LONGBLOB\'\n }\n ]\n },\n \'attachments\' => {\n \'FIELDS\' => [\n \'attach_id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'creation_ts\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n },\n \'modification_time\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n },\n \'description\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'TINYTEXT\'\n },\n \'mimetype\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'TINYTEXT\'\n },\n \'ispatch\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'filename\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(255)\'\n },\n \'submitter_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'isobsolete\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'isprivate\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n }\n ],\n \'INDEXES\' => [\n \'attachments_bug_id_idx\',\n [\n \'bug_id\'\n ],\n \'attachments_creation_ts_idx\',\n [\n \'creation_ts\'\n ],\n \'attachments_modification_time_idx\',\n [\n \'modification_time\'\n ],\n \'attachments_submitter_id_idx\',\n [\n \'submitter_id\',\n \'bug_id\'\n ]\n ]\n },\n \'audit_log\' => {\n \'FIELDS\' => [\n \'user_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'SET NULL\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'class\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(255)\'\n },\n \'object_id\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT4\'\n },\n \'field\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'removed\',\n {\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'added\',\n {\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'at_time\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n }\n ],\n \'INDEXES\' => [\n \'audit_log_class_idx\',\n [\n \'class\',\n \'at_time\'\n ]\n ]\n },\n \'bug_group_map\' => {\n \'FIELDS\' => [\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'group_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'groups\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'bug_group_map_bug_id_idx\',\n {\n \'FIELDS\' => [\n \'bug_id\',\n \'group_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'bug_group_map_group_id_idx\',\n [\n \'group_id\'\n ]\n ]\n },\n \'bug_see_also\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(255)\'\n },\n \'class\',\n {\n \'DEFAULT\' => \'\\\'\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(255)\'\n }\n ],\n \'INDEXES\' => [\n \'bug_see_also_bug_id_idx\',\n {\n \'FIELDS\' => [\n \'bug_id\',\n \'value\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'bug_severity\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'SMALLSERIAL\'\n },\n \'value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'sortkey\',\n {\n \'DEFAULT\' => 0,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'isactive\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'visibility_value_id\',\n {\n \'TYPE\' => \'INT2\'\n }\n ],\n \'INDEXES\' => [\n \'bug_severity_value_idx\',\n {\n \'FIELDS\' => [\n \'value\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'bug_severity_sortkey_idx\',\n [\n \'sortkey\',\n \'value\'\n ],\n \'bug_severity_visibility_value_id_idx\',\n [\n \'visibility_value_id\'\n ]\n ]\n },\n \'bug_status\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'SMALLSERIAL\'\n },\n \'value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'sortkey\',\n {\n \'DEFAULT\' => 0,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'isactive\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'visibility_value_id\',\n {\n \'TYPE\' => \'INT2\'\n },\n \'is_open\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n }\n ],\n \'INDEXES\' => [\n \'bug_status_value_idx\',\n {\n \'FIELDS\' => [\n \'value\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'bug_status_sortkey_idx\',\n [\n \'sortkey\',\n \'value\'\n ],\n \'bug_status_visibility_value_id_idx\',\n [\n \'visibility_value_id\'\n ]\n ]\n },\n \'bug_tag\' => {\n \'FIELDS\' => [\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'tag_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'tag\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'bug_tag_bug_id_idx\',\n {\n \'FIELDS\' => [\n \'bug_id\',\n \'tag_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'bug_user_last_visit\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'INTSERIAL\'\n },\n \'user_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'last_visit_ts\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n }\n ],\n \'INDEXES\' => [\n \'bug_user_last_visit_idx\',\n {\n \'FIELDS\' => [\n \'user_id\',\n \'bug_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'bug_user_last_visit_last_visit_ts_idx\',\n [\n \'last_visit_ts\'\n ]\n ]\n },\n \'bugs\' => {\n \'FIELDS\' => [\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'assigned_to\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'bug_file_loc\',\n {\n \'DEFAULT\' => \'\\\'\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'bug_severity\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'bug_status\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'creation_ts\',\n {\n \'TYPE\' => \'DATETIME\'\n },\n \'delta_ts\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n },\n \'short_desc\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(255)\'\n },\n \'op_sys\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'priority\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'product_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'TABLE\' => \'products\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'rep_platform\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'reporter\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'version\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'component_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'TABLE\' => \'components\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'resolution\',\n {\n \'DEFAULT\' => \'\\\'\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'target_milestone\',\n {\n \'DEFAULT\' => \'\\\'---\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'qa_contact\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'status_whiteboard\',\n {\n \'DEFAULT\' => \'\\\'\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'lastdiffed\',\n {\n \'TYPE\' => \'DATETIME\'\n },\n \'everconfirmed\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'reporter_accessible\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'cclist_accessible\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'estimated_time\',\n {\n \'DEFAULT\' => \'0\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'decimal(7,2)\'\n },\n \'remaining_time\',\n {\n \'DEFAULT\' => \'0\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'decimal(7,2)\'\n },\n \'deadline\',\n {\n \'TYPE\' => \'DATETIME\'\n }\n ],\n \'INDEXES\' => [\n \'bugs_assigned_to_idx\',\n [\n \'assigned_to\'\n ],\n \'bugs_creation_ts_idx\',\n [\n \'creation_ts\'\n ],\n \'bugs_delta_ts_idx\',\n [\n \'delta_ts\'\n ],\n \'bugs_bug_severity_idx\',\n [\n \'bug_severity\'\n ],\n \'bugs_bug_status_idx\',\n [\n \'bug_status\'\n ],\n \'bugs_op_sys_idx\',\n [\n \'op_sys\'\n ],\n \'bugs_priority_idx\',\n [\n \'priority\'\n ],\n \'bugs_product_id_idx\',\n [\n \'product_id\'\n ],\n \'bugs_reporter_idx\',\n [\n \'reporter\'\n ],\n \'bugs_version_idx\',\n [\n \'version\'\n ],\n \'bugs_component_id_idx\',\n [\n \'component_id\'\n ],\n \'bugs_resolution_idx\',\n [\n \'resolution\'\n ],\n \'bugs_target_milestone_idx\',\n [\n \'target_milestone\'\n ],\n \'bugs_qa_contact_idx\',\n [\n \'qa_contact\'\n ]\n ]\n },\n \'bugs_activity\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'INTSERIAL\'\n },\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'attach_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'attach_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'attachments\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'who\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'bug_when\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n },\n \'fieldid\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'TABLE\' => \'fielddefs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'added\',\n {\n \'TYPE\' => \'varchar(255)\'\n },\n \'removed\',\n {\n \'TYPE\' => \'varchar(255)\'\n },\n \'comment_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'comment_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'longdescs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT4\'\n }\n ],\n \'INDEXES\' => [\n \'bugs_activity_bug_id_idx\',\n [\n \'bug_id\'\n ],\n \'bugs_activity_who_idx\',\n [\n \'who\'\n ],\n \'bugs_activity_bug_when_idx\',\n [\n \'bug_when\'\n ],\n \'bugs_activity_fieldid_idx\',\n [\n \'fieldid\'\n ],\n \'bugs_activity_added_idx\',\n [\n \'added\'\n ],\n \'bugs_activity_removed_idx\',\n [\n \'removed\'\n ]\n ]\n },\n \'bugs_aliases\' => {\n \'FIELDS\' => [\n \'alias\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(40)\'\n },\n \'bug_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'bugs_aliases_bug_id_idx\',\n [\n \'bug_id\'\n ],\n \'bugs_aliases_alias_idx\',\n {\n \'FIELDS\' => [\n \'alias\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'bugs_fulltext\' => {\n \'FIELDS\' => [\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'short_desc\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(255)\'\n },\n \'comments\',\n {\n \'TYPE\' => \'LONGTEXT\'\n },\n \'comments_noprivate\',\n {\n \'TYPE\' => \'LONGTEXT\'\n }\n ],\n \'INDEXES\' => [\n \'bugs_fulltext_short_desc_idx\',\n {\n \'FIELDS\' => [\n \'short_desc\'\n ],\n \'TYPE\' => \'FULLTEXT\'\n },\n \'bugs_fulltext_comments_idx\',\n {\n \'FIELDS\' => [\n \'comments\'\n ],\n \'TYPE\' => \'FULLTEXT\'\n },\n \'bugs_fulltext_comments_noprivate_idx\',\n {\n \'FIELDS\' => [\n \'comments_noprivate\'\n ],\n \'TYPE\' => \'FULLTEXT\'\n }\n ]\n },\n \'bz_schema\' => {\n \'FIELDS\' => [\n \'schema_data\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'LONGBLOB\'\n },\n \'version\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'decimal(3,2)\'\n }\n ]\n },\n \'category_group_map\' => {\n \'FIELDS\' => [\n \'category_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'series_categories\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'group_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'groups\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'category_group_map_category_id_idx\',\n {\n \'FIELDS\' => [\n \'category_id\',\n \'group_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'cc\' => {\n \'FIELDS\' => [\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'who\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'cc_bug_id_idx\',\n {\n \'FIELDS\' => [\n \'bug_id\',\n \'who\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'cc_who_idx\',\n [\n \'who\'\n ]\n ]\n },\n \'classifications\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'SMALLSERIAL\'\n },\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'description\',\n {\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'sortkey\',\n {\n \'DEFAULT\' => \'0\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n }\n ],\n \'INDEXES\' => [\n \'classifications_name_idx\',\n {\n \'FIELDS\' => [\n \'name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'component_cc\' => {\n \'FIELDS\' => [\n \'user_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'component_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'components\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'component_cc_user_id_idx\',\n {\n \'FIELDS\' => [\n \'component_id\',\n \'user_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'components\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'product_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'products\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'initialowner\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'initialqacontact\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'SET NULL\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'description\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'isactive\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n }\n ],\n \'INDEXES\' => [\n \'components_product_id_idx\',\n {\n \'FIELDS\' => [\n \'product_id\',\n \'name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'components_name_idx\',\n [\n \'name\'\n ]\n ]\n },\n \'dependencies\' => {\n \'FIELDS\' => [\n \'blocked\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'dependson\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'dependencies_blocked_idx\',\n {\n \'FIELDS\' => [\n \'blocked\',\n \'dependson\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'dependencies_dependson_idx\',\n [\n \'dependson\'\n ]\n ]\n },\n \'duplicates\' => {\n \'FIELDS\' => [\n \'dupe_of\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'dupe\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ]\n },\n \'email_bug_ignore\' => {\n \'FIELDS\' => [\n \'user_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'email_bug_ignore_user_id_idx\',\n {\n \'FIELDS\' => [\n \'user_id\',\n \'bug_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'email_setting\' => {\n \'FIELDS\' => [\n \'user_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'relationship\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT1\'\n },\n \'event\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT1\'\n }\n ],\n \'INDEXES\' => [\n \'email_setting_user_id_idx\',\n {\n \'FIELDS\' => [\n \'user_id\',\n \'relationship\',\n \'event\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'field_visibility\' => {\n \'FIELDS\' => [\n \'field_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'fielddefs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'value_id\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n }\n ],\n \'INDEXES\' => [\n \'field_visibility_field_id_idx\',\n {\n \'FIELDS\' => [\n \'field_id\',\n \'value_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'fielddefs\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'type\',\n {\n \'DEFAULT\' => 0,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'custom\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'description\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'TINYTEXT\'\n },\n \'long_desc\',\n {\n \'DEFAULT\' => \'\\\'\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(255)\'\n },\n \'mailhead\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'sortkey\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'obsolete\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'enter_bug\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'buglist\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'visibility_field_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'TABLE\' => \'fielddefs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'value_field_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'TABLE\' => \'fielddefs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'reverse_desc\',\n {\n \'TYPE\' => \'TINYTEXT\'\n },\n \'is_mandatory\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'is_numeric\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n }\n ],\n \'INDEXES\' => [\n \'fielddefs_name_idx\',\n {\n \'FIELDS\' => [\n \'name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'fielddefs_sortkey_idx\',\n [\n \'sortkey\'\n ],\n \'fielddefs_value_field_id_idx\',\n [\n \'value_field_id\'\n ],\n \'fielddefs_is_mandatory_idx\',\n [\n \'is_mandatory\'\n ]\n ]\n },\n \'flagexclusions\' => {\n \'FIELDS\' => [\n \'type_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'flagtypes\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'product_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'products\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'component_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'components\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'flagexclusions_type_id_idx\',\n {\n \'FIELDS\' => [\n \'type_id\',\n \'product_id\',\n \'component_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'flaginclusions\' => {\n \'FIELDS\' => [\n \'type_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'flagtypes\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'product_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'products\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'component_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'components\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'flaginclusions_type_id_idx\',\n {\n \'FIELDS\' => [\n \'type_id\',\n \'product_id\',\n \'component_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'flags\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'type_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'flagtypes\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'status\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'char(1)\'\n },\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'attach_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'attach_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'attachments\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'creation_date\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n },\n \'modification_date\',\n {\n \'TYPE\' => \'DATETIME\'\n },\n \'setter_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'requestee_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'flags_bug_id_idx\',\n [\n \'bug_id\',\n \'attach_id\'\n ],\n \'flags_setter_id_idx\',\n [\n \'setter_id\'\n ],\n \'flags_requestee_id_idx\',\n [\n \'requestee_id\'\n ],\n \'flags_type_id_idx\',\n [\n \'type_id\'\n ]\n ]\n },\n \'flagtypes\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(50)\'\n },\n \'description\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'cc_list\',\n {\n \'TYPE\' => \'varchar(200)\'\n },\n \'target_type\',\n {\n \'DEFAULT\' => \'\\\'b\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'char(1)\'\n },\n \'is_active\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'is_requestable\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'is_requesteeble\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'is_multiplicable\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'sortkey\',\n {\n \'DEFAULT\' => \'0\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'grant_group_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'SET NULL\',\n \'TABLE\' => \'groups\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'request_group_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'SET NULL\',\n \'TABLE\' => \'groups\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ]\n },\n \'group_control_map\' => {\n \'FIELDS\' => [\n \'group_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'groups\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'product_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'products\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'entry\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'membercontrol\',\n {\n \'DEFAULT\' => \'0\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT1\'\n },\n \'othercontrol\',\n {\n \'DEFAULT\' => \'0\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT1\'\n },\n \'canedit\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'editcomponents\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'editbugs\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'canconfirm\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n }\n ],\n \'INDEXES\' => [\n \'group_control_map_product_id_idx\',\n {\n \'FIELDS\' => [\n \'product_id\',\n \'group_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'group_control_map_group_id_idx\',\n [\n \'group_id\'\n ]\n ]\n },\n \'group_group_map\' => {\n \'FIELDS\' => [\n \'member_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'groups\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'grantor_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'groups\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'grant_type\',\n {\n \'DEFAULT\' => \'0\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT1\'\n }\n ],\n \'INDEXES\' => [\n \'group_group_map_member_id_idx\',\n {\n \'FIELDS\' => [\n \'member_id\',\n \'grantor_id\',\n \'grant_type\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'groups\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(255)\'\n },\n \'description\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'isbuggroup\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'userregexp\',\n {\n \'DEFAULT\' => \'\\\'\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'TINYTEXT\'\n },\n \'isactive\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'icon_url\',\n {\n \'TYPE\' => \'TINYTEXT\'\n }\n ],\n \'INDEXES\' => [\n \'groups_name_idx\',\n {\n \'FIELDS\' => [\n \'name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'keyworddefs\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'SMALLSERIAL\'\n },\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'description\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'MEDIUMTEXT\'\n }\n ],\n \'INDEXES\' => [\n \'keyworddefs_name_idx\',\n {\n \'FIELDS\' => [\n \'name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'keywords\' => {\n \'FIELDS\' => [\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'keywordid\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'keyworddefs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n }\n ],\n \'INDEXES\' => [\n \'keywords_bug_id_idx\',\n {\n \'FIELDS\' => [\n \'bug_id\',\n \'keywordid\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'keywords_keywordid_idx\',\n [\n \'keywordid\'\n ]\n ]\n },\n \'login_failure\' => {\n \'FIELDS\' => [\n \'user_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'login_time\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n },\n \'ip_addr\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(40)\'\n }\n ],\n \'INDEXES\' => [\n \'login_failure_user_id_idx\',\n [\n \'user_id\'\n ]\n ]\n },\n \'logincookies\' => {\n \'FIELDS\' => [\n \'cookie\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'varchar(16)\'\n },\n \'userid\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'ipaddr\',\n {\n \'TYPE\' => \'varchar(40)\'\n },\n \'lastused\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n }\n ],\n \'INDEXES\' => [\n \'logincookies_lastused_idx\',\n [\n \'lastused\'\n ]\n ]\n },\n \'longdescs\' => {\n \'FIELDS\' => [\n \'comment_id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'INTSERIAL\'\n },\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'who\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'bug_when\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n },\n \'work_time\',\n {\n \'DEFAULT\' => \'0\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'decimal(7,2)\'\n },\n \'thetext\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'LONGTEXT\'\n },\n \'isprivate\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'already_wrapped\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'type\',\n {\n \'DEFAULT\' => \'0\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'extra_data\',\n {\n \'TYPE\' => \'varchar(255)\'\n }\n ],\n \'INDEXES\' => [\n \'longdescs_bug_id_idx\',\n [\n \'bug_id\',\n \'work_time\'\n ],\n \'longdescs_who_idx\',\n [\n \'who\',\n \'bug_id\'\n ],\n \'longdescs_bug_when_idx\',\n [\n \'bug_when\'\n ]\n ]\n },\n \'longdescs_tags\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'comment_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'comment_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'longdescs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT4\'\n },\n \'tag\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(24)\'\n }\n ],\n \'INDEXES\' => [\n \'longdescs_tags_idx\',\n {\n \'FIELDS\' => [\n \'comment_id\',\n \'tag\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'longdescs_tags_activity\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'bug_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'bug_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bugs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'comment_id\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'comment_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'longdescs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT4\'\n },\n \'who\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'bug_when\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n },\n \'added\',\n {\n \'TYPE\' => \'varchar(24)\'\n },\n \'removed\',\n {\n \'TYPE\' => \'varchar(24)\'\n }\n ],\n \'INDEXES\' => [\n \'longdescs_tags_activity_bug_id_idx\',\n [\n \'bug_id\'\n ]\n ]\n },\n \'longdescs_tags_weights\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'tag\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(24)\'\n },\n \'weight\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'longdescs_tags_weights_tag_idx\',\n {\n \'FIELDS\' => [\n \'tag\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'mail_staging\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'INTSERIAL\'\n },\n \'message\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'LONGBLOB\'\n }\n ]\n },\n \'milestones\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'product_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'products\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'sortkey\',\n {\n \'DEFAULT\' => 0,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'isactive\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n }\n ],\n \'INDEXES\' => [\n \'milestones_product_id_idx\',\n {\n \'FIELDS\' => [\n \'product_id\',\n \'value\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'namedqueries\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'userid\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'query\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'LONGTEXT\'\n }\n ],\n \'INDEXES\' => [\n \'namedqueries_userid_idx\',\n {\n \'FIELDS\' => [\n \'userid\',\n \'name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'namedqueries_link_in_footer\' => {\n \'FIELDS\' => [\n \'namedquery_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'namedqueries\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'user_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'namedqueries_link_in_footer_id_idx\',\n {\n \'FIELDS\' => [\n \'namedquery_id\',\n \'user_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'namedqueries_link_in_footer_userid_idx\',\n [\n \'user_id\'\n ]\n ]\n },\n \'namedquery_group_map\' => {\n \'FIELDS\' => [\n \'namedquery_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'namedqueries\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'group_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'groups\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'namedquery_group_map_namedquery_id_idx\',\n {\n \'FIELDS\' => [\n \'namedquery_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'namedquery_group_map_group_id_idx\',\n [\n \'group_id\'\n ]\n ]\n },\n \'op_sys\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'SMALLSERIAL\'\n },\n \'value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'sortkey\',\n {\n \'DEFAULT\' => 0,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'isactive\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'visibility_value_id\',\n {\n \'TYPE\' => \'INT2\'\n }\n ],\n \'INDEXES\' => [\n \'op_sys_value_idx\',\n {\n \'FIELDS\' => [\n \'value\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'op_sys_sortkey_idx\',\n [\n \'sortkey\',\n \'value\'\n ],\n \'op_sys_visibility_value_id_idx\',\n [\n \'visibility_value_id\'\n ]\n ]\n },\n \'priority\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'SMALLSERIAL\'\n },\n \'value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'sortkey\',\n {\n \'DEFAULT\' => 0,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'isactive\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'visibility_value_id\',\n {\n \'TYPE\' => \'INT2\'\n }\n ],\n \'INDEXES\' => [\n \'priority_value_idx\',\n {\n \'FIELDS\' => [\n \'value\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'priority_sortkey_idx\',\n [\n \'sortkey\',\n \'value\'\n ],\n \'priority_visibility_value_id_idx\',\n [\n \'visibility_value_id\'\n ]\n ]\n },\n \'products\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'SMALLSERIAL\'\n },\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'classification_id\',\n {\n \'DEFAULT\' => \'1\',\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'classifications\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'description\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'isactive\',\n {\n \'DEFAULT\' => 1,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'defaultmilestone\',\n {\n \'DEFAULT\' => \'\\\'---\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'allows_unconfirmed\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n }\n ],\n \'INDEXES\' => [\n \'products_name_idx\',\n {\n \'FIELDS\' => [\n \'name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'profile_search\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'INTSERIAL\'\n },\n \'user_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'bug_list\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'list_order\',\n {\n \'TYPE\' => \'MEDIUMTEXT\'\n }\n ],\n \'INDEXES\' => [\n \'profile_search_user_id_idx\',\n [\n \'user_id\'\n ]\n ]\n },\n \'profile_setting\' => {\n \'FIELDS\' => [\n \'user_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'setting_name\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'name\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'setting\',\n \'created\' => 1\n },\n \'TYPE\' => \'varchar(32)\'\n },\n \'setting_value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(32)\'\n }\n ],\n \'INDEXES\' => [\n \'profile_setting_value_unique_idx\',\n {\n \'FIELDS\' => [\n \'user_id\',\n \'setting_name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'profiles\' => {\n \'FIELDS\' => [\n \'userid\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'login_name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(255)\'\n },\n \'cryptpassword\',\n {\n \'TYPE\' => \'varchar(128)\'\n },\n \'realname\',\n {\n \'DEFAULT\' => \'\\\'\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(255)\'\n },\n \'disabledtext\',\n {\n \'DEFAULT\' => \'\\\'\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'disable_mail\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'mybugslink\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'extern_id\',\n {\n \'TYPE\' => \'varchar(64)\'\n },\n \'is_enabled\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'last_seen_date\',\n {\n \'TYPE\' => \'DATETIME\'\n }\n ],\n \'INDEXES\' => [\n \'profiles_login_name_idx\',\n {\n \'FIELDS\' => [\n \'login_name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'profiles_extern_id_idx\',\n {\n \'FIELDS\' => [\n \'extern_id\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'profiles_activity\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'userid\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'who\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'profiles_when\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n },\n \'fieldid\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'TABLE\' => \'fielddefs\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'oldvalue\',\n {\n \'TYPE\' => \'TINYTEXT\'\n },\n \'newvalue\',\n {\n \'TYPE\' => \'TINYTEXT\'\n }\n ],\n \'INDEXES\' => [\n \'profiles_activity_userid_idx\',\n [\n \'userid\'\n ],\n \'profiles_activity_profiles_when_idx\',\n [\n \'profiles_when\'\n ],\n \'profiles_activity_fieldid_idx\',\n [\n \'fieldid\'\n ]\n ]\n },\n \'quips\' => {\n \'FIELDS\' => [\n \'quipid\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'userid\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'SET NULL\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'quip\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(512)\'\n },\n \'approved\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n }\n ]\n },\n \'rep_platform\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'SMALLSERIAL\'\n },\n \'value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'sortkey\',\n {\n \'DEFAULT\' => 0,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'isactive\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'visibility_value_id\',\n {\n \'TYPE\' => \'INT2\'\n }\n ],\n \'INDEXES\' => [\n \'rep_platform_value_idx\',\n {\n \'FIELDS\' => [\n \'value\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'rep_platform_sortkey_idx\',\n [\n \'sortkey\',\n \'value\'\n ],\n \'rep_platform_visibility_value_id_idx\',\n [\n \'visibility_value_id\'\n ]\n ]\n },\n \'reports\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'user_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'query\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'LONGTEXT\'\n }\n ],\n \'INDEXES\' => [\n \'reports_user_id_idx\',\n {\n \'FIELDS\' => [\n \'user_id\',\n \'name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'resolution\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'SMALLSERIAL\'\n },\n \'value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'sortkey\',\n {\n \'DEFAULT\' => 0,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'isactive\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'visibility_value_id\',\n {\n \'TYPE\' => \'INT2\'\n }\n ],\n \'INDEXES\' => [\n \'resolution_value_idx\',\n {\n \'FIELDS\' => [\n \'value\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'resolution_sortkey_idx\',\n [\n \'sortkey\',\n \'value\'\n ],\n \'resolution_visibility_value_id_idx\',\n [\n \'visibility_value_id\'\n ]\n ]\n },\n \'series\' => {\n \'FIELDS\' => [\n \'series_id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'creator\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'category\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'series_categories\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'subcategory\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'series_categories\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'frequency\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'query\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'is_public\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n }\n ],\n \'INDEXES\' => [\n \'series_creator_idx\',\n [\n \'creator\'\n ],\n \'series_category_idx\',\n {\n \'FIELDS\' => [\n \'category\',\n \'subcategory\',\n \'name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'series_categories\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'SMALLSERIAL\'\n },\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n }\n ],\n \'INDEXES\' => [\n \'series_categories_name_idx\',\n {\n \'FIELDS\' => [\n \'name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'series_data\' => {\n \'FIELDS\' => [\n \'series_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'series_id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'series\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'series_date\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n },\n \'series_value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'series_data_series_id_idx\',\n {\n \'FIELDS\' => [\n \'series_id\',\n \'series_date\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'setting\' => {\n \'FIELDS\' => [\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'varchar(32)\'\n },\n \'default_value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(32)\'\n },\n \'is_enabled\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'subclass\',\n {\n \'TYPE\' => \'varchar(32)\'\n }\n ]\n },\n \'setting_value\' => {\n \'FIELDS\' => [\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'name\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'setting\',\n \'created\' => 1\n },\n \'TYPE\' => \'varchar(32)\'\n },\n \'value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(32)\'\n },\n \'sortindex\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n }\n ],\n \'INDEXES\' => [\n \'setting_value_nv_unique_idx\',\n {\n \'FIELDS\' => [\n \'name\',\n \'value\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'setting_value_ns_unique_idx\',\n {\n \'FIELDS\' => [\n \'name\',\n \'sortindex\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'status_workflow\' => {\n \'FIELDS\' => [\n \'old_status\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bug_status\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'new_status\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'bug_status\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'require_comment\',\n {\n \'DEFAULT\' => 0,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT1\'\n }\n ],\n \'INDEXES\' => [\n \'status_workflow_idx\',\n {\n \'FIELDS\' => [\n \'old_status\',\n \'new_status\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'tag\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'name\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'user_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'tag_user_id_idx\',\n {\n \'FIELDS\' => [\n \'user_id\',\n \'name\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'tokens\' => {\n \'FIELDS\' => [\n \'userid\',\n {\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'issuedate\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'DATETIME\'\n },\n \'token\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'varchar(16)\'\n },\n \'tokentype\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(16)\'\n },\n \'eventdata\',\n {\n \'TYPE\' => \'TINYTEXT\'\n }\n ],\n \'INDEXES\' => [\n \'tokens_userid_idx\',\n [\n \'userid\'\n ]\n ]\n },\n \'ts_error\' => {\n \'FIELDS\' => [\n \'error_time\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT4\'\n },\n \'jobid\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT4\'\n },\n \'message\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(255)\'\n },\n \'funcid\',\n {\n \'DEFAULT\' => 0,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT4\'\n }\n ],\n \'INDEXES\' => [\n \'ts_error_funcid_idx\',\n [\n \'funcid\',\n \'error_time\'\n ],\n \'ts_error_error_time_idx\',\n [\n \'error_time\'\n ],\n \'ts_error_jobid_idx\',\n [\n \'jobid\'\n ]\n ]\n },\n \'ts_exitstatus\' => {\n \'FIELDS\' => [\n \'jobid\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'INTSERIAL\'\n },\n \'funcid\',\n {\n \'DEFAULT\' => 0,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT4\'\n },\n \'status\',\n {\n \'TYPE\' => \'INT2\'\n },\n \'completion_time\',\n {\n \'TYPE\' => \'INT4\'\n },\n \'delete_after\',\n {\n \'TYPE\' => \'INT4\'\n }\n ],\n \'INDEXES\' => [\n \'ts_exitstatus_funcid_idx\',\n [\n \'funcid\'\n ],\n \'ts_exitstatus_delete_after_idx\',\n [\n \'delete_after\'\n ]\n ]\n },\n \'ts_funcmap\' => {\n \'FIELDS\' => [\n \'funcid\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'INTSERIAL\'\n },\n \'funcname\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(255)\'\n }\n ],\n \'INDEXES\' => [\n \'ts_funcmap_funcname_idx\',\n {\n \'FIELDS\' => [\n \'funcname\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'ts_job\' => {\n \'FIELDS\' => [\n \'jobid\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'INTSERIAL\'\n },\n \'funcid\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT4\'\n },\n \'arg\',\n {\n \'TYPE\' => \'LONGBLOB\'\n },\n \'uniqkey\',\n {\n \'TYPE\' => \'varchar(255)\'\n },\n \'insert_time\',\n {\n \'TYPE\' => \'INT4\'\n },\n \'run_after\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT4\'\n },\n \'grabbed_until\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT4\'\n },\n \'priority\',\n {\n \'TYPE\' => \'INT2\'\n },\n \'coalesce\',\n {\n \'TYPE\' => \'varchar(255)\'\n }\n ],\n \'INDEXES\' => [\n \'ts_job_funcid_idx\',\n {\n \'FIELDS\' => [\n \'funcid\',\n \'uniqkey\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'ts_job_run_after_idx\',\n [\n \'run_after\',\n \'funcid\'\n ],\n \'ts_job_coalesce_idx\',\n [\n \'coalesce\',\n \'funcid\'\n ]\n ]\n },\n \'ts_note\' => {\n \'FIELDS\' => [\n \'jobid\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT4\'\n },\n \'notekey\',\n {\n \'TYPE\' => \'varchar(255)\'\n },\n \'value\',\n {\n \'TYPE\' => \'LONGBLOB\'\n }\n ],\n \'INDEXES\' => [\n \'ts_note_jobid_idx\',\n {\n \'FIELDS\' => [\n \'jobid\',\n \'notekey\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'user_api_keys\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'INTSERIAL\'\n },\n \'user_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'api_key\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'VARCHAR(40)\'\n },\n \'description\',\n {\n \'TYPE\' => \'VARCHAR(255)\'\n },\n \'revoked\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'last_used\',\n {\n \'TYPE\' => \'DATETIME\'\n }\n ],\n \'INDEXES\' => [\n \'user_api_keys_api_key_idx\',\n {\n \'FIELDS\' => [\n \'api_key\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'user_api_keys_user_id_idx\',\n [\n \'user_id\'\n ]\n ]\n },\n \'user_group_map\' => {\n \'FIELDS\' => [\n \'user_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'group_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'groups\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'isbless\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'grant_type\',\n {\n \'DEFAULT\' => 0,\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT1\'\n }\n ],\n \'INDEXES\' => [\n \'user_group_map_user_id_idx\',\n {\n \'FIELDS\' => [\n \'user_id\',\n \'group_id\',\n \'grant_type\',\n \'isbless\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'versions\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'value\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'product_id\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'products\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT2\'\n },\n \'isactive\',\n {\n \'DEFAULT\' => \'TRUE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n }\n ],\n \'INDEXES\' => [\n \'versions_product_id_idx\',\n {\n \'FIELDS\' => [\n \'product_id\',\n \'value\'\n ],\n \'TYPE\' => \'UNIQUE\'\n }\n ]\n },\n \'watch\' => {\n \'FIELDS\' => [\n \'watcher\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'watched\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n }\n ],\n \'INDEXES\' => [\n \'watch_watcher_idx\',\n {\n \'FIELDS\' => [\n \'watcher\',\n \'watched\'\n ],\n \'TYPE\' => \'UNIQUE\'\n },\n \'watch_watched_idx\',\n [\n \'watched\'\n ]\n ]\n },\n \'whine_events\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'owner_userid\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'userid\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'profiles\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'subject\',\n {\n \'TYPE\' => \'varchar(128)\'\n },\n \'body\',\n {\n \'TYPE\' => \'MEDIUMTEXT\'\n },\n \'mailifnobugs\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n }\n ]\n },\n \'whine_queries\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'eventid\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'whine_events\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'query_name\',\n {\n \'DEFAULT\' => \'\\\'\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(64)\'\n },\n \'sortkey\',\n {\n \'DEFAULT\' => \'0\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n },\n \'onemailperbug\',\n {\n \'DEFAULT\' => \'FALSE\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'BOOLEAN\'\n },\n \'title\',\n {\n \'DEFAULT\' => \'\\\'\\\'\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'varchar(128)\'\n }\n ],\n \'INDEXES\' => [\n \'whine_queries_eventid_idx\',\n [\n \'eventid\'\n ]\n ]\n },\n \'whine_schedules\' => {\n \'FIELDS\' => [\n \'id\',\n {\n \'NOTNULL\' => 1,\n \'PRIMARYKEY\' => 1,\n \'TYPE\' => \'MEDIUMSERIAL\'\n },\n \'eventid\',\n {\n \'NOTNULL\' => 1,\n \'REFERENCES\' => {\n \'COLUMN\' => \'id\',\n \'DELETE\' => \'CASCADE\',\n \'TABLE\' => \'whine_events\',\n \'created\' => 1\n },\n \'TYPE\' => \'INT3\'\n },\n \'run_day\',\n {\n \'TYPE\' => \'varchar(32)\'\n },\n \'run_time\',\n {\n \'TYPE\' => \'varchar(32)\'\n },\n \'run_next\',\n {\n \'TYPE\' => \'DATETIME\'\n },\n \'mailto\',\n {\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT3\'\n },\n \'mailto_type\',\n {\n \'DEFAULT\' => \'0\',\n \'NOTNULL\' => 1,\n \'TYPE\' => \'INT2\'\n }\n ],\n \'INDEXES\' => [\n \'whine_schedules_run_next_idx\',\n [\n \'run_next\'\n ],\n \'whine_schedules_eventid_idx\',\n [\n \'eventid\'\n ]\n ]\n }\n };\n',3.00); +/*!40000 ALTER TABLE `bz_schema` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `category_group_map` +-- + +DROP TABLE IF EXISTS `category_group_map`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `category_group_map` ( + `category_id` smallint(6) NOT NULL, + `group_id` mediumint(9) NOT NULL, + UNIQUE KEY `category_group_map_category_id_idx` (`category_id`,`group_id`), + KEY `fk_category_group_map_group_id_groups_id` (`group_id`), + CONSTRAINT `fk_category_group_map_category_id_series_categories_id` FOREIGN KEY (`category_id`) REFERENCES `series_categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_category_group_map_group_id_groups_id` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `category_group_map` +-- + +LOCK TABLES `category_group_map` WRITE; +/*!40000 ALTER TABLE `category_group_map` DISABLE KEYS */; +/*!40000 ALTER TABLE `category_group_map` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `cc` +-- + +DROP TABLE IF EXISTS `cc`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `cc` ( + `bug_id` mediumint(9) NOT NULL, + `who` mediumint(9) NOT NULL, + UNIQUE KEY `cc_bug_id_idx` (`bug_id`,`who`), + KEY `cc_who_idx` (`who`), + CONSTRAINT `fk_cc_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_cc_who_profiles_userid` FOREIGN KEY (`who`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `cc` +-- + +LOCK TABLES `cc` WRITE; +/*!40000 ALTER TABLE `cc` DISABLE KEYS */; +/*!40000 ALTER TABLE `cc` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `classifications` +-- + +DROP TABLE IF EXISTS `classifications`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `classifications` ( + `id` smallint(6) NOT NULL AUTO_INCREMENT, + `name` varchar(64) NOT NULL, + `description` mediumtext DEFAULT NULL, + `sortkey` smallint(6) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `classifications_name_idx` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `classifications` +-- + +LOCK TABLES `classifications` WRITE; +/*!40000 ALTER TABLE `classifications` DISABLE KEYS */; +INSERT INTO `classifications` VALUES (1,'Unclassified','Not assigned to any classification',0); +/*!40000 ALTER TABLE `classifications` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `component_cc` +-- + +DROP TABLE IF EXISTS `component_cc`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `component_cc` ( + `user_id` mediumint(9) NOT NULL, + `component_id` mediumint(9) NOT NULL, + UNIQUE KEY `component_cc_user_id_idx` (`component_id`,`user_id`), + KEY `fk_component_cc_user_id_profiles_userid` (`user_id`), + CONSTRAINT `fk_component_cc_component_id_components_id` FOREIGN KEY (`component_id`) REFERENCES `components` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_component_cc_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `component_cc` +-- + +LOCK TABLES `component_cc` WRITE; +/*!40000 ALTER TABLE `component_cc` DISABLE KEYS */; +/*!40000 ALTER TABLE `component_cc` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `components` +-- + +DROP TABLE IF EXISTS `components`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `components` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `name` varchar(64) NOT NULL, + `product_id` smallint(6) NOT NULL, + `initialowner` mediumint(9) NOT NULL, + `initialqacontact` mediumint(9) DEFAULT NULL, + `description` mediumtext NOT NULL, + `isactive` tinyint(4) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + UNIQUE KEY `components_product_id_idx` (`product_id`,`name`), + KEY `components_name_idx` (`name`), + KEY `fk_components_initialqacontact_profiles_userid` (`initialqacontact`), + KEY `fk_components_initialowner_profiles_userid` (`initialowner`), + CONSTRAINT `fk_components_initialowner_profiles_userid` FOREIGN KEY (`initialowner`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE, + CONSTRAINT `fk_components_initialqacontact_profiles_userid` FOREIGN KEY (`initialqacontact`) REFERENCES `profiles` (`userid`) ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT `fk_components_product_id_products_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `components` +-- + +LOCK TABLES `components` WRITE; +/*!40000 ALTER TABLE `components` DISABLE KEYS */; +INSERT INTO `components` VALUES (1,'TestComponent',1,1,NULL,'This is a test component in the test product database. This ought to be blown away and replaced with real stuff in a finished installation of Bugzilla.',1),(2,'python-bugzilla',2,1,NULL,'Lorem ipsum dolor sit amet',1),(3,'Kernel',3,1,NULL,'Lorem ipsum',1),(4,'Containers',3,1,NULL,'Lorem ipsum',1); +/*!40000 ALTER TABLE `components` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `dependencies` +-- + +DROP TABLE IF EXISTS `dependencies`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `dependencies` ( + `blocked` mediumint(9) NOT NULL, + `dependson` mediumint(9) NOT NULL, + UNIQUE KEY `dependencies_blocked_idx` (`blocked`,`dependson`), + KEY `dependencies_dependson_idx` (`dependson`), + CONSTRAINT `fk_dependencies_blocked_bugs_bug_id` FOREIGN KEY (`blocked`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_dependencies_dependson_bugs_bug_id` FOREIGN KEY (`dependson`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `dependencies` +-- + +LOCK TABLES `dependencies` WRITE; +/*!40000 ALTER TABLE `dependencies` DISABLE KEYS */; +/*!40000 ALTER TABLE `dependencies` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `duplicates` +-- + +DROP TABLE IF EXISTS `duplicates`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `duplicates` ( + `dupe_of` mediumint(9) NOT NULL, + `dupe` mediumint(9) NOT NULL, + PRIMARY KEY (`dupe`), + KEY `fk_duplicates_dupe_of_bugs_bug_id` (`dupe_of`), + CONSTRAINT `fk_duplicates_dupe_bugs_bug_id` FOREIGN KEY (`dupe`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_duplicates_dupe_of_bugs_bug_id` FOREIGN KEY (`dupe_of`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `duplicates` +-- + +LOCK TABLES `duplicates` WRITE; +/*!40000 ALTER TABLE `duplicates` DISABLE KEYS */; +/*!40000 ALTER TABLE `duplicates` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `email_bug_ignore` +-- + +DROP TABLE IF EXISTS `email_bug_ignore`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `email_bug_ignore` ( + `user_id` mediumint(9) NOT NULL, + `bug_id` mediumint(9) NOT NULL, + UNIQUE KEY `email_bug_ignore_user_id_idx` (`user_id`,`bug_id`), + KEY `fk_email_bug_ignore_bug_id_bugs_bug_id` (`bug_id`), + CONSTRAINT `fk_email_bug_ignore_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_email_bug_ignore_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `email_bug_ignore` +-- + +LOCK TABLES `email_bug_ignore` WRITE; +/*!40000 ALTER TABLE `email_bug_ignore` DISABLE KEYS */; +/*!40000 ALTER TABLE `email_bug_ignore` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `email_setting` +-- + +DROP TABLE IF EXISTS `email_setting`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `email_setting` ( + `user_id` mediumint(9) NOT NULL, + `relationship` tinyint(4) NOT NULL, + `event` tinyint(4) NOT NULL, + UNIQUE KEY `email_setting_user_id_idx` (`user_id`,`relationship`,`event`), + CONSTRAINT `fk_email_setting_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `email_setting` +-- + +LOCK TABLES `email_setting` WRITE; +/*!40000 ALTER TABLE `email_setting` DISABLE KEYS */; +INSERT INTO `email_setting` VALUES (1,0,0),(1,0,1),(1,0,2),(1,0,3),(1,0,4),(1,0,5),(1,0,6),(1,0,7),(1,0,9),(1,0,10),(1,0,11),(1,0,50),(1,1,0),(1,1,1),(1,1,2),(1,1,3),(1,1,4),(1,1,5),(1,1,6),(1,1,7),(1,1,9),(1,1,10),(1,1,11),(1,1,50),(1,2,0),(1,2,1),(1,2,2),(1,2,3),(1,2,4),(1,2,5),(1,2,6),(1,2,7),(1,2,8),(1,2,9),(1,2,10),(1,2,11),(1,2,50),(1,3,0),(1,3,1),(1,3,2),(1,3,3),(1,3,4),(1,3,5),(1,3,6),(1,3,7),(1,3,9),(1,3,10),(1,3,11),(1,3,50),(1,5,0),(1,5,1),(1,5,2),(1,5,3),(1,5,4),(1,5,5),(1,5,6),(1,5,7),(1,5,9),(1,5,10),(1,5,11),(1,5,50),(1,100,100),(1,100,101); +/*!40000 ALTER TABLE `email_setting` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `field_visibility` +-- + +DROP TABLE IF EXISTS `field_visibility`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `field_visibility` ( + `field_id` mediumint(9) DEFAULT NULL, + `value_id` smallint(6) NOT NULL, + UNIQUE KEY `field_visibility_field_id_idx` (`field_id`,`value_id`), + CONSTRAINT `fk_field_visibility_field_id_fielddefs_id` FOREIGN KEY (`field_id`) REFERENCES `fielddefs` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `field_visibility` +-- + +LOCK TABLES `field_visibility` WRITE; +/*!40000 ALTER TABLE `field_visibility` DISABLE KEYS */; +/*!40000 ALTER TABLE `field_visibility` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `fielddefs` +-- + +DROP TABLE IF EXISTS `fielddefs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `fielddefs` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `name` varchar(64) NOT NULL, + `type` smallint(6) NOT NULL DEFAULT 0, + `custom` tinyint(4) NOT NULL DEFAULT 0, + `description` tinytext NOT NULL, + `long_desc` varchar(255) NOT NULL DEFAULT '', + `mailhead` tinyint(4) NOT NULL DEFAULT 0, + `sortkey` smallint(6) NOT NULL, + `obsolete` tinyint(4) NOT NULL DEFAULT 0, + `enter_bug` tinyint(4) NOT NULL DEFAULT 0, + `buglist` tinyint(4) NOT NULL DEFAULT 0, + `visibility_field_id` mediumint(9) DEFAULT NULL, + `value_field_id` mediumint(9) DEFAULT NULL, + `reverse_desc` tinytext DEFAULT NULL, + `is_mandatory` tinyint(4) NOT NULL DEFAULT 0, + `is_numeric` tinyint(4) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `fielddefs_name_idx` (`name`), + KEY `fielddefs_sortkey_idx` (`sortkey`), + KEY `fielddefs_value_field_id_idx` (`value_field_id`), + KEY `fielddefs_is_mandatory_idx` (`is_mandatory`), + KEY `fk_fielddefs_visibility_field_id_fielddefs_id` (`visibility_field_id`), + CONSTRAINT `fk_fielddefs_value_field_id_fielddefs_id` FOREIGN KEY (`value_field_id`) REFERENCES `fielddefs` (`id`) ON UPDATE CASCADE, + CONSTRAINT `fk_fielddefs_visibility_field_id_fielddefs_id` FOREIGN KEY (`visibility_field_id`) REFERENCES `fielddefs` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=60 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `fielddefs` +-- + +LOCK TABLES `fielddefs` WRITE; +/*!40000 ALTER TABLE `fielddefs` DISABLE KEYS */; +INSERT INTO `fielddefs` VALUES (1,'bug_id',0,0,'Bug #','',1,100,0,0,1,NULL,NULL,NULL,0,1),(2,'short_desc',0,0,'Summary','',1,200,0,0,1,NULL,NULL,NULL,1,0),(3,'classification',2,0,'Classification','',1,300,0,0,1,NULL,NULL,NULL,0,0),(4,'product',2,0,'Product','',1,400,0,0,1,NULL,NULL,NULL,1,0),(5,'version',0,0,'Version','',1,500,0,0,1,NULL,NULL,NULL,1,0),(6,'rep_platform',2,0,'Platform','',1,600,0,0,1,NULL,NULL,NULL,0,0),(7,'bug_file_loc',0,0,'URL','',1,700,0,0,1,NULL,NULL,NULL,0,0),(8,'op_sys',2,0,'OS/Version','',1,800,0,0,1,NULL,NULL,NULL,0,0),(9,'bug_status',2,0,'Status','',1,900,0,0,1,NULL,NULL,NULL,0,0),(10,'status_whiteboard',0,0,'Status Whiteboard','',1,1000,0,0,1,NULL,NULL,NULL,0,0),(11,'keywords',8,0,'Keywords','',1,1100,0,0,1,NULL,NULL,NULL,0,0),(12,'resolution',2,0,'Resolution','',0,1200,0,0,1,NULL,NULL,NULL,0,0),(13,'bug_severity',2,0,'Severity','',1,1300,0,0,1,NULL,NULL,NULL,0,0),(14,'priority',2,0,'Priority','',1,1400,0,0,1,NULL,NULL,NULL,0,0),(15,'component',2,0,'Component','',1,1500,0,0,1,NULL,NULL,NULL,1,0),(16,'assigned_to',0,0,'AssignedTo','',1,1600,0,0,1,NULL,NULL,NULL,0,0),(17,'reporter',0,0,'ReportedBy','',1,1700,0,0,1,NULL,NULL,NULL,0,0),(18,'qa_contact',0,0,'QAContact','',1,1800,0,0,1,NULL,NULL,NULL,0,0),(19,'assigned_to_realname',0,0,'AssignedToName','',0,1900,0,0,1,NULL,NULL,NULL,0,0),(20,'reporter_realname',0,0,'ReportedByName','',0,2000,0,0,1,NULL,NULL,NULL,0,0),(21,'qa_contact_realname',0,0,'QAContactName','',0,2100,0,0,1,NULL,NULL,NULL,0,0),(22,'cc',0,0,'CC','',1,2200,0,0,0,NULL,NULL,NULL,0,0),(23,'dependson',0,0,'Depends on','',1,2300,0,0,1,NULL,NULL,NULL,0,1),(24,'blocked',0,0,'Blocks','',1,2400,0,0,1,NULL,NULL,NULL,0,1),(25,'attachments.description',0,0,'Attachment description','',0,2500,0,0,0,NULL,NULL,NULL,0,0),(26,'attachments.filename',0,0,'Attachment filename','',0,2600,0,0,0,NULL,NULL,NULL,0,0),(27,'attachments.mimetype',0,0,'Attachment mime type','',0,2700,0,0,0,NULL,NULL,NULL,0,0),(28,'attachments.ispatch',0,0,'Attachment is patch','',0,2800,0,0,0,NULL,NULL,NULL,0,1),(29,'attachments.isobsolete',0,0,'Attachment is obsolete','',0,2900,0,0,0,NULL,NULL,NULL,0,1),(30,'attachments.isprivate',0,0,'Attachment is private','',0,3000,0,0,0,NULL,NULL,NULL,0,1),(31,'attachments.submitter',0,0,'Attachment creator','',0,3100,0,0,0,NULL,NULL,NULL,0,0),(32,'target_milestone',0,0,'Target Milestone','',1,3200,0,0,1,NULL,NULL,NULL,0,0),(33,'creation_ts',0,0,'Creation date','',0,3300,0,0,1,NULL,NULL,NULL,0,0),(34,'delta_ts',0,0,'Last changed date','',0,3400,0,0,1,NULL,NULL,NULL,0,0),(35,'longdesc',0,0,'Comment','',0,3500,0,0,0,NULL,NULL,NULL,0,0),(36,'longdescs.isprivate',0,0,'Comment is private','',0,3600,0,0,0,NULL,NULL,NULL,0,1),(37,'longdescs.count',0,0,'Number of Comments','',0,3700,0,0,1,NULL,NULL,NULL,0,1),(38,'alias',0,0,'Alias','',0,3800,0,0,1,NULL,NULL,NULL,0,0),(39,'everconfirmed',0,0,'Ever Confirmed','',0,3900,0,0,0,NULL,NULL,NULL,0,1),(40,'reporter_accessible',0,0,'Reporter Accessible','',0,4000,0,0,0,NULL,NULL,NULL,0,1),(41,'cclist_accessible',0,0,'CC Accessible','',0,4100,0,0,0,NULL,NULL,NULL,0,1),(42,'bug_group',0,0,'Group','',1,4200,0,0,0,NULL,NULL,NULL,0,0),(43,'estimated_time',0,0,'Estimated Hours','',1,4300,0,0,1,NULL,NULL,NULL,0,1),(44,'remaining_time',0,0,'Remaining Hours','',0,4400,0,0,1,NULL,NULL,NULL,0,1),(45,'deadline',5,0,'Deadline','',1,4500,0,0,1,NULL,NULL,NULL,0,0),(46,'commenter',0,0,'Commenter','',0,4600,0,0,0,NULL,NULL,NULL,0,0),(47,'flagtypes.name',0,0,'Flags','',0,4700,0,0,1,NULL,NULL,NULL,0,0),(48,'requestees.login_name',0,0,'Flag Requestee','',0,4800,0,0,0,NULL,NULL,NULL,0,0),(49,'setters.login_name',0,0,'Flag Setter','',0,4900,0,0,0,NULL,NULL,NULL,0,0),(50,'work_time',0,0,'Hours Worked','',0,5000,0,0,1,NULL,NULL,NULL,0,1),(51,'percentage_complete',0,0,'Percentage Complete','',0,5100,0,0,1,NULL,NULL,NULL,0,1),(52,'content',0,0,'Content','',0,5200,0,0,0,NULL,NULL,NULL,0,0),(53,'attach_data.thedata',0,0,'Attachment data','',0,5300,0,0,0,NULL,NULL,NULL,0,0),(54,'owner_idle_time',0,0,'Time Since Assignee Touched','',0,5400,0,0,0,NULL,NULL,NULL,0,0),(55,'see_also',7,0,'See Also','',0,5500,0,0,0,NULL,NULL,NULL,0,0),(56,'tag',8,0,'Personal Tags','',0,5600,0,0,1,NULL,NULL,NULL,0,0),(57,'last_visit_ts',5,0,'Last Visit','',0,5700,0,0,1,NULL,NULL,NULL,0,0),(58,'comment_tag',0,0,'Comment Tag','',0,5800,0,0,0,NULL,NULL,NULL,0,0),(59,'days_elapsed',0,0,'Days since bug changed','',0,5900,0,0,0,NULL,NULL,NULL,0,0); +/*!40000 ALTER TABLE `fielddefs` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `flagexclusions` +-- + +DROP TABLE IF EXISTS `flagexclusions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `flagexclusions` ( + `type_id` mediumint(9) NOT NULL, + `product_id` smallint(6) DEFAULT NULL, + `component_id` mediumint(9) DEFAULT NULL, + UNIQUE KEY `flagexclusions_type_id_idx` (`type_id`,`product_id`,`component_id`), + KEY `fk_flagexclusions_component_id_components_id` (`component_id`), + KEY `fk_flagexclusions_product_id_products_id` (`product_id`), + CONSTRAINT `fk_flagexclusions_component_id_components_id` FOREIGN KEY (`component_id`) REFERENCES `components` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_flagexclusions_product_id_products_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_flagexclusions_type_id_flagtypes_id` FOREIGN KEY (`type_id`) REFERENCES `flagtypes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `flagexclusions` +-- + +LOCK TABLES `flagexclusions` WRITE; +/*!40000 ALTER TABLE `flagexclusions` DISABLE KEYS */; +/*!40000 ALTER TABLE `flagexclusions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `flaginclusions` +-- + +DROP TABLE IF EXISTS `flaginclusions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `flaginclusions` ( + `type_id` mediumint(9) NOT NULL, + `product_id` smallint(6) DEFAULT NULL, + `component_id` mediumint(9) DEFAULT NULL, + UNIQUE KEY `flaginclusions_type_id_idx` (`type_id`,`product_id`,`component_id`), + KEY `fk_flaginclusions_component_id_components_id` (`component_id`), + KEY `fk_flaginclusions_product_id_products_id` (`product_id`), + CONSTRAINT `fk_flaginclusions_component_id_components_id` FOREIGN KEY (`component_id`) REFERENCES `components` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_flaginclusions_product_id_products_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_flaginclusions_type_id_flagtypes_id` FOREIGN KEY (`type_id`) REFERENCES `flagtypes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `flaginclusions` +-- + +LOCK TABLES `flaginclusions` WRITE; +/*!40000 ALTER TABLE `flaginclusions` DISABLE KEYS */; +/*!40000 ALTER TABLE `flaginclusions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `flags` +-- + +DROP TABLE IF EXISTS `flags`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `flags` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `type_id` mediumint(9) NOT NULL, + `status` char(1) NOT NULL, + `bug_id` mediumint(9) NOT NULL, + `attach_id` mediumint(9) DEFAULT NULL, + `creation_date` datetime NOT NULL, + `modification_date` datetime DEFAULT NULL, + `setter_id` mediumint(9) NOT NULL, + `requestee_id` mediumint(9) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `flags_bug_id_idx` (`bug_id`,`attach_id`), + KEY `flags_setter_id_idx` (`setter_id`), + KEY `flags_requestee_id_idx` (`requestee_id`), + KEY `flags_type_id_idx` (`type_id`), + KEY `fk_flags_attach_id_attachments_attach_id` (`attach_id`), + CONSTRAINT `fk_flags_attach_id_attachments_attach_id` FOREIGN KEY (`attach_id`) REFERENCES `attachments` (`attach_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_flags_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_flags_requestee_id_profiles_userid` FOREIGN KEY (`requestee_id`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE, + CONSTRAINT `fk_flags_setter_id_profiles_userid` FOREIGN KEY (`setter_id`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE, + CONSTRAINT `fk_flags_type_id_flagtypes_id` FOREIGN KEY (`type_id`) REFERENCES `flagtypes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `flags` +-- + +LOCK TABLES `flags` WRITE; +/*!40000 ALTER TABLE `flags` DISABLE KEYS */; +/*!40000 ALTER TABLE `flags` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `flagtypes` +-- + +DROP TABLE IF EXISTS `flagtypes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `flagtypes` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `description` mediumtext NOT NULL, + `cc_list` varchar(200) DEFAULT NULL, + `target_type` char(1) NOT NULL DEFAULT 'b', + `is_active` tinyint(4) NOT NULL DEFAULT 1, + `is_requestable` tinyint(4) NOT NULL DEFAULT 0, + `is_requesteeble` tinyint(4) NOT NULL DEFAULT 0, + `is_multiplicable` tinyint(4) NOT NULL DEFAULT 0, + `sortkey` smallint(6) NOT NULL DEFAULT 0, + `grant_group_id` mediumint(9) DEFAULT NULL, + `request_group_id` mediumint(9) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `fk_flagtypes_request_group_id_groups_id` (`request_group_id`), + KEY `fk_flagtypes_grant_group_id_groups_id` (`grant_group_id`), + CONSTRAINT `fk_flagtypes_grant_group_id_groups_id` FOREIGN KEY (`grant_group_id`) REFERENCES `groups` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT `fk_flagtypes_request_group_id_groups_id` FOREIGN KEY (`request_group_id`) REFERENCES `groups` (`id`) ON DELETE SET NULL ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `flagtypes` +-- + +LOCK TABLES `flagtypes` WRITE; +/*!40000 ALTER TABLE `flagtypes` DISABLE KEYS */; +/*!40000 ALTER TABLE `flagtypes` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `group_control_map` +-- + +DROP TABLE IF EXISTS `group_control_map`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `group_control_map` ( + `group_id` mediumint(9) NOT NULL, + `product_id` smallint(6) NOT NULL, + `entry` tinyint(4) NOT NULL DEFAULT 0, + `membercontrol` tinyint(4) NOT NULL DEFAULT 0, + `othercontrol` tinyint(4) NOT NULL DEFAULT 0, + `canedit` tinyint(4) NOT NULL DEFAULT 0, + `editcomponents` tinyint(4) NOT NULL DEFAULT 0, + `editbugs` tinyint(4) NOT NULL DEFAULT 0, + `canconfirm` tinyint(4) NOT NULL DEFAULT 0, + UNIQUE KEY `group_control_map_product_id_idx` (`product_id`,`group_id`), + KEY `group_control_map_group_id_idx` (`group_id`), + CONSTRAINT `fk_group_control_map_group_id_groups_id` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_group_control_map_product_id_products_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `group_control_map` +-- + +LOCK TABLES `group_control_map` WRITE; +/*!40000 ALTER TABLE `group_control_map` DISABLE KEYS */; +/*!40000 ALTER TABLE `group_control_map` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `group_group_map` +-- + +DROP TABLE IF EXISTS `group_group_map`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `group_group_map` ( + `member_id` mediumint(9) NOT NULL, + `grantor_id` mediumint(9) NOT NULL, + `grant_type` tinyint(4) NOT NULL DEFAULT 0, + UNIQUE KEY `group_group_map_member_id_idx` (`member_id`,`grantor_id`,`grant_type`), + KEY `fk_group_group_map_grantor_id_groups_id` (`grantor_id`), + CONSTRAINT `fk_group_group_map_grantor_id_groups_id` FOREIGN KEY (`grantor_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_group_group_map_member_id_groups_id` FOREIGN KEY (`member_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `group_group_map` +-- + +LOCK TABLES `group_group_map` WRITE; +/*!40000 ALTER TABLE `group_group_map` DISABLE KEYS */; +INSERT INTO `group_group_map` VALUES (1,1,0),(1,1,1),(1,1,2),(1,2,0),(1,2,1),(1,2,2),(1,3,0),(1,3,1),(1,3,2),(1,4,0),(1,4,1),(1,4,2),(1,5,0),(1,5,1),(1,5,2),(1,6,0),(1,6,1),(1,6,2),(1,7,0),(1,7,1),(1,7,2),(1,8,0),(1,8,1),(1,8,2),(1,9,0),(1,9,1),(1,9,2),(1,10,0),(1,10,1),(1,10,2),(1,11,0),(1,11,1),(1,11,2),(8,11,0),(10,11,0),(1,12,0),(1,12,1),(1,12,2),(1,13,0),(1,13,1),(1,13,2),(12,13,0),(1,14,0),(1,14,1),(1,14,2); +/*!40000 ALTER TABLE `group_group_map` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `groups` +-- + +DROP TABLE IF EXISTS `groups`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `groups` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `description` mediumtext NOT NULL, + `isbuggroup` tinyint(4) NOT NULL, + `userregexp` tinytext NOT NULL DEFAULT '', + `isactive` tinyint(4) NOT NULL DEFAULT 1, + `icon_url` tinytext DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `groups_name_idx` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `groups` +-- + +LOCK TABLES `groups` WRITE; +/*!40000 ALTER TABLE `groups` DISABLE KEYS */; +INSERT INTO `groups` VALUES (1,'admin','Administrators',0,'',1,NULL),(2,'tweakparams','Can change Parameters',0,'',1,NULL),(3,'editusers','Can edit or disable users',0,'',1,NULL),(4,'creategroups','Can create and destroy groups',0,'',1,NULL),(5,'editclassifications','Can create, destroy, and edit classifications',0,'',1,NULL),(6,'editcomponents','Can create, destroy, and edit components',0,'',1,NULL),(7,'editkeywords','Can create, destroy, and edit keywords',0,'',1,NULL),(8,'editbugs','Can edit all bug fields',0,'.*',1,NULL),(9,'canconfirm','Can confirm a bug or mark it a duplicate',0,'',1,NULL),(10,'bz_canusewhineatothers','Can configure whine reports for other users',0,'',1,NULL),(11,'bz_canusewhines','User can configure whine reports for self',0,'',1,NULL),(12,'bz_sudoers','Can perform actions as other users',0,'',1,NULL),(13,'bz_sudo_protect','Can not be impersonated by other users',0,'',1,NULL),(14,'bz_quip_moderators','Can moderate quips',0,'',1,NULL); +/*!40000 ALTER TABLE `groups` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `keyworddefs` +-- + +DROP TABLE IF EXISTS `keyworddefs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `keyworddefs` ( + `id` smallint(6) NOT NULL AUTO_INCREMENT, + `name` varchar(64) NOT NULL, + `description` mediumtext NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `keyworddefs_name_idx` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `keyworddefs` +-- + +LOCK TABLES `keyworddefs` WRITE; +/*!40000 ALTER TABLE `keyworddefs` DISABLE KEYS */; +/*!40000 ALTER TABLE `keyworddefs` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `keywords` +-- + +DROP TABLE IF EXISTS `keywords`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `keywords` ( + `bug_id` mediumint(9) NOT NULL, + `keywordid` smallint(6) NOT NULL, + UNIQUE KEY `keywords_bug_id_idx` (`bug_id`,`keywordid`), + KEY `keywords_keywordid_idx` (`keywordid`), + CONSTRAINT `fk_keywords_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_keywords_keywordid_keyworddefs_id` FOREIGN KEY (`keywordid`) REFERENCES `keyworddefs` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `keywords` +-- + +LOCK TABLES `keywords` WRITE; +/*!40000 ALTER TABLE `keywords` DISABLE KEYS */; +/*!40000 ALTER TABLE `keywords` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `login_failure` +-- + +DROP TABLE IF EXISTS `login_failure`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `login_failure` ( + `user_id` mediumint(9) NOT NULL, + `login_time` datetime NOT NULL, + `ip_addr` varchar(40) NOT NULL, + KEY `login_failure_user_id_idx` (`user_id`), + CONSTRAINT `fk_login_failure_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `login_failure` +-- + +LOCK TABLES `login_failure` WRITE; +/*!40000 ALTER TABLE `login_failure` DISABLE KEYS */; +/*!40000 ALTER TABLE `login_failure` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `logincookies` +-- + +DROP TABLE IF EXISTS `logincookies`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `logincookies` ( + `cookie` varchar(16) NOT NULL, + `userid` mediumint(9) NOT NULL, + `ipaddr` varchar(40) DEFAULT NULL, + `lastused` datetime NOT NULL, + PRIMARY KEY (`cookie`), + KEY `logincookies_lastused_idx` (`lastused`), + KEY `fk_logincookies_userid_profiles_userid` (`userid`), + CONSTRAINT `fk_logincookies_userid_profiles_userid` FOREIGN KEY (`userid`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `logincookies` +-- + +LOCK TABLES `logincookies` WRITE; +/*!40000 ALTER TABLE `logincookies` DISABLE KEYS */; +INSERT INTO `logincookies` VALUES ('Ypt6rPqHjG',1,NULL,'2023-11-27 15:53:08'); +/*!40000 ALTER TABLE `logincookies` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `longdescs` +-- + +DROP TABLE IF EXISTS `longdescs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `longdescs` ( + `comment_id` int(11) NOT NULL AUTO_INCREMENT, + `bug_id` mediumint(9) NOT NULL, + `who` mediumint(9) NOT NULL, + `bug_when` datetime NOT NULL, + `work_time` decimal(7,2) NOT NULL DEFAULT 0.00, + `thetext` mediumtext NOT NULL, + `isprivate` tinyint(4) NOT NULL DEFAULT 0, + `already_wrapped` tinyint(4) NOT NULL DEFAULT 0, + `type` smallint(6) NOT NULL DEFAULT 0, + `extra_data` varchar(255) DEFAULT NULL, + PRIMARY KEY (`comment_id`), + KEY `longdescs_bug_id_idx` (`bug_id`,`work_time`), + KEY `longdescs_who_idx` (`who`,`bug_id`), + KEY `longdescs_bug_when_idx` (`bug_when`), + CONSTRAINT `fk_longdescs_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_longdescs_who_profiles_userid` FOREIGN KEY (`who`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `longdescs` +-- + +LOCK TABLES `longdescs` WRITE; +/*!40000 ALTER TABLE `longdescs` DISABLE KEYS */; +INSERT INTO `longdescs` VALUES (1,1,1,'2023-11-27 15:35:33',0.00,'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\nAt vero eos et accusam et justo duo dolores et ea rebum.',0,0,0,NULL),(2,1,1,'2023-11-27 15:37:05',0.00,'Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.',0,0,0,NULL),(3,2,1,'2023-11-27 15:38:45',0.00,'Nobody expects the Spanish Inquisition! \n\nOur chief weapon is surprise, surprise and fear, fear and surprise. \n\nOur two weapons are fear and surprise, and ruthless efficiency. \n\nOur three weapons are fear and surprise and ruthless efficiency and an almost fanatical dedication to the pope.',0,0,0,NULL); +/*!40000 ALTER TABLE `longdescs` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `longdescs_tags` +-- + +DROP TABLE IF EXISTS `longdescs_tags`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `longdescs_tags` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `comment_id` int(11) DEFAULT NULL, + `tag` varchar(24) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `longdescs_tags_idx` (`comment_id`,`tag`), + CONSTRAINT `fk_longdescs_tags_comment_id_longdescs_comment_id` FOREIGN KEY (`comment_id`) REFERENCES `longdescs` (`comment_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `longdescs_tags` +-- + +LOCK TABLES `longdescs_tags` WRITE; +/*!40000 ALTER TABLE `longdescs_tags` DISABLE KEYS */; +/*!40000 ALTER TABLE `longdescs_tags` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `longdescs_tags_activity` +-- + +DROP TABLE IF EXISTS `longdescs_tags_activity`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `longdescs_tags_activity` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `bug_id` mediumint(9) NOT NULL, + `comment_id` int(11) DEFAULT NULL, + `who` mediumint(9) NOT NULL, + `bug_when` datetime NOT NULL, + `added` varchar(24) DEFAULT NULL, + `removed` varchar(24) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `longdescs_tags_activity_bug_id_idx` (`bug_id`), + KEY `fk_longdescs_tags_activity_comment_id_longdescs_comment_id` (`comment_id`), + KEY `fk_longdescs_tags_activity_who_profiles_userid` (`who`), + CONSTRAINT `fk_longdescs_tags_activity_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_longdescs_tags_activity_comment_id_longdescs_comment_id` FOREIGN KEY (`comment_id`) REFERENCES `longdescs` (`comment_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_longdescs_tags_activity_who_profiles_userid` FOREIGN KEY (`who`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `longdescs_tags_activity` +-- + +LOCK TABLES `longdescs_tags_activity` WRITE; +/*!40000 ALTER TABLE `longdescs_tags_activity` DISABLE KEYS */; +/*!40000 ALTER TABLE `longdescs_tags_activity` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `longdescs_tags_weights` +-- + +DROP TABLE IF EXISTS `longdescs_tags_weights`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `longdescs_tags_weights` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `tag` varchar(24) NOT NULL, + `weight` mediumint(9) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `longdescs_tags_weights_tag_idx` (`tag`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `longdescs_tags_weights` +-- + +LOCK TABLES `longdescs_tags_weights` WRITE; +/*!40000 ALTER TABLE `longdescs_tags_weights` DISABLE KEYS */; +/*!40000 ALTER TABLE `longdescs_tags_weights` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `mail_staging` +-- + +DROP TABLE IF EXISTS `mail_staging`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `mail_staging` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `message` longblob NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `mail_staging` +-- + +LOCK TABLES `mail_staging` WRITE; +/*!40000 ALTER TABLE `mail_staging` DISABLE KEYS */; +/*!40000 ALTER TABLE `mail_staging` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `milestones` +-- + +DROP TABLE IF EXISTS `milestones`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `milestones` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `product_id` smallint(6) NOT NULL, + `value` varchar(64) NOT NULL, + `sortkey` smallint(6) NOT NULL DEFAULT 0, + `isactive` tinyint(4) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + UNIQUE KEY `milestones_product_id_idx` (`product_id`,`value`), + CONSTRAINT `fk_milestones_product_id_products_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `milestones` +-- + +LOCK TABLES `milestones` WRITE; +/*!40000 ALTER TABLE `milestones` DISABLE KEYS */; +INSERT INTO `milestones` VALUES (1,1,'---',0,1),(2,2,'---',0,1),(3,3,'---',0,1); +/*!40000 ALTER TABLE `milestones` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `namedqueries` +-- + +DROP TABLE IF EXISTS `namedqueries`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `namedqueries` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `userid` mediumint(9) NOT NULL, + `name` varchar(64) NOT NULL, + `query` mediumtext NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `namedqueries_userid_idx` (`userid`,`name`), + CONSTRAINT `fk_namedqueries_userid_profiles_userid` FOREIGN KEY (`userid`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `namedqueries` +-- + +LOCK TABLES `namedqueries` WRITE; +/*!40000 ALTER TABLE `namedqueries` DISABLE KEYS */; +/*!40000 ALTER TABLE `namedqueries` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `namedqueries_link_in_footer` +-- + +DROP TABLE IF EXISTS `namedqueries_link_in_footer`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `namedqueries_link_in_footer` ( + `namedquery_id` mediumint(9) NOT NULL, + `user_id` mediumint(9) NOT NULL, + UNIQUE KEY `namedqueries_link_in_footer_id_idx` (`namedquery_id`,`user_id`), + KEY `namedqueries_link_in_footer_userid_idx` (`user_id`), + CONSTRAINT `fk_namedqueries_link_in_footer_namedquery_id_namedqueries_id` FOREIGN KEY (`namedquery_id`) REFERENCES `namedqueries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_namedqueries_link_in_footer_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `namedqueries_link_in_footer` +-- + +LOCK TABLES `namedqueries_link_in_footer` WRITE; +/*!40000 ALTER TABLE `namedqueries_link_in_footer` DISABLE KEYS */; +/*!40000 ALTER TABLE `namedqueries_link_in_footer` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `namedquery_group_map` +-- + +DROP TABLE IF EXISTS `namedquery_group_map`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `namedquery_group_map` ( + `namedquery_id` mediumint(9) NOT NULL, + `group_id` mediumint(9) NOT NULL, + UNIQUE KEY `namedquery_group_map_namedquery_id_idx` (`namedquery_id`), + KEY `namedquery_group_map_group_id_idx` (`group_id`), + CONSTRAINT `fk_namedquery_group_map_group_id_groups_id` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_namedquery_group_map_namedquery_id_namedqueries_id` FOREIGN KEY (`namedquery_id`) REFERENCES `namedqueries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `namedquery_group_map` +-- + +LOCK TABLES `namedquery_group_map` WRITE; +/*!40000 ALTER TABLE `namedquery_group_map` DISABLE KEYS */; +/*!40000 ALTER TABLE `namedquery_group_map` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `op_sys` +-- + +DROP TABLE IF EXISTS `op_sys`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `op_sys` ( + `id` smallint(6) NOT NULL AUTO_INCREMENT, + `value` varchar(64) NOT NULL, + `sortkey` smallint(6) NOT NULL DEFAULT 0, + `isactive` tinyint(4) NOT NULL DEFAULT 1, + `visibility_value_id` smallint(6) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `op_sys_value_idx` (`value`), + KEY `op_sys_sortkey_idx` (`sortkey`,`value`), + KEY `op_sys_visibility_value_id_idx` (`visibility_value_id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `op_sys` +-- + +LOCK TABLES `op_sys` WRITE; +/*!40000 ALTER TABLE `op_sys` DISABLE KEYS */; +INSERT INTO `op_sys` VALUES (1,'All',100,1,NULL),(2,'Windows',200,1,NULL),(3,'Mac OS',300,1,NULL),(4,'Linux',400,1,NULL),(5,'Other',500,1,NULL); +/*!40000 ALTER TABLE `op_sys` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `priority` +-- + +DROP TABLE IF EXISTS `priority`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `priority` ( + `id` smallint(6) NOT NULL AUTO_INCREMENT, + `value` varchar(64) NOT NULL, + `sortkey` smallint(6) NOT NULL DEFAULT 0, + `isactive` tinyint(4) NOT NULL DEFAULT 1, + `visibility_value_id` smallint(6) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `priority_value_idx` (`value`), + KEY `priority_sortkey_idx` (`sortkey`,`value`), + KEY `priority_visibility_value_id_idx` (`visibility_value_id`) +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `priority` +-- + +LOCK TABLES `priority` WRITE; +/*!40000 ALTER TABLE `priority` DISABLE KEYS */; +INSERT INTO `priority` VALUES (1,'Highest',100,1,NULL),(2,'High',200,1,NULL),(3,'Normal',300,1,NULL),(4,'Low',400,1,NULL),(5,'Lowest',500,1,NULL),(6,'---',600,1,NULL); +/*!40000 ALTER TABLE `priority` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `products` +-- + +DROP TABLE IF EXISTS `products`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `products` ( + `id` smallint(6) NOT NULL AUTO_INCREMENT, + `name` varchar(64) NOT NULL, + `classification_id` smallint(6) NOT NULL DEFAULT 1, + `description` mediumtext NOT NULL, + `isactive` tinyint(4) NOT NULL DEFAULT 1, + `defaultmilestone` varchar(64) NOT NULL DEFAULT '---', + `allows_unconfirmed` tinyint(4) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + UNIQUE KEY `products_name_idx` (`name`), + KEY `fk_products_classification_id_classifications_id` (`classification_id`), + CONSTRAINT `fk_products_classification_id_classifications_id` FOREIGN KEY (`classification_id`) REFERENCES `classifications` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `products` +-- + +LOCK TABLES `products` WRITE; +/*!40000 ALTER TABLE `products` DISABLE KEYS */; +INSERT INTO `products` VALUES (1,'TestProduct',1,'This is a test product. This ought to be blown away and replaced with real stuff in a finished installation of bugzilla.',1,'---',1),(2,'Red Hat Enterprise Linux 9',1,'Lorem ipsum',1,'---',1),(3,'SUSE Linux Enterprise Server 15 SP6',1,'Lorem ipsum dolor sit amet',1,'---',1); +/*!40000 ALTER TABLE `products` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `profile_search` +-- + +DROP TABLE IF EXISTS `profile_search`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `profile_search` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` mediumint(9) NOT NULL, + `bug_list` mediumtext NOT NULL, + `list_order` mediumtext DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `profile_search_user_id_idx` (`user_id`), + CONSTRAINT `fk_profile_search_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `profile_search` +-- + +LOCK TABLES `profile_search` WRITE; +/*!40000 ALTER TABLE `profile_search` DISABLE KEYS */; +INSERT INTO `profile_search` VALUES (1,1,'1','bug_status,priority,assigned_to,bug_id'); +/*!40000 ALTER TABLE `profile_search` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `profile_setting` +-- + +DROP TABLE IF EXISTS `profile_setting`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `profile_setting` ( + `user_id` mediumint(9) NOT NULL, + `setting_name` varchar(32) NOT NULL, + `setting_value` varchar(32) NOT NULL, + UNIQUE KEY `profile_setting_value_unique_idx` (`user_id`,`setting_name`), + KEY `fk_profile_setting_setting_name_setting_name` (`setting_name`), + CONSTRAINT `fk_profile_setting_setting_name_setting_name` FOREIGN KEY (`setting_name`) REFERENCES `setting` (`name`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_profile_setting_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `profile_setting` +-- + +LOCK TABLES `profile_setting` WRITE; +/*!40000 ALTER TABLE `profile_setting` DISABLE KEYS */; +/*!40000 ALTER TABLE `profile_setting` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `profiles` +-- + +DROP TABLE IF EXISTS `profiles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `profiles` ( + `userid` mediumint(9) NOT NULL AUTO_INCREMENT, + `login_name` varchar(255) NOT NULL, + `cryptpassword` varchar(128) DEFAULT NULL, + `realname` varchar(255) NOT NULL DEFAULT '', + `disabledtext` mediumtext NOT NULL DEFAULT '', + `disable_mail` tinyint(4) NOT NULL DEFAULT 0, + `mybugslink` tinyint(4) NOT NULL DEFAULT 1, + `extern_id` varchar(64) DEFAULT NULL, + `is_enabled` tinyint(4) NOT NULL DEFAULT 1, + `last_seen_date` datetime DEFAULT NULL, + PRIMARY KEY (`userid`), + UNIQUE KEY `profiles_login_name_idx` (`login_name`), + UNIQUE KEY `profiles_extern_id_idx` (`extern_id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `profiles` +-- + +LOCK TABLES `profiles` WRITE; +/*!40000 ALTER TABLE `profiles` DISABLE KEYS */; +INSERT INTO `profiles` VALUES (1,'andreas@hasenkopf.xyz','2207pp7o,ialUTtf7x78ge5SbbN7+W+1lXGJBXmMlYt26C1egd4g{SHA-256}','Andreas','',0,1,NULL,1,'2023-11-27 00:00:00'); +/*!40000 ALTER TABLE `profiles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `profiles_activity` +-- + +DROP TABLE IF EXISTS `profiles_activity`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `profiles_activity` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `userid` mediumint(9) NOT NULL, + `who` mediumint(9) NOT NULL, + `profiles_when` datetime NOT NULL, + `fieldid` mediumint(9) NOT NULL, + `oldvalue` tinytext DEFAULT NULL, + `newvalue` tinytext DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `profiles_activity_userid_idx` (`userid`), + KEY `profiles_activity_profiles_when_idx` (`profiles_when`), + KEY `profiles_activity_fieldid_idx` (`fieldid`), + KEY `fk_profiles_activity_who_profiles_userid` (`who`), + CONSTRAINT `fk_profiles_activity_fieldid_fielddefs_id` FOREIGN KEY (`fieldid`) REFERENCES `fielddefs` (`id`) ON UPDATE CASCADE, + CONSTRAINT `fk_profiles_activity_userid_profiles_userid` FOREIGN KEY (`userid`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_profiles_activity_who_profiles_userid` FOREIGN KEY (`who`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `profiles_activity` +-- + +LOCK TABLES `profiles_activity` WRITE; +/*!40000 ALTER TABLE `profiles_activity` DISABLE KEYS */; +INSERT INTO `profiles_activity` VALUES (1,1,1,'2023-09-20 13:12:55',33,NULL,'2023-09-20 13:12:55'); +/*!40000 ALTER TABLE `profiles_activity` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `quips` +-- + +DROP TABLE IF EXISTS `quips`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `quips` ( + `quipid` mediumint(9) NOT NULL AUTO_INCREMENT, + `userid` mediumint(9) DEFAULT NULL, + `quip` varchar(512) NOT NULL, + `approved` tinyint(4) NOT NULL DEFAULT 1, + PRIMARY KEY (`quipid`), + KEY `fk_quips_userid_profiles_userid` (`userid`), + CONSTRAINT `fk_quips_userid_profiles_userid` FOREIGN KEY (`userid`) REFERENCES `profiles` (`userid`) ON DELETE SET NULL ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `quips` +-- + +LOCK TABLES `quips` WRITE; +/*!40000 ALTER TABLE `quips` DISABLE KEYS */; +/*!40000 ALTER TABLE `quips` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `rep_platform` +-- + +DROP TABLE IF EXISTS `rep_platform`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `rep_platform` ( + `id` smallint(6) NOT NULL AUTO_INCREMENT, + `value` varchar(64) NOT NULL, + `sortkey` smallint(6) NOT NULL DEFAULT 0, + `isactive` tinyint(4) NOT NULL DEFAULT 1, + `visibility_value_id` smallint(6) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `rep_platform_value_idx` (`value`), + KEY `rep_platform_sortkey_idx` (`sortkey`,`value`), + KEY `rep_platform_visibility_value_id_idx` (`visibility_value_id`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `rep_platform` +-- + +LOCK TABLES `rep_platform` WRITE; +/*!40000 ALTER TABLE `rep_platform` DISABLE KEYS */; +INSERT INTO `rep_platform` VALUES (1,'All',100,1,NULL),(2,'PC',200,1,NULL),(3,'Macintosh',300,1,NULL),(4,'Other',400,1,NULL); +/*!40000 ALTER TABLE `rep_platform` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `reports` +-- + +DROP TABLE IF EXISTS `reports`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `reports` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `user_id` mediumint(9) NOT NULL, + `name` varchar(64) NOT NULL, + `query` mediumtext NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `reports_user_id_idx` (`user_id`,`name`), + CONSTRAINT `fk_reports_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `reports` +-- + +LOCK TABLES `reports` WRITE; +/*!40000 ALTER TABLE `reports` DISABLE KEYS */; +/*!40000 ALTER TABLE `reports` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `resolution` +-- + +DROP TABLE IF EXISTS `resolution`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `resolution` ( + `id` smallint(6) NOT NULL AUTO_INCREMENT, + `value` varchar(64) NOT NULL, + `sortkey` smallint(6) NOT NULL DEFAULT 0, + `isactive` tinyint(4) NOT NULL DEFAULT 1, + `visibility_value_id` smallint(6) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `resolution_value_idx` (`value`), + KEY `resolution_sortkey_idx` (`sortkey`,`value`), + KEY `resolution_visibility_value_id_idx` (`visibility_value_id`) +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `resolution` +-- + +LOCK TABLES `resolution` WRITE; +/*!40000 ALTER TABLE `resolution` DISABLE KEYS */; +INSERT INTO `resolution` VALUES (1,'',100,1,NULL),(2,'FIXED',200,1,NULL),(3,'INVALID',300,1,NULL),(4,'WONTFIX',400,1,NULL),(5,'DUPLICATE',500,1,NULL),(6,'WORKSFORME',600,1,NULL); +/*!40000 ALTER TABLE `resolution` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `series` +-- + +DROP TABLE IF EXISTS `series`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `series` ( + `series_id` mediumint(9) NOT NULL AUTO_INCREMENT, + `creator` mediumint(9) DEFAULT NULL, + `category` smallint(6) NOT NULL, + `subcategory` smallint(6) NOT NULL, + `name` varchar(64) NOT NULL, + `frequency` smallint(6) NOT NULL, + `query` mediumtext NOT NULL, + `is_public` tinyint(4) NOT NULL DEFAULT 0, + PRIMARY KEY (`series_id`), + UNIQUE KEY `series_category_idx` (`category`,`subcategory`,`name`), + KEY `series_creator_idx` (`creator`), + KEY `fk_series_subcategory_series_categories_id` (`subcategory`), + CONSTRAINT `fk_series_category_series_categories_id` FOREIGN KEY (`category`) REFERENCES `series_categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_series_creator_profiles_userid` FOREIGN KEY (`creator`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_series_subcategory_series_categories_id` FOREIGN KEY (`subcategory`) REFERENCES `series_categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `series` +-- + +LOCK TABLES `series` WRITE; +/*!40000 ALTER TABLE `series` DISABLE KEYS */; +INSERT INTO `series` VALUES (1,1,1,2,'UNCONFIRMED',1,'bug_status=UNCONFIRMED&product=Red%20Hat%20Enterprise%20Linux%209',1),(2,1,1,2,'CONFIRMED',1,'bug_status=CONFIRMED&product=Red%20Hat%20Enterprise%20Linux%209',1),(3,1,1,2,'IN_PROGRESS',1,'bug_status=IN_PROGRESS&product=Red%20Hat%20Enterprise%20Linux%209',1),(4,1,1,2,'RESOLVED',1,'bug_status=RESOLVED&product=Red%20Hat%20Enterprise%20Linux%209',1),(5,1,1,2,'VERIFIED',1,'bug_status=VERIFIED&product=Red%20Hat%20Enterprise%20Linux%209',1),(6,1,1,2,'FIXED',1,'resolution=FIXED&product=Red%20Hat%20Enterprise%20Linux%209',1),(7,1,1,2,'INVALID',1,'resolution=INVALID&product=Red%20Hat%20Enterprise%20Linux%209',1),(8,1,1,2,'WONTFIX',1,'resolution=WONTFIX&product=Red%20Hat%20Enterprise%20Linux%209',1),(9,1,1,2,'DUPLICATE',1,'resolution=DUPLICATE&product=Red%20Hat%20Enterprise%20Linux%209',1),(10,1,1,2,'WORKSFORME',1,'resolution=WORKSFORME&product=Red%20Hat%20Enterprise%20Linux%209',1),(11,1,1,2,'All Open',1,'bug_status=UNCONFIRMED&bug_status=CONFIRMED&bug_status=IN_PROGRESS&product=Red%20Hat%20Enterprise%20Linux%209',1),(12,1,1,3,'All Open',1,'field0-0-0=resolution&type0-0-0=notregexp&value0-0-0=.&product=Red%20Hat%20Enterprise%20Linux%209&component=python-bugzilla',1),(13,1,1,3,'All Closed',1,'field0-0-0=resolution&type0-0-0=regexp&value0-0-0=.&product=Red%20Hat%20Enterprise%20Linux%209&component=python-bugzilla',1),(14,1,4,2,'UNCONFIRMED',1,'bug_status=UNCONFIRMED&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6',1),(15,1,4,2,'CONFIRMED',1,'bug_status=CONFIRMED&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6',1),(16,1,4,2,'IN_PROGRESS',1,'bug_status=IN_PROGRESS&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6',1),(17,1,4,2,'RESOLVED',1,'bug_status=RESOLVED&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6',1),(18,1,4,2,'VERIFIED',1,'bug_status=VERIFIED&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6',1),(19,1,4,2,'FIXED',1,'resolution=FIXED&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6',1),(20,1,4,2,'INVALID',1,'resolution=INVALID&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6',1),(21,1,4,2,'WONTFIX',1,'resolution=WONTFIX&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6',1),(22,1,4,2,'DUPLICATE',1,'resolution=DUPLICATE&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6',1),(23,1,4,2,'WORKSFORME',1,'resolution=WORKSFORME&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6',1),(24,1,4,2,'All Open',1,'bug_status=UNCONFIRMED&bug_status=CONFIRMED&bug_status=IN_PROGRESS&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6',1),(25,1,4,5,'All Open',1,'field0-0-0=resolution&type0-0-0=notregexp&value0-0-0=.&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6&component=Kernel',1),(26,1,4,5,'All Closed',1,'field0-0-0=resolution&type0-0-0=regexp&value0-0-0=.&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6&component=Kernel',1),(27,1,4,6,'All Open',1,'field0-0-0=resolution&type0-0-0=notregexp&value0-0-0=.&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6&component=Containers',1),(28,1,4,6,'All Closed',1,'field0-0-0=resolution&type0-0-0=regexp&value0-0-0=.&product=SUSE%20Linux%20Enterprise%20Server%2015%20SP6&component=Containers',1); +/*!40000 ALTER TABLE `series` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `series_categories` +-- + +DROP TABLE IF EXISTS `series_categories`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `series_categories` ( + `id` smallint(6) NOT NULL AUTO_INCREMENT, + `name` varchar(64) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `series_categories_name_idx` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `series_categories` +-- + +LOCK TABLES `series_categories` WRITE; +/*!40000 ALTER TABLE `series_categories` DISABLE KEYS */; +INSERT INTO `series_categories` VALUES (2,'-All-'),(6,'Containers'),(5,'Kernel'),(3,'python-bugzilla'),(1,'Red Hat Enterprise Linux 9'),(4,'SUSE Linux Enterprise Server 15 SP6'); +/*!40000 ALTER TABLE `series_categories` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `series_data` +-- + +DROP TABLE IF EXISTS `series_data`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `series_data` ( + `series_id` mediumint(9) NOT NULL, + `series_date` datetime NOT NULL, + `series_value` mediumint(9) NOT NULL, + UNIQUE KEY `series_data_series_id_idx` (`series_id`,`series_date`), + CONSTRAINT `fk_series_data_series_id_series_series_id` FOREIGN KEY (`series_id`) REFERENCES `series` (`series_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `series_data` +-- + +LOCK TABLES `series_data` WRITE; +/*!40000 ALTER TABLE `series_data` DISABLE KEYS */; +/*!40000 ALTER TABLE `series_data` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `setting` +-- + +DROP TABLE IF EXISTS `setting`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `setting` ( + `name` varchar(32) NOT NULL, + `default_value` varchar(32) NOT NULL, + `is_enabled` tinyint(4) NOT NULL DEFAULT 1, + `subclass` varchar(32) DEFAULT NULL, + PRIMARY KEY (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `setting` +-- + +LOCK TABLES `setting` WRITE; +/*!40000 ALTER TABLE `setting` DISABLE KEYS */; +INSERT INTO `setting` VALUES ('bugmail_new_prefix','on',1,NULL),('comment_box_position','before_comments',1,NULL),('comment_sort_order','oldest_to_newest',1,NULL),('csv_colsepchar',',',1,NULL),('display_quips','on',1,NULL),('email_format','html',1,NULL),('lang','en',1,'Lang'),('possible_duplicates','on',1,NULL),('post_bug_submit_action','next_bug',1,NULL),('quicksearch_fulltext','on',1,NULL),('quote_replies','quoted_reply',1,NULL),('requestee_cc','on',1,NULL),('skin','Dusk',1,'Skin'),('state_addselfcc','cc_unless_role',1,NULL),('timezone','local',1,'Timezone'),('zoom_textareas','on',1,NULL); +/*!40000 ALTER TABLE `setting` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `setting_value` +-- + +DROP TABLE IF EXISTS `setting_value`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `setting_value` ( + `name` varchar(32) NOT NULL, + `value` varchar(32) NOT NULL, + `sortindex` smallint(6) NOT NULL, + UNIQUE KEY `setting_value_nv_unique_idx` (`name`,`value`), + UNIQUE KEY `setting_value_ns_unique_idx` (`name`,`sortindex`), + CONSTRAINT `fk_setting_value_name_setting_name` FOREIGN KEY (`name`) REFERENCES `setting` (`name`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `setting_value` +-- + +LOCK TABLES `setting_value` WRITE; +/*!40000 ALTER TABLE `setting_value` DISABLE KEYS */; +INSERT INTO `setting_value` VALUES ('bugmail_new_prefix','on',5),('bugmail_new_prefix','off',10),('comment_box_position','before_comments',5),('comment_box_position','after_comments',10),('comment_sort_order','oldest_to_newest',5),('comment_sort_order','newest_to_oldest',10),('comment_sort_order','newest_to_oldest_desc_first',15),('csv_colsepchar',',',5),('csv_colsepchar',';',10),('display_quips','on',5),('display_quips','off',10),('email_format','html',5),('email_format','text_only',10),('possible_duplicates','on',5),('possible_duplicates','off',10),('post_bug_submit_action','next_bug',5),('post_bug_submit_action','same_bug',10),('post_bug_submit_action','nothing',15),('quicksearch_fulltext','on',5),('quicksearch_fulltext','off',10),('quote_replies','quoted_reply',5),('quote_replies','simple_reply',10),('quote_replies','off',15),('requestee_cc','on',5),('requestee_cc','off',10),('state_addselfcc','always',5),('state_addselfcc','never',10),('state_addselfcc','cc_unless_role',15),('zoom_textareas','on',5),('zoom_textareas','off',10); +/*!40000 ALTER TABLE `setting_value` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `status_workflow` +-- + +DROP TABLE IF EXISTS `status_workflow`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `status_workflow` ( + `old_status` smallint(6) DEFAULT NULL, + `new_status` smallint(6) NOT NULL, + `require_comment` tinyint(4) NOT NULL DEFAULT 0, + UNIQUE KEY `status_workflow_idx` (`old_status`,`new_status`), + KEY `fk_status_workflow_new_status_bug_status_id` (`new_status`), + CONSTRAINT `fk_status_workflow_new_status_bug_status_id` FOREIGN KEY (`new_status`) REFERENCES `bug_status` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_status_workflow_old_status_bug_status_id` FOREIGN KEY (`old_status`) REFERENCES `bug_status` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `status_workflow` +-- + +LOCK TABLES `status_workflow` WRITE; +/*!40000 ALTER TABLE `status_workflow` DISABLE KEYS */; +INSERT INTO `status_workflow` VALUES (NULL,1,0),(NULL,2,0),(NULL,3,0),(1,2,0),(1,3,0),(1,4,0),(2,3,0),(2,4,0),(3,2,0),(3,4,0),(4,1,0),(4,2,0),(4,5,0),(5,1,0),(5,2,0); +/*!40000 ALTER TABLE `status_workflow` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `tag` +-- + +DROP TABLE IF EXISTS `tag`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tag` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `name` varchar(64) NOT NULL, + `user_id` mediumint(9) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `tag_user_id_idx` (`user_id`,`name`), + CONSTRAINT `fk_tag_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `tag` +-- + +LOCK TABLES `tag` WRITE; +/*!40000 ALTER TABLE `tag` DISABLE KEYS */; +/*!40000 ALTER TABLE `tag` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `tokens` +-- + +DROP TABLE IF EXISTS `tokens`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tokens` ( + `userid` mediumint(9) DEFAULT NULL, + `issuedate` datetime NOT NULL, + `token` varchar(16) NOT NULL, + `tokentype` varchar(16) NOT NULL, + `eventdata` tinytext DEFAULT NULL, + PRIMARY KEY (`token`), + KEY `tokens_userid_idx` (`userid`), + CONSTRAINT `fk_tokens_userid_profiles_userid` FOREIGN KEY (`userid`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `tokens` +-- + +LOCK TABLES `tokens` WRITE; +/*!40000 ALTER TABLE `tokens` DISABLE KEYS */; +INSERT INTO `tokens` VALUES (1,'2023-11-27 15:46:15','5HVJhRRo6t','session','edit_parameters'),(1,'2023-11-27 12:25:54','a9MgwT7N7x','session','edit_product'),(1,'2023-11-27 15:42:50','CRSwDhzaXc','session','edit_parameters'),(1,'2023-11-27 12:29:18','DXFuAIZ5GH','session','edit_product'),(1,'2023-09-20 13:13:14','ery9F3ZaAV','session','edit_user_prefs'),(1,'2023-11-27 15:44:26','gnPazrbni2','session','edit_product'),(1,'2023-11-27 15:43:10','GZT1mYgIAF','session','edit_settings'),(1,'2023-11-27 15:42:57','hYkjAGXNIj','session','add_field'),(1,'2023-11-27 15:46:35','ibDe8MPzGE','session','edit_parameters'),(1,'2023-09-20 13:13:14','oukIJJwYod','api_token',''),(1,'2023-11-27 12:26:29','PIjhZLJ29K','session','edit_product'),(1,'2023-11-27 12:23:39','pIrqNpsRDo','api_token',''),(1,'2023-11-27 15:44:36','rkyOtDBxr4','session','edit_group_controls'),(1,'2023-09-20 13:13:20','VLrgLovfH9','session','edit_user_prefs'),(1,'2023-11-27 15:45:59','xgQpxIS10M','session','edit_user_prefs'); +/*!40000 ALTER TABLE `tokens` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `ts_error` +-- + +DROP TABLE IF EXISTS `ts_error`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ts_error` ( + `error_time` int(11) NOT NULL, + `jobid` int(11) NOT NULL, + `message` varchar(255) NOT NULL, + `funcid` int(11) NOT NULL DEFAULT 0, + KEY `ts_error_funcid_idx` (`funcid`,`error_time`), + KEY `ts_error_error_time_idx` (`error_time`), + KEY `ts_error_jobid_idx` (`jobid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `ts_error` +-- + +LOCK TABLES `ts_error` WRITE; +/*!40000 ALTER TABLE `ts_error` DISABLE KEYS */; +/*!40000 ALTER TABLE `ts_error` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `ts_exitstatus` +-- + +DROP TABLE IF EXISTS `ts_exitstatus`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ts_exitstatus` ( + `jobid` int(11) NOT NULL AUTO_INCREMENT, + `funcid` int(11) NOT NULL DEFAULT 0, + `status` smallint(6) DEFAULT NULL, + `completion_time` int(11) DEFAULT NULL, + `delete_after` int(11) DEFAULT NULL, + PRIMARY KEY (`jobid`), + KEY `ts_exitstatus_funcid_idx` (`funcid`), + KEY `ts_exitstatus_delete_after_idx` (`delete_after`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `ts_exitstatus` +-- + +LOCK TABLES `ts_exitstatus` WRITE; +/*!40000 ALTER TABLE `ts_exitstatus` DISABLE KEYS */; +/*!40000 ALTER TABLE `ts_exitstatus` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `ts_funcmap` +-- + +DROP TABLE IF EXISTS `ts_funcmap`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ts_funcmap` ( + `funcid` int(11) NOT NULL AUTO_INCREMENT, + `funcname` varchar(255) NOT NULL, + PRIMARY KEY (`funcid`), + UNIQUE KEY `ts_funcmap_funcname_idx` (`funcname`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `ts_funcmap` +-- + +LOCK TABLES `ts_funcmap` WRITE; +/*!40000 ALTER TABLE `ts_funcmap` DISABLE KEYS */; +/*!40000 ALTER TABLE `ts_funcmap` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `ts_job` +-- + +DROP TABLE IF EXISTS `ts_job`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ts_job` ( + `jobid` int(11) NOT NULL AUTO_INCREMENT, + `funcid` int(11) NOT NULL, + `arg` longblob DEFAULT NULL, + `uniqkey` varchar(255) DEFAULT NULL, + `insert_time` int(11) DEFAULT NULL, + `run_after` int(11) NOT NULL, + `grabbed_until` int(11) NOT NULL, + `priority` smallint(6) DEFAULT NULL, + `coalesce` varchar(255) DEFAULT NULL, + PRIMARY KEY (`jobid`), + UNIQUE KEY `ts_job_funcid_idx` (`funcid`,`uniqkey`), + KEY `ts_job_run_after_idx` (`run_after`,`funcid`), + KEY `ts_job_coalesce_idx` (`coalesce`,`funcid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `ts_job` +-- + +LOCK TABLES `ts_job` WRITE; +/*!40000 ALTER TABLE `ts_job` DISABLE KEYS */; +/*!40000 ALTER TABLE `ts_job` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `ts_note` +-- + +DROP TABLE IF EXISTS `ts_note`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ts_note` ( + `jobid` int(11) NOT NULL, + `notekey` varchar(255) DEFAULT NULL, + `value` longblob DEFAULT NULL, + UNIQUE KEY `ts_note_jobid_idx` (`jobid`,`notekey`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `ts_note` +-- + +LOCK TABLES `ts_note` WRITE; +/*!40000 ALTER TABLE `ts_note` DISABLE KEYS */; +/*!40000 ALTER TABLE `ts_note` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_api_keys` +-- + +DROP TABLE IF EXISTS `user_api_keys`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_api_keys` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` mediumint(9) NOT NULL, + `api_key` varchar(40) NOT NULL, + `description` varchar(255) DEFAULT NULL, + `revoked` tinyint(4) NOT NULL DEFAULT 0, + `last_used` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_api_keys_api_key_idx` (`api_key`), + KEY `user_api_keys_user_id_idx` (`user_id`), + CONSTRAINT `fk_user_api_keys_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_api_keys` +-- + +LOCK TABLES `user_api_keys` WRITE; +/*!40000 ALTER TABLE `user_api_keys` DISABLE KEYS */; +INSERT INTO `user_api_keys` VALUES (1,1,'AxBntHGSL97CmoTahkey8RNyo2K65NEfJBuk5ATe','',0,NULL); +/*!40000 ALTER TABLE `user_api_keys` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_group_map` +-- + +DROP TABLE IF EXISTS `user_group_map`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_group_map` ( + `user_id` mediumint(9) NOT NULL, + `group_id` mediumint(9) NOT NULL, + `isbless` tinyint(4) NOT NULL DEFAULT 0, + `grant_type` tinyint(4) NOT NULL DEFAULT 0, + UNIQUE KEY `user_group_map_user_id_idx` (`user_id`,`group_id`,`grant_type`,`isbless`), + KEY `fk_user_group_map_group_id_groups_id` (`group_id`), + CONSTRAINT `fk_user_group_map_group_id_groups_id` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_user_group_map_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_group_map` +-- + +LOCK TABLES `user_group_map` WRITE; +/*!40000 ALTER TABLE `user_group_map` DISABLE KEYS */; +INSERT INTO `user_group_map` VALUES (1,1,0,0),(1,1,1,0),(1,3,0,0),(1,8,0,2); +/*!40000 ALTER TABLE `user_group_map` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `versions` +-- + +DROP TABLE IF EXISTS `versions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `versions` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `value` varchar(64) NOT NULL, + `product_id` smallint(6) NOT NULL, + `isactive` tinyint(4) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + UNIQUE KEY `versions_product_id_idx` (`product_id`,`value`), + CONSTRAINT `fk_versions_product_id_products_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `versions` +-- + +LOCK TABLES `versions` WRITE; +/*!40000 ALTER TABLE `versions` DISABLE KEYS */; +INSERT INTO `versions` VALUES (1,'unspecified',1,1),(2,'unspecified',2,1),(3,'9.0',2,1),(4,'9.1',2,1),(5,'unspecified',3,1); +/*!40000 ALTER TABLE `versions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `watch` +-- + +DROP TABLE IF EXISTS `watch`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `watch` ( + `watcher` mediumint(9) NOT NULL, + `watched` mediumint(9) NOT NULL, + UNIQUE KEY `watch_watcher_idx` (`watcher`,`watched`), + KEY `watch_watched_idx` (`watched`), + CONSTRAINT `fk_watch_watched_profiles_userid` FOREIGN KEY (`watched`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_watch_watcher_profiles_userid` FOREIGN KEY (`watcher`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `watch` +-- + +LOCK TABLES `watch` WRITE; +/*!40000 ALTER TABLE `watch` DISABLE KEYS */; +/*!40000 ALTER TABLE `watch` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `whine_events` +-- + +DROP TABLE IF EXISTS `whine_events`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `whine_events` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `owner_userid` mediumint(9) NOT NULL, + `subject` varchar(128) DEFAULT NULL, + `body` mediumtext DEFAULT NULL, + `mailifnobugs` tinyint(4) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `fk_whine_events_owner_userid_profiles_userid` (`owner_userid`), + CONSTRAINT `fk_whine_events_owner_userid_profiles_userid` FOREIGN KEY (`owner_userid`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `whine_events` +-- + +LOCK TABLES `whine_events` WRITE; +/*!40000 ALTER TABLE `whine_events` DISABLE KEYS */; +/*!40000 ALTER TABLE `whine_events` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `whine_queries` +-- + +DROP TABLE IF EXISTS `whine_queries`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `whine_queries` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `eventid` mediumint(9) NOT NULL, + `query_name` varchar(64) NOT NULL DEFAULT '', + `sortkey` smallint(6) NOT NULL DEFAULT 0, + `onemailperbug` tinyint(4) NOT NULL DEFAULT 0, + `title` varchar(128) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + KEY `whine_queries_eventid_idx` (`eventid`), + CONSTRAINT `fk_whine_queries_eventid_whine_events_id` FOREIGN KEY (`eventid`) REFERENCES `whine_events` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `whine_queries` +-- + +LOCK TABLES `whine_queries` WRITE; +/*!40000 ALTER TABLE `whine_queries` DISABLE KEYS */; +/*!40000 ALTER TABLE `whine_queries` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `whine_schedules` +-- + +DROP TABLE IF EXISTS `whine_schedules`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `whine_schedules` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `eventid` mediumint(9) NOT NULL, + `run_day` varchar(32) DEFAULT NULL, + `run_time` varchar(32) DEFAULT NULL, + `run_next` datetime DEFAULT NULL, + `mailto` mediumint(9) NOT NULL, + `mailto_type` smallint(6) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `whine_schedules_run_next_idx` (`run_next`), + KEY `whine_schedules_eventid_idx` (`eventid`), + CONSTRAINT `fk_whine_schedules_eventid_whine_events_id` FOREIGN KEY (`eventid`) REFERENCES `whine_events` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `whine_schedules` +-- + +LOCK TABLES `whine_schedules` WRITE; +/*!40000 ALTER TABLE `whine_schedules` DISABLE KEYS */; +/*!40000 ALTER TABLE `whine_schedules` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2023-11-27 16:56:56 diff --git a/tests/services/bugzilla.conf b/tests/services/bugzilla.conf new file mode 100644 index 00000000..c0de1250 --- /dev/null +++ b/tests/services/bugzilla.conf @@ -0,0 +1,9 @@ + + DocumentRoot /var/www/webapps/bugzilla + + AddHandler cgi-script .cgi + Options +ExecCGI + DirectoryIndex index.cgi index.html + AllowOverride All + + diff --git a/tests/services/bugzillarc b/tests/services/bugzillarc new file mode 100644 index 00000000..7f6dafaa --- /dev/null +++ b/tests/services/bugzillarc @@ -0,0 +1,2 @@ +[localhost] +api_key = AxBntHGSL97CmoTahkey8RNyo2K65NEfJBuk5ATe diff --git a/tests/services/localconfig b/tests/services/localconfig new file mode 100644 index 00000000..f3bddb99 --- /dev/null +++ b/tests/services/localconfig @@ -0,0 +1,19 @@ +$create_htaccess = 1; +$webservergroup = 'www-data'; +$use_suexec = 0; +$db_driver = 'mysql'; +$db_host = 'mariadb'; +$db_name = 'bugs'; +$db_user = 'bugs'; +$db_pass = 'secret'; +$db_port = 3306; +$db_sock = ''; +$db_check = 1; +$db_mysql_ssl_ca_file = ''; +$db_mysql_ssl_ca_path = ''; +$db_mysql_ssl_client_cert = ''; +$db_mysql_ssl_client_key = ''; +$index_html = 0; +$interdiffbin = ''; +$diffpath = '/usr/bin'; +$site_wide_secret = 'oCIbi5WC04h86lW7L8fDcPCrVjb3JNeA2St94QlQtfjZrorjKmOdeVV0feHNDeFH'; diff --git a/tests/services/params.json b/tests/services/params.json new file mode 100644 index 00000000..a0ea93ce --- /dev/null +++ b/tests/services/params.json @@ -0,0 +1,104 @@ +{ + "LDAPBaseDN" : "", + "LDAPbinddn" : "", + "LDAPfilter" : "", + "LDAPmailattribute" : "mail", + "LDAPserver" : "", + "LDAPstarttls" : "0", + "LDAPuidattribute" : "uid", + "RADIUS_NAS_IP" : "", + "RADIUS_email_suffix" : "", + "RADIUS_secret" : "", + "RADIUS_server" : "", + "ajax_user_autocompletion" : "1", + "allow_attachment_deletion" : "0", + "allow_attachment_display" : "0", + "allowbugdeletion" : "0", + "allowemailchange" : "1", + "allowuserdeletion" : "0", + "announcehtml" : "", + "attachment_base" : "", + "auth_env_email" : "", + "auth_env_id" : "", + "auth_env_realname" : "", + "chartgroup" : "editbugs", + "collapsed_comment_tags" : "obsolete, spam", + "comment_taggers_group" : "editbugs", + "commentonchange_resolution" : "0", + "commentonduplicate" : "0", + "confirmuniqueusermatch" : "1", + "cookiedomain" : "", + "cookiepath" : "/", + "createemailregexp" : ".*", + "debug_group" : "admin", + "default_search_limit" : "500", + "defaultopsys" : "", + "defaultplatform" : "", + "defaultpriority" : "---", + "defaultquery" : "resolution=---&emailassigned_to1=1&emailassigned_to2=1&emailreporter2=1&emailcc2=1&emailqa_contact2=1&emaillongdesc3=1&order=Importance&long_desc_type=substring", + "defaultseverity" : "enhancement", + "duplicate_or_move_bug_status" : "RESOLVED", + "emailregexp" : "^[\\w\\.\\+\\-=']+@[\\w\\.\\-]+\\.[\\w\\-]+$", + "emailregexpdesc" : "A legal address must contain exactly one '@', and at least one '.' after the @.", + "emailsuffix" : "", + "font_file" : "", + "globalwatchers" : "", + "inbound_proxies" : "", + "insidergroup" : "", + "last_visit_keep_days" : "10", + "letsubmitterchoosemilestone" : "1", + "letsubmitterchoosepriority" : "1", + "mail_delivery_method" : "None", + "mailfrom" : "bugzilla-daemon", + "maintainer" : "andreas@hasenkopf.xyz", + "makeproductgroups" : "0", + "max_search_results" : "10000", + "maxattachmentsize" : "1000", + "maxlocalattachment" : "0", + "maxusermatches" : "1000", + "memcached_namespace" : "bugzilla:", + "memcached_servers" : "", + "musthavemilestoneonaccept" : "0", + "mybugstemplate" : "buglist.cgi?resolution=---&emailassigned_to1=1&emailreporter1=1&emailtype1=exact&email1=%userid%", + "noresolveonopenblockers" : "0", + "or_groups" : "1", + "password_check_on_login" : "1", + "password_complexity" : "no_constraints", + "proxy_url" : "", + "querysharegroup" : "editbugs", + "quip_list_entry_control" : "open", + "rememberlogin" : "on", + "requirelogin" : "0", + "search_allow_no_criteria" : "1", + "shadowdb" : "", + "shadowdbhost" : "", + "shadowdbport" : "3306", + "shadowdbsock" : "", + "shutdownhtml" : "", + "smtp_debug" : "0", + "smtp_password" : "", + "smtp_ssl" : "0", + "smtp_username" : "", + "smtpserver" : "localhost", + "ssl_redirect" : "0", + "sslbase" : "", + "strict_isolation" : "0", + "strict_transport_security" : "off", + "timetrackinggroup" : "editbugs", + "upgrade_notification" : "latest_stable_release", + "urlbase" : "", + "use_mailer_queue" : "0", + "use_see_also" : "1", + "useclassification" : "0", + "usemenuforusers" : "0", + "useqacontact" : "0", + "user_info_class" : "CGI", + "user_verify_class" : "DB", + "usestatuswhiteboard" : "0", + "usetargetmilestone" : "0", + "usevisibilitygroups" : "0", + "utf8" : "1", + "webdotbase" : "", + "webservice_email_filter" : "0", + "whinedays" : "7" +} diff --git a/tests/utils.py b/tests/utils.py index cfa1b424..058d81bf 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -10,6 +10,7 @@ import shlex import sys +from bugzilla import Bugzilla import bugzilla._cli import tests @@ -50,6 +51,10 @@ def open_functional_bz(bzclass, url, kwargs): return bz +def open_bz(url, bzclass=Bugzilla, **kwargs): + return open_functional_bz(bzclass=bzclass, url=url, kwargs=kwargs) + + def diff_compare(inputdata, filename, expect_out=None): """Compare passed string output to contents of filename""" def _process(data): From a4b7f6dbb57674b6c336c0494d184d5314a93bf8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:43:28 +0000 Subject: [PATCH 085/106] ci: bump actions/setup-python from 4 to 5 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 771d9b72..5a592570 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -97,7 +97,7 @@ jobs: mkdir -p ~/.config/python-bugzilla/ cp tests/services/bugzillarc ~/.config/python-bugzilla/ - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From c50c0d6bf7bc0217cc056de00839fc4513137396 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:43:24 +0000 Subject: [PATCH 086/106] ci: bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5a592570..f8b8661d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -87,7 +87,7 @@ jobs: matrix: python-version: ["3.x"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install MariaDB utils run: sudo apt install --no-install-recommends -q -y mariadb-client - name: Restore DB dump From 430d96516b31455634491a6f1cb046a814ee3bae Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Thu, 13 Jun 2024 10:59:37 +0200 Subject: [PATCH 087/106] Prep for release 3.3.0 --- NEWS.md | 13 +++++++++++++ bugzilla/apiversion.py | 2 +- python-bugzilla.spec | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index e642fbf1..f3c91ab1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,18 @@ # python-bugzilla release news +## Release 3.3.0 (June, 2024) +- Expose error codes from the REST API (Stanislav Levin) +- Fixed broken link in documentation (Danilo C. L. de Paula) +- Set `Bug.weburl` that is compatible with the REST API +- Do not convert 'blocks' or 'depends' to int in `Bugzilla.build_update` (Adam Williamson) +- Use proper REST API route for getting a single bug +- Avoid duplicate entries when one id is 0 (Ricardo Branco) +- Removed unused argument from `Bugzilla.add_dict` +- Fixed API key leak (Ricardo Branco) +- Automatically include alias in include_fields in `Bugzilla._getbugs` +- Added method `Bugzilla.query_return_extra` +- cli: Support --field and --field-json for bugzilla attach + ## Release 3.2.0 (January 12, 2022) - Use soon-to-be-required Authorization header for RH bugzilla - Remove cookie auth support diff --git a/bugzilla/apiversion.py b/bugzilla/apiversion.py index 3a6d3e83..c4c11d14 100644 --- a/bugzilla/apiversion.py +++ b/bugzilla/apiversion.py @@ -4,5 +4,5 @@ # This work is licensed under the GNU GPLv2 or later. # See the COPYING file in the top-level directory. -version = "3.2.0" +version = "3.3.0" __version__ = version diff --git a/python-bugzilla.spec b/python-bugzilla.spec index fe4e459f..716fc933 100644 --- a/python-bugzilla.spec +++ b/python-bugzilla.spec @@ -1,5 +1,5 @@ Name: python-bugzilla -Version: 3.2.0 +Version: 3.3.0 Release: 1%{?dist} Summary: Python library for interacting with Bugzilla From 526e70ab692558c940fc9d1247c5b704acf9f911 Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Thu, 19 Sep 2024 17:35:56 +0200 Subject: [PATCH 088/106] Fixed issue in `Bugzilla.fix_url` In a new patch version of Python 3.12 the behavior of `urllib.parse.urlunparse` changed. This change ensures that this method works correctly with the old and new behavior of `urllib.parse.urlunparse`. --- bugzilla/base.py | 3 +++ bugzilla/bug.py | 2 +- tests/test_api_misc.py | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/bugzilla/base.py b/bugzilla/base.py index eef84aba..eb0f9244 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -155,6 +155,9 @@ def fix_url(url, force_rest=False): if force_rest: path = "rest/" + if not path.startswith("/"): + path = "/" + path + newurl = urllib.parse.urlunparse( (scheme, netloc, path, params, query, fragment)) return newurl diff --git a/bugzilla/bug.py b/bugzilla/bug.py index ec0e9c00..0a7c2d16 100644 --- a/bugzilla/bug.py +++ b/bugzilla/bug.py @@ -48,7 +48,7 @@ def _generate_weburl(self): """ parsed = urlparse(self.bugzilla.url) return urlunparse((parsed.scheme, parsed.netloc, - 'show_bug.cgi', '', 'id=%s' % self.bug_id, + '/show_bug.cgi', '', 'id=%s' % self.bug_id, '')) def __str__(self): diff --git a/tests/test_api_misc.py b/tests/test_api_misc.py index ea1f2e47..30889887 100644 --- a/tests/test_api_misc.py +++ b/tests/test_api_misc.py @@ -44,6 +44,7 @@ def test_fixurl(): "https://example.com/xmlrpc.cgi") assert (bugzilla.Bugzilla.fix_url("http://example.com/somepath.cgi") == "http://example.com/somepath.cgi") + assert bugzilla.Bugzilla.fix_url("http:///foo") == "http:///foo" def testPostTranslation(): From 5eedea31bcef0f1ba7a22eb38aba1cdd9b3d7981 Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Thu, 19 Sep 2024 16:11:17 +0200 Subject: [PATCH 089/106] Use non-deprecated argument name in test-suite --- tests/conftest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 938932b6..8c9c868d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -42,14 +42,14 @@ def pytest_addoption(parser): parser.addoption("--only-xmlrpc", action="store_true", default=False) -def pytest_ignore_collect(path, config): +def pytest_ignore_collect(collection_path, config): has_ro = config.getoption("--ro-functional") has_ro_i = config.getoption("--ro-integration") has_rw = config.getoption("--rw-functional") - base = os.path.basename(str(path)) + base = os.path.basename(str(collection_path)) is_ro = base == "test_ro_functional.py" - is_ro_i = "tests/integration/ro" in str(path) + is_ro_i = "tests/integration/ro" in str(collection_path) is_rw = base == "test_rw_functional.py" if is_ro_i and not has_ro_i: From 379bf17ec6738ec3a07c3add8659ddc7de220422 Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Fri, 20 Sep 2024 12:06:00 +0200 Subject: [PATCH 090/106] Publish package on PyPI from CI --- .github/workflows/publish.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..8ca7db69 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,32 @@ +# This workflow will publish the package on PyPI +# For more information see: https://github.com/pypa/gh-action-pypi-publish + +name: Publish +on: + release: + types: [released] + +jobs: + publish: + name: Upload release to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/python-bugzilla + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + - name: Install pypa/build + run: pip install build + - name: Build a source tarball + run: python -m build --sdist + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + skip-existing: false + verbose: false From 567cfd6ef568457629d2f08a62c5519a7473849a Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Mon, 23 Sep 2024 09:44:46 +0200 Subject: [PATCH 091/106] ci: Test against all supported Python versions --- .github/workflows/build.yml | 4 ++-- setup.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f8b8661d..5df40255 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,8 +33,8 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - # python 3.6 is for rhel/centos8 compat - python-version: ["3.6", "3.x"] + # python 3.6 is for rhel/centos8/sles15 compat + python-version: ["3.6", "3.9", "3.10", "3.11", "3.12", "3.13.0-rc.2"] steps: - uses: actions/checkout@v4 diff --git a/setup.py b/setup.py index 38ce974d..b9db594d 100755 --- a/setup.py +++ b/setup.py @@ -130,12 +130,12 @@ def _parse_requirements(fname): 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', ], packages=['bugzilla'], data_files=[('share/man/man1', ['man/bugzilla.1'])], From 5f89e286041d367dbff93af31ab628a55c33dfc5 Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Fri, 11 Oct 2024 17:10:58 +0200 Subject: [PATCH 092/106] test: Use a class to organize tests in test_backend_rest.py --- tests/test_backend_rest.py | 45 +++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/tests/test_backend_rest.py b/tests/test_backend_rest.py index fdfbd05b..45e3e780 100644 --- a/tests/test_backend_rest.py +++ b/tests/test_backend_rest.py @@ -4,32 +4,41 @@ from bugzilla._session import _BugzillaSession -def test_getbug(): - session = _BugzillaSession(url="http://example.com", +class TestGetBug: + @property + def session(self): + return _BugzillaSession(url="http://example.com", user_agent="py-bugzilla-test", sslverify=False, cert=None, tokencache={}, api_key="", is_redhat_bugzilla=False) - backend = _BackendREST(url="http://example.com", - bugzillasession=session) - def _assertion(self, *args): - self.assertion_called = True - assert args and args[0] == url + @property + def backend(self): + return _BackendREST(url="http://example.com", + bugzillasession=self.session) - setattr(backend, "_get", MethodType(_assertion, backend)) + def test_getbug__not_permissive(self): + backend = self.backend - for _ids, aliases, url in ( - (1, None, "/bug/1"), - ([1], [], "/bug/1"), - (None, "CVE-1999-0001", "/bug/CVE-1999-0001"), - ([], ["CVE-1999-0001"], "/bug/CVE-1999-0001"), - (1, "CVE-1999-0001", "/bug"), - ): - backend.assertion_called = False + def _assertion(self, *args): + self.assertion_called = True + assert args and args[0] == url - backend.bug_get(_ids, aliases, {}) + setattr(backend, "_get", MethodType(_assertion, backend)) - assert backend.assertion_called is True + for _ids, aliases, url in ( + (1, None, "/bug/1"), + ([1], [], "/bug/1"), + (None, "CVE-1999-0001", "/bug/CVE-1999-0001"), + ([], ["CVE-1999-0001"], "/bug/CVE-1999-0001"), + (1, "CVE-1999-0001", "/bug"), + ([1, 2], None, "/bug") + ): + backend.assertion_called = False + + backend.bug_get(_ids, aliases, {}) + + assert backend.assertion_called is True From 7b8d18eaba2c1ae1ba4ab107f83081dac0bdc509 Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Fri, 11 Oct 2024 17:11:28 +0200 Subject: [PATCH 093/106] fix: Emulate `permissive` on REST backend (closes #222) `_BackendREST.bug_get` cannot pass a `permissive` parameter to the server as the REST API does not honor such a parameter. With this change, permissiveness is handled inside the method: If `permissive` is false and a single ID or alias is requested, the "get" method is used and an exception gets raised in case of error. Otherwise, the "search" method is used, which may return an empty list, if the client is not authenticated or an ID or alias does not exist. --- bugzilla/_backendrest.py | 3 ++- tests/test_backend_rest.py | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/bugzilla/_backendrest.py b/bugzilla/_backendrest.py index 00b5563a..45bc4999 100644 --- a/bugzilla/_backendrest.py +++ b/bugzilla/_backendrest.py @@ -109,6 +109,7 @@ def bug_fields(self, paramdict): def bug_get(self, bug_ids, aliases, paramdict): bug_list = listify(bug_ids) alias_list = listify(aliases) + permissive = paramdict.pop("permissive", False) data = paramdict.copy() # FYI: The high-level API expects the backends to raise an exception @@ -116,7 +117,7 @@ def bug_get(self, bug_ids, aliases, paramdict): # API), but the REST API simply returns an empty search result set. # To ensure compliant behavior, the REST backend needs to use the # explicit URL to get a single bug. - if len(bug_list or []) + len(alias_list or []) == 1: + if not permissive and len(bug_list or []) + len(alias_list or []) == 1: for id_list in (bug_list, alias_list): if id_list: return self._get("/bug/%s" % id_list[0], data) diff --git a/tests/test_backend_rest.py b/tests/test_backend_rest.py index 45e3e780..14836975 100644 --- a/tests/test_backend_rest.py +++ b/tests/test_backend_rest.py @@ -42,3 +42,26 @@ def _assertion(self, *args): backend.bug_get(_ids, aliases, {}) assert backend.assertion_called is True + + def test_getbug__permissive(self): + backend = self.backend + + def _assertion(self, *args): + self.assertion_called = True + assert args and args[0] == url and args[1] == params + + setattr(backend, "_get", MethodType(_assertion, backend)) + + for _ids, aliases, url, params in ( + (1, None, "/bug", {"id": [1], "alias": None}), + ([1], [], "/bug", {"id": [1], "alias": []}), + (None, "CVE-1999-0001", "/bug", {"alias": ["CVE-1999-0001"], "id": None}), + ([], ["CVE-1999-0001"], "/bug", {"alias": ["CVE-1999-0001"], "id": []}), + (1, "CVE-1999-0001", "/bug", {"id": [1], "alias": ["CVE-1999-0001"]}), + ([1, 2], None, "/bug", {"id": [1, 2], "alias": None}) + ): + backend.assertion_called = False + + backend.bug_get(_ids, aliases, {"permissive": True}) + + assert backend.assertion_called is True From 8f77896948a3331a478864ca2005028d2d4e5f96 Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Tue, 15 Oct 2024 16:11:37 +0200 Subject: [PATCH 094/106] ci: More functional tests Migrated more functional RO tests to the new integration suite. Also, added a comment to the old tests to indicate which integration test corresponds to it. Updated the SQL fixture accordingly. And fixed a typo in the README. --- tests/integration/ro_api_test.py | 54 ++++++++++++++++++++++++++++-- tests/integration/ro_cli_test.py | 57 ++++++++++++++++++++++++++++++++ tests/services/README.md | 2 +- tests/services/bugs.sql | 57 ++++++++++++++++++-------------- tests/test_ro_functional.py | 20 +++++++++++ 5 files changed, 161 insertions(+), 29 deletions(-) diff --git a/tests/integration/ro_api_test.py b/tests/integration/ro_api_test.py index 3f096587..22ee5442 100644 --- a/tests/integration/ro_api_test.py +++ b/tests/integration/ro_api_test.py @@ -1,5 +1,7 @@ # Ignoring pytest-related warnings: # pylint: disable=redefined-outer-name,unused-argument +from xmlrpc.client import Fault + import pytest from bugzilla import BugzillaError @@ -85,7 +87,7 @@ def test_query(mocked_responses, backends): assert bugs[0].summary == "Expect the Spanish inquisition" bz = open_bz(url=TEST_URL, **backends) - query = bz.build_query(product="SUSE Linux Enterprise Server 15 SP6") + query = bz.build_query(product="SUSE Linux Enterprise Server 15 SP6", component="Containers") bugs = bz.query(query=query) assert len(bugs) == 1 @@ -94,8 +96,54 @@ def test_query(mocked_responses, backends): def test_get_bug_alias(mocked_responses, backends): + bug_id, alias = 1, "FOO-1" bz = open_bz(url=TEST_URL, **backends) - bug = bz.getbug("FOO-1") + bug = bz.getbug(alias) - assert bug.id == 1 + assert bug.id == bug_id + assert bug.bug_id == bug_id + assert bug.alias == [alias] assert bug.summary == "ZeroDivisionError in function foo_bar()" + + +def test_get_bug_alias_included_field(mocked_responses, backends): + bug_id, alias = 1, "FOO-1" + bz = open_bz(url=TEST_URL, **backends) + bug = bz.getbug(alias, include_fields=["id"]) + + assert bug.id == bug_id + assert bug.bug_id == bug_id + assert bug.alias == [alias] + assert not hasattr(bug, "summary") + + +def test_get_bug_404(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + try: + bz.getbug(666) + except Fault as error: # XMLRPC API + assert error.faultCode == 101 + except BugzillaError as error: # REST API + assert error.code == 101 + else: + raise AssertionError("No exception raised") + + +def test_get_bug_alias_404(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + try: + bz.getbug("CVE-1234-4321") + except Fault as error: # XMLRPC API + assert error.faultCode == 100 + except BugzillaError as error: # REST API + assert error.code == 100 + else: + raise AssertionError("No exception raised") + + +def test_get_bug_fields(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + fields = bz.getbugfields(names=["product"]) + assert fields == ["product"] + bz.getbugfields(names=["product", "bug_status"], force_refresh=True) + assert set(bz.bugfields) == {"product", "bug_status"} diff --git a/tests/integration/ro_cli_test.py b/tests/integration/ro_cli_test.py index f4d308d9..d5073a78 100644 --- a/tests/integration/ro_cli_test.py +++ b/tests/integration/ro_cli_test.py @@ -1,5 +1,8 @@ # Ignoring pytest-related warnings: # pylint: disable=unused-argument +import re +from urllib.parse import urljoin + from ..utils import open_bz from . import TEST_URL, TEST_PRODUCTS, TEST_SUSE_COMPONENTS, TEST_OWNER @@ -45,3 +48,57 @@ def test_query(mocked_responses, run_cli, backends): assert len(lines) == 1 assert lines[0].startswith("#2") assert "Expect the Spanish inquisition" in lines[0] + + +def test_query_full(mocked_responses, run_cli, backends): + bz = open_bz(url=TEST_URL, **backends) + out = run_cli("bugzilla query --full --bug_id 2", bzinstance=bz) + lines = out.strip().splitlines() + assert len(lines) == 5 + + for name in ('Component', 'CC', 'Blocked', 'Depends'): + assert name in out + + assert "Status Whiteboard" not in out + + +def test_query_raw(mocked_responses, run_cli, backends): + bz = open_bz(url=TEST_URL, **backends) + out = run_cli("bugzilla query --raw --bug_id 2", bzinstance=bz) + + assert "ATTRIBUTE[whiteboard]: lorem ipsum" in out + assert "ATTRIBUTE[id]: 2" in out + + +def test_query_oneline(mocked_responses, run_cli, backends): + bz = open_bz(url=TEST_URL, **backends) + out = run_cli("bugzilla query --oneline --bug_id 2", bzinstance=bz) + lines = out.strip().splitlines() + assert len(lines) == 1 + assert "python-bugzilla" in lines[0] + + +def test_query_extra(mocked_responses, run_cli, backends): + bz = open_bz(url=TEST_URL, **backends) + out = run_cli("bugzilla query --extra --bug_id 2", bzinstance=bz) + lines = out.strip().splitlines() + assert len(lines) == 5 + assert "Keywords: FooBar" in out + assert "Status Whiteboard: lorem ipsum" in out + + +def test_query_format(mocked_responses, run_cli, backends): + bz = open_bz(url=TEST_URL, **backends) + out = run_cli("bugzilla query --outputformat=\"id=%{bug_id} " + "sw=%{whiteboard:status} needinfo=%{flag:needinfo} " + "sum=%{summary}\" --bug_id 2", bzinstance=bz) + lines = out.strip().splitlines() + assert len(lines) == 1 + assert out.strip() == "id=2 sw=lorem ipsum needinfo=? sum=Expect the Spanish inquisition" + + +def test_query_url(mocked_responses, run_cli, backends): + url = urljoin(TEST_URL, "/buglist.cgi?version=9.1") + bz = open_bz(url=TEST_URL, **backends) + out = run_cli(f"bugzilla query --from-url \"{url}\"", bzinstance=bz) + assert re.search(r"#2\s+CONFIRMED", out) diff --git a/tests/services/README.md b/tests/services/README.md index 029241b8..c59174f3 100644 --- a/tests/services/README.md +++ b/tests/services/README.md @@ -47,7 +47,7 @@ Bugzilla container and edit the data in Bugzilla. Once done, one needs to dump t the file again: ```shell -$ mariadb-dump -u bugs -h 127.0.0.1 -P 3306 --password=secret bugs > bugs.qql +$ mariadb-dump -u bugs -h 127.0.0.1 -P 3306 --password=secret bugs > bugs.sql ``` ## Testing diff --git a/tests/services/bugs.sql b/tests/services/bugs.sql index c0ddf4ba..15c78d34 100644 --- a/tests/services/bugs.sql +++ b/tests/services/bugs.sql @@ -1,4 +1,5 @@ --- MariaDB dump 10.19 Distrib 10.6.12-MariaDB, for debian-linux-gnu (x86_64) +/*!999999\- enable the sandbox mode */ +-- MariaDB dump 10.19 Distrib 10.6.18-MariaDB, for debian-linux-gnu (x86_64) -- -- Host: 127.0.0.1 Database: bugs -- ------------------------------------------------------ @@ -104,7 +105,7 @@ CREATE TABLE `audit_log` ( LOCK TABLES `audit_log` WRITE; /*!40000 ALTER TABLE `audit_log` DISABLE KEYS */; -INSERT INTO `audit_log` VALUES (NULL,'Bugzilla::Field',1,'__create__',NULL,'bug_id','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',2,'__create__',NULL,'short_desc','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',3,'__create__',NULL,'classification','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',4,'__create__',NULL,'product','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',5,'__create__',NULL,'version','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',6,'__create__',NULL,'rep_platform','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',7,'__create__',NULL,'bug_file_loc','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',8,'__create__',NULL,'op_sys','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',9,'__create__',NULL,'bug_status','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',10,'__create__',NULL,'status_whiteboard','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',11,'__create__',NULL,'keywords','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',12,'__create__',NULL,'resolution','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',13,'__create__',NULL,'bug_severity','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',14,'__create__',NULL,'priority','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',15,'__create__',NULL,'component','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',16,'__create__',NULL,'assigned_to','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',17,'__create__',NULL,'reporter','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',18,'__create__',NULL,'qa_contact','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',19,'__create__',NULL,'assigned_to_realname','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',20,'__create__',NULL,'reporter_realname','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',21,'__create__',NULL,'qa_contact_realname','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',22,'__create__',NULL,'cc','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',23,'__create__',NULL,'dependson','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',24,'__create__',NULL,'blocked','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',25,'__create__',NULL,'attachments.description','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',26,'__create__',NULL,'attachments.filename','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',27,'__create__',NULL,'attachments.mimetype','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',28,'__create__',NULL,'attachments.ispatch','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',29,'__create__',NULL,'attachments.isobsolete','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',30,'__create__',NULL,'attachments.isprivate','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',31,'__create__',NULL,'attachments.submitter','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',32,'__create__',NULL,'target_milestone','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',33,'__create__',NULL,'creation_ts','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',34,'__create__',NULL,'delta_ts','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',35,'__create__',NULL,'longdesc','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',36,'__create__',NULL,'longdescs.isprivate','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',37,'__create__',NULL,'longdescs.count','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',38,'__create__',NULL,'alias','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',39,'__create__',NULL,'everconfirmed','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',40,'__create__',NULL,'reporter_accessible','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',41,'__create__',NULL,'cclist_accessible','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',42,'__create__',NULL,'bug_group','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',43,'__create__',NULL,'estimated_time','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',44,'__create__',NULL,'remaining_time','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',45,'__create__',NULL,'deadline','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',46,'__create__',NULL,'commenter','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',47,'__create__',NULL,'flagtypes.name','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',48,'__create__',NULL,'requestees.login_name','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',49,'__create__',NULL,'setters.login_name','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',50,'__create__',NULL,'work_time','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',51,'__create__',NULL,'percentage_complete','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',52,'__create__',NULL,'content','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',53,'__create__',NULL,'attach_data.thedata','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',54,'__create__',NULL,'owner_idle_time','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',55,'__create__',NULL,'see_also','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',56,'__create__',NULL,'tag','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',57,'__create__',NULL,'last_visit_ts','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',58,'__create__',NULL,'comment_tag','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',59,'__create__',NULL,'days_elapsed','2023-09-20 13:12:35'),(NULL,'Bugzilla::Classification',1,'__create__',NULL,'Unclassified','2023-09-20 13:12:35'),(NULL,'Bugzilla::Group',1,'__create__',NULL,'admin','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',2,'__create__',NULL,'tweakparams','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',3,'__create__',NULL,'editusers','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',4,'__create__',NULL,'creategroups','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',5,'__create__',NULL,'editclassifications','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',6,'__create__',NULL,'editcomponents','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',7,'__create__',NULL,'editkeywords','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',8,'__create__',NULL,'editbugs','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',9,'__create__',NULL,'canconfirm','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',10,'__create__',NULL,'bz_canusewhineatothers','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',11,'__create__',NULL,'bz_canusewhines','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',12,'__create__',NULL,'bz_sudoers','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',13,'__create__',NULL,'bz_sudo_protect','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',14,'__create__',NULL,'bz_quip_moderators','2023-09-20 13:12:40'),(NULL,'Bugzilla::User',1,'__create__',NULL,'andreas@hasenkopf.xyz','2023-09-20 13:12:55'),(NULL,'Bugzilla::Product',1,'__create__',NULL,'TestProduct','2023-09-20 13:12:55'),(NULL,'Bugzilla::Version',1,'__create__',NULL,'unspecified','2023-09-20 13:12:55'),(NULL,'Bugzilla::Milestone',1,'__create__',NULL,'---','2023-09-20 13:12:55'),(NULL,'Bugzilla::Component',1,'__create__',NULL,'TestComponent','2023-09-20 13:12:55'),(1,'Bugzilla::Product',2,'__create__',NULL,'Red Hat Enterprise Linux 9','2023-11-27 12:25:54'),(1,'Bugzilla::Version',2,'__create__',NULL,'unspecified','2023-11-27 12:25:54'),(1,'Bugzilla::Milestone',2,'__create__',NULL,'---','2023-11-27 12:25:54'),(1,'Bugzilla::Component',2,'__create__',NULL,'python-bugzilla','2023-11-27 12:25:54'),(1,'Bugzilla::Version',3,'__create__',NULL,'9.0','2023-11-27 12:26:06'),(1,'Bugzilla::Version',4,'__create__',NULL,'9.1','2023-11-27 12:26:14'),(1,'Bugzilla::Product',3,'__create__',NULL,'SUSE Linux Enterprise Server 15 SP6','2023-11-27 12:29:18'),(1,'Bugzilla::Version',5,'__create__',NULL,'unspecified','2023-11-27 12:29:18'),(1,'Bugzilla::Milestone',3,'__create__',NULL,'---','2023-11-27 12:29:18'),(1,'Bugzilla::Component',3,'__create__',NULL,'Kernel','2023-11-27 12:29:18'),(1,'Bugzilla::Component',4,'__create__',NULL,'Containers','2023-11-27 12:29:46'); +INSERT INTO `audit_log` VALUES (NULL,'Bugzilla::Field',1,'__create__',NULL,'bug_id','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',2,'__create__',NULL,'short_desc','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',3,'__create__',NULL,'classification','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',4,'__create__',NULL,'product','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',5,'__create__',NULL,'version','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',6,'__create__',NULL,'rep_platform','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',7,'__create__',NULL,'bug_file_loc','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',8,'__create__',NULL,'op_sys','2023-09-20 13:12:34'),(NULL,'Bugzilla::Field',9,'__create__',NULL,'bug_status','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',10,'__create__',NULL,'status_whiteboard','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',11,'__create__',NULL,'keywords','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',12,'__create__',NULL,'resolution','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',13,'__create__',NULL,'bug_severity','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',14,'__create__',NULL,'priority','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',15,'__create__',NULL,'component','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',16,'__create__',NULL,'assigned_to','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',17,'__create__',NULL,'reporter','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',18,'__create__',NULL,'qa_contact','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',19,'__create__',NULL,'assigned_to_realname','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',20,'__create__',NULL,'reporter_realname','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',21,'__create__',NULL,'qa_contact_realname','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',22,'__create__',NULL,'cc','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',23,'__create__',NULL,'dependson','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',24,'__create__',NULL,'blocked','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',25,'__create__',NULL,'attachments.description','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',26,'__create__',NULL,'attachments.filename','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',27,'__create__',NULL,'attachments.mimetype','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',28,'__create__',NULL,'attachments.ispatch','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',29,'__create__',NULL,'attachments.isobsolete','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',30,'__create__',NULL,'attachments.isprivate','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',31,'__create__',NULL,'attachments.submitter','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',32,'__create__',NULL,'target_milestone','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',33,'__create__',NULL,'creation_ts','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',34,'__create__',NULL,'delta_ts','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',35,'__create__',NULL,'longdesc','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',36,'__create__',NULL,'longdescs.isprivate','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',37,'__create__',NULL,'longdescs.count','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',38,'__create__',NULL,'alias','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',39,'__create__',NULL,'everconfirmed','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',40,'__create__',NULL,'reporter_accessible','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',41,'__create__',NULL,'cclist_accessible','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',42,'__create__',NULL,'bug_group','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',43,'__create__',NULL,'estimated_time','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',44,'__create__',NULL,'remaining_time','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',45,'__create__',NULL,'deadline','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',46,'__create__',NULL,'commenter','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',47,'__create__',NULL,'flagtypes.name','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',48,'__create__',NULL,'requestees.login_name','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',49,'__create__',NULL,'setters.login_name','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',50,'__create__',NULL,'work_time','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',51,'__create__',NULL,'percentage_complete','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',52,'__create__',NULL,'content','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',53,'__create__',NULL,'attach_data.thedata','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',54,'__create__',NULL,'owner_idle_time','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',55,'__create__',NULL,'see_also','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',56,'__create__',NULL,'tag','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',57,'__create__',NULL,'last_visit_ts','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',58,'__create__',NULL,'comment_tag','2023-09-20 13:12:35'),(NULL,'Bugzilla::Field',59,'__create__',NULL,'days_elapsed','2023-09-20 13:12:35'),(NULL,'Bugzilla::Classification',1,'__create__',NULL,'Unclassified','2023-09-20 13:12:35'),(NULL,'Bugzilla::Group',1,'__create__',NULL,'admin','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',2,'__create__',NULL,'tweakparams','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',3,'__create__',NULL,'editusers','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',4,'__create__',NULL,'creategroups','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',5,'__create__',NULL,'editclassifications','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',6,'__create__',NULL,'editcomponents','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',7,'__create__',NULL,'editkeywords','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',8,'__create__',NULL,'editbugs','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',9,'__create__',NULL,'canconfirm','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',10,'__create__',NULL,'bz_canusewhineatothers','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',11,'__create__',NULL,'bz_canusewhines','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',12,'__create__',NULL,'bz_sudoers','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',13,'__create__',NULL,'bz_sudo_protect','2023-09-20 13:12:40'),(NULL,'Bugzilla::Group',14,'__create__',NULL,'bz_quip_moderators','2023-09-20 13:12:40'),(NULL,'Bugzilla::User',1,'__create__',NULL,'andreas@hasenkopf.xyz','2023-09-20 13:12:55'),(NULL,'Bugzilla::Product',1,'__create__',NULL,'TestProduct','2023-09-20 13:12:55'),(NULL,'Bugzilla::Version',1,'__create__',NULL,'unspecified','2023-09-20 13:12:55'),(NULL,'Bugzilla::Milestone',1,'__create__',NULL,'---','2023-09-20 13:12:55'),(NULL,'Bugzilla::Component',1,'__create__',NULL,'TestComponent','2023-09-20 13:12:55'),(1,'Bugzilla::Product',2,'__create__',NULL,'Red Hat Enterprise Linux 9','2023-11-27 12:25:54'),(1,'Bugzilla::Version',2,'__create__',NULL,'unspecified','2023-11-27 12:25:54'),(1,'Bugzilla::Milestone',2,'__create__',NULL,'---','2023-11-27 12:25:54'),(1,'Bugzilla::Component',2,'__create__',NULL,'python-bugzilla','2023-11-27 12:25:54'),(1,'Bugzilla::Version',3,'__create__',NULL,'9.0','2023-11-27 12:26:06'),(1,'Bugzilla::Version',4,'__create__',NULL,'9.1','2023-11-27 12:26:14'),(1,'Bugzilla::Product',3,'__create__',NULL,'SUSE Linux Enterprise Server 15 SP6','2023-11-27 12:29:18'),(1,'Bugzilla::Version',5,'__create__',NULL,'unspecified','2023-11-27 12:29:18'),(1,'Bugzilla::Milestone',3,'__create__',NULL,'---','2023-11-27 12:29:18'),(1,'Bugzilla::Component',3,'__create__',NULL,'Kernel','2023-11-27 12:29:18'),(1,'Bugzilla::Component',4,'__create__',NULL,'Containers','2023-11-27 12:29:46'),(1,'Bugzilla::Keyword',1,'__create__',NULL,'FooBar','2024-10-15 13:05:27'),(1,'Bugzilla::Keyword',2,'__create__',NULL,'LoremIpsum','2024-10-15 13:05:52'),(1,'Bugzilla::FlagType',1,'__create__',NULL,'needinfo','2024-10-15 13:26:28'),(1,'Bugzilla::User',2,'__create__',NULL,'nemo@example.com','2024-10-15 13:28:58'); /*!40000 ALTER TABLE `audit_log` ENABLE KEYS */; UNLOCK TABLES; @@ -266,7 +267,7 @@ CREATE TABLE `bug_user_last_visit` ( KEY `fk_bug_user_last_visit_bug_id_bugs_bug_id` (`bug_id`), CONSTRAINT `fk_bug_user_last_visit_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_bug_user_last_visit_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -275,7 +276,7 @@ CREATE TABLE `bug_user_last_visit` ( LOCK TABLES `bug_user_last_visit` WRITE; /*!40000 ALTER TABLE `bug_user_last_visit` DISABLE KEYS */; -INSERT INTO `bug_user_last_visit` VALUES (1,1,1,'2023-11-27 15:53:08'),(2,1,2,'2023-11-27 15:38:47'); +INSERT INTO `bug_user_last_visit` VALUES (1,1,1,'2024-10-15 14:00:54'),(2,1,2,'2024-10-15 14:00:49'),(3,1,3,'2024-10-15 13:45:42'); /*!40000 ALTER TABLE `bug_user_last_visit` ENABLE KEYS */; UNLOCK TABLES; @@ -333,7 +334,7 @@ CREATE TABLE `bugs` ( CONSTRAINT `fk_bugs_product_id_products_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON UPDATE CASCADE, CONSTRAINT `fk_bugs_qa_contact_profiles_userid` FOREIGN KEY (`qa_contact`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE, CONSTRAINT `fk_bugs_reporter_profiles_userid` FOREIGN KEY (`reporter`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -342,7 +343,7 @@ CREATE TABLE `bugs` ( LOCK TABLES `bugs` WRITE; /*!40000 ALTER TABLE `bugs` DISABLE KEYS */; -INSERT INTO `bugs` VALUES (1,1,'','major','IN_PROGRESS','2023-11-27 15:35:33','2023-11-27 15:53:04','ZeroDivisionError in function foo_bar()','Linux','---',3,'PC',1,'unspecified',4,'','---',NULL,'AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:L','2023-11-27 15:53:04',1,1,1,0.00,0.00,NULL),(2,1,'','enhancement','CONFIRMED','2023-11-27 15:38:45','2023-11-27 15:38:45','Expect the Spanish inquisition','Linux','---',2,'PC',1,'9.1',2,'','---',NULL,'','2023-11-27 15:38:45',1,1,1,0.00,0.00,NULL); +INSERT INTO `bugs` VALUES (1,1,'','major','IN_PROGRESS','2023-11-27 15:35:33','2023-11-27 15:53:04','ZeroDivisionError in function foo_bar()','Linux','---',3,'PC',1,'unspecified',4,'','---',NULL,'AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:L','2023-11-27 15:53:04',1,1,1,0.00,0.00,NULL),(2,1,'','enhancement','CONFIRMED','2023-11-27 15:38:45','2024-10-15 13:29:13','Expect the Spanish inquisition','Linux','---',2,'PC',1,'9.1',2,'','---',NULL,'lorem ipsum','2024-10-15 13:29:13',1,1,1,0.00,0.00,NULL),(3,1,'','enhancement','CONFIRMED','2024-10-15 13:45:40','2024-10-15 13:45:40','Kernel Panic in the Discothek','Linux','---',3,'PC',1,'unspecified',3,'','---',NULL,'','2024-10-15 13:45:40',1,1,1,0.00,0.00,NULL); /*!40000 ALTER TABLE `bugs` ENABLE KEYS */; UNLOCK TABLES; @@ -377,7 +378,7 @@ CREATE TABLE `bugs_activity` ( CONSTRAINT `fk_bugs_activity_comment_id_longdescs_comment_id` FOREIGN KEY (`comment_id`) REFERENCES `longdescs` (`comment_id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_bugs_activity_fieldid_fielddefs_id` FOREIGN KEY (`fieldid`) REFERENCES `fielddefs` (`id`) ON UPDATE CASCADE, CONSTRAINT `fk_bugs_activity_who_profiles_userid` FOREIGN KEY (`who`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -386,7 +387,7 @@ CREATE TABLE `bugs_activity` ( LOCK TABLES `bugs_activity` WRITE; /*!40000 ALTER TABLE `bugs_activity` DISABLE KEYS */; -INSERT INTO `bugs_activity` VALUES (1,1,NULL,1,'2023-11-27 15:45:09',9,'IN_PROGRESS','CONFIRMED',NULL),(2,1,NULL,1,'2023-11-27 15:47:58',10,'AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:L','',NULL),(3,1,NULL,1,'2023-11-27 15:53:04',38,'FOO-1','',NULL); +INSERT INTO `bugs_activity` VALUES (1,1,NULL,1,'2023-11-27 15:45:09',9,'IN_PROGRESS','CONFIRMED',NULL),(2,1,NULL,1,'2023-11-27 15:47:58',10,'AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:L','',NULL),(3,1,NULL,1,'2023-11-27 15:53:04',38,'FOO-1','',NULL),(4,2,NULL,1,'2024-10-15 13:08:14',10,'lorem ipsum','',NULL),(5,2,NULL,1,'2024-10-15 13:08:14',11,'FooBar','',NULL),(6,2,NULL,1,'2024-10-15 13:29:13',47,'needinfo?(nemo@example.com)','',NULL),(7,2,NULL,1,'2024-10-15 13:29:13',22,'nemo@example.com','',NULL); /*!40000 ALTER TABLE `bugs_activity` ENABLE KEYS */; UNLOCK TABLES; @@ -442,7 +443,7 @@ CREATE TABLE `bugs_fulltext` ( LOCK TABLES `bugs_fulltext` WRITE; /*!40000 ALTER TABLE `bugs_fulltext` DISABLE KEYS */; -INSERT INTO `bugs_fulltext` VALUES (1,'ZeroDivisionError in function foo_bar()','Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\nAt vero eos et accusam et justo duo dolores et ea rebum.\nStet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.','Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\nAt vero eos et accusam et justo duo dolores et ea rebum.\nStet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.'),(2,'Expect the Spanish inquisition','Nobody expects the Spanish Inquisition! \n\nOur chief weapon is surprise, surprise and fear, fear and surprise. \n\nOur two weapons are fear and surprise, and ruthless efficiency. \n\nOur three weapons are fear and surprise and ruthless efficiency and an almost fanatical dedication to the pope.','Nobody expects the Spanish Inquisition! \n\nOur chief weapon is surprise, surprise and fear, fear and surprise. \n\nOur two weapons are fear and surprise, and ruthless efficiency. \n\nOur three weapons are fear and surprise and ruthless efficiency and an almost fanatical dedication to the pope.'); +INSERT INTO `bugs_fulltext` VALUES (1,'ZeroDivisionError in function foo_bar()','Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\nAt vero eos et accusam et justo duo dolores et ea rebum.\nStet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.','Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\nAt vero eos et accusam et justo duo dolores et ea rebum.\nStet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.'),(2,'Expect the Spanish inquisition','Nobody expects the Spanish Inquisition! \n\nOur chief weapon is surprise, surprise and fear, fear and surprise. \n\nOur two weapons are fear and surprise, and ruthless efficiency. \n\nOur three weapons are fear and surprise and ruthless efficiency and an almost fanatical dedication to the pope.','Nobody expects the Spanish Inquisition! \n\nOur chief weapon is surprise, surprise and fear, fear and surprise. \n\nOur two weapons are fear and surprise, and ruthless efficiency. \n\nOur three weapons are fear and surprise and ruthless efficiency and an almost fanatical dedication to the pope.'),(3,'Kernel Panic in the Discothek','lorem ipsum dolor sit amet','lorem ipsum dolor sit amet'); /*!40000 ALTER TABLE `bugs_fulltext` ENABLE KEYS */; UNLOCK TABLES; @@ -518,6 +519,7 @@ CREATE TABLE `cc` ( LOCK TABLES `cc` WRITE; /*!40000 ALTER TABLE `cc` DISABLE KEYS */; +INSERT INTO `cc` VALUES (2,2); /*!40000 ALTER TABLE `cc` ENABLE KEYS */; UNLOCK TABLES; @@ -710,7 +712,7 @@ CREATE TABLE `email_setting` ( LOCK TABLES `email_setting` WRITE; /*!40000 ALTER TABLE `email_setting` DISABLE KEYS */; -INSERT INTO `email_setting` VALUES (1,0,0),(1,0,1),(1,0,2),(1,0,3),(1,0,4),(1,0,5),(1,0,6),(1,0,7),(1,0,9),(1,0,10),(1,0,11),(1,0,50),(1,1,0),(1,1,1),(1,1,2),(1,1,3),(1,1,4),(1,1,5),(1,1,6),(1,1,7),(1,1,9),(1,1,10),(1,1,11),(1,1,50),(1,2,0),(1,2,1),(1,2,2),(1,2,3),(1,2,4),(1,2,5),(1,2,6),(1,2,7),(1,2,8),(1,2,9),(1,2,10),(1,2,11),(1,2,50),(1,3,0),(1,3,1),(1,3,2),(1,3,3),(1,3,4),(1,3,5),(1,3,6),(1,3,7),(1,3,9),(1,3,10),(1,3,11),(1,3,50),(1,5,0),(1,5,1),(1,5,2),(1,5,3),(1,5,4),(1,5,5),(1,5,6),(1,5,7),(1,5,9),(1,5,10),(1,5,11),(1,5,50),(1,100,100),(1,100,101); +INSERT INTO `email_setting` VALUES (1,0,0),(1,0,1),(1,0,2),(1,0,3),(1,0,4),(1,0,5),(1,0,6),(1,0,7),(1,0,9),(1,0,10),(1,0,11),(1,0,50),(1,1,0),(1,1,1),(1,1,2),(1,1,3),(1,1,4),(1,1,5),(1,1,6),(1,1,7),(1,1,9),(1,1,10),(1,1,11),(1,1,50),(1,2,0),(1,2,1),(1,2,2),(1,2,3),(1,2,4),(1,2,5),(1,2,6),(1,2,7),(1,2,8),(1,2,9),(1,2,10),(1,2,11),(1,2,50),(1,3,0),(1,3,1),(1,3,2),(1,3,3),(1,3,4),(1,3,5),(1,3,6),(1,3,7),(1,3,9),(1,3,10),(1,3,11),(1,3,50),(1,5,0),(1,5,1),(1,5,2),(1,5,3),(1,5,4),(1,5,5),(1,5,6),(1,5,7),(1,5,9),(1,5,10),(1,5,11),(1,5,50),(1,100,100),(1,100,101),(2,0,0),(2,0,1),(2,0,2),(2,0,3),(2,0,4),(2,0,5),(2,0,6),(2,0,7),(2,0,9),(2,0,10),(2,0,11),(2,0,50),(2,1,0),(2,1,1),(2,1,2),(2,1,3),(2,1,4),(2,1,5),(2,1,6),(2,1,7),(2,1,9),(2,1,10),(2,1,11),(2,1,50),(2,2,0),(2,2,1),(2,2,2),(2,2,3),(2,2,4),(2,2,5),(2,2,6),(2,2,7),(2,2,8),(2,2,9),(2,2,10),(2,2,11),(2,2,50),(2,3,0),(2,3,1),(2,3,2),(2,3,3),(2,3,4),(2,3,5),(2,3,6),(2,3,7),(2,3,9),(2,3,10),(2,3,11),(2,3,50),(2,5,0),(2,5,1),(2,5,2),(2,5,3),(2,5,4),(2,5,5),(2,5,6),(2,5,7),(2,5,9),(2,5,10),(2,5,11),(2,5,50),(2,100,100),(2,100,101); /*!40000 ALTER TABLE `email_setting` ENABLE KEYS */; UNLOCK TABLES; @@ -838,6 +840,7 @@ CREATE TABLE `flaginclusions` ( LOCK TABLES `flaginclusions` WRITE; /*!40000 ALTER TABLE `flaginclusions` DISABLE KEYS */; +INSERT INTO `flaginclusions` VALUES (1,NULL,NULL); /*!40000 ALTER TABLE `flaginclusions` ENABLE KEYS */; UNLOCK TABLES; @@ -869,7 +872,7 @@ CREATE TABLE `flags` ( CONSTRAINT `fk_flags_requestee_id_profiles_userid` FOREIGN KEY (`requestee_id`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE, CONSTRAINT `fk_flags_setter_id_profiles_userid` FOREIGN KEY (`setter_id`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE, CONSTRAINT `fk_flags_type_id_flagtypes_id` FOREIGN KEY (`type_id`) REFERENCES `flagtypes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -878,6 +881,7 @@ CREATE TABLE `flags` ( LOCK TABLES `flags` WRITE; /*!40000 ALTER TABLE `flags` DISABLE KEYS */; +INSERT INTO `flags` VALUES (1,1,'?',2,NULL,'2024-10-15 13:29:13','2024-10-15 13:29:13',1,2); /*!40000 ALTER TABLE `flags` ENABLE KEYS */; UNLOCK TABLES; @@ -906,7 +910,7 @@ CREATE TABLE `flagtypes` ( KEY `fk_flagtypes_grant_group_id_groups_id` (`grant_group_id`), CONSTRAINT `fk_flagtypes_grant_group_id_groups_id` FOREIGN KEY (`grant_group_id`) REFERENCES `groups` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, CONSTRAINT `fk_flagtypes_request_group_id_groups_id` FOREIGN KEY (`request_group_id`) REFERENCES `groups` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -915,6 +919,7 @@ CREATE TABLE `flagtypes` ( LOCK TABLES `flagtypes` WRITE; /*!40000 ALTER TABLE `flagtypes` DISABLE KEYS */; +INSERT INTO `flagtypes` VALUES (1,'needinfo','Need more Info','','b',1,1,1,1,0,NULL,NULL); /*!40000 ALTER TABLE `flagtypes` ENABLE KEYS */; UNLOCK TABLES; @@ -1022,7 +1027,7 @@ CREATE TABLE `keyworddefs` ( `description` mediumtext NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `keyworddefs_name_idx` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1031,6 +1036,7 @@ CREATE TABLE `keyworddefs` ( LOCK TABLES `keyworddefs` WRITE; /*!40000 ALTER TABLE `keyworddefs` DISABLE KEYS */; +INSERT INTO `keyworddefs` VALUES (1,'FooBar','This needs no explanation'),(2,'LoremIpsum','dolor sit amet ...'); /*!40000 ALTER TABLE `keyworddefs` ENABLE KEYS */; UNLOCK TABLES; @@ -1057,6 +1063,7 @@ CREATE TABLE `keywords` ( LOCK TABLES `keywords` WRITE; /*!40000 ALTER TABLE `keywords` DISABLE KEYS */; +INSERT INTO `keywords` VALUES (2,1); /*!40000 ALTER TABLE `keywords` ENABLE KEYS */; UNLOCK TABLES; @@ -1110,7 +1117,7 @@ CREATE TABLE `logincookies` ( LOCK TABLES `logincookies` WRITE; /*!40000 ALTER TABLE `logincookies` DISABLE KEYS */; -INSERT INTO `logincookies` VALUES ('Ypt6rPqHjG',1,NULL,'2023-11-27 15:53:08'); +INSERT INTO `logincookies` VALUES ('StQdHXDOZ2',1,NULL,'2024-10-15 14:02:53'); /*!40000 ALTER TABLE `logincookies` ENABLE KEYS */; UNLOCK TABLES; @@ -1138,7 +1145,7 @@ CREATE TABLE `longdescs` ( KEY `longdescs_bug_when_idx` (`bug_when`), CONSTRAINT `fk_longdescs_bug_id_bugs_bug_id` FOREIGN KEY (`bug_id`) REFERENCES `bugs` (`bug_id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_longdescs_who_profiles_userid` FOREIGN KEY (`who`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1147,7 +1154,7 @@ CREATE TABLE `longdescs` ( LOCK TABLES `longdescs` WRITE; /*!40000 ALTER TABLE `longdescs` DISABLE KEYS */; -INSERT INTO `longdescs` VALUES (1,1,1,'2023-11-27 15:35:33',0.00,'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\nAt vero eos et accusam et justo duo dolores et ea rebum.',0,0,0,NULL),(2,1,1,'2023-11-27 15:37:05',0.00,'Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.',0,0,0,NULL),(3,2,1,'2023-11-27 15:38:45',0.00,'Nobody expects the Spanish Inquisition! \n\nOur chief weapon is surprise, surprise and fear, fear and surprise. \n\nOur two weapons are fear and surprise, and ruthless efficiency. \n\nOur three weapons are fear and surprise and ruthless efficiency and an almost fanatical dedication to the pope.',0,0,0,NULL); +INSERT INTO `longdescs` VALUES (1,1,1,'2023-11-27 15:35:33',0.00,'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\nAt vero eos et accusam et justo duo dolores et ea rebum.',0,0,0,NULL),(2,1,1,'2023-11-27 15:37:05',0.00,'Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.',0,0,0,NULL),(3,2,1,'2023-11-27 15:38:45',0.00,'Nobody expects the Spanish Inquisition! \n\nOur chief weapon is surprise, surprise and fear, fear and surprise. \n\nOur two weapons are fear and surprise, and ruthless efficiency. \n\nOur three weapons are fear and surprise and ruthless efficiency and an almost fanatical dedication to the pope.',0,0,0,NULL),(4,3,1,'2024-10-15 13:45:40',0.00,'lorem ipsum dolor sit amet',0,0,0,NULL); /*!40000 ALTER TABLE `longdescs` ENABLE KEYS */; UNLOCK TABLES; @@ -1474,7 +1481,7 @@ CREATE TABLE `profile_search` ( PRIMARY KEY (`id`), KEY `profile_search_user_id_idx` (`user_id`), CONSTRAINT `fk_profile_search_user_id_profiles_userid` FOREIGN KEY (`user_id`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1483,7 +1490,7 @@ CREATE TABLE `profile_search` ( LOCK TABLES `profile_search` WRITE; /*!40000 ALTER TABLE `profile_search` DISABLE KEYS */; -INSERT INTO `profile_search` VALUES (1,1,'1','bug_status,priority,assigned_to,bug_id'); +INSERT INTO `profile_search` VALUES (1,1,'1','bug_status,priority,assigned_to,bug_id'),(2,1,'1,2','priority,bug_severity'),(3,1,'2','bug_status,priority,assigned_to,bug_id'); /*!40000 ALTER TABLE `profile_search` ENABLE KEYS */; UNLOCK TABLES; @@ -1535,7 +1542,7 @@ CREATE TABLE `profiles` ( PRIMARY KEY (`userid`), UNIQUE KEY `profiles_login_name_idx` (`login_name`), UNIQUE KEY `profiles_extern_id_idx` (`extern_id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1544,7 +1551,7 @@ CREATE TABLE `profiles` ( LOCK TABLES `profiles` WRITE; /*!40000 ALTER TABLE `profiles` DISABLE KEYS */; -INSERT INTO `profiles` VALUES (1,'andreas@hasenkopf.xyz','2207pp7o,ialUTtf7x78ge5SbbN7+W+1lXGJBXmMlYt26C1egd4g{SHA-256}','Andreas','',0,1,NULL,1,'2023-11-27 00:00:00'); +INSERT INTO `profiles` VALUES (1,'andreas@hasenkopf.xyz','2207pp7o,ialUTtf7x78ge5SbbN7+W+1lXGJBXmMlYt26C1egd4g{SHA-256}','Andreas','',0,1,NULL,1,'2024-10-15 00:00:00'),(2,'nemo@example.com','rimPrF6O,Y0jPDDD1IeOR5myBbCCkt5rW36hOlVe7k/IH8wG513Y{SHA-256}','Nemo','',1,1,NULL,1,NULL); /*!40000 ALTER TABLE `profiles` ENABLE KEYS */; UNLOCK TABLES; @@ -1571,7 +1578,7 @@ CREATE TABLE `profiles_activity` ( CONSTRAINT `fk_profiles_activity_fieldid_fielddefs_id` FOREIGN KEY (`fieldid`) REFERENCES `fielddefs` (`id`) ON UPDATE CASCADE, CONSTRAINT `fk_profiles_activity_userid_profiles_userid` FOREIGN KEY (`userid`) REFERENCES `profiles` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_profiles_activity_who_profiles_userid` FOREIGN KEY (`who`) REFERENCES `profiles` (`userid`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1580,7 +1587,7 @@ CREATE TABLE `profiles_activity` ( LOCK TABLES `profiles_activity` WRITE; /*!40000 ALTER TABLE `profiles_activity` DISABLE KEYS */; -INSERT INTO `profiles_activity` VALUES (1,1,1,'2023-09-20 13:12:55',33,NULL,'2023-09-20 13:12:55'); +INSERT INTO `profiles_activity` VALUES (1,1,1,'2023-09-20 13:12:55',33,NULL,'2023-09-20 13:12:55'),(2,2,1,'2024-10-15 13:28:58',33,NULL,'2024-10-15 13:28:58'); /*!40000 ALTER TABLE `profiles_activity` ENABLE KEYS */; UNLOCK TABLES; @@ -1916,7 +1923,7 @@ CREATE TABLE `tokens` ( LOCK TABLES `tokens` WRITE; /*!40000 ALTER TABLE `tokens` DISABLE KEYS */; -INSERT INTO `tokens` VALUES (1,'2023-11-27 15:46:15','5HVJhRRo6t','session','edit_parameters'),(1,'2023-11-27 12:25:54','a9MgwT7N7x','session','edit_product'),(1,'2023-11-27 15:42:50','CRSwDhzaXc','session','edit_parameters'),(1,'2023-11-27 12:29:18','DXFuAIZ5GH','session','edit_product'),(1,'2023-09-20 13:13:14','ery9F3ZaAV','session','edit_user_prefs'),(1,'2023-11-27 15:44:26','gnPazrbni2','session','edit_product'),(1,'2023-11-27 15:43:10','GZT1mYgIAF','session','edit_settings'),(1,'2023-11-27 15:42:57','hYkjAGXNIj','session','add_field'),(1,'2023-11-27 15:46:35','ibDe8MPzGE','session','edit_parameters'),(1,'2023-09-20 13:13:14','oukIJJwYod','api_token',''),(1,'2023-11-27 12:26:29','PIjhZLJ29K','session','edit_product'),(1,'2023-11-27 12:23:39','pIrqNpsRDo','api_token',''),(1,'2023-11-27 15:44:36','rkyOtDBxr4','session','edit_group_controls'),(1,'2023-09-20 13:13:20','VLrgLovfH9','session','edit_user_prefs'),(1,'2023-11-27 15:45:59','xgQpxIS10M','session','edit_user_prefs'); +INSERT INTO `tokens` VALUES (1,'2023-11-27 15:46:15','5HVJhRRo6t','session','edit_parameters'),(1,'2024-10-15 13:06:14','5NG9DysR5W','session','edit_parameters'),(1,'2024-10-15 13:10:16','6m73C0nqfo','session','edit_parameters'),(1,'2024-10-15 13:10:09','7RlXVAQiOb','session','edit_parameters'),(1,'2023-11-27 12:25:54','a9MgwT7N7x','session','edit_product'),(1,'2024-10-15 13:27:02','bSVcXqgap4','session','edit_flagtype'),(1,'2024-10-15 13:06:09','BWsu8P8e2D','session','edit_parameters'),(1,'2023-11-27 15:42:50','CRSwDhzaXc','session','edit_parameters'),(1,'2024-10-15 14:02:08','dAVlRMDOg7','session','edit_component'),(1,'2023-11-27 12:29:18','DXFuAIZ5GH','session','edit_product'),(1,'2023-09-20 13:13:14','ery9F3ZaAV','session','edit_user_prefs'),(1,'2024-10-15 12:46:48','gEsxMu9BHz','api_token',''),(1,'2023-11-27 15:44:26','gnPazrbni2','session','edit_product'),(1,'2023-11-27 15:43:10','GZT1mYgIAF','session','edit_settings'),(1,'2023-11-27 15:42:57','hYkjAGXNIj','session','add_field'),(1,'2024-10-15 13:15:12','I9aiLWHFRJ','session','workflow_edit'),(1,'2023-11-27 15:46:35','ibDe8MPzGE','session','edit_parameters'),(1,'2024-10-15 14:00:21','ITqzn9Ed9n','session','edit_product'),(1,'2024-10-15 13:06:33','jK4PGdugR8','session','edit_parameters'),(1,'2024-10-15 14:02:05','JOhZj5gVqg','session','edit_product'),(1,'2023-09-20 13:13:14','oukIJJwYod','api_token',''),(1,'2023-11-27 12:26:29','PIjhZLJ29K','session','edit_product'),(1,'2023-11-27 12:23:39','pIrqNpsRDo','api_token',''),(1,'2024-10-15 13:28:58','qO1ZPdshDu','session','edit_user'),(1,'2023-11-27 15:44:36','rkyOtDBxr4','session','edit_group_controls'),(1,'2023-09-20 13:13:20','VLrgLovfH9','session','edit_user_prefs'),(1,'2024-10-15 13:10:07','w7KWafB5zu','session','edit_parameters'),(1,'2023-11-27 15:45:59','xgQpxIS10M','session','edit_user_prefs'),(1,'2024-10-15 14:02:53','YnDsGT0jbR','session','add_component'); /*!40000 ALTER TABLE `tokens` ENABLE KEYS */; UNLOCK TABLES; @@ -2112,7 +2119,7 @@ CREATE TABLE `user_group_map` ( LOCK TABLES `user_group_map` WRITE; /*!40000 ALTER TABLE `user_group_map` DISABLE KEYS */; -INSERT INTO `user_group_map` VALUES (1,1,0,0),(1,1,1,0),(1,3,0,0),(1,8,0,2); +INSERT INTO `user_group_map` VALUES (1,1,0,0),(1,1,1,0),(1,3,0,0),(1,8,0,2),(2,8,0,2); /*!40000 ALTER TABLE `user_group_map` ENABLE KEYS */; UNLOCK TABLES; @@ -2267,4 +2274,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-11-27 16:56:56 +-- Dump completed on 2024-10-15 16:07:04 diff --git a/tests/test_ro_functional.py b/tests/test_ro_functional.py index df7f9e5d..4a40b114 100644 --- a/tests/test_ro_functional.py +++ b/tests/test_ro_functional.py @@ -51,6 +51,7 @@ class MyBugzilla(bugzilla.Bugzilla): assert bz._is_redhat_bugzilla is True # pylint: disable=protected-access +# See also: tests/integration/ro_api_test.py::test_rest_xmlrpc_detection def test_rest_xmlrpc_detection(): # The default: use XMLRPC bz = _open_bz("bugzilla.redhat.com") @@ -71,6 +72,7 @@ def test_rest_xmlrpc_detection(): assert bz._proxy # pylint: disable=protected-access +# See also: tests/integration/ro_api_test.py::test_apikey_error_scraping def test_apikey_error_scraping(): # Ensure the API key does not leak into any requests exceptions fakekey = "FOOBARMYKEY" @@ -98,6 +100,7 @@ def test_apikey_error_scraping(): assert fakekey not in str(e.value) +# See also: tests/integration/ro_api_test.py::test_xmlrpc_bad_url def test_xmlrpc_bad_url(): with pytest.raises(bugzilla.BugzillaError) as e: _open_bz("https://example.com/#xmlrpc") @@ -142,6 +145,7 @@ def test_gentoo(backends): ################## +# See also: tests/integration/ro_cli_test.py::test_get_products def testInfoProducts(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -149,6 +153,7 @@ def testInfoProducts(run_cli, backends): _check(out, 123, "Virtualization Tools") +# See also: tests/integration/ro_cli_test.py::test_get_components def testInfoComps(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -156,6 +161,7 @@ def testInfoComps(run_cli, backends): _check(out, 8, "virtinst") +# See also: tests/integration/ro_cli_test.py::test_get_versions def testInfoVers(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -163,6 +169,7 @@ def testInfoVers(run_cli, backends): _check(out, 17, "rawhide") +# See also: tests/integration/ro_cli_test.py::test_get_component_owners def testInfoCompOwners(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -171,6 +178,7 @@ def testInfoCompOwners(run_cli, backends): _check(out, None, "libvirt: Libvirt Maintainers") +# See also: tests/integration/ro_cli_test.py::test_query def testQuery(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -191,6 +199,7 @@ def testQuery(run_cli, backends): l2 == expectbug]) +# See also: tests/integration/ro_cli_test.py::test_query_full def testQueryFull(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -199,6 +208,7 @@ def testQueryFull(run_cli, backends): _check(out, 60, "end-of-life (EOL)") +# See also: tests/integration/ro_cli_test.py::test_query_raw def testQueryRaw(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -207,6 +217,7 @@ def testQueryRaw(run_cli, backends): _check(out, 70, "ATTRIBUTE[whiteboard]: bzcl34nup") +# See also: tests/integration/ro_cli_test.py::test_query_oneline def testQueryOneline(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -223,6 +234,7 @@ def testQueryOneline(run_cli, backends): assert " CVE-2011-2527" in out +# See also: tests/integration/ro_cli_test.py::test_query_extra def testQueryExtra(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -232,6 +244,7 @@ def testQueryExtra(run_cli, backends): assert " +Status Whiteboard: bzcl34nup" in out +# See also: tests/integration/ro_cli_test.py::test_query_format def testQueryFormat(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -253,6 +266,7 @@ def testQueryFormat(run_cli, backends): assert "V34 — system" in out +# See also: tests/integration/ro_cli_test.py::test_query_url def testQueryURL(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -290,6 +304,7 @@ def testQueryExtrafieldPool(run_cli, backends): assert "current_sprint_id" in out2 +# See also: tests/integration/ro_api_test.py::test_get_component_detail def testComponentsDetails(backends): """ Fresh call to getcomponentsdetails should properly refresh @@ -299,6 +314,7 @@ def testComponentsDetails(backends): assert bool(bz.getcomponentsdetails("Red Hat Developer Toolset")) +# See also: tests/integration/ro_api_test.py::test_get_bug_alias def testGetBugAlias(backends): """ getbug() works if passed an alias @@ -309,6 +325,7 @@ def testGetBugAlias(backends): assert bug.bug_id == 720773 +# See also: tests/integration/ro_api_test.py::test_get_bug_404 def testGetBug404(backends): """ getbug() is expected to raise an error, if a bug ID or alias does not exist @@ -325,6 +342,7 @@ def testGetBug404(backends): raise AssertionError("No exception raised") +# See also: tests/integration/ro_api_test.py::test_get_bug_alias_404 def testGetBugAlias404(backends): """ getbug() is expected to raise an error, if a bug ID or alias does not exist @@ -341,6 +359,7 @@ def testGetBugAlias404(backends): raise AssertionError("No exception raised") +# See also: tests/integration/ro_api_test.py::test_get_bug_alias_included_field def testGetBugAliasIncludedField(backends): bz = _open_bz(REDHAT_URL, **backends) @@ -358,6 +377,7 @@ def testQuerySubComponent(run_cli, backends): assert "#1060931 " in out +# See also: tests/integration/ro_api_test.py::test_get_bug_fields def testBugFields(backends): bz = _open_bz(REDHAT_URL, **backends) From 2d3ba4e805acb0e5ddda7e081f5d9b7a72c86b1a Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Thu, 17 Oct 2024 09:57:31 +0200 Subject: [PATCH 095/106] ci: More functional tests Migrated all generic functional RO tests to the new integration suite. Also, added a comment to the old tests to indicate which integration test corresponds to it. --- tests/conftest.py | 2 +- tests/integration/ro_api_test.py | 71 ++++++++++++++++++++++++++++++++ tests/integration/ro_cli_test.py | 32 +++++++++++++- tests/services/params.json | 2 +- tests/test_ro_functional.py | 7 ++++ 5 files changed, 111 insertions(+), 3 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 8c9c868d..0740fab6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -141,7 +141,7 @@ def status_callback(request): test_url = os.getenv("BUGZILLA_URL") if test_url: - passthrough += (test_url, ) + passthrough += (test_url, test_url.replace("http://", "https://")) with responses.RequestsMock(passthru_prefixes=passthrough, assert_all_requests_are_fired=False) as mock: mock.add_callback( diff --git a/tests/integration/ro_api_test.py b/tests/integration/ro_api_test.py index 22ee5442..1c47146c 100644 --- a/tests/integration/ro_api_test.py +++ b/tests/integration/ro_api_test.py @@ -1,5 +1,6 @@ # Ignoring pytest-related warnings: # pylint: disable=redefined-outer-name,unused-argument +from urllib.parse import urljoin from xmlrpc.client import Fault import pytest @@ -62,6 +63,18 @@ def test_get_products(mocked_responses, backends): assert {v["name"] for v in rhel["versions"]} == {"9.0", "9.1", "unspecified"} +def test_get_product(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + + product_ids = {product["id"] for product in bz.product_get(ptype="enterable", + include_fields=["id"])} + product_names = {product["name"] for product in bz.product_get(ptype="selectable", + include_fields=["name"])} + assert product_ids == {1, 2, 3} + assert product_names == {'Red Hat Enterprise Linux 9', 'SUSE Linux Enterprise Server 15 SP6', + 'TestProduct'} + + def test_get_components(mocked_responses, backends): bz = open_bz(url=TEST_URL, **backends) components = bz.getcomponents(product="SUSE Linux Enterprise Server 15 SP6") @@ -106,6 +119,16 @@ def test_get_bug_alias(mocked_responses, backends): assert bug.summary == "ZeroDivisionError in function foo_bar()" +def test_bug_url(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + bug_id = 2 + + # Ensure weburl is generated consistently whether + # we are using XMLRPC or REST + bug = bz.getbug(bug_id) + assert bug.weburl == urljoin(TEST_URL, f"/show_bug.cgi?id={bug_id}") + + def test_get_bug_alias_included_field(mocked_responses, backends): bug_id, alias = 1, "FOO-1" bz = open_bz(url=TEST_URL, **backends) @@ -117,6 +140,18 @@ def test_get_bug_alias_included_field(mocked_responses, backends): assert not hasattr(bug, "summary") +def test_get_bug_exclude_fields(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + + # Check default extra_fields will pull in comments + bug = bz.getbug(2, exclude_fields=["product"]) + assert not hasattr(bug, "product") + + # Ensure that include_fields overrides default extra_fields + bug = bz.getbug(2) + assert hasattr(bug, "product") + + def test_get_bug_404(mocked_responses, backends): bz = open_bz(url=TEST_URL, **backends) try: @@ -147,3 +182,39 @@ def test_get_bug_fields(mocked_responses, backends): assert fields == ["product"] bz.getbugfields(names=["product", "bug_status"], force_refresh=True) assert set(bz.bugfields) == {"product", "bug_status"} + + +def test_query_autorefresh(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + + bz.bug_autorefresh = True + bug = bz.query(bz.build_query(bug_id=1, include_fields=["summary"]))[0] + assert hasattr(bug, "component") + assert bool(bug.component) + + bz.bug_autorefresh = False + bug = bz.query(bz.build_query(bug_id=1, include_fields=["summary"]))[0] + assert not hasattr(bug, "component") + try: + assert bool(bug.component) + except Exception as e: + assert "adjust your include_fields" in str(e) + + +def test_login_stubs(mocked_responses, backends): + # Explicitly set configpaths to avoid interference with an API key set by another test + bz = open_bz(url=TEST_URL, configpaths="/dev/null", **backends) + bz_apikey = open_bz(url=TEST_URL, api_key="random-and-secure-api-key", **backends) + + # Failed login, verifies our backends are calling the correct API + with pytest.raises(BugzillaError) as e: + bz.login("foo", "bar") + assert "Login failed" in str(e) + + # Login is prohibited, when an API key is defined + with pytest.raises(ValueError) as e: + bz_apikey.login("foo", "bar") + assert "cannot login when using an API key" in str(e) + + # Works fine when not logged in + bz.logout() diff --git a/tests/integration/ro_cli_test.py b/tests/integration/ro_cli_test.py index d5073a78..9cd5b3dd 100644 --- a/tests/integration/ro_cli_test.py +++ b/tests/integration/ro_cli_test.py @@ -1,12 +1,34 @@ # Ignoring pytest-related warnings: # pylint: disable=unused-argument import re -from urllib.parse import urljoin +from urllib.parse import urljoin, urlparse, urlunparse from ..utils import open_bz from . import TEST_URL, TEST_PRODUCTS, TEST_SUSE_COMPONENTS, TEST_OWNER +def test_fails(mocked_responses, run_cli, backends): + bz = open_bz(url=TEST_URL, **backends) + out = run_cli("bugzilla query --field=IDONTEXIST=FOO", bzinstance=bz, expectfail=True) + assert "Server error:" in out + + out = run_cli("bugzilla --bugzilla https://example.com/xmlrpc.cgi query --field=IDONTEXIST=FOO", + bzinstance=None, expectfail=True) + assert "Connection lost/failed" in out + + parsed = urlparse(TEST_URL) + netloc = parsed.netloc + if not re.search(r":\d+$", netloc): + netloc += ":80" + + https_test_url = urlunparse(("https", netloc, parsed.path, parsed.params, parsed.query, + parsed.fragment)) + out = run_cli(f"bugzilla --bugzilla {https_test_url} query --bug_id 1234", + bzinstance=None, expectfail=True) + assert "trust the remote server" in out + assert "--nosslverify" in out + + def test_get_products(mocked_responses, run_cli, backends): bz = open_bz(url=TEST_URL, **backends) out = run_cli("bugzilla info --products", bzinstance=bz) @@ -24,6 +46,14 @@ def test_get_components(mocked_responses, run_cli, backends): assert comp in out +def test_get_active_components(mocked_responses, run_cli, backends): + bz = open_bz(url=TEST_URL, **backends) + out = run_cli("bugzilla info --components 'SUSE Linux Enterprise Server 15 SP6' " + "--active-components", bzinstance=bz) + assert "Containers" in out + assert "Kernel" in out + + def test_get_component_owners(mocked_responses, run_cli, backends): bz = open_bz(url=TEST_URL, **backends) out = run_cli("bugzilla info --component_owners 'SUSE Linux Enterprise Server 15 SP6'", diff --git a/tests/services/params.json b/tests/services/params.json index a0ea93ce..1b6f2d14 100644 --- a/tests/services/params.json +++ b/tests/services/params.json @@ -69,7 +69,7 @@ "quip_list_entry_control" : "open", "rememberlogin" : "on", "requirelogin" : "0", - "search_allow_no_criteria" : "1", + "search_allow_no_criteria" : "0", "shadowdb" : "", "shadowdbhost" : "", "shadowdbport" : "3306", diff --git a/tests/test_ro_functional.py b/tests/test_ro_functional.py index 4a40b114..8af5c763 100644 --- a/tests/test_ro_functional.py +++ b/tests/test_ro_functional.py @@ -387,6 +387,7 @@ def testBugFields(backends): assert set(bz.bugfields) == set(["product", "bug_status"]) +# See also: tests/integration/ro_api_test.py::test_get_product def testProductGetMisc(backends): bz = _open_bz(REDHAT_URL, **backends) @@ -394,6 +395,7 @@ def testProductGetMisc(backends): assert bz.product_get(ptype="selectable", include_fields=["name"]) +# See also: tests/integration/ro_api_test.py::test_query_autorefresh def testBugAutoRefresh(backends): bz = _open_bz(REDHAT_URL, **backends) @@ -415,6 +417,7 @@ def testBugAutoRefresh(backends): assert "adjust your include_fields" in str(e) +# See also (in part): tests/integration/ro_api_test.py::test_get_bug_exclude_fields def testExtraFields(backends): bz = _open_bz(REDHAT_URL, **backends) @@ -438,6 +441,7 @@ def testExternalBugsOutput(run_cli, backends): assert "External bug: https://bugs.launchpad.net/bugs/1203576" in out +# See also: tests/integration/ro_cli_test.py::test_get_active_components def testActiveComps(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -449,6 +453,7 @@ def testActiveComps(run_cli, backends): assert "virtinst" not in out +# See also: tests/integration/ro_cli_test.py::test_fails def testFaults(run_cli, backends): bz = _open_bz(REDHAT_URL, **backends) @@ -469,6 +474,7 @@ def testFaults(run_cli, backends): assert "--nosslverify" in out +# See also: tests/integration/ro_api_test.py::test_login_stubs def test_login_stubs(backends): bz = _open_bz(REDHAT_URL, **backends) @@ -489,6 +495,7 @@ def test_redhat_version(backends): _test_version(bz, bzversion) +# See also: tests/integration/ro_api_test.py::test_bug_url def test_bug_misc(backends): bz = _open_bz(REDHAT_URL, **backends) From adf33b6239c363bd3760390934d401455c97b8aa Mon Sep 17 00:00:00 2001 From: Ali Bahrani Date: Wed, 30 Oct 2024 01:59:02 -0700 Subject: [PATCH 096/106] Rename method getcomments to get_comments for better readablity (#226) Using the api, I used to think the only way to retrieve the comments is to go through a Bugzilla instance. Later, I figured out the the Bug objects have the method `get_attachments` and I was amazed that there is no `get_comments`. Digging into the code I realized the method has been named `getcomments`. I have changed all the occurrences of `getcomments` method of the Bug class to `get_comment` --------- Co-authored-by: abahrani Co-authored-by: Andreas Hasenkopf --- bugzilla/bug.py | 4 ++-- examples/getbug.py | 4 ++-- examples/update.py | 4 ++-- tests/test_api_bug.py | 1 + tests/test_rw_functional.py | 5 ++++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/bugzilla/bug.py b/bugzilla/bug.py index 0a7c2d16..6f3ec43b 100644 --- a/bugzilla/bug.py +++ b/bugzilla/bug.py @@ -292,14 +292,14 @@ def addcomment(self, comment, private=False): return self.bugzilla.update_bugs(self.bug_id, vals) - def getcomments(self): + def get_comments(self): """ Returns an array of comment dictionaries for this bug """ comment_list = self.bugzilla.get_comments([self.bug_id]) return comment_list['bugs'][str(self.bug_id)]['comments'] - + getcomments = get_comments ##################### # Get/Set bug flags # ##################### diff --git a/examples/getbug.py b/examples/getbug.py index faf4c30f..f164c0a1 100644 --- a/examples/getbug.py +++ b/examples/getbug.py @@ -33,8 +33,8 @@ # comments must be fetched separately on stock bugzilla. this just returns # a raw dict with all the info. -comments = bug.getcomments() +comments = bug.get_comments() print("\nLast comment data:\n%s" % pprint.pformat(comments[-1])) -# getcomments is just a wrapper around bzapi.get_comments(), which can be +# get_comments is just a wrapper around bzapi.get_comments(), which can be # used for bulk comments fetching diff --git a/examples/update.py b/examples/update.py index cd76992e..86b1967c 100644 --- a/examples/update.py +++ b/examples/update.py @@ -38,7 +38,7 @@ # Now let's add a comment -comments = bug.getcomments() +comments = bug.get_comments() print("Bug originally has %d comments" % len(comments)) update = bzapi.build_update(comment="new example comment %s" % time.time()) @@ -46,7 +46,7 @@ # refresh() actually isn't required here because comments are fetched # on demand -comments = bug.getcomments() +comments = bug.get_comments() print("Bug now has %d comments. Last comment=%s" % (len(comments), comments[-1]["text"])) diff --git a/tests/test_api_bug.py b/tests/test_api_bug.py index 61572fc5..9d5c2564 100644 --- a/tests/test_api_bug.py +++ b/tests/test_api_bug.py @@ -200,6 +200,7 @@ def _get_fake_bug(apiname): # Stub API testing bug = fakebz.getbug(1165434) bug.get_history_raw() + bug.get_comments() bug.getcomments() # Some hackery to hit a few attachment code paths diff --git a/tests/test_rw_functional.py b/tests/test_rw_functional.py index 3d49688e..b59d84cd 100644 --- a/tests/test_rw_functional.py +++ b/tests/test_rw_functional.py @@ -318,8 +318,11 @@ def test05ModifyStatus(run_cli, backends): assert bug.longdescs[-1]["text"] == comment assert bug.longdescs[-1]["is_private"] == 0 - # Confirm comments is same as getcomments + # Confirm comments is same as get_comments + assert bug.comments == bug.get_comments() + # This method will be removed in a future version assert bug.comments == bug.getcomments() + assert bug.get_comments() == bug.getcomments() # Reset state run_cli(cmd + "--status %s" % origstatus, bz) From ad14b85af03b728becf898ba5c2864506c43189b Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Mon, 4 Nov 2024 13:16:10 -0500 Subject: [PATCH 097/106] tests: ro-functional: Handle redhat disabling User.log{in,out} Signed-off-by: Cole Robinson --- tests/test_ro_functional.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/test_ro_functional.py b/tests/test_ro_functional.py index 8af5c763..6bb770da 100644 --- a/tests/test_ro_functional.py +++ b/tests/test_ro_functional.py @@ -478,13 +478,26 @@ def testFaults(run_cli, backends): def test_login_stubs(backends): bz = _open_bz(REDHAT_URL, **backends) - # Failed login, verifies our backends are calling the correct API + # In 2024 bugzilla.redhat.com disabled User.login and User.logout APIs + # for xmlrpc API + with pytest.raises(bugzilla.BugzillaError) as e: bz.login("foo", "bar") assert "Login failed" in str(e) - # Works fine when not logged in - bz.logout() + is_rest = bz.is_rest() + is_xmlrpc = bz.is_xmlrpc() + + msg = None + try: + bz.logout() + except Exception as error: + msg = str(error) + + if is_rest and msg: + raise AssertionError("didn't expect exception: %s" % msg) + if is_xmlrpc: + assert "'User.logout' was not found" in str(msg) def test_redhat_version(backends): From 98de1e242644903a0379f5ed0ae3ec3f138ee585 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Mon, 4 Nov 2024 13:20:29 -0500 Subject: [PATCH 098/106] tests: rw-functional: Handle new permission errors with target-milestone Signed-off-by: Cole Robinson --- tests/test_rw_functional.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_rw_functional.py b/tests/test_rw_functional.py index b59d84cd..200f7939 100644 --- a/tests/test_rw_functional.py +++ b/tests/test_rw_functional.py @@ -534,8 +534,10 @@ def test071ModifyMisc(run_cli, backends): assert targetbug.target_milestone == "rc" assert targetbug.target_release == ["6.10"] except RuntimeError as e: - if have_dev: - raise + # As of Nov 2024 this needs even extra permissions, probably + # due to RHEL products being locked down + # if have_dev: + # raise assert perm_error in str(e) try: From 35c4510314ee62cc4b7dfd50acfbaca0c8baa366 Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Mon, 4 Nov 2024 15:48:20 +0100 Subject: [PATCH 099/106] CI: Added RW integration tests * Migrated some RW tests from test_rw_functional.py * Run RW tests in CI * Updated Bugzilla parameter to allow tagging of comments * Use TestProduct for RW tests --- .github/workflows/build.yml | 4 +- tests/conftest.py | 6 ++ tests/integration/ro_api_test.py | 5 ++ tests/integration/rw_api_test.py | 120 +++++++++++++++++++++++++++++++ tests/services/params.json | 2 +- tests/test_rw_functional.py | 8 +++ 6 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 tests/integration/rw_api_test.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5df40255..72dd3388 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,7 +67,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} # Run functional tests - integrationRO: + integration: runs-on: ubuntu-latest services: mariadb: @@ -106,7 +106,7 @@ jobs: pip install pytest pytest-cov pip install -r requirements.txt -r test-requirements.txt - name: Test with pytest - run: pytest --ro-integration + run: pytest --ro-integration --rw-integration env: BUGZILLA_URL: http://localhost diff --git a/tests/conftest.py b/tests/conftest.py index 0740fab6..d398437d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -21,6 +21,8 @@ def pytest_addoption(parser): parser.addoption("--ro-integration", action="store_true", default=False, help="Run readonly tests against local Bugzilla instance.") + parser.addoption("--rw-integration", action="store_true", default=False, + help="Run read-write tests against local Bugzilla instance.") parser.addoption("--ro-functional", action="store_true", default=False, help=("Run readonly functional tests against actual " "bugzilla instances. This will be very slow.")) @@ -46,14 +48,18 @@ def pytest_ignore_collect(collection_path, config): has_ro = config.getoption("--ro-functional") has_ro_i = config.getoption("--ro-integration") has_rw = config.getoption("--rw-functional") + has_rw_i = config.getoption("--rw-integration") base = os.path.basename(str(collection_path)) is_ro = base == "test_ro_functional.py" is_ro_i = "tests/integration/ro" in str(collection_path) is_rw = base == "test_rw_functional.py" + is_rw_i = "tests/integration/rw" in str(collection_path) if is_ro_i and not has_ro_i: return True + if is_rw_i and not has_rw_i: + return True if is_ro and not has_ro: return True diff --git a/tests/integration/ro_api_test.py b/tests/integration/ro_api_test.py index 1c47146c..3c45cc09 100644 --- a/tests/integration/ro_api_test.py +++ b/tests/integration/ro_api_test.py @@ -201,6 +201,11 @@ def test_query_autorefresh(mocked_responses, backends): assert "adjust your include_fields" in str(e) +def test_logged_in_no_creds(mocked_responses, backends): + bz = open_bz(url=TEST_URL, use_creds=False, **backends) + assert not bz.logged_in + + def test_login_stubs(mocked_responses, backends): # Explicitly set configpaths to avoid interference with an API key set by another test bz = open_bz(url=TEST_URL, configpaths="/dev/null", **backends) diff --git a/tests/integration/rw_api_test.py b/tests/integration/rw_api_test.py new file mode 100644 index 00000000..8fba7dfb --- /dev/null +++ b/tests/integration/rw_api_test.py @@ -0,0 +1,120 @@ +# pylint: disable=unused-argument +from uuid import uuid4 +from xmlrpc.client import Fault + +from pytest import raises +from pytest import mark + +from bugzilla import Bugzilla, BugzillaError +from bugzilla.bug import Bug + +from ..utils import open_bz +from . import TEST_URL + +# NOTE: The tests in this file assume that an API key is defined in the bugzillarc! + + +DEFAULT_PARAMS = {"product": "TestProduct", + "component": "TestComponent", + "version": "unspecified", + "summary": "A new bug", + "description": "Details on how to reproduce", + "cc": "nemo@example.com", + "op_sys": "Linux", + "platform": "PC"} + + +def _create_bug(bz: Bugzilla, **kwargs) -> Bug: + """ + Create a new bug with overwrite-able defaults + """ + params = DEFAULT_PARAMS.copy() + params.update(kwargs) + + return bz.createbug(**bz.build_createbug(**params)) + + +def test_create_bug(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + bug = _create_bug(bz) + + assert isinstance(bug, Bug) + assert bug.id + + bug = bz.getbug(bug.id) + for field in ("product", "component", "version", "summary"): + assert getattr(bug, field) == DEFAULT_PARAMS[field] + + +def test_create_bug_anonymous(mocked_responses, backends): + bz = open_bz(url=TEST_URL, configpaths="/dev/null", **backends) + with raises((Fault, BugzillaError)): + _create_bug(bz) + + +def test_create_bug_alias(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + alias = uuid4().hex + bug = _create_bug(bz, alias=alias) + + bug = bz.getbug(bug.id) + assert alias in bug.alias + + with raises((Fault, BugzillaError)): + _create_bug(bz, alias=alias) + + +def test_update_bug(mocked_responses, backends): + email = "nemo@example.com" + bz = open_bz(url=TEST_URL, **backends) + bug = _create_bug(bz) + params = bz.build_update(resolution="WONTFIX", status="RESOLVED", cc_remove=email) + bz.update_bugs(bug.id, params) + bug.refresh() + + assert bug.resolution == "WONTFIX" + assert bug.status == "RESOLVED" + assert bug.cc == [] + + params = bz.build_update(cc_add=email) + bz.update_bugs(bug.id, params) + bug.refresh() + + assert bug.cc == [email] + + +# Bugzilla instance has no CLOSED status +@mark.xfail +def test_close_bug(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + bug = _create_bug(bz) + bug.close(resolution="WORKSFORME", comment="Bla bla", isprivate=True) + bug.refresh() + + assert bug.resolution == "WORKSFORME" + assert bug.status == "CLOSED" + + +def test_add_comment(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + bug = bz.getbug(1) + + comment_count = len(bug.get_comments()) + bug.addcomment("Bla Bla bla", private=True) + bug.refresh() + + assert len(bug.get_comments()) == comment_count + 1 + + +def test_update_flags(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + bug = _create_bug(bz) + flag = {"requestee": "nemo@example.com", "name": "needinfo", "status": "?"} + params = bz.build_update(flags=[flag]) + bz.update_bugs([bug.id], params) + bug.refresh() + + assert len(bug.flags) == 1 + + for key, value in flag.items(): + assert bug.flags[0][key] == value diff --git a/tests/services/params.json b/tests/services/params.json index 1b6f2d14..9a6a9034 100644 --- a/tests/services/params.json +++ b/tests/services/params.json @@ -44,7 +44,7 @@ "font_file" : "", "globalwatchers" : "", "inbound_proxies" : "", - "insidergroup" : "", + "insidergroup" : "editbugs", "last_visit_keep_days" : "10", "letsubmitterchoosemilestone" : "1", "letsubmitterchoosepriority" : "1", diff --git a/tests/test_rw_functional.py b/tests/test_rw_functional.py index 200f7939..600fa7ed 100644 --- a/tests/test_rw_functional.py +++ b/tests/test_rw_functional.py @@ -191,6 +191,7 @@ def _make_subcomponent_bug(run_cli, bz): # test cases # ############## +# See also: tests/integration/rw_api_test.py::test_logged_in_no_creds def test0LoggedInNoCreds(backends): bz = _open_bz(**backends, use_creds=False) assert not bz.logged_in @@ -201,6 +202,9 @@ def test0ClassDetection(): assert bz.__class__ is bugzilla.RHBugzilla +# See also: tests/integration/rw_api_test.py::test_create_bug +# tests/integration/rw_api_test.py::test_create_bug_alias +# tests/integration/rw_api_test.py::test_update_bug def test04NewBugAllFields(run_cli, backends): """ Create a bug using all 'new' fields, check some values, close it @@ -303,6 +307,7 @@ def test05ModifyStatus(run_cli, backends): raise assert perm_error in str(e) + # See also: tests/integration/rw_api_test.py::test_close_bug # bz.close test fixed_in = str(datetime.datetime.today()) bug.close("ERRATA", fixedin=fixed_in) @@ -311,6 +316,7 @@ def test05ModifyStatus(run_cli, backends): assert bug.resolution == "ERRATA" assert bug.fixed_in == fixed_in + # See also: tests/integration/rw_api_test.py::test_add_comment # bz.addcomment test comment = ("yet another test comment %s" % datetime.datetime.today()) bug.addcomment(comment, private=False) @@ -330,6 +336,7 @@ def test05ModifyStatus(run_cli, backends): assert bug.status == origstatus +# See also: tests/integration/rw_api_test.py::test_update_bug def test06ModifyEmails(run_cli, backends): """ Modify cc, assignee, qa_contact for existing bug @@ -391,6 +398,7 @@ def test06ModifyEmails(run_cli, backends): assert perm_error in str(e) +# See also: tests/integration/rw_api_test.py::test_update_flags def test070ModifyMultiFlags(run_cli, backends): """ Modify flags and fixed_in for 2 bugs From a7e6f282b9e0472f846840c590ab6925711c1b16 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 18:00:36 +0000 Subject: [PATCH 100/106] ci: bump codecov/codecov-action from 4 to 5 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 72dd3388..c2a27f3b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,7 @@ jobs: pytest --cov --cov-report=xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} From 83eee1902ac5d7a838f5181793bf4359b132c1f2 Mon Sep 17 00:00:00 2001 From: George Redivo Date: Thu, 20 Feb 2025 14:41:42 -0300 Subject: [PATCH 101/106] Create Bug.setsummary() method Create a method in Bug class to set summary of the bug. --- bugzilla/bug.py | 16 ++++++++++++++++ tests/test_api_bug.py | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/bugzilla/bug.py b/bugzilla/bug.py index 6f3ec43b..15cea004 100644 --- a/bugzilla/bug.py +++ b/bugzilla/bug.py @@ -360,6 +360,22 @@ def updateflags(self, flags): self.bugzilla.build_update(flags=flaglist)) + ####################### + # Bug fields handling # + ####################### + + def setsummary(self, summary): + """ + Set the summary of bug to the given summary string + """ + # Create update object + vals = self.bugzilla.build_update(summary=summary) + + log.debug("setsummary: update=%s", vals) + + # Send update to bugzilla and return + return self.bugzilla.update_bugs(self.bug_id, vals) + ######################## # Experimental methods # ######################## diff --git a/tests/test_api_bug.py b/tests/test_api_bug.py index 9d5c2564..23448d6b 100644 --- a/tests/test_api_bug.py +++ b/tests/test_api_bug.py @@ -184,6 +184,10 @@ def _get_fake_bug(apiname): assert bug.get_flags("NOPE") is None assert bug.get_flag_status("NOPE") is None + # bug.setsummary test + bug = _get_fake_bug("setsummary") + bug.setsummary("My new summary") + # Minor get_history_raw wrapper fakebz = tests.mockbackend.make_bz(rhbz=True, bug_history_args="data/mockargs/test_bug_api_history.txt", From bad7092a495817eb4001023fd1cb532c5725271f Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Tue, 6 May 2025 08:56:46 +0200 Subject: [PATCH 102/106] Stop using Ubuntu 20.04 image (#232) The Ubuntu 20.04 image got retired and is no longer available. Tests for Python 3.6 are run in a Docker image instead. --- .github/workflows/build.yml | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c2a27f3b..f0b450d8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,15 +26,30 @@ jobs: run: | pylint --output-format colorized --rcfile .pylintrc \ bugzilla-cli setup.py bugzilla examples tests + test_3_6: + # python 3.6 is for rhel/centos8/sles15 compat + runs-on: ubuntu-latest + container: + image: python:3.6 + steps: + - uses: actions/checkout@v4 - build: - # We stick with 20.04 to get access to python 3.6 - # https://github.com/actions/setup-python/issues/544 - runs-on: ubuntu-20.04 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest + pip install -r requirements.txt -r test-requirements.txt + + - name: Test with pytest + run: | + pytest + + + test: + runs-on: ubuntu-latest strategy: matrix: - # python 3.6 is for rhel/centos8/sles15 compat - python-version: ["3.6", "3.9", "3.10", "3.11", "3.12", "3.13.0-rc.2"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 From 5b7e6e3ed462d82a5bfdec4fa2f57700c6210628 Mon Sep 17 00:00:00 2001 From: Andreas Hasenkopf Date: Mon, 5 May 2025 11:30:42 +0200 Subject: [PATCH 103/106] Support "resolution" in `Bug.build_query` This change allows to search and filter bugs by resolution. Closes #231. --- bugzilla/base.py | 4 +++- tests/integration/ro_api_test.py | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/bugzilla/base.py b/bugzilla/base.py index eb0f9244..ddda9137 100644 --- a/bugzilla/base.py +++ b/bugzilla/base.py @@ -1205,7 +1205,8 @@ def build_query(self, tags=None, exclude_fields=None, extra_fields=None, - limit=None): + limit=None, + resolution=None): """ Build a query string from passed arguments. Will handle query parameter differences between various bugzilla versions. @@ -1239,6 +1240,7 @@ def build_query(self, "savedsearch": savedsearch, "sharer_id": savedsearch_sharer_id, "limit": limit, + "resolution": resolution, # RH extensions... don't add any more. See comment below "sub_components": listify(sub_component), diff --git a/tests/integration/ro_api_test.py b/tests/integration/ro_api_test.py index 3c45cc09..d3d6b526 100644 --- a/tests/integration/ro_api_test.py +++ b/tests/integration/ro_api_test.py @@ -223,3 +223,16 @@ def test_login_stubs(mocked_responses, backends): # Works fine when not logged in bz.logout() + + +def test_query_resolution(mocked_responses, backends): + bz = open_bz(url=TEST_URL, **backends) + + bugs = bz.query(bz.build_query(short_desc="ZeroDivisionError", resolution=None)) + assert len(bugs) == 1 + + bugs = bz.query(bz.build_query(short_desc="ZeroDivisionError", resolution="---")) + assert len(bugs) == 1 + + bugs = bz.query(bz.build_query(short_desc="ZeroDivisionError", resolution="DUPLICATE")) + assert len(bugs) == 0 From 78f9ada45640778639e353b8331fae550b075779 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 20:16:58 +0200 Subject: [PATCH 104/106] ci: bump actions/checkout from 4 to 5 (#235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
Release notes

Sourced from actions/checkout's releases.

v5.0.0

What's Changed

⚠️ Minimum Compatible Runner Version

v2.327.1
Release Notes

Make sure your runner is updated to this version or newer to use this release.

Full Changelog: https://github.com/actions/checkout/compare/v4...v5.0.0

v4.3.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4...v4.3.0

v4.2.2

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.2.1...v4.2.2

v4.2.1

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.2.0...v4.2.1

... (truncated)

Changelog

Sourced from actions/checkout's changelog.

Changelog

V5.0.0

V4.3.0

v4.2.2

v4.2.1

v4.2.0

v4.1.7

v4.1.6

v4.1.5

v4.1.4

v4.1.3

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4&new-version=5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 10 +++++----- .github/workflows/publish.yml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f0b450d8..abeba41d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: matrix: python-version: ["3.x"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: @@ -32,7 +32,7 @@ jobs: container: image: python:3.6 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install dependencies run: | @@ -52,7 +52,7 @@ jobs: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 @@ -102,7 +102,7 @@ jobs: matrix: python-version: ["3.x"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install MariaDB utils run: sudo apt install --no-install-recommends -q -y mariadb-client - name: Restore DB dump @@ -133,7 +133,7 @@ jobs: python-version: ["3.x"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8ca7db69..bfe244c6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,7 +16,7 @@ jobs: permissions: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v5 with: From a6e67ab1fd3b19a1c40914de683b53d5a6914349 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:40:56 +0100 Subject: [PATCH 105/106] ci: bump actions/setup-python from 5 to 6 (#237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
Release notes

Sourced from actions/setup-python's releases.

v6.0.0

What's Changed

Breaking Changes

Make sure your runner is on version v2.327.1 or later to ensure compatibility with this release. See Release Notes

Enhancements:

Bug fixes:

Dependency updates:

New Contributors

Full Changelog: https://github.com/actions/setup-python/compare/v5...v6.0.0

v5.6.0

What's Changed

Full Changelog: https://github.com/actions/setup-python/compare/v5...v5.6.0

v5.5.0

What's Changed

Enhancements:

Bug fixes:

... (truncated)

Commits
  • e797f83 Upgrade to node 24 (#1164)
  • 3d1e2d2 Revert "Enhance cache-dependency-path handling to support files outside the w...
  • 65b0712 Clarify pythonLocation behavior for PyPy and GraalPy in environment variables...
  • 5b668cf Bump actions/checkout from 4 to 5 (#1181)
  • f62a0e2 Change missing cache directory error to warning (#1182)
  • 9322b3c Upgrade setuptools to 78.1.1 to fix path traversal vulnerability in PackageIn...
  • fbeb884 Bump form-data to fix critical vulnerabilities #182 & #183 (#1163)
  • 03bb615 Bump idna from 2.9 to 3.7 in /tests/data (#843)
  • 36da51d Add version parsing from Pipfile (#1067)
  • 3c6f142 update documentation (#1156)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/setup-python&package-manager=github_actions&previous-version=5&new-version=6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 8 ++++---- .github/workflows/publish.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index abeba41d..27075192 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -55,7 +55,7 @@ jobs: - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -112,7 +112,7 @@ jobs: mkdir -p ~/.config/python-bugzilla/ cp tests/services/bugzillarc ~/.config/python-bugzilla/ - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -136,7 +136,7 @@ jobs: - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bfe244c6..1a1f2a5c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install pypa/build From ce9093ed8a7aa7a37509e66f56628a904c5ee8a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:41:44 +0100 Subject: [PATCH 106/106] ci: bump actions/checkout from 5 to 6 (#239) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [//]: # (dependabot-start) ⚠️ **Dependabot is rebasing this PR** ⚠️ Rebasing might not happen immediately, so don't worry if this takes some time. Note: if you make any changes to this PR yourself, they will take precedence over the rebase. --- [//]: # (dependabot-end) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
Release notes

Sourced from actions/checkout's releases.

v6.0.0

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v5.0.0...v6.0.0

v6-beta

What's Changed

Updated persist-credentials to store the credentials under $RUNNER_TEMP instead of directly in the local git config.

This requires a minimum Actions Runner version of v2.329.0 to access the persisted credentials for Docker container action scenarios.

v5.0.1

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v5...v5.0.1

Changelog

Sourced from actions/checkout's changelog.

Changelog

V6.0.0

V5.0.1

V5.0.0

V4.3.1

V4.3.0

v4.2.2

v4.2.1

v4.2.0

v4.1.7

v4.1.6

v4.1.5

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=5&new-version=6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 10 +++++----- .github/workflows/publish.yml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 27075192..d01e8bf9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: matrix: python-version: ["3.x"] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: @@ -32,7 +32,7 @@ jobs: container: image: python:3.6 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install dependencies run: | @@ -52,7 +52,7 @@ jobs: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 @@ -102,7 +102,7 @@ jobs: matrix: python-version: ["3.x"] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install MariaDB utils run: sudo apt install --no-install-recommends -q -y mariadb-client - name: Restore DB dump @@ -133,7 +133,7 @@ jobs: python-version: ["3.x"] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1a1f2a5c..3b537cb9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,7 +16,7 @@ jobs: permissions: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v6 with: