We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Give bodhi-push a -y flag so that it can be run in cron
We should give ```bodhi-push``` a -y flag that answers yes to the various questions so that it can be run in cron.
Give bodhi-push a -y flag so that it can be run in cron
We should give ```bodhi-push``` a -y flag that answers yes to the various questions so that it can be run in cron.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bodhi/server/push.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # Copyright © 2007-2018 Red Hat, Inc.
3 #
4 # This file is part of Bodhi.
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 """The CLI tool for triggering update pushes."""
20 from sqlalchemy.sql import or_
21 import click
22 
23 from bodhi.server import (buildsys, initialize_db, get_koji)
24 from bodhi.server.config import config
25 from bodhi.server.models import (Compose, ComposeState, Release, ReleaseState, Build, Update,
26                                  UpdateRequest)
27 from bodhi.server.util import transactional_session_maker
28 import bodhi.server.notifications
29 
30 
31 _koji = None
32 
33 
34 def update_sig_status(update):
35     """Update build signature status for builds in update."""
36     global _koji
37     if _koji is None:
38         # We don't want to authenticate to the buildsystem, because this script is often mistakenly
39         # run as root and this can cause the ticket cache to become root owned with 0600 perms,
40         # which will cause the compose to fail when it tries to use it to authenticate to Koji.
41         buildsys.setup_buildsystem(config, authenticate=False)
42         _koji = get_koji(None)
43     for build in update.builds:
44         if not build.signed:
45             build_tags = build.get_tags(_koji)
46             if update.release.pending_signing_tag not in build_tags:
47                 click.echo('Build %s was refreshed as signed' % build.nvr)
48                 build.signed = True
49             else:
50                 click.echo('Build %s still unsigned' % build.nvr)
51 
52 
53 @click.command()
54 @click.option('--builds', help='Push updates for a comma-separated list of builds')
55 @click.option('--cert-prefix', default="shell",
56               help="The prefix of a fedmsg cert used to sign the message")
57 @click.option('--releases', help=('Push updates for a comma-separated list of releases (default: '
58                                   'current and pending releases)'))
59 @click.option('--request', default='testing,stable',
60               help='Push updates with a specific request (default: testing,stable)')
61 @click.option('--resume', help='Resume one or more previously failed pushes',
62               is_flag=True, default=False)
63 @click.option('--username', prompt=True)
64 @click.version_option(message='%(version)s')
65 def push(username, cert_prefix, **kwargs):
66     """Push builds out to the repositories."""
67     resume = kwargs.pop('resume')
68     resume_all = False
69 
70     initialize_db(config)
71     db_factory = transactional_session_maker()
72     composes = []
73     with db_factory() as session:
74         if not resume and session.query(Compose).count():
75             click.confirm('Existing composes detected: {}. Do you wish to resume them all?'.format(
76                 ', '.join([str(c) for c in session.query(Compose).all()])), abort=True)
77             resume = True
78             resume_all = True
79 
80         # If we're resuming a push
81         if resume:
82             for compose in session.query(Compose).all():
83                 if len(compose.updates) == 0:
84                     # Compose objects can end up with 0 updates in them if the masher ejects all the
85                     # updates in a compose for some reason. Composes with no updates cannot be
86                     # serialized because their content_type property uses the content_type of the
87                     # first update in the Compose. Additionally, it doesn't really make sense to go
88                     # forward with running an empty Compose. It makes the most sense to delete them.
89                     click.echo("{} has no updates. It is being removed.".format(compose))
90                     session.delete(compose)
91                     continue
92 
93                 if not resume_all and not click.confirm('Resume {}?'.format(compose)):
94                     continue
95 
96                 # Reset the Compose's state and error message.
97                 compose.state = ComposeState.requested
98                 compose.error_message = u''
99 
100                 composes.append(compose)
101         else:
102             updates = []
103             # Accept both comma and space separated request list
104             requests = kwargs['request'].replace(',', ' ').split(' ')
105             requests = [UpdateRequest.from_string(val) for val in requests]
106 
107             query = session.query(Update).filter(Update.request.in_(requests))
108 
109             if kwargs.get('builds'):
110                 query = query.join(Update.builds)
111                 query = query.filter(
112                     or_(*[Build.nvr == build for build in kwargs['builds'].split(',')]))
113 
114             query = _filter_releases(session, query, kwargs.get('releases'))
115 
116             for update in query.all():
117                 # Skip unsigned updates (this checks that all builds in the update are signed)
118                 update_sig_status(update)
119 
120                 if not update.signed:
121                     click.echo('Warning: %s has unsigned builds and has been skipped' %
122                                update.title)
123                     continue
124 
125                 updates.append(update)
126 
127             composes = Compose.from_updates(updates)
128             for c in composes:
129                 session.add(c)
130 
131             # We need to flush so the database knows about the new Compose objects, so the
132             # Compose.updates relationship will work properly. This is due to us overriding the
133             # primaryjoin on the relationship between Composes and Updates.
134             session.flush()
135 
136             # Now we need to refresh the composes so their updates property will not be empty.
137             for compose in composes:
138                 session.refresh(compose)
139 
140         # Now we need to sort the composes so their security property can be used to prioritize
141         # security updates. The security property relies on the updates property being
142         # non-empty, so this must happen after the refresh above.
143         composes = sorted(composes)
144 
145         for compose in composes:
146             click.echo('\n\n===== {} =====\n'.format(compose))
147             for update in compose.updates:
148                 click.echo(update.title)
149 
150         if composes:
151             click.confirm('\n\nPush these {:d} updates?'.format(
152                 sum([len(c.updates) for c in composes])), abort=True)
153             click.echo('\nLocking updates...')
154         else:
155             click.echo('\nThere are no updates to push.')
156 
157         composes = [c.__json__(composer=True) for c in composes]
158 
159     if composes:
160         click.echo('\nSending masher.start fedmsg')
161         # Because we're a script, we want to send to the fedmsg-relay,
162         # that's why we say active=True
163         bodhi.server.notifications.init(active=True, cert_prefix=cert_prefix)
164         bodhi.server.notifications.publish(
165             topic='masher.start',
166             msg=dict(
167                 api_version=2,
168                 composes=composes,
169                 resume=resume,
170                 agent=username,
171             ),
172             force=True,
173         )
174 
175 
176 def _filter_releases(session, query, releases=None):
177     """
178     Filter the given query by releases.
179 
180     Apply a filter() transformation to the given query on Updates to filter updates that match the
181     given releases argument. If releases evaluates "Falsey", this function will filter for Updates
182     that are part of a current Release.
183 
184     :param session:  The database session
185     :param query:    An Update query that we want to modify by filtering based on Releases
186     :param releases: A comma-separated string of release names
187 
188     :returns:        A filtered version of query with an additional filter based on releases.
189     """
190     # We will store models.Release object here that we want to filter by
191     _releases = []
192 
193     if releases:
194         for r in releases.split(','):
195             release = session.query(Release).filter(
196                 or_(Release.name == r,
197                     Release.name == r.upper(),
198                     Release.version == r)).first()
199             if not release:
200                 raise click.BadParameter('Unknown release: %s' % r)
201             else:
202                 _releases.append(release)
203     else:
204         # Since the user didn't ask for specific Releases, let's just filter for releases that are
205         # current or pending.
206         _releases = session.query(Release).filter(or_(Release.state == ReleaseState.current,
207                                                       Release.state == ReleaseState.pending))
208 
209     return query.filter(or_(*[Update.release == r for r in _releases]))
210 
211 
212 if __name__ == '__main__':
213     push()
214
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/bodhi/server/push.py b/bodhi/server/push.py
--- a/bodhi/server/push.py
+++ b/bodhi/server/push.py
@@ -61,8 +61,10 @@
 @click.option('--resume', help='Resume one or more previously failed pushes',
               is_flag=True, default=False)
 @click.option('--username', prompt=True)
+@click.option('--yes', '-y', is_flag=True, default=False,
+              help='Answers yes to the various questions')
 @click.version_option(message='%(version)s')
-def push(username, cert_prefix, **kwargs):
+def push(username, cert_prefix, yes, **kwargs):
     """Push builds out to the repositories."""
     resume = kwargs.pop('resume')
     resume_all = False
@@ -72,8 +74,14 @@
     composes = []
     with db_factory() as session:
         if not resume and session.query(Compose).count():
-            click.confirm('Existing composes detected: {}. Do you wish to resume them all?'.format(
-                ', '.join([str(c) for c in session.query(Compose).all()])), abort=True)
+            if yes:
+                click.echo('Existing composes detected: {}. Resuming all.'.format(
+                    ', '.join([str(c) for c in session.query(Compose).all()])))
+            else:
+                click.confirm(
+                    'Existing composes detected: {}. Do you wish to resume them all?'.format(
+                        ', '.join([str(c) for c in session.query(Compose).all()])),
+                    abort=True)
             resume = True
             resume_all = True
 
@@ -90,8 +98,11 @@
                     session.delete(compose)
                     continue
 
-                if not resume_all and not click.confirm('Resume {}?'.format(compose)):
-                    continue
+                if not resume_all:
+                    if yes:
+                        click.echo('Resuming {}.'.format(compose))
+                    elif not click.confirm('Resume {}?'.format(compose)):
+                        continue
 
                 # Reset the Compose's state and error message.
                 compose.state = ComposeState.requested
@@ -148,8 +159,12 @@
                 click.echo(update.title)
 
         if composes:
-            click.confirm('\n\nPush these {:d} updates?'.format(
-                sum([len(c.updates) for c in composes])), abort=True)
+            if yes:
+                click.echo('\n\nPushing {:d} updates.'.format(
+                    sum([len(c.updates) for c in composes])))
+            else:
+                click.confirm('\n\nPush these {:d} updates?'.format(
+                    sum([len(c.updates) for c in composes])), abort=True)
             click.echo('\nLocking updates...')
         else:
             click.echo('\nThere are no updates to push.')

{"golden_diff": "diff --git a/bodhi/server/push.py b/bodhi/server/push.py\n--- a/bodhi/server/push.py\n+++ b/bodhi/server/push.py\n@@ -61,8 +61,10 @@\n @click.option('--resume', help='Resume one or more previously failed pushes',\n               is_flag=True, default=False)\n @click.option('--username', prompt=True)\n+@click.option('--yes', '-y', is_flag=True, default=False,\n+              help='Answers yes to the various questions')\n @click.version_option(message='%(version)s')\n-def push(username, cert_prefix, **kwargs):\n+def push(username, cert_prefix, yes, **kwargs):\n     \"\"\"Push builds out to the repositories.\"\"\"\n     resume = kwargs.pop('resume')\n     resume_all = False\n@@ -72,8 +74,14 @@\n     composes = []\n     with db_factory() as session:\n         if not resume and session.query(Compose).count():\n-            click.confirm('Existing composes detected: {}. Do you wish to resume them all?'.format(\n-                ', '.join([str(c) for c in session.query(Compose).all()])), abort=True)\n+            if yes:\n+                click.echo('Existing composes detected: {}. Resuming all.'.format(\n+                    ', '.join([str(c) for c in session.query(Compose).all()])))\n+            else:\n+                click.confirm(\n+                    'Existing composes detected: {}. Do you wish to resume them all?'.format(\n+                        ', '.join([str(c) for c in session.query(Compose).all()])),\n+                    abort=True)\n             resume = True\n             resume_all = True\n \n@@ -90,8 +98,11 @@\n                     session.delete(compose)\n                     continue\n \n-                if not resume_all and not click.confirm('Resume {}?'.format(compose)):\n-                    continue\n+                if not resume_all:\n+                    if yes:\n+                        click.echo('Resuming {}.'.format(compose))\n+                    elif not click.confirm('Resume {}?'.format(compose)):\n+                        continue\n \n                 # Reset the Compose's state and error message.\n                 compose.state = ComposeState.requested\n@@ -148,8 +159,12 @@\n                 click.echo(update.title)\n \n         if composes:\n-            click.confirm('\\n\\nPush these {:d} updates?'.format(\n-                sum([len(c.updates) for c in composes])), abort=True)\n+            if yes:\n+                click.echo('\\n\\nPushing {:d} updates.'.format(\n+                    sum([len(c.updates) for c in composes])))\n+            else:\n+                click.confirm('\\n\\nPush these {:d} updates?'.format(\n+                    sum([len(c.updates) for c in composes])), abort=True)\n             click.echo('\\nLocking updates...')\n         else:\n             click.echo('\\nThere are no updates to push.')\n", "issue": "Give bodhi-push a -y flag so that it can be run in cron\nWe should give ```bodhi-push``` a -y flag that answers yes to the various questions so that it can be run in cron.\nGive bodhi-push a -y flag so that it can be run in cron\nWe should give ```bodhi-push``` a -y flag that answers yes to the various questions so that it can be run in cron.\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright \u00a9 2007-2018 Red Hat, Inc.\n#\n# This file is part of Bodhi.\n#\n# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n\"\"\"The CLI tool for triggering update pushes.\"\"\"\nfrom sqlalchemy.sql import or_\nimport click\n\nfrom bodhi.server import (buildsys, initialize_db, get_koji)\nfrom bodhi.server.config import config\nfrom bodhi.server.models import (Compose, ComposeState, Release, ReleaseState, Build, Update,\n                                 UpdateRequest)\nfrom bodhi.server.util import transactional_session_maker\nimport bodhi.server.notifications\n\n\n_koji = None\n\n\ndef update_sig_status(update):\n    \"\"\"Update build signature status for builds in update.\"\"\"\n    global _koji\n    if _koji is None:\n        # We don't want to authenticate to the buildsystem, because this script is often mistakenly\n        # run as root and this can cause the ticket cache to become root owned with 0600 perms,\n        # which will cause the compose to fail when it tries to use it to authenticate to Koji.\n        buildsys.setup_buildsystem(config, authenticate=False)\n        _koji = get_koji(None)\n    for build in update.builds:\n        if not build.signed:\n            build_tags = build.get_tags(_koji)\n            if update.release.pending_signing_tag not in build_tags:\n                click.echo('Build %s was refreshed as signed' % build.nvr)\n                build.signed = True\n            else:\n                click.echo('Build %s still unsigned' % build.nvr)\n\n\n@click.command()\n@click.option('--builds', help='Push updates for a comma-separated list of builds')\n@click.option('--cert-prefix', default=\"shell\",\n              help=\"The prefix of a fedmsg cert used to sign the message\")\n@click.option('--releases', help=('Push updates for a comma-separated list of releases (default: '\n                                  'current and pending releases)'))\n@click.option('--request', default='testing,stable',\n              help='Push updates with a specific request (default: testing,stable)')\n@click.option('--resume', help='Resume one or more previously failed pushes',\n              is_flag=True, default=False)\n@click.option('--username', prompt=True)\n@click.version_option(message='%(version)s')\ndef push(username, cert_prefix, **kwargs):\n    \"\"\"Push builds out to the repositories.\"\"\"\n    resume = kwargs.pop('resume')\n    resume_all = False\n\n    initialize_db(config)\n    db_factory = transactional_session_maker()\n    composes = []\n    with db_factory() as session:\n        if not resume and session.query(Compose).count():\n            click.confirm('Existing composes detected: {}. Do you wish to resume them all?'.format(\n                ', '.join([str(c) for c in session.query(Compose).all()])), abort=True)\n            resume = True\n            resume_all = True\n\n        # If we're resuming a push\n        if resume:\n            for compose in session.query(Compose).all():\n                if len(compose.updates) == 0:\n                    # Compose objects can end up with 0 updates in them if the masher ejects all the\n                    # updates in a compose for some reason. Composes with no updates cannot be\n                    # serialized because their content_type property uses the content_type of the\n                    # first update in the Compose. Additionally, it doesn't really make sense to go\n                    # forward with running an empty Compose. It makes the most sense to delete them.\n                    click.echo(\"{} has no updates. It is being removed.\".format(compose))\n                    session.delete(compose)\n                    continue\n\n                if not resume_all and not click.confirm('Resume {}?'.format(compose)):\n                    continue\n\n                # Reset the Compose's state and error message.\n                compose.state = ComposeState.requested\n                compose.error_message = u''\n\n                composes.append(compose)\n        else:\n            updates = []\n            # Accept both comma and space separated request list\n            requests = kwargs['request'].replace(',', ' ').split(' ')\n            requests = [UpdateRequest.from_string(val) for val in requests]\n\n            query = session.query(Update).filter(Update.request.in_(requests))\n\n            if kwargs.get('builds'):\n                query = query.join(Update.builds)\n                query = query.filter(\n                    or_(*[Build.nvr == build for build in kwargs['builds'].split(',')]))\n\n            query = _filter_releases(session, query, kwargs.get('releases'))\n\n            for update in query.all():\n                # Skip unsigned updates (this checks that all builds in the update are signed)\n                update_sig_status(update)\n\n                if not update.signed:\n                    click.echo('Warning: %s has unsigned builds and has been skipped' %\n                               update.title)\n                    continue\n\n                updates.append(update)\n\n            composes = Compose.from_updates(updates)\n            for c in composes:\n                session.add(c)\n\n            # We need to flush so the database knows about the new Compose objects, so the\n            # Compose.updates relationship will work properly. This is due to us overriding the\n            # primaryjoin on the relationship between Composes and Updates.\n            session.flush()\n\n            # Now we need to refresh the composes so their updates property will not be empty.\n            for compose in composes:\n                session.refresh(compose)\n\n        # Now we need to sort the composes so their security property can be used to prioritize\n        # security updates. The security property relies on the updates property being\n        # non-empty, so this must happen after the refresh above.\n        composes = sorted(composes)\n\n        for compose in composes:\n            click.echo('\\n\\n===== {} =====\\n'.format(compose))\n            for update in compose.updates:\n                click.echo(update.title)\n\n        if composes:\n            click.confirm('\\n\\nPush these {:d} updates?'.format(\n                sum([len(c.updates) for c in composes])), abort=True)\n            click.echo('\\nLocking updates...')\n        else:\n            click.echo('\\nThere are no updates to push.')\n\n        composes = [c.__json__(composer=True) for c in composes]\n\n    if composes:\n        click.echo('\\nSending masher.start fedmsg')\n        # Because we're a script, we want to send to the fedmsg-relay,\n        # that's why we say active=True\n        bodhi.server.notifications.init(active=True, cert_prefix=cert_prefix)\n        bodhi.server.notifications.publish(\n            topic='masher.start',\n            msg=dict(\n                api_version=2,\n                composes=composes,\n                resume=resume,\n                agent=username,\n            ),\n            force=True,\n        )\n\n\ndef _filter_releases(session, query, releases=None):\n    \"\"\"\n    Filter the given query by releases.\n\n    Apply a filter() transformation to the given query on Updates to filter updates that match the\n    given releases argument. If releases evaluates \"Falsey\", this function will filter for Updates\n    that are part of a current Release.\n\n    :param session:  The database session\n    :param query:    An Update query that we want to modify by filtering based on Releases\n    :param releases: A comma-separated string of release names\n\n    :returns:        A filtered version of query with an additional filter based on releases.\n    \"\"\"\n    # We will store models.Release object here that we want to filter by\n    _releases = []\n\n    if releases:\n        for r in releases.split(','):\n            release = session.query(Release).filter(\n                or_(Release.name == r,\n                    Release.name == r.upper(),\n                    Release.version == r)).first()\n            if not release:\n                raise click.BadParameter('Unknown release: %s' % r)\n            else:\n                _releases.append(release)\n    else:\n        # Since the user didn't ask for specific Releases, let's just filter for releases that are\n        # current or pending.\n        _releases = session.query(Release).filter(or_(Release.state == ReleaseState.current,\n                                                      Release.state == ReleaseState.pending))\n\n    return query.filter(or_(*[Update.release == r for r in _releases]))\n\n\nif __name__ == '__main__':\n    push()\n", "path": "bodhi/server/push.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright \u00a9 2007-2018 Red Hat, Inc.\n#\n# This file is part of Bodhi.\n#\n# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n\"\"\"The CLI tool for triggering update pushes.\"\"\"\nfrom sqlalchemy.sql import or_\nimport click\n\nfrom bodhi.server import (buildsys, initialize_db, get_koji)\nfrom bodhi.server.config import config\nfrom bodhi.server.models import (Compose, ComposeState, Release, ReleaseState, Build, Update,\n                                 UpdateRequest)\nfrom bodhi.server.util import transactional_session_maker\nimport bodhi.server.notifications\n\n\n_koji = None\n\n\ndef update_sig_status(update):\n    \"\"\"Update build signature status for builds in update.\"\"\"\n    global _koji\n    if _koji is None:\n        # We don't want to authenticate to the buildsystem, because this script is often mistakenly\n        # run as root and this can cause the ticket cache to become root owned with 0600 perms,\n        # which will cause the compose to fail when it tries to use it to authenticate to Koji.\n        buildsys.setup_buildsystem(config, authenticate=False)\n        _koji = get_koji(None)\n    for build in update.builds:\n        if not build.signed:\n            build_tags = build.get_tags(_koji)\n            if update.release.pending_signing_tag not in build_tags:\n                click.echo('Build %s was refreshed as signed' % build.nvr)\n                build.signed = True\n            else:\n                click.echo('Build %s still unsigned' % build.nvr)\n\n\n@click.command()\n@click.option('--builds', help='Push updates for a comma-separated list of builds')\n@click.option('--cert-prefix', default=\"shell\",\n              help=\"The prefix of a fedmsg cert used to sign the message\")\n@click.option('--releases', help=('Push updates for a comma-separated list of releases (default: '\n                                  'current and pending releases)'))\n@click.option('--request', default='testing,stable',\n              help='Push updates with a specific request (default: testing,stable)')\n@click.option('--resume', help='Resume one or more previously failed pushes',\n              is_flag=True, default=False)\n@click.option('--username', prompt=True)\n@click.option('--yes', '-y', is_flag=True, default=False,\n              help='Answers yes to the various questions')\n@click.version_option(message='%(version)s')\ndef push(username, cert_prefix, yes, **kwargs):\n    \"\"\"Push builds out to the repositories.\"\"\"\n    resume = kwargs.pop('resume')\n    resume_all = False\n\n    initialize_db(config)\n    db_factory = transactional_session_maker()\n    composes = []\n    with db_factory() as session:\n        if not resume and session.query(Compose).count():\n            if yes:\n                click.echo('Existing composes detected: {}. Resuming all.'.format(\n                    ', '.join([str(c) for c in session.query(Compose).all()])))\n            else:\n                click.confirm(\n                    'Existing composes detected: {}. Do you wish to resume them all?'.format(\n                        ', '.join([str(c) for c in session.query(Compose).all()])),\n                    abort=True)\n            resume = True\n            resume_all = True\n\n        # If we're resuming a push\n        if resume:\n            for compose in session.query(Compose).all():\n                if len(compose.updates) == 0:\n                    # Compose objects can end up with 0 updates in them if the masher ejects all the\n                    # updates in a compose for some reason. Composes with no updates cannot be\n                    # serialized because their content_type property uses the content_type of the\n                    # first update in the Compose. Additionally, it doesn't really make sense to go\n                    # forward with running an empty Compose. It makes the most sense to delete them.\n                    click.echo(\"{} has no updates. It is being removed.\".format(compose))\n                    session.delete(compose)\n                    continue\n\n                if not resume_all:\n                    if yes:\n                        click.echo('Resuming {}.'.format(compose))\n                    elif not click.confirm('Resume {}?'.format(compose)):\n                        continue\n\n                # Reset the Compose's state and error message.\n                compose.state = ComposeState.requested\n                compose.error_message = u''\n\n                composes.append(compose)\n        else:\n            updates = []\n            # Accept both comma and space separated request list\n            requests = kwargs['request'].replace(',', ' ').split(' ')\n            requests = [UpdateRequest.from_string(val) for val in requests]\n\n            query = session.query(Update).filter(Update.request.in_(requests))\n\n            if kwargs.get('builds'):\n                query = query.join(Update.builds)\n                query = query.filter(\n                    or_(*[Build.nvr == build for build in kwargs['builds'].split(',')]))\n\n            query = _filter_releases(session, query, kwargs.get('releases'))\n\n            for update in query.all():\n                # Skip unsigned updates (this checks that all builds in the update are signed)\n                update_sig_status(update)\n\n                if not update.signed:\n                    click.echo('Warning: %s has unsigned builds and has been skipped' %\n                               update.title)\n                    continue\n\n                updates.append(update)\n\n            composes = Compose.from_updates(updates)\n            for c in composes:\n                session.add(c)\n\n            # We need to flush so the database knows about the new Compose objects, so the\n            # Compose.updates relationship will work properly. This is due to us overriding the\n            # primaryjoin on the relationship between Composes and Updates.\n            session.flush()\n\n            # Now we need to refresh the composes so their updates property will not be empty.\n            for compose in composes:\n                session.refresh(compose)\n\n        # Now we need to sort the composes so their security property can be used to prioritize\n        # security updates. The security property relies on the updates property being\n        # non-empty, so this must happen after the refresh above.\n        composes = sorted(composes)\n\n        for compose in composes:\n            click.echo('\\n\\n===== {} =====\\n'.format(compose))\n            for update in compose.updates:\n                click.echo(update.title)\n\n        if composes:\n            if yes:\n                click.echo('\\n\\nPushing {:d} updates.'.format(\n                    sum([len(c.updates) for c in composes])))\n            else:\n                click.confirm('\\n\\nPush these {:d} updates?'.format(\n                    sum([len(c.updates) for c in composes])), abort=True)\n            click.echo('\\nLocking updates...')\n        else:\n            click.echo('\\nThere are no updates to push.')\n\n        composes = [c.__json__(composer=True) for c in composes]\n\n    if composes:\n        click.echo('\\nSending masher.start fedmsg')\n        # Because we're a script, we want to send to the fedmsg-relay,\n        # that's why we say active=True\n        bodhi.server.notifications.init(active=True, cert_prefix=cert_prefix)\n        bodhi.server.notifications.publish(\n            topic='masher.start',\n            msg=dict(\n                api_version=2,\n                composes=composes,\n                resume=resume,\n                agent=username,\n            ),\n            force=True,\n        )\n\n\ndef _filter_releases(session, query, releases=None):\n    \"\"\"\n    Filter the given query by releases.\n\n    Apply a filter() transformation to the given query on Updates to filter updates that match the\n    given releases argument. If releases evaluates \"Falsey\", this function will filter for Updates\n    that are part of a current Release.\n\n    :param session:  The database session\n    :param query:    An Update query that we want to modify by filtering based on Releases\n    :param releases: A comma-separated string of release names\n\n    :returns:        A filtered version of query with an additional filter based on releases.\n    \"\"\"\n    # We will store models.Release object here that we want to filter by\n    _releases = []\n\n    if releases:\n        for r in releases.split(','):\n            release = session.query(Release).filter(\n                or_(Release.name == r,\n                    Release.name == r.upper(),\n                    Release.version == r)).first()\n            if not release:\n                raise click.BadParameter('Unknown release: %s' % r)\n            else:\n                _releases.append(release)\n    else:\n        # Since the user didn't ask for specific Releases, let's just filter for releases that are\n        # current or pending.\n        _releases = session.query(Release).filter(or_(Release.state == ReleaseState.current,\n                                                      Release.state == ReleaseState.pending))\n\n    return query.filter(or_(*[Update.release == r for r in _releases]))\n\n\nif __name__ == '__main__':\n    push()\n", "path": "bodhi/server/push.py"}]}