Add support for package update notifications

Introduce a new notification option to receive notifications when a new
commit is pushed to a package repository.

Implements FS#30109.

Signed-off-by: Lukas Fleischer <lfleischer@archlinux.org>
This commit is contained in:
Lukas Fleischer 2015-11-10 20:18:13 +01:00
parent aa5e58db81
commit 64072461df
8 changed files with 68 additions and 12 deletions

View file

@ -5,6 +5,7 @@ import mysql.connector
import os import os
import pygit2 import pygit2
import re import re
import subprocess
import sys import sys
import srcinfo.parse import srcinfo.parse
@ -19,6 +20,8 @@ aur_db_user = config.get('database', 'user')
aur_db_pass = config.get('database', 'password') aur_db_pass = config.get('database', 'password')
aur_db_socket = config.get('database', 'socket') aur_db_socket = config.get('database', 'socket')
notify_cmd = config.get('notifications', 'notify-cmd')
repo_path = config.get('serve', 'repo-path') repo_path = config.get('serve', 'repo-path')
repo_regex = config.get('serve', 'repo-regex') repo_regex = config.get('serve', 'repo-regex')
@ -169,6 +172,13 @@ def save_metadata(metadata, db, cur, user):
db.commit() db.commit()
def update_notify(db, cur, user, pkgbase_id):
# Obtain the user ID of the new maintainer.
cur.execute("SELECT ID FROM Users WHERE Username = %s", [user])
user_id = int(cur.fetchone()[0])
# Execute the notification script.
subprocess.Popen((notify_cmd, 'update', str(user_id), str(pkgbase_id)))
def die(msg): def die(msg):
sys.stderr.write("error: {:s}\n".format(msg)) sys.stderr.write("error: {:s}\n".format(msg))
@ -336,8 +346,6 @@ for pkgname in srcinfo.utils.get_package_names(metadata):
# Store package base details in the database. # Store package base details in the database.
save_metadata(metadata, db, cur, user) save_metadata(metadata, db, cur, user)
db.close()
# Create (or update) a branch with the name of the package base for better # Create (or update) a branch with the name of the package base for better
# accessibility. # accessibility.
repo.create_reference('refs/heads/' + pkgbase, sha1_new, True) repo.create_reference('refs/heads/' + pkgbase, sha1_new, True)
@ -347,3 +355,9 @@ repo.create_reference('refs/heads/' + pkgbase, sha1_new, True)
# http://git.661346.n2.nabble.com/PATCH-receive-pack-Create-a-HEAD-ref-for-ref-namespace-td7632149.html # http://git.661346.n2.nabble.com/PATCH-receive-pack-Create-a-HEAD-ref-for-ref-namespace-td7632149.html
# for details. # for details.
repo.create_reference('refs/namespaces/' + pkgbase + '/HEAD', sha1_new, True) repo.create_reference('refs/namespaces/' + pkgbase + '/HEAD', sha1_new, True)
# Send package update notifications.
update_notify(db, cur, user, pkgbase_id)
# Close the database.
db.close()

View file

@ -39,6 +39,7 @@ CREATE TABLE Users (
InactivityTS BIGINT UNSIGNED NOT NULL DEFAULT 0, InactivityTS BIGINT UNSIGNED NOT NULL DEFAULT 0,
RegistrationTS TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, RegistrationTS TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CommentNotify TINYINT(1) NOT NULL DEFAULT 1, CommentNotify TINYINT(1) NOT NULL DEFAULT 1,
UpdateNotify TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (ID), PRIMARY KEY (ID),
UNIQUE (Username), UNIQUE (Username),
UNIQUE (Email), UNIQUE (Email),

View file

@ -105,6 +105,16 @@ def get_comment_recipients(cur, pkgbase_id, uid):
return [row[0] for row in cur.fetchall()] return [row[0] for row in cur.fetchall()]
def get_update_recipients(cur, pkgbase_id, uid):
cur.execute('SELECT DISTINCT Users.Email FROM Users ' +
'INNER JOIN PackageNotifications ' +
'ON PackageNotifications.UserID = Users.ID WHERE ' +
'Users.UpdateNotify = 1 AND ' +
'PackageNotifications.UserID != %s AND ' +
'PackageNotifications.PackageBaseID = %s', [uid, pkgbase_id])
return [row[0] for row in cur.fetchall()]
def get_request_recipients(cur, pkgbase_id, uid): def get_request_recipients(cur, pkgbase_id, uid):
cur.execute('SELECT DISTINCT Users.Email FROM Users ' + cur.execute('SELECT DISTINCT Users.Email FROM Users ' +
'INNER JOIN PackageBases ' + 'INNER JOIN PackageBases ' +
@ -189,6 +199,28 @@ def comment(cur, uid, pkgbase_id, comment_id):
send_notification(to, subject, body, refs, headers) send_notification(to, subject, body, refs, headers)
def update(cur, uid, pkgbase_id):
user = username_from_id(cur, uid)
pkgbase = pkgbase_from_id(cur, pkgbase_id)
to = get_update_recipients(cur, pkgbase_id, uid)
user_uri = aur_location + '/account/' + user + '/'
pkgbase_uri = aur_location + '/pkgbase/' + pkgbase + '/'
subject = 'AUR Package Update: %s' % (pkgbase)
body = '%s [1] pushed a new commit to %s [2].' % (user, pkgbase)
body += '\n\n'
body += 'If you no longer wish to receive notifications about this ' \
'package, please go to the package page [2] and select "%s".' % \
('Disable notifications')
refs = '[1] ' + user_uri + '\n'
refs += '[2] ' + pkgbase_uri
thread_id = '<pkg-notifications-' + pkgbase + '@aur.archlinux.org>'
headers = headers_reply(thread_id)
send_notification(to, subject, body, refs, headers)
def flag(cur, uid, pkgbase_id): def flag(cur, uid, pkgbase_id):
user = username_from_id(cur, uid) user = username_from_id(cur, uid)
pkgbase = pkgbase_from_id(cur, pkgbase_id) pkgbase = pkgbase_from_id(cur, pkgbase_id)
@ -327,6 +359,7 @@ if __name__ == '__main__':
'send-resetkey': send_resetkey, 'send-resetkey': send_resetkey,
'welcome': welcome, 'welcome': welcome,
'comment': comment, 'comment': comment,
'update': update,
'flag': flag, 'flag': flag,
'comaintainer-add': comaintainer_add, 'comaintainer-add': comaintainer_add,
'comaintainer-remove': comaintainer_remove, 'comaintainer-remove': comaintainer_remove,

View file

@ -52,5 +52,6 @@ ALTER TABLE CommentNotify RENAME TO PackageNotifications;
---- ----
ALTER TABLE Users ALTER TABLE Users
ADD COLUMN CommentNotify TINYINT(1) NOT NULL DEFAULT 1; ADD COLUMN CommentNotify TINYINT(1) NOT NULL DEFAULT 1,
ADD COLUMN UpdateNotify TINYINT(1) NOT NULL DEFAULT 0;
---- ----

View file

@ -35,8 +35,8 @@ if ($action == "UpdateAccount") {
in_request("E"), in_request("H"), in_request("P"), in_request("E"), in_request("H"), in_request("P"),
in_request("C"), in_request("R"), in_request("L"), in_request("C"), in_request("R"), in_request("L"),
in_request("I"), in_request("K"), in_request("PK"), in_request("I"), in_request("K"), in_request("PK"),
in_request("J"), in_request("CN"), in_request("ID"), in_request("J"), in_request("CN"), in_request("UN"),
$row["Username"]); in_request("ID"), $row["Username"]);
} }
} }
@ -83,7 +83,7 @@ if (isset($_COOKIE["AURSID"])) {
$row["HideEmail"], "", "", $row["RealName"], $row["HideEmail"], "", "", $row["RealName"],
$row["LangPreference"], $row["IRCNick"], $row["PGPKey"], $PK, $row["LangPreference"], $row["IRCNick"], $row["PGPKey"], $PK,
$row["InactivityTS"] ? 1 : 0, $row["CommentNotify"], $row["InactivityTS"] ? 1 : 0, $row["CommentNotify"],
$row["ID"], $row["Username"]); $row["UpdateNotify"], $row["ID"], $row["Username"]);
} else { } else {
print __("You do not have permission to edit this account."); print __("You do not have permission to edit this account.");
} }
@ -123,8 +123,8 @@ if (isset($_COOKIE["AURSID"])) {
in_request("R"), in_request("L"), in_request("R"), in_request("L"),
in_request("I"), in_request("K"), in_request("I"), in_request("K"),
in_request("PK"), in_request("J"), in_request("PK"), in_request("J"),
in_request("CN"), in_request("ID"), in_request("CN"), in_request("UN"),
$row["Username"]); in_request("ID"), $row["Username"]);
} }
} else { } else {

View file

@ -23,7 +23,7 @@ if (in_request("Action") == "NewAccount") {
"new", "NewAccount", in_request("U"), 1, 0, "new", "NewAccount", in_request("U"), 1, 0,
in_request("E"), in_request("H"), '', '', in_request("R"), in_request("E"), in_request("H"), '', '', in_request("R"),
in_request("L"), in_request("I"), in_request("K"), in_request("L"), in_request("I"), in_request("K"),
in_request("PK"), 0, in_request("CN")); in_request("PK"), 0, in_request("CN"), in_request("UN"));
print $message; print $message;
@ -31,7 +31,7 @@ if (in_request("Action") == "NewAccount") {
display_account_form("NewAccount", in_request("U"), 1, 0, display_account_form("NewAccount", in_request("U"), 1, 0,
in_request("E"), in_request("H"), '', '', in_request("R"), in_request("E"), in_request("H"), '', '', in_request("R"),
in_request("L"), in_request("I"), in_request("K"), in_request("L"), in_request("I"), in_request("K"),
in_request("PK"), 0, in_request("CN")); in_request("PK"), 0, in_request("CN"), in_request("UN"));
} }
} else { } else {
print '<p>' . __("Use this form to create an account.") . '</p>'; print '<p>' . __("Use this form to create an account.") . '</p>';

View file

@ -57,13 +57,14 @@ function html_format_pgp_fingerprint($fingerprint) {
* @param string $PK The list of SSH public keys * @param string $PK The list of SSH public keys
* @param string $J The inactivity status of the displayed user * @param string $J The inactivity status of the displayed user
* @param string $CN Whether to notify of new comments * @param string $CN Whether to notify of new comments
* @param string $UN Whether to notify of package updates
* @param string $UID The user ID of the displayed user * @param string $UID The user ID of the displayed user
* @param string $N The username as present in the database * @param string $N The username as present in the database
* *
* @return void * @return void
*/ */
function display_account_form($A,$U="",$T="",$S="",$E="",$H="",$P="",$C="",$R="", function display_account_form($A,$U="",$T="",$S="",$E="",$H="",$P="",$C="",$R="",
$L="",$I="",$K="",$PK="",$J="",$CN="",$UID=0,$N="") { $L="",$I="",$K="",$PK="",$J="",$CN="",$UN="",$UID=0,$N="") {
global $SUPPORTED_LANGS; global $SUPPORTED_LANGS;
include("account_edit_form.php"); include("account_edit_form.php");
@ -90,13 +91,14 @@ function display_account_form($A,$U="",$T="",$S="",$E="",$H="",$P="",$C="",$R=""
* @param string $PK The list of public SSH keys * @param string $PK The list of public SSH keys
* @param string $J The inactivity status of the user * @param string $J The inactivity status of the user
* @param string $CN Whether to notify of new comments * @param string $CN Whether to notify of new comments
* @param string $UN Whether to notify of package updates
* @param string $UID The user ID of the modified account * @param string $UID The user ID of the modified account
* @param string $N The username as present in the database * @param string $N The username as present in the database
* *
* @return array Boolean indicating success and message to be printed * @return array Boolean indicating success and message to be printed
*/ */
function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$H="",$P="",$C="", function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$H="",$P="",$C="",
$R="",$L="",$I="",$K="",$PK="",$J="",$CN="",$UID=0,$N="") { $R="",$L="",$I="",$K="",$PK="",$J="",$CN="",$UN="",$UID=0,$N="") {
global $SUPPORTED_LANGS; global $SUPPORTED_LANGS;
$error = ''; $error = '';
@ -344,6 +346,7 @@ function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$H="",$P="",$C=""
$q.= ", PGPKey = " . $dbh->quote(str_replace(" ", "", $K)); $q.= ", PGPKey = " . $dbh->quote(str_replace(" ", "", $K));
$q.= ", InactivityTS = " . $inactivity_ts; $q.= ", InactivityTS = " . $inactivity_ts;
$q.= ", CommentNotify = " . ($CN ? "1" : "0"); $q.= ", CommentNotify = " . ($CN ? "1" : "0");
$q.= ", UpdateNotify = " . ($UN ? "1" : "0");
$q.= " WHERE ID = ".intval($UID); $q.= " WHERE ID = ".intval($UID);
$result = $dbh->exec($q); $result = $dbh->exec($q);

View file

@ -139,6 +139,10 @@
<label for="id_commentnotify"><?= __("Notify of new comments") ?>:</label> <label for="id_commentnotify"><?= __("Notify of new comments") ?>:</label>
<input type="checkbox" name="CN" id="id_commentnotify" <?= $CN ? 'checked="checked"' : '' ?> /> <input type="checkbox" name="CN" id="id_commentnotify" <?= $CN ? 'checked="checked"' : '' ?> />
</p> </p>
<p>
<label for="id_updatenotify"><?= __("Notify of package updates") ?>:</label>
<input type="checkbox" name="UN" id="id_updatenotify" <?= $UN ? 'checked="checked"' : '' ?> />
</p>
</fieldset> </fieldset>
<fieldset> <fieldset>