12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559 |
- """
- ForgeFed plugin for Pagure.
- Copyright (C) 2020-2021 zPlus <zplus@peers.community>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with this program; if not, see <https://www.gnu.org/licenses/>.
- SPDX-FileCopyrightText: 2020-2021 zPlus <zplus@peers.community>
- SPDX-License-Identifier: GPL-2.0-only
- """
- import celery
- import json
- import os
- import pagure
- from .. import APP_URL
- from .. import activitypub
- from .. import feeds
- from .. import model
- from .. import settings
- from . import broker
- from . import broker_url
- from . import database_session
- log = celery.utils.log.get_task_logger(__name__)
- log.setLevel(settings.LOG_LEVEL)
- # The following is a decorator that accepts a Pagure notification ID as input,
- # for example "issue.new", and adds it to _USER_ACTIONS_ together with the
- # decorated function. _USER_ACTIONS_ is a dictionary mapping Pagure notification
- # IDs to a function to be executed when a new notification is received.
- _USER_ACTIONS_ = {}
- def action(notification_id):
- def closure(func):
- def decorator(*args, **kwargs):
- func(*args, **kwargs)
- global _USER_ACTIONS_
- _USER_ACTIONS_[notification_id] = decorator
- return decorator
- return closure
- @broker.task
- def handle_pagure_signal(notification_id, message, forgefed_worker):
- """
- This task receives notifications from Pagure about events that happen on
- the instance, creates a new activity, and schedules their delivery.
- :param forgefed_worker: The value of envvar FORGEFED_WORKER of the process
- that scheduled the task.
- """
- if forgefed_worker.upper() == 'TRUE':
- log.debug('Ignoring notification {} in ForgeFed worker thread.'.format(notification_id))
- return
- if notification_id not in _USER_ACTIONS_:
- log.debug('Unhandled user action {}.'.format(notification_id))
- return
- log.debug('New Pagure notification: {}\n{}'.format(
- notification_id, json.dumps(message, indent=4, sort_keys=True)))
- with database_session() as database:
- # Handle the user action
- return _USER_ACTIONS_[notification_id](database, message)
- # Git
- # -----------------------------------------------------------------------------
- @action('git.receive')
- def git_commit(database, message):
- """
- A user has committed something to a Repository.
- """
- # This is the person that has pushed the commits, not the author
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['repo']['id']) \
- .one_or_none()
- repository = database \
- .query(model.Repository) \
- .filter(model.Repository.id == project.id) \
- .one_or_none()
- # This should never raise an error otherwise there's a bug in Pagure
- assert person and project and repository
- if repository.is_remote:
- log.debug('Local users should not push to remote repositories. '
- 'Open a MergeRequest instead. If you have commit access to a '
- 'remote repository, you should push to its URL.')
- return
- log.debug('Sending Push notification to Repository {} followers.'.format(repository.uri))
- repository_jsonld = activitypub.fetch(repository.local_uri)
- # TODO The Pagure notification only gives us the start_commit and end_commit.
- # We cannot rely on fetching from the repo all the commits between these
- # two, because there could be other commits in between, pushed earlier.
- # We've got to modify the Pagure notification (in the Pagure codebase)
- # to return the entire list of commits instead of only 2.
- commits_uri = []
- for hash in [ message['start_commit'], message['end_commit'] ]:
- commits_uri.append('{}/c/{}'.format(project.uri, hash))
- activitypub.Activity(
- type = 'Push',
- actor = person.uri,
- object = {
- 'type': 'OrderedCollection',
- 'totalItems': message['total_commits'],
- 'items': list(set(commits_uri))
- },
- to = repository_jsonld['followers'],
- target = activitypub.Branch(project, repository, message['branch'])['id'],
- context = repository.uri
- ).distribute()
- @action('git.tag.creation')
- def git_tag(database, message):
- """
- A user has tagged a Repository.
- """
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['repo']['id']) \
- .one_or_none()
- repository = database \
- .query(model.Repository) \
- .filter(model.Repository.id == project.id) \
- .one_or_none()
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assert project and repository and person
- if repository.is_remote:
- log.debug('Local users should not push to remote repositories. '
- 'If you have commit access to a remote repository, you should '
- 'push to its URL.')
- return
- log.debug('Sending Create(Tag) notification to Repository {} followers.'.format(repository.uri))
- ref = activitypub.TagRef(repository, 'refs/tags/{}'.format(message['tag']))
- repository_jsonld = activitypub.fetch(repository.local_uri)
- activitypub.Activity(
- type = 'Create',
- actor = person.uri,
- object = ref['id'],
- to = repository_jsonld['followers']
- ).distribute()
- # Actor
- # -----------------------------------------------------------------------------
- @action('forgefed.follow')
- def follow(database, message):
- """
- An Actor has followed another Actor
- """
- follower = model.Person.test_or_set(database, message['follower']['id'])
- followed = model.Person.test_or_set(database, message['followed']['id'])
- if follower.is_remote:
- log.debug('Follower Actor cannot be remote.')
- return
- activitypub.Activity(
- type = 'Follow',
- actor = follower.uri,
- object = followed.uri,
- to = followed.uri
- ).distribute()
- # Ticket
- # -----------------------------------------------------------------------------
- @action('issue.new')
- def new_issue(database, message):
- """
- A user has created a new Issue.
- """
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['issue']['user']['name']) \
- .one_or_none()
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- tickettracker = database \
- .query(model.TicketTracker) \
- .filter(model.TicketTracker.id == project.id) \
- .one_or_none()
- ticket = database \
- .query(model.Ticket) \
- .filter(model.Ticket.id == message['issue']['id'],
- model.Ticket.project_id == message['project']['id']) \
- .one_or_none()
- # This should never raise an error otherwise there's a bug in Pagure
- assert person and project and tickettracker and ticket
- if tickettracker.is_remote:
- log.debug('Sending new Ticket to remote tracker.')
- # Get the JSONLD of the Ticket
- ticket_jsonld = activitypub.fetch(ticket.local_uri)
- activitypub.Activity(
- type = 'Create',
- actor = person.uri,
- to = tickettracker.remote_uri,
- object = ticket_jsonld
- ).distribute()
- else:
- log.debug('Sending new Ticket to TicketTracker followers.')
- # Get the JSONLD of the TicketTracker
- tickettracker_jsonld = activitypub.fetch(tickettracker.local_uri)
- activitypub.Activity(
- type = 'Create',
- actor = person.uri,
- to = tickettracker_jsonld['followers'],
- object = ticket.local_uri
- ).distribute()
- @action('issue.comment.added')
- def new_issue_comment(database, message):
- """
- A user has commented on an issue
- """
- # The Pagure notification contains *all* the comments of the issue
- # in an ordered list, so we need to extract the last one from the
- # list of comments.
- comment = database \
- .query(model.TicketComment) \
- .filter(model.TicketComment.id == message['issue']['comments'][-1]['id']) \
- .one_or_none()
- person = database \
- .query(model.Person) \
- .filter(model.Person.id == comment.user.id) \
- .one_or_none()
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- tickettracker = database \
- .query(model.TicketTracker) \
- .filter(model.TicketTracker.id == project.id) \
- .one_or_none()
- # Our local ticket
- ticket = database \
- .query(model.Ticket) \
- .filter(model.Ticket.id == message['issue']['id'],
- model.Ticket.project_id == project.id) \
- .one_or_none()
- if tickettracker.is_remote:
- if not person.is_remote:
- log.debug('Sending new comment to remote tracker...')
- activitypub.Activity(
- type = 'Create',
- actor = person.uri,
- object = comment.uri,
- to = tickettracker.uri
- ).distribute()
- else:
- log.debug('Sending new comment to TicketTracker followers.')
- # Retrieve the pagure "watchlist"
- watchlist = pagure.lib.query.get_watch_list(database, ticket)
- actors = []
- for username in watchlist:
- actor = database \
- .query(model.Person) \
- .filter(model.Person.user == username) \
- .one_or_none()
- actors.append(actor.uri)
- # Send the Activity
- activitypub.Activity(
- type = 'Create',
- actor = person.uri,
- object = comment.uri,
- to = actors
- ).distribute()
- @action('issue.edit')
- def edit_issue(database, message):
- """
- A Pagure issue (Ticket) has been edited.
- """
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- tickettracker = database \
- .query(model.TicketTracker) \
- .filter(model.TicketTracker.id == project.id) \
- .one_or_none()
- ticket = database \
- .query(model.Ticket) \
- .filter(model.Ticket.id == message['issue']['id'],
- model.Ticket.project_id == project.id) \
- .one_or_none()
- # The user that edited the Ticket
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assert project and tickettracker and ticket and person
- if 'status' in message['fields'] \
- and message['issue']['status'].upper() == 'CLOSED':
- """
- A user has closed an issue
- """
- # If the user has closed the Ticket of a remote tracker, we just
- # send the Activity to the tracker
- if ticket.is_remote:
- log.debug('Local user has closed the remote Ticket {}'.format(ticket.uri))
- activitypub.Activity(
- type = 'Resolve',
- actor = person.uri,
- object = ticket.remote_uri
- ).distribute()
- # otherwise we simply send the Activity to the Ticket's watchlist
- else:
- log.debug('Local user has closed the local Ticket {}'.format(ticket.uri))
- # Retrieve the pagure "watchlist"
- watchlist = pagure.lib.query.get_watch_list(database, ticket)
- actors = []
- for username in watchlist:
- actor = database \
- .query(model.Person) \
- .filter(model.Person.user == username) \
- .one_or_none()
- actors.append(actor.uri)
- activitypub.Activity(
- type = 'Resolve',
- actor = person.uri,
- object = ticket.local_uri,
- to = actors
- ).distribute()
- if 'status' in message['fields'] \
- and message['issue']['status'].upper() == 'OPEN':
- """
- A user has reopened an issue
- """
- # If the user has opened the Ticket of a remote tracker, we just
- # send the Activity to the tracker
- if ticket.is_remote:
- log.debug('Local user has reopened the remote Ticket {}'.format(ticket.uri))
- activitypub.Activity(
- type = 'Reopen',
- actor = person.uri,
- object = ticket.remote_uri
- ).distribute()
- # otherwise we simply send the Activity to the Ticket's watchlist
- else:
- log.debug('Local user has reopened the local Ticket {}'.format(ticket.uri))
- # Retrieve the pagure "watchlist"
- watchlist = pagure.lib.query.get_watch_list(database, ticket)
- actors = []
- for username in watchlist:
- actor = database \
- .query(model.Person) \
- .filter(model.Person.user == username) \
- .one_or_none()
- actors.append(actor.uri)
- activitypub.Activity(
- type = 'Reopen',
- actor = person.uri,
- object = ticket.local_uri,
- to = actors
- ).distribute()
- if 'milestone' in message['fields']:
- """
- A user has changed an issue's milestone
- """
- if ticket.is_remote:
- log.debug('User {} has changed milestone of the remote Ticket {}'.format(
- person.uri, ticket.uri))
- activitypub.Activity(
- type = 'Milestone',
- actor = person.uri,
- object = ticket.remote_uri,
- milestone = [ message['issue']['milestone'] ],
- to = tickettracker.uri
- ).distribute()
- else:
- log.debug('User {} has changed milestone of the local Ticket {}'.format(
- person.uri, ticket.uri))
- tickettracker_jsonld = activitypub.fetch(tickettracker.uri)
- activitypub.Activity(
- type = 'Milestone',
- actor = person.uri,
- object = ticket.local_uri,
- milestone = [ message['issue']['milestone'] ],
- to = tickettracker_jsonld['followers']
- ).distribute()
- if any(field in message['fields'] for field in ['title', 'content']):
- """
- A user has edited the content of the issue.
- """
- result = {}
- if 'title' in message['fields']: result['summary'] = message['issue']['title']
- if 'content' in message['fields']: result['content'] = message['issue']['content']
- if ticket.is_remote:
- log.debug('Local user has edited the remote Ticket {}'.format(ticket.uri))
- activitypub.Activity(
- type = 'Update',
- actor = person.uri,
- object = ticket.remote_uri,
- to = tickettracker.remote_uri,
- result = result
- ).distribute()
- else:
- log.debug('Local user has edited the local Ticket {}'.format(ticket.uri))
- tickettracker_jsonld = activitypub.fetch(tickettracker.local_uri)
- if not tickettracker_jsonld:
- raise Exception('Cannot fetch TicketTracker {}'.format(ticket.local_uri))
- activitypub.Activity(
- type = 'Update',
- actor = person.uri,
- object = ticket.local_uri,
- to = tickettracker_jsonld['followers'],
- result = result
- ).distribute()
- @action('issue.assigned.added')
- def assign_issue(database, message):
- """
- A Pagure issue (Ticket) has a new assigned Person.
- """
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- tickettracker = database \
- .query(model.TicketTracker) \
- .filter(model.TicketTracker.id == project.id) \
- .one_or_none()
- ticket = database \
- .query(model.Ticket) \
- .filter(model.Ticket.id == message['issue']['id'],
- model.Ticket.project_id == project.id) \
- .one_or_none()
- # The user that edited the Ticket
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assignee = database \
- .query(model.Person) \
- .filter(model.Person.user == message['issue']['assignee']['name']) \
- .one_or_none()
- assert project and tickettracker and ticket and person and assignee
- if ticket.is_remote:
- log.debug('User {} has assigned {} to remote Ticket {}'.format(
- person.uri, assignee.uri, ticket.uri))
- activitypub.Activity(
- type = 'Assign',
- actor = person.uri,
- object = assignee.uri,
- target = ticket.remote_uri,
- ).distribute()
- else:
- log.debug('User {} has assigned {} to local Ticket {}'.format(
- person.uri, assignee.uri, ticket.uri))
- activitypub.Activity(
- type = 'Assign',
- actor = person.uri,
- object = assignee.uri,
- target = ticket.local_uri,
- ).distribute()
- @action('issue.assigned.reset')
- def unassign_issue(database, message):
- """
- A Pagure issue (Ticket) has removed the assigned Person.
- """
- log.debug('Not implemented. Have to change notification in Pagure first.')
- return
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- tickettracker = database \
- .query(model.TicketTracker) \
- .filter(model.TicketTracker.id == project.id) \
- .one_or_none()
- ticket = database \
- .query(model.Ticket) \
- .filter(model.Ticket.id == message['issue']['id'],
- model.Ticket.project_id == project.id) \
- .one_or_none()
- # The user that edited the Ticket
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assert project and tickettracker and ticket and person
- if ticket.is_remote:
- log.debug('User {} has removed assignee from remote Ticket {}'.format(
- person.uri, ticket.uri))
- activitypub.Activity(
- type = 'Assign',
- actor = person.uri,
- object = None,
- target = ticket.remote_uri,
- ).distribute()
- else:
- log.debug('User {} has removed assignee from local Ticket {}'.format(
- person.uri, ticket.uri))
- activitypub.Activity(
- type = 'Assign',
- actor = person.uri,
- object = None,
- target = ticket.local_uri,
- ).distribute()
- @action('issue.tag.added')
- def tag_issue(database, message):
- """
- A Pagure issue (Ticket) has new tags.
- """
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- tickettracker = database \
- .query(model.TicketTracker) \
- .filter(model.TicketTracker.id == project.id) \
- .one_or_none()
- ticket = database \
- .query(model.Ticket) \
- .filter(model.Ticket.id == message['issue']['id'],
- model.Ticket.project_id == project.id) \
- .one_or_none()
- # The user that edited the Ticket
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assert project and tickettracker and ticket and person
- tags = []
- for tag in message['tags']:
- tag = database \
- .query(model.Tag) \
- .filter(model.Tag.project_id == project.id,
- model.Tag.tag == tag) \
- .one_or_none()
- if tag:
- tags.append(tag)
- for tag in tags:
- if ticket.is_remote:
- log.debug('User {} has added tags of remote Ticket {}'.format(person.uri, ticket.uri))
- activitypub.Activity(
- type = 'Add',
- actor = person.uri,
- object = tag.uri,
- target = ticket.remote_uri,
- ).distribute()
- else:
- log.debug('User {} has added tags to local Ticket {}'.format(person.uri, ticket.uri))
- activitypub.Activity(
- type = 'Add',
- actor = person.uri,
- object = tag.uri,
- target = ticket.local_uri
- ).distribute()
- @action('issue.dependency.added')
- def depend_issue(database, message):
- """
- A Pagure issue (Ticket) has new dependency.
- """
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- tickettracker = database \
- .query(model.TicketTracker) \
- .filter(model.TicketTracker.id == project.id) \
- .one_or_none()
- ticket = database \
- .query(model.Ticket) \
- .filter(model.Ticket.project_id == project.id,
- model.Ticket.id == message['added_dependency']) \
- .one_or_none()
- dependency = database \
- .query(model.Ticket) \
- .filter(model.Ticket.project_id == project.id,
- model.Ticket.id == message['issue']['id']) \
- .one_or_none()
- # The user that edited the Ticket
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assert project and tickettracker and ticket and person and dependency
- if ticket.is_remote:
- log.debug('User {} has added dependency {} to remote Ticket {}'.format(
- person.uri, dependency.uri, ticket.uri))
- activitypub.Activity(
- type = 'Depend',
- actor = person.uri,
- object = ticket.remote_uri,
- target = dependency.uri
- ).distribute()
- else:
- log.debug('User {} has added dependency {} to local Ticket {}'.format(
- person.uri, dependency.uri, ticket.uri))
- activitypub.Activity(
- type = 'Depend',
- actor = person.uri,
- object = ticket.local_uri,
- target = dependency.uri
- ).distribute()
- @action('issue.drop')
- def drop_issue(database, message):
- """
- A Pagure issue (Ticket) was deleted.
- """
- tickettracker = database \
- .query(model.TicketTracker) \
- .filter(model.TicketTracker.id == message['project']['id']) \
- .one_or_none()
- # We cannot query the database because the Issue has been deleted
- ticket = message['issue']
- # The user that edited the Ticket
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assert tickettracker and ticket and person
- if person.is_remote:
- log.debug('Ignoring notification triggered by remote action.')
- return
- # Is this a local or remote Ticket?
- if ticket['full_url'].startswith(APP_URL):
- tickettracker_jsonld = activitypub.fetch(tickettracker.uri)
- activitypub.Activity(
- type = 'Delete',
- actor = person.uri,
- object = ticket['full_url'],
- origin = tickettracker.uri,
- to = tickettracker_jsonld['followers']
- ).distribute()
- else:
- activitypub.Activity(
- type = 'Delete',
- actor = person.uri,
- object = ticket['full_url'],
- origin = tickettracker.uri,
- to = tickettracker.uri
- ).distribute()
- # MergeRequest
- # -----------------------------------------------------------------------------
- @action('pull-request.new')
- def new_merge_request(database, message):
- """
- A user has created a Pull Request in Pagure.
- """
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['pullrequest']['user']['name']) \
- .one_or_none()
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['pullrequest']['project']['id']) \
- .one_or_none()
- repository = database \
- .query(model.Repository) \
- .filter(model.Repository.id == project.id) \
- .one_or_none()
- mergerequest = database \
- .query(model.MergeRequest) \
- .filter(model.MergeRequest.id == message['pullrequest']['id'],
- model.MergeRequest.project_id == project.id) \
- .one_or_none()
- # This should never raise an error otherwise there's a bug in Pagure
- assert person and project and repository and mergerequest
- if repository.is_remote:
- log.debug('Sending new MergeRequest to remote repository...')
- activitypub.Activity(
- type = 'Create',
- actor = person.uri,
- to = repository.remote_uri,
- object = mergerequest.local_uri
- ).distribute()
- @action('pull-request.comment.added')
- def new_merge_request_comment(database, message):
- """
- A user has created a new comment for a Pull Request.
- """
- # The Pagure notification contains *all* the comments of the pull
- # request in an ordered list, so we need to extract the last one from
- # the list of comments.
- comment = database \
- .query(model.MergeRequestComment) \
- .filter(model.MergeRequestComment.id == message['pullrequest']['comments'][-1]['id']) \
- .one_or_none()
- person = database \
- .query(model.Person) \
- .filter(model.Person.id == comment.user.id) \
- .one_or_none()
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['pullrequest']['project']['id']) \
- .one_or_none()
- repository = database \
- .query(model.Repository) \
- .filter(model.Repository.id == project.id) \
- .one_or_none()
- repository_jsonld = activitypub.fetch(repository.uri)
- # Our local pull-request
- mergerequest = database \
- .query(model.MergeRequest) \
- .filter(model.MergeRequest.id == message['pullrequest']['id'],
- model.MergeRequest.project_id == project.id) \
- .one_or_none()
- assert comment and person and project and repository and mergerequest
- if repository.is_remote:
- if not person.is_remote:
- log.debug('Sending new comment to remote repository...')
- activitypub.Activity(
- type = 'Create',
- actor = person.uri,
- object = comment.uri,
- to = repository.uri
- ).distribute()
- else:
- log.debug('Sending new comment to Repository followers.')
- """
- # Retrieve the pagure "watchlist"
- watchlist = pagure.lib.query.get_watch_list(database, mergerequest)
- actors = []
- for username in watchlist:
- actor = database \
- .query(model.Person) \
- .filter(model.Person.user == username) \
- .one_or_none()
- actors.append(actor.uri)
- """
- # Send the Activity
- activitypub.Activity(
- type = 'Create',
- actor = person.uri,
- object = comment.uri,
- to = repository_jsonld['followers']
- ).distribute()
- @action('pull-request.initial_comment.edited')
- def edit_merge_request(database, message):
- """
- A user has edited the content of a MergeRequest.
- """
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['pullrequest']['project']['id']) \
- .one_or_none()
- repository = database \
- .query(model.Repository) \
- .filter(model.Repository.id == project.id) \
- .one_or_none()
- # Our local pull-request
- mergerequest = database \
- .query(model.MergeRequest) \
- .filter(model.MergeRequest.id == message['pullrequest']['id'],
- model.MergeRequest.project_id == project.id) \
- .one_or_none()
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assert project and repository and mergerequest and person
- result = {
- 'summary': message['pullrequest']['title'],
- 'content': message['pullrequest']['initial_comment']
- }
- if mergerequest.is_remote:
- log.debug('Local user has edited the remote MergeRequest {}'.format(mergerequest.uri))
- activitypub.Activity(
- type = 'Update',
- actor = person.uri,
- object = mergerequest.remote_uri,
- to = repository.remote_uri,
- result = result
- ).distribute()
- else:
- log.debug('Local user has edited the local MergeRequest {}'.format(mergerequest.uri))
- repository_jsonld = activitypub.fetch(repository.local_uri)
- if not repository_jsonld:
- raise Exception('Cannot fetch TicketTracker {}'.format(ticket.local_uri))
- activitypub.Activity(
- type = 'Update',
- actor = person.uri,
- object = mergerequest.local_uri,
- to = repository_jsonld['followers'],
- result = result
- ).distribute()
- @action('pull-request.assigned.added')
- def assign_pullrequest(database, message):
- """
- A Pagure pull request has a new assigned Person.
- """
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- repository = database \
- .query(model.Repository) \
- .filter(model.Repository.id == project.id) \
- .one_or_none()
- mergerequest = database \
- .query(model.MergeRequest) \
- .filter(model.MergeRequest.id == message['pullrequest']['id'],
- model.MergeRequest.project_id == project.id) \
- .one_or_none()
- # The user that edited the pull request
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assignee = database \
- .query(model.Person) \
- .filter(model.Person.user == message['pullrequest']['assignee']['name']) \
- .one_or_none()
- assert project and repository and mergerequest and person and assignee
- if mergerequest.is_remote:
- pass
- else:
- log.debug('User {} has assigned {} to MergeRequest {}'.format(
- person.uri, assignee.uri, mergerequest.uri))
- activitypub.Activity(
- type = 'Assign',
- actor = person.uri,
- object = assignee.uri,
- target = mergerequest.local_uri,
- ).distribute()
- @action('pull-request.assigned.reset')
- def unassign_pullrequest(database, message):
- """
- A Pagure issue (Ticket) has removed the assigned Person.
- """
- log.debug('Not implemented. Have to change notification in Pagure first.')
- return
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- tickettracker = database \
- .query(model.TicketTracker) \
- .filter(model.TicketTracker.id == project.id) \
- .one_or_none()
- ticket = database \
- .query(model.Ticket) \
- .filter(model.Ticket.id == message['issue']['id'],
- model.Ticket.project_id == project.id) \
- .one_or_none()
- # The user that edited the Ticket
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assert project and tickettracker and ticket and person
- if ticket.is_remote:
- log.debug('User {} has removed assignee from remote Ticket {}'.format(
- person.uri, ticket.uri))
- activitypub.Activity(
- type = 'Assign',
- actor = person.uri,
- object = None,
- target = ticket.remote_uri,
- ).distribute()
- else:
- log.debug('User {} has removed assignee from local Ticket {}'.format(
- person.uri, ticket.uri))
- activitypub.Activity(
- type = 'Assign',
- actor = person.uri,
- object = None,
- target = ticket.local_uri,
- ).distribute()
- @action('pull-request.tag.added')
- def tag_mergerequest(database, message):
- """
- A Pagure pull request has new tags.
- """
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- repository = database \
- .query(model.Repository) \
- .filter(model.Repository.id == project.id) \
- .one_or_none()
- mergerequest = database \
- .query(model.MergeRequest) \
- .filter(model.MergeRequest.id == message['pullrequest']['id'],
- model.MergeRequest.project_id == project.id) \
- .one_or_none()
- # The user that edited the pull request
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assert project and repository and mergerequest and person
- tags = []
- for tag in message['tags']:
- tag = database \
- .query(model.Tag) \
- .filter(model.Tag.project_id == project.id,
- model.Tag.tag == tag) \
- .one_or_none()
- if tag:
- tags.append(tag)
- for tag in tags:
- if mergerequest.is_remote:
- log.debug('User {} has added tag {} to remote MergeRequest {}'.format(
- person.uri, tag.tag, mergerequest.uri))
- activitypub.Activity(
- type = 'Add',
- actor = person.uri,
- object = tag.uri,
- target = mergerequest.remote_uri,
- ).distribute()
- else:
- log.debug('User {} has added tag {} to local Ticket {}'.format(
- person.uri, tag.tag, mergerequest.uri))
- activitypub.Activity(
- type = 'Add',
- actor = person.uri,
- object = tag.uri,
- target = mergerequest.local_uri
- ).distribute()
- @action('pull-request.closed')
- def close_merge_request(database, message):
- """
- A user has closed a Pull Request. This notification does not distinguish between
- "Merged" and "Closed" (without merge). It's the same notification for both events
- so we have to look at the MergeRequest status.
- """
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['pullrequest']['project']['id']) \
- .one_or_none()
- repository = database \
- .query(model.Repository) \
- .filter(model.Repository.id == project.id) \
- .one_or_none()
- # Our local pull-request
- mergerequest = database \
- .query(model.MergeRequest) \
- .filter(model.MergeRequest.id == message['pullrequest']['id'],
- model.MergeRequest.project_id == project.id) \
- .one_or_none()
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assert project and repository and mergerequest and person
- if message['merged'] == True and mergerequest.is_remote:
- log.debug('Local user cannot merge the remote MergeRequest {}'.format(mergerequest.uri))
- if message['merged'] == False and mergerequest.is_remote:
- log.debug('Local user has closed the remote MergeRequest {}'.format(mergerequest.uri))
- activitypub.Activity(
- type = 'Close',
- actor = person.uri,
- object = mergerequest.local_uri,
- to = repository.remote_uri
- ).distribute()
- if message['merged'] == True and not mergerequest.is_remote:
- log.debug('Local user has merged the local MergeRequest {}'.format(mergerequest.uri))
- repository_jsonld = activitypub.fetch(repository.local_uri)
- activitypub.Activity(
- type = 'Resolve',
- actor = person.uri,
- object = mergerequest.local_uri,
- to = repository_jsonld['followers']
- ).distribute()
- if message['merged'] == False and not mergerequest.is_remote:
- log.debug('Local user has closed the local MergeRequest {}'.format(mergerequest.uri))
- repository_jsonld = activitypub.fetch(repository.local_uri)
- activitypub.Activity(
- type = 'Close',
- actor = person.uri,
- object = mergerequest.local_uri,
- to = repository_jsonld['followers']
- ).distribute()
- @action('pull-request.reopened')
- def reopen_merge_request(database, message):
- """
- A user has reopened a Pull Request.
- """
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['pullrequest']['project']['id']) \
- .one_or_none()
- repository = database \
- .query(model.Repository) \
- .filter(model.Repository.id == project.id) \
- .one_or_none()
- # Our local pull-request
- mergerequest = database \
- .query(model.MergeRequest) \
- .filter(model.MergeRequest.id == message['pullrequest']['id'],
- model.MergeRequest.project_id == project.id) \
- .one_or_none()
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- assert project and repository and mergerequest and person
- if person.is_remote:
- log.debug('Remote user has reopened the MergeRequest {}'.format(mergerequest.uri))
- return
- if mergerequest.is_remote:
- log.debug('Local user has reopened the remote MergeRequest {}'.format(mergerequest.uri))
- activitypub.Activity(
- type = 'Reopen',
- actor = person.uri,
- object = mergerequest.remote_uri,
- to = repository.remote_uri
- ).distribute()
- if not mergerequest.is_remote:
- log.debug('Local user has reopened the local MergeRequest {}'.format(mergerequest.uri))
- repository_jsonld = activitypub.fetch(repository.local_uri)
- activitypub.Activity(
- type = 'Reopen',
- actor = person.uri,
- object = mergerequest.local_uri,
- to = repository_jsonld['followers']
- ).distribute()
- # Groups and Roles
- # -----------------------------------------------------------------------------
- @action('project.user.added')
- def add_user(database, message):
- """
- A user has been added to a Pagure project.
- """
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- new_member = database \
- .query(model.Person) \
- .filter(model.Person.user == message['new_user']) \
- .one_or_none()
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- role = database \
- .query(model.Role) \
- .filter(model.Role.project_id == project.id,
- model.Role.user_id == new_member.id,
- model.Role.access == message['access']) \
- .one_or_none()
- # This should never raise an error otherwise there's a bug in Pagure
- assert person and project and new_member and role
- if project.is_local:
- log.debug('{} has added user {} to role {}'.format(person.uri, new_member.uri, role.uri))
- activitypub.Activity(
- type = 'Add',
- actor = person.uri,
- object = new_member.uri,
- target = role.uri,
- context = project.uri
- ).distribute()
- @action('project.user.access.updated')
- def update_user_access(database, message):
- """
- A user access level has been updated.
- """
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- member = database \
- .query(model.Person) \
- .filter(model.Person.user == message['new_user']) \
- .one_or_none()
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- role = database \
- .query(model.Role) \
- .filter(model.Role.project_id == project.id,
- model.Role.user_id == member.id,
- model.Role.access == message['new_access']) \
- .one_or_none()
- # This should never raise an error otherwise there's a bug in Pagure
- assert person and project and member and role
- if project.is_local:
- log.debug('{} has updated user {} to role {}'.format(person.uri, member.uri, role.uri))
- activitypub.Activity(
- type = 'Update',
- actor = person.uri,
- object = member.uri,
- result = { 'roles': [ role.uri for role in member.roles ] }
- ).distribute()
- @action('project.user.removed')
- def remove_user(database, message):
- """
- A user has been removed from a Pagure project.
- """
- log.debug('Waiting for https://pagure.io/pagure/pull-request/5156 to be '
- 'merged before letting this through.')
- return
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- new_member = database \
- .query(model.Person) \
- .filter(model.Person.user == message['removed_user']) \
- .one_or_none()
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- role = database \
- .query(model.Role) \
- .filter(model.Role.project_id == project.id,
- model.Role.user_id == new_member.id,
- model.Role.access == message['access']) \
- .one_or_none()
- # This should never raise an error otherwise there's a bug in Pagure
- assert person and project and role
- if project.is_local:
- log.debug('{} has removed user {} from role {}'.format(person.uri, new_member.uri, role.uri))
- activitypub.Activity(
- type = 'Remove',
- actor = person.uri,
- object = new_member.uri,
- target = role.uri,
- context = project.uri
- ).distribute()
- @action('project.group.added')
- def add_group(database, message):
- """
- A group has been added to a Pagure project.
- """
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- new_group = database \
- .query(model.Group) \
- .filter(model.Group.group_name == message['new_group']) \
- .one_or_none()
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- role = database \
- .query(model.ProjectRole) \
- .filter(model.ProjectRole.project_id == project.id,
- model.ProjectRole.group_id == new_group.id,
- model.ProjectRole.access == message['access']) \
- .one_or_none()
- # This should never raise an error otherwise there's a bug in Pagure
- assert person and project and new_group and role
- if project.is_local:
- log.debug('{} has added group {} to role {}'.format(person.uri, new_group.uri, role.uri))
- activitypub.Activity(
- type = 'Add',
- actor = person.uri,
- object = new_group.uri,
- target = role.uri,
- context = project.uri
- ).distribute()
- @action('project.group.access.updated')
- def update_group_access(database, message):
- """
- A group access level has been updated.
- """
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- group = database \
- .query(model.Group) \
- .filter(model.Group.group_name == message['new_group']) \
- .one_or_none()
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- role = database \
- .query(model.ProjectRole) \
- .filter(model.ProjectRole.project_id == project.id,
- model.ProjectRole.group_id == group.id,
- model.ProjectRole.access == message['new_access']) \
- .one_or_none()
- # This should never raise an error otherwise there's a bug in Pagure
- assert person and group and project and role
- if project.is_local:
- log.debug('{} has updated group {} to role {}'.format(person.uri, group.uri, role.uri))
- activitypub.Activity(
- type = 'Update',
- actor = person.uri,
- object = group.uri,
- result = { 'roles': [ role.uri for role in group.roles ] }
- ).distribute()
- @action('project.group.removed')
- def remove_group(database, message):
- """
- A user has been removed from a Pagure project.
- """
- log.debug('Waiting for https://pagure.io/pagure/pull-request/5156 to be '
- 'merged before letting this through.')
- return
- person = database \
- .query(model.Person) \
- .filter(model.Person.user == message['agent']) \
- .one_or_none()
- new_member = database \
- .query(model.Person) \
- .filter(model.Person.user == message['removed_user']) \
- .one_or_none()
- project = database \
- .query(model.Project) \
- .filter(model.Project.id == message['project']['id']) \
- .one_or_none()
- role = database \
- .query(model.Role) \
- .filter(model.Role.project_id == project.id,
- model.Role.user_id == new_member.id,
- model.Role.access == message['access']) \
- .one_or_none()
- # This should never raise an error otherwise there's a bug in Pagure
- assert person and project and role
- if project.is_local:
- log.debug('{} has removed user {} from role {}'.format(person.uri, new_member.uri, role.uri))
- activitypub.Activity(
- type = 'Remove',
- actor = person.uri,
- object = new_member.uri,
- target = role.uri,
- context = project.uri
- ).distribute()
|