diff --git a/client/create_repo_github.py b/client/create_repo_github.py index 06a6e9b..bc93452 100644 --- a/client/create_repo_github.py +++ b/client/create_repo_github.py @@ -32,7 +32,7 @@ ] if len(sys.argv) != 3: - print 'Usage: {program} '.format(program=sys.argv[0]) + print('Usage: {program} '.format(program=sys.argv[0])) sys.exit(1) orga, repository = sys.argv[1:] @@ -53,13 +53,13 @@ # https://developer.github.com/v3/#pagination try: repo = get('https://api.github.com/repos/{orga}/{repository}') - print fmt('Repository exists: {repository}') -except subprocess.CalledProcessError, exc: + print(fmt('Repository exists: {repository}')) +except subprocess.CalledProcessError as exc: if "404 Not Found" in exc.output: - print fmt('Creating repository: {repository}') + print(fmt('Creating repository: {repository}')) post('https://api.github.com/orgs/{orga}/repos', {'name': repository}) else: - print exc.output + print(exc.output) sys.exit(1) wait() @@ -77,10 +77,10 @@ 'config': {'url': url, 'content_type': 'json', }, 'events': ['push', 'pull_request'], } if url not in existing_hooks: - print fmt('Adding hook: {url}') + print(fmt('Adding hook: {url}')) post('https://api.github.com/repos/{orga}/{repository}/hooks', config) wait() else: - print fmt('Updating existing hook: {url}') + print(fmt('Updating existing hook: {url}')) patch('https://api.github.com/repos/{orga}/{repository}/hooks/%s' % existing_hooks[url], config) wait() diff --git a/rpm/boss-launcher-webhook.spec b/rpm/boss-launcher-webhook.spec index a6cb856..9b02a50 100644 --- a/rpm/boss-launcher-webhook.spec +++ b/rpm/boss-launcher-webhook.spec @@ -11,17 +11,21 @@ Source: %{name}-%{version}.tar.gz BuildArch: noarch -BuildRequires: python-setuptools -BuildRequires: python-rpm-macros +BuildRequires: python3-setuptools +BuildRequires: python3-rpm-macros Requires(post): boss-standard-workflow-common -Requires(post): python-boss-skynet >= 0.6.6 -Requires: python-Django -Requires: python-boss-common >= 0.27.10 -Requires: python-djangorestframework -Requires: python-requests -Requires: python-xml -Requires: python2-django-filter +Requires(post): python3-boss-skynet >= 0.6.6 +Requires: python3-Django +Requires: python3-boss-common >= 0.27.10 +Requires: python3-djangorestframework +Requires: python3-requests +Requires: python3-xml +Requires: python3-django-filter +Requires: python3-mysqlclient +Requires: uwsgi-python3 +Recommends: boss-participant-create_project +Recommends: boss-participant-auto_promote Summary: VCS webhook handler @@ -37,60 +41,61 @@ This package provides the service to generate source from git inside an OBS sour %package -n obs-service-webhook Requires: obs-source_service -Requires: python-argparse -Requires: python-requests +Requires: python3-argparse +Requires: python3-requests Summary: OBS source service to manage webhooks %description -n obs-service-webhook This package provides the service to update webhooks from OBS. It ensures that only users who have access to a package can update the webhook for that package. %package -n boss-participant-trigger_service Requires(post): boss-standard-workflow-common -Requires(post): python-boss-skynet >= 0.6.6 -Requires: osc -Requires: python-boss-common >= 0.27.10 -Requires: python-lxml -Requires: python-yaml +Requires(post): python3-boss-skynet >= 0.6.6 +Requires: osc3 +Requires: python3-boss-common >= 0.27.10 +Requires: python3-lxml +Requires: python3-pyaml Summary: BOSS participant to handle webhooks %description -n boss-participant-trigger_service This package provides the participant that handles creating and/or triggering _service files in OBS, in response to webhook triggers %package -n boss-participant-create_project Requires(post): boss-standard-workflow-common -Requires(post): python-boss-skynet >= 0.6.6 +Requires(post): python3-boss-skynet >= 0.6.6 Requires: boss-launcher-webhook -Requires: osc -Requires: python-boss-common >= 0.27.10 -Requires: python-lxml +Requires: osc3 +Requires: python3-boss-common >= 0.27.10 +Requires: python3-lxml Summary: BOSS participant to handle webhooks %description -n boss-participant-create_project This package provides the participant that handles creating project files in OBS, in response to webhook triggers +It must run on the webhook server as it accesses the database. %package -n boss-participant-get_src_state Requires(post): boss-standard-workflow-common -Requires(post): python-boss-skynet >= 0.6.6 -Requires: python-boss-common >= 0.27.10 +Requires(post): python3-boss-skynet >= 0.6.6 +Requires: python3-boss-common >= 0.27.10 Summary: BOSS participant to handle webhooks %description -n boss-participant-get_src_state This package provides the participant that checks that there is src is ready to build in OBS projects. Usually this means the service has succeeded. %package -n boss-participant-auto_promote Requires(post): boss-standard-workflow-common -Requires(post): python-boss-skynet >= 0.6.6 +Requires(post): python3-boss-skynet >= 0.6.6 Requires: boss-launcher-webhook -Requires: python-boss-common >= 0.27.10 +Requires: python3-boss-common >= 0.27.10 Summary: BOSS participant to handle webhooks %description -n boss-participant-auto_promote This package provides the participant that handles promotion of gated projects, in response to webhook triggers - +It must run on the webhook server as it accesses the database. %prep %setup -q %{name}-%{version} %build -%python2_build +%python3_build %install -%python2_install +%python3_install make PREFIX=%{_prefix} DESTDIR=%{buildroot} install @@ -139,8 +144,8 @@ fi %config(noreplace) %{svdir}/delete_webhook.conf %config(noreplace) %{svdir}/handle_webhook.conf %config(noreplace) %{svdir}/relay_webhook.conf -%{python_sitelib}/webhook_launcher -%{python_sitelib}/*egg-info +%{python3_sitelib}/webhook_launcher +%{python3_sitelib}/*egg-info %{_datadir}/webhook_launcher %{_datadir}/boss-skynet/delete_webhook.py* %{_datadir}/boss-skynet/handle_webhook.py* diff --git a/src/manage.py b/src/manage.py index fd153e9..fe0d113 100755 --- a/src/manage.py +++ b/src/manage.py @@ -33,7 +33,7 @@ def launch_log(*args, **kwargs): ", ".join(repr(x) for x in args), ", ".join("\n%s=%s" % (k, repr(v)) for k, v in kwargs.items()), ] if x) - print "launch(%s)" % params + print("launch(%s)" % params) return DEFAULT diff --git a/src/participants/auto_promote.py b/src/participants/auto_promote.py index f7cd19c..77d8de1 100755 --- a/src/participants/auto_promote.py +++ b/src/participants/auto_promote.py @@ -40,7 +40,7 @@ """ from boss.obs import BuildServiceParticipant -from urllib2 import HTTPError +from urllib.error import HTTPError import os from boss.bz.config import parse_bz_config @@ -86,7 +86,7 @@ def handle_wi(self, wid): gated_project = f.gated_project if not project or not gated_project: - print "Nothing to do, no project or gated_project in the fields" + print("Nothing to do, no project or gated_project in the fields") wid.result = True return @@ -101,21 +101,21 @@ def handle_wi(self, wid): description = "%s @ %s" % ( webhook.tag or webhook.rev_or_head, str(webhook)) comment = "" - print "Requesting actions: %s\ndesc: %s" % (actions, description) + print("Requesting actions: %s\ndesc: %s" % (actions, description)) try: result = self.obs.createRequest( options_list=actions, description=description, comment=comment, supersede=True, opt_sourceupdate="cleanup") except HTTPError as e: - print "%s\n%s" % (e, e.read()) + print("%s\n%s" % (e, e.read())) result = None if not result: raise RuntimeError( "Something went wrong while creating project %s" % project) - print "Created submit request from %s/%s to %s/%s : %s" % (project, package, gated_project, package, description) + print("Created submit request from %s/%s to %s/%s : %s" % (project, package, gated_project, package, description)) else: - print "No gated Project matching gated_project: %s" % (gated_project) + print("No gated Project matching gated_project: %s" % (gated_project)) wid.result = True diff --git a/src/participants/create_project.py b/src/participants/create_project.py index 256060c..f159def 100644 --- a/src/participants/create_project.py +++ b/src/participants/create_project.py @@ -43,7 +43,7 @@ from boss.obs import BuildServiceParticipant import osc -from urlparse import urlparse +from urllib.parse import urlparse import os from lxml import etree import json @@ -135,14 +135,14 @@ def handle_wi(self, wid): # TODO: deduce project name from "official" mappings of the same repo # for now just short circuit here wid.result = True - print "No project given. Continuing" + print("No project given. Continuing") return # events for official projects that are gated get diverted to a side # project prjobj = Project.get_matching(project, self.obs.apiurl) if prjobj and prjobj.gated: - print "%s is gated" % prjobj + print("%s is gated" % prjobj) linked_project = project f.gated_project = project project += ":gate:%s" % package @@ -169,7 +169,7 @@ def handle_wi(self, wid): linked_project = ":".join(prj_parts[0:-3]) fea = "%s#%s" % (prj_parts[-2], prj_parts[-1]) # Go through each bugzilla we support - for (bugzillaname, bugzilla) in self.bzs.iteritems(): + for (bugzillaname, bugzilla) in self.bzs.items(): for match in bugzilla['compiled_re'].finditer(fea): bugnum = match.group('key') try: @@ -177,9 +177,9 @@ def handle_wi(self, wid): bugnum)['summary'] desc = bugzilla['interface'].comment_get( bugnum, 0)['text'] - except BugzillaError, error: + except BugzillaError as error: if error.code == 101: - print "Bug %s not found" % bugnum + print("Bug %s not found" % bugnum) else: raise if project not in project_list: @@ -214,17 +214,17 @@ def handle_wi(self, wid): if not result: raise RuntimeError( "Something went wrong while creating project %s" % project) - print "Created project %s" % project + print("Created project %s" % project) else: - print "Didn't need to create project %s" % project + print("Didn't need to create project %s" % project) wid.result = True try: self._set_blame_emails( project, package, get_or_none(LastSeenRevision, mapping_id=f.pk)) - except Exception, exc: - print "Ignoring exception: %s" % exc + except Exception as exc: + print("Ignoring exception: %s" % exc) pass def _set_blame_emails(self, project, package, lsr): diff --git a/src/participants/get_src_state.py b/src/participants/get_src_state.py index 127da44..c9463df 100644 --- a/src/participants/get_src_state.py +++ b/src/participants/get_src_state.py @@ -85,12 +85,12 @@ def handle_wi(self, wid): if f.project and f.package: project = f.project package = f.package - print "setting %s/%s from fields" % (project, package) + print("setting %s/%s from fields" % (project, package)) if p.project and p.package: project = p.project package = p.package - print "setting %s/%s from params" % (project, package) + print("setting %s/%s from params" % (project, package)) err = [] if not project: @@ -101,8 +101,8 @@ def handle_wi(self, wid): raise RuntimeError( "Missing mandatory field or parameter: %s" % ", ".join(err)) - print "Checking service for %s/%s" % (project, package) + print("Checking service for %s/%s" % (project, package)) f.service_state = self.obs.getServiceState(project, package) - print "State : %s" % f.service_state + print("State : %s" % f.service_state) if f.service_state == "succeeded": wid.result = True diff --git a/src/participants/handle_webhook.py b/src/participants/handle_webhook.py index 0940c82..84f22a2 100755 --- a/src/participants/handle_webhook.py +++ b/src/participants/handle_webhook.py @@ -63,13 +63,18 @@ def handle_wi(self, wid): if wid.fields.payload is None: raise RuntimeError("Missing mandatory field: payload") - md5 = hashlib.md5(json.dumps(wid.fields.payload.as_dict(), - sort_keys=True)).hexdigest() + md5 = hashlib.md5( + json.dumps(wid.fields.payload.as_dict(), + sort_keys=True).encode('utf8')).hexdigest() now = time.time() - # purge seen hashes + # purge seen hashes. Find then purge to avoid + # RuntimeError: dictionary changed size during iteration + aged = [] for seen_md5, seen_time in self.seen.items(): if now - seen_time > 30: - del self.seen[seen_md5] + aged.append(seen_md5) + for seen_md5 in aged: + del self.seen[seen_md5] if md5 in self.seen: print("Ignoring duplicate webhook (possible resend or " diff --git a/src/participants/trigger_service.py b/src/participants/trigger_service.py index d9b8601..40ff41f 100755 --- a/src/participants/trigger_service.py +++ b/src/participants/trigger_service.py @@ -47,11 +47,11 @@ """ from boss.obs import BuildServiceParticipant -from urlparse import urlparse +from urllib.parse import urlparse from osc import core -from StringIO import StringIO +from io import StringIO from lxml import etree -import urllib2 +import urllib.request, urllib.error, urllib.parse import yaml import re @@ -110,7 +110,7 @@ def make_constraint(package): for elem in ["disk", "memory"]: if elem in constraint: node = etree.SubElement(hardware, elem) - etree.SubElement(node, "size", unit="G").text = unicode(constraint[elem]) + etree.SubElement(node, "size", unit="G").text = str(constraint[elem]) return etree.tostring(constraints, pretty_print=True) @@ -263,9 +263,9 @@ def handle_wi(self, wid): u = core.makeurl(self.obs.apiurl, ['source', project, package, "_constraints"]) core.http_PUT(u, data=constraint_xml) - print "New _constraints file:\n%s" % constraint_xml + print("New _constraints file:\n%s" % constraint_xml) else: - print "No _constraints for %s" % package + print("No _constraints for %s" % package) # Start with an empty XML doc try: # to get any existing _service file. @@ -274,7 +274,7 @@ def handle_wi(self, wid): print("Trying to get _service file for %s/%s" % (project, package)) services_xml = self.obs.getFile( project, package, "_service", expand=0) - except urllib2.HTTPError as e: + except urllib.error.HTTPError as e: print("Exception %s trying to get _service file for %s/%s" % (e, project, package)) if e.code == 404: diff --git a/src/webhook_launcher/app/admin.py b/src/webhook_launcher/app/admin.py index 88c213b..90917d2 100644 --- a/src/webhook_launcher/app/admin.py +++ b/src/webhook_launcher/app/admin.py @@ -21,7 +21,7 @@ from django.contrib import admin, messages from django.contrib.auth.models import User -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import models from django.forms import TextInput from django.http import HttpResponseRedirect @@ -32,6 +32,7 @@ VCSNameSpace, VCSService, WebHookMapping ) from webhook_launcher.app.payload import get_payload +from functools import reduce class LastSeenRevisionInline(admin.StackedInline): diff --git a/src/webhook_launcher/app/boss.py b/src/webhook_launcher/app/boss.py index 9549a9c..a3bd3b6 100644 --- a/src/webhook_launcher/app/boss.py +++ b/src/webhook_launcher/app/boss.py @@ -33,7 +33,7 @@ def launch(process, fields): amqp_pass = settings.BOSS_PASS, amqp_vhost = settings.BOSS_VHOST) - print "launching to (%s,%s)" %(settings.BOSS_HOST, settings.BOSS_VHOST) + print("launching to (%s,%s)" %(settings.BOSS_HOST, settings.BOSS_VHOST)) launcher.launch(pdef, fields) def launch_queue(fields): diff --git a/src/webhook_launcher/app/migrations/0001_initial.py b/src/webhook_launcher/app/migrations/0001_initial.py index 8c4d9b9..8b189d4 100644 --- a/src/webhook_launcher/app/migrations/0001_initial.py +++ b/src/webhook_launcher/app/migrations/0001_initial.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # Generated by Django 1.10.1 on 2016-11-14 12:47 -from __future__ import unicode_literals + from django.conf import settings from django.db import migrations, models diff --git a/src/webhook_launcher/app/migrations/0002_auto_20170329_2140.py b/src/webhook_launcher/app/migrations/0002_auto_20170329_2140.py index 12f21f5..adbcd5e 100644 --- a/src/webhook_launcher/app/migrations/0002_auto_20170329_2140.py +++ b/src/webhook_launcher/app/migrations/0002_auto_20170329_2140.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # Generated by Django 1.10.5 on 2017-03-29 18:40 -from __future__ import unicode_literals + from django.db import migrations diff --git a/src/webhook_launcher/app/migrations/0003_auto_20170330_1606.py b/src/webhook_launcher/app/migrations/0003_auto_20170330_1606.py index e2890fc..4de4733 100644 --- a/src/webhook_launcher/app/migrations/0003_auto_20170330_1606.py +++ b/src/webhook_launcher/app/migrations/0003_auto_20170330_1606.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # Generated by Django 1.10.5 on 2017-03-30 13:06 -from __future__ import unicode_literals + from django.db import migrations, models diff --git a/src/webhook_launcher/app/migrations/0004_webhookmapping_placeholder.py b/src/webhook_launcher/app/migrations/0004_webhookmapping_placeholder.py index ea67df0..1d8d790 100644 --- a/src/webhook_launcher/app/migrations/0004_webhookmapping_placeholder.py +++ b/src/webhook_launcher/app/migrations/0004_webhookmapping_placeholder.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # Generated by Django 1.10.5 on 2017-03-30 13:11 -from __future__ import unicode_literals + from django.db import migrations, models diff --git a/src/webhook_launcher/app/migrations/0005_flag_old_placeholders.py b/src/webhook_launcher/app/migrations/0005_flag_old_placeholders.py index 1f6456c..4a68e8c 100644 --- a/src/webhook_launcher/app/migrations/0005_flag_old_placeholders.py +++ b/src/webhook_launcher/app/migrations/0005_flag_old_placeholders.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from functools import partial diff --git a/src/webhook_launcher/app/migrations/0006_auto_20200429_0456.py b/src/webhook_launcher/app/migrations/0006_auto_20200429_0456.py new file mode 100644 index 0000000..d1abda4 --- /dev/null +++ b/src/webhook_launcher/app/migrations/0006_auto_20200429_0456.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2020-04-29 04:56 + + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0005_flag_old_placeholders'), + ] + + operations = [ + migrations.AlterField( + model_name='buildservice', + name='namespace', + field=models.CharField(help_text='This is also used to identify the OBS alias in BOSS processes', max_length=50, unique=True), + ), + migrations.AlterField( + model_name='project', + name='allowed', + field=models.BooleanField(default=True, help_text='If not set then webhooks are not allowed for this project. This is useful for projects which should only have specific versions of packages promoted to them.'), + ), + migrations.AlterField( + model_name='project', + name='gated', + field=models.BooleanField(default=False, help_text='If set then webhooks pointing at this project will be triggered to a side project instead and then an autopromotion attempted. This is useful for projects which apply formal entry checks and/or QA.'), + ), + migrations.AlterField( + model_name='project', + name='match', + field=models.CharField(blank=True, help_text='If set then used as well as name to re.match() project names', max_length=250, null=True), + ), + migrations.AlterField( + model_name='project', + name='name', + field=models.CharField(help_text='The OBS project name. eg nemo:mw', max_length=250), + ), + migrations.AlterField( + model_name='project', + name='official', + field=models.BooleanField(default=True, help_text='If set then only valid namespaces can be used for the git repo'), + ), + migrations.AlterField( + model_name='relaytarget', + name='active', + field=models.BooleanField(default=True, help_text='Whether this relay will fire on matching events'), + ), + migrations.AlterField( + model_name='relaytarget', + name='name', + field=models.CharField(help_text='Friendly name of recipient, for example: Organization name', max_length=50), + ), + migrations.AlterField( + model_name='relaytarget', + name='sources', + field=models.ManyToManyField(help_text='List of VCS namespaces (for example github organization or gitlab groups)', to='app.VCSNameSpace'), + ), + migrations.AlterField( + model_name='relaytarget', + name='url', + field=models.CharField(help_text='HTTP(S) endpoint which will receive POST of GIT events (for example http://webhook.example.com/webhook/)', max_length=200), + ), + migrations.AlterField( + model_name='relaytarget', + name='verify_SSL', + field=models.BooleanField(default=True, help_text='Turn on SSL certificate verification'), + ), + migrations.AlterField( + model_name='vcsnamespace', + name='default_project', + field=models.ForeignKey(blank=True, help_text='Default project for webhook placeholder creation', null=True, on_delete=django.db.models.deletion.CASCADE, to='app.Project'), + ), + migrations.AlterField( + model_name='vcsnamespace', + name='path', + field=models.CharField(help_text='the network path (gitlab group or github organization eg. /mer-core)', max_length=200), + ), + migrations.AlterField( + model_name='vcsnamespace', + name='service', + field=models.ForeignKey(help_text='VCS service where this namespace is hosted', on_delete=django.db.models.deletion.CASCADE, to='app.VCSService'), + ), + migrations.AlterField( + model_name='vcsservice', + name='ips', + field=models.TextField(blank=True, help_text='Known IP adresses of this service (optional)', null=True), + ), + migrations.AlterField( + model_name='vcsservice', + name='name', + field=models.CharField(help_text='Friendly name of this VCS hosting service', max_length=50, unique=True), + ), + migrations.AlterField( + model_name='vcsservice', + name='netloc', + field=models.CharField(help_text='Network location from payload (for example: git@git.merproject.org:1234)', max_length=200, unique=True), + ), + migrations.AlterField( + model_name='webhookmapping', + name='branch', + field=models.CharField(default='master', help_text='name of branch to use. If not specified default branch (or currently checked out one) will be used', max_length=100), + ), + migrations.AlterField( + model_name='webhookmapping', + name='build', + field=models.BooleanField(default=True, help_text='Enable OBS build triggering'), + ), + migrations.AlterField( + model_name='webhookmapping', + name='comment', + field=models.TextField(blank=True, default='', null=True), + ), + migrations.AlterField( + model_name='webhookmapping', + name='debian', + field=models.CharField(blank=True, choices=[('N', 'N'), ('Y', 'Y')], default='', help_text='Choose Y to turn on debian packaging support', max_length=2, null=True), + ), + migrations.AlterField( + model_name='webhookmapping', + name='dumb', + field=models.CharField(blank=True, choices=[('N', 'N'), ('Y', 'Y')], default='', help_text='Choose Y to take content of revision as-is without automatic processing (example: tarballs in git)', max_length=2, null=True), + ), + migrations.AlterField( + model_name='webhookmapping', + name='notify', + field=models.BooleanField(default=True, help_text='Enable IRC notifications of events'), + ), + migrations.AlterField( + model_name='webhookmapping', + name='package', + field=models.CharField(help_text='name of the package to create or update in OBS', max_length=250), + ), + migrations.AlterField( + model_name='webhookmapping', + name='placeholder', + field=models.BooleanField(default=False, editable=False, help_text='Marks automatically created placeholders for filtering in admin UI'), + ), + migrations.AlterField( + model_name='webhookmapping', + name='project', + field=models.CharField(help_text='name of an existing project under which to create or update the package', max_length=250), + ), + migrations.AlterField( + model_name='webhookmapping', + name='repourl', + field=models.CharField(help_text='url of git repo to clone from. Should be a remote http[s]', max_length=200), + ), + migrations.AlterField( + model_name='webhookmapping', + name='token', + field=models.CharField(blank=True, default='', help_text='a token that should exist in tag names and changelog entry headers to enable handling them', max_length=100, null=True), + ), + ] diff --git a/src/webhook_launcher/app/misc.py b/src/webhook_launcher/app/misc.py index 338743a..b16f66b 100644 --- a/src/webhook_launcher/app/misc.py +++ b/src/webhook_launcher/app/misc.py @@ -1,4 +1,4 @@ -import urlparse +import urllib.parse as urlparse import requests from django.conf import settings diff --git a/src/webhook_launcher/app/models.py b/src/webhook_launcher/app/models.py index 2065429..c1a6c09 100644 --- a/src/webhook_launcher/app/models.py +++ b/src/webhook_launcher/app/models.py @@ -52,7 +52,7 @@ class BuildService(models.Model): unique=True, ) - def __unicode__(self): + def __str__(self): return self.weburl @@ -74,7 +74,7 @@ class VCSService(models.Model): help_text="Known IP adresses of this service (optional)", ) - def __unicode__(self): + def __str__(self): return self.netloc @@ -82,6 +82,7 @@ class VCSNameSpace(models.Model): service = models.ForeignKey( VCSService, help_text="VCS service where this namespace is hosted", + on_delete=models.CASCADE, ) path = models.CharField( max_length=200, @@ -93,10 +94,11 @@ class VCSNameSpace(models.Model): blank=True, null=True, help_text="Default project for webhook placeholder creation", + on_delete=models.CASCADE, ) - def __unicode__(self): - return "%s%s" % (self.service, self.path) + def __str__(self): + return f"{self.service}{self.path}" @staticmethod def find(repourl): @@ -115,6 +117,7 @@ class Project(models.Model): ) obs = models.ForeignKey( BuildService, + on_delete=models.CASCADE, ) official = models.BooleanField( default=True, @@ -153,8 +156,8 @@ class Project(models.Model): class Meta: unique_together = (("name", "obs"),) - def __unicode__(self): - return "%s on %s" % (self.name, self.obs) + def __str__(self): + return f"{self.name} on {self.obs}" def is_repourl_allowed(self, repourl): @@ -280,15 +283,15 @@ class WebHookMapping(models.Model): user = models.ForeignKey( User, editable=False, + on_delete=models.CASCADE, ) obs = models.ForeignKey( BuildService, + on_delete=models.CASCADE, ) - def __unicode__(self): - return "%s/%s -> %s/%s" % ( - self.repourl, self.branch, self.project, self.package - ) + def __str__(self): + return f"{self.repourl}/{self.branch} -> {self.project}/{self.package}" @property def tag(self): @@ -327,9 +330,8 @@ def project_disabled(self): # Just search all Projects for a match for project in Project.objects.all(): if project.matches(self.project): - print "Project disable check: %s matches rules in %s" % ( - self.project, project.name - ) + print("Project disable check: %s matches rules in %s" % + (self.project, project.name)) if project and not project.allowed: # Disabled if Project is marked not-allowed return True @@ -431,12 +433,12 @@ def trigger_build(self, user=None, tag=None, force=False): # rely on validation since a Project may forbid hooks after # the hook was created if self.project_disabled: - print "Project has build disabled" + print("Project has build disabled") return handled = self.lsr.handled and self.lsr.tag == tag and not force if handled: - print "build already handled, skipping" + print("build already handled, skipping") build = self.build and self.mapped and not handled qp = None if user is None: @@ -453,8 +455,8 @@ def trigger_build(self, user=None, tag=None, force=False): ) for qp in qps: if qp.delay() and not qp.override(webuser=user): - print "Build trigger for %s delayed by %s" % (self, qp) - print qp.comment + print("Build trigger for %s delayed by %s" % (self, qp)) + print(qp.comment) build = False break else: @@ -540,7 +542,7 @@ def handle_commit(self, user=None, notify=None): fields = self.to_fields() fields['msg'] = message - print message.encode('utf-8') + print(message.encode('utf-8')) launch_notify(fields) def to_fields(self): @@ -570,6 +572,7 @@ def to_fields(self): class LastSeenRevision(models.Model): mapping = models.ForeignKey( WebHookMapping, + on_delete=models.CASCADE, ) revision = models.CharField( max_length=250, @@ -597,10 +600,9 @@ class LastSeenRevision(models.Model): editable=False, ) - def __unicode__(self): - return "%s @ %s/%s" % ( - self.revision, self.mapping.repourl, self.mapping.branch - ) + def __str__(self): + return(f"{self.revision} @ " + f"{self.mapping.repourl}/{self.mapping.branch}") class QueuePeriod(models.Model): @@ -634,12 +636,11 @@ class Meta: ("can_override_queueperiod", "Can override queue periods"), ) - def __unicode__(self): - return "Queue period from %s %s to %s %s for %s" % ( - self.start_date or "", self.start_time, self.end_date or "", - self.end_time, - ",".join([str(prj) for prj in self.projects.all()]) - ) + def __str__(self): + return(f"Queue period " + f"from {self.start_date or ''} {self.start_time} " + f"to {self.end_date or ''} {self.end_time} " + f"for {','.join([str(prj) for prj in self.projects.all()])}") def override(self, user): if not user: @@ -694,5 +695,5 @@ class RelayTarget(models.Model): "(for example github organization or gitlab groups)", ) - def __unicode__(self): - return "%s webhook relay" % self.name + def __str__(self): + return f"{self.name} webhook relay" diff --git a/src/webhook_launcher/app/payload.py b/src/webhook_launcher/app/payload.py index 2220e30..e5ec9ff 100644 --- a/src/webhook_launcher/app/payload.py +++ b/src/webhook_launcher/app/payload.py @@ -16,11 +16,11 @@ # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import print_function + import json import os -import urlparse +import urllib.parse as urlparse import requests from django.conf import settings @@ -405,7 +405,7 @@ def handle(self): tag_branches.add(branch['name']) # Handle commits in branches - for branch, (revision, commits) in branches.iteritems(): + for branch, (revision, commits) in branches.items(): mapobjs = WebHookMapping.objects.filter( repourl=self.url, branch=branch, ) @@ -440,7 +440,7 @@ def handle(self): notified = True # Handle tags - for tag, (revision, branches) in tags.iteritems(): + for tag, (revision, branches) in tags.items(): if not branches: print("No branch found for tag '%s'" % tag) continue diff --git a/src/webhook_launcher/app/test/test_webhookmapping.py b/src/webhook_launcher/app/test/test_webhookmapping.py index e429ef4..0d5503e 100644 --- a/src/webhook_launcher/app/test/test_webhookmapping.py +++ b/src/webhook_launcher/app/test/test_webhookmapping.py @@ -65,7 +65,7 @@ def test_uniqueness_form(self): self.assertFalse(form.is_valid()) self.assertIn( "already exists", - u"|".join(unicode(x) for x in form.errors.values()) + "|".join(str(x) for x in list(form.errors.values())) ) test_data = data.copy() diff --git a/src/webhook_launcher/app/views.py b/src/webhook_launcher/app/views.py index 5172fcd..10e63fa 100644 --- a/src/webhook_launcher/app/views.py +++ b/src/webhook_launcher/app/views.py @@ -34,7 +34,7 @@ ) from django.shortcuts import render from rest_framework import permissions, status, viewsets -from rest_framework.decorators import detail_route +from rest_framework.decorators import action from rest_framework.response import Response from webhook_launcher.app.boss import launch_queue @@ -119,9 +119,8 @@ def collect_pname(P1, pname1): if settings.POST_IP_FILTER_HAS_REV_PROXY: # Take the last value only to avoid spoofing ip = request.META["HTTP_X_FORWARDED_FOR"].split(",")[-1] - print "Using %s as IP from HTTP_X_FORWARDED_FOR: %s" % ( - ip, request.META["HTTP_X_FORWARDED_FOR"] - ) + print("Using %s as IP from HTTP_X_FORWARDED_FOR: %s" % + (ip, request.META["HTTP_X_FORWARDED_FOR"])) else: ip = request.META["REMOTE_IP"] ipaddr = struct.unpack('