Closed jasker5183 closed 7 months ago
For FreshPorts to tell you that a new package is available for something on your watch list, the following needs to exist (what follows is mostly notes for myself)
port.id
)packages
table to the port
(packages.port_id
)packages
table (package_imports.processed_date
)I think a report can be generated from the existing data without database modification.
new package available
) and would use existing watch lists.This new feature requires careful consideration. I've been thinking about it over the past few days.
We need to define 'new package'.
It is not enough that a new repo build is available (I'll also refer to that as a 'new build', 'import', or 'repo' - they all mean the same thing). We have that information already. The user does not want to be notified that a new build is available. They don't care if it's the same version as the last build. "Hey, user, foo-1.3 has just been built" is not useful if you have foo-1.3
installed.
I think the objective is knowing that the package version/revision has been modified. That sounds like a trigger to me (the trigger would add that port to a list). As a new repo build is imported into FreshPorts, the triggers will build list of 'new package' instances. After the import is done, emails will go out.
I see 'new package' notifications should go out immediately upon the above import. Why?
If a user is watching multiple ABI, they might get multiple emails per day. If they are watching just one ABI, they'll get at most one email per new.
NOTES on table structures:
package_imports
contains stats on all imports ever done - current row count - 2821latest
/ quarterly
for that ABIlatest
& quarterly
for that ABIThe latest import for FreeBSD:14:amd64
on latest
:
(see https://www.postgresql.org/docs/current/sql-select.html#SQL-DISTINCT)
freshports.dvl=# with latest_imports as
(SELECT distinct on (abi_id, package_set) *
FROM public.package_imports
order by abi_id, package_set)
select abi.name, LI.package_set, LI.processed_date
from latest_imports LI join abi on LI.abi_id = abi.id and abi.name = 'FreeBSD:14:amd64' and LI.package_set= 'latest'
join packages P on P.abi_id = LI.abi_id and P.package_name = 'bacula13-server' and P.package_set = 'latest';
name | package_set | processed_date
------------------+-------------+-------------------------------
FreeBSD:14:amd64 | latest | 2023-12-23 22:11:19.165986+00
(1 row)
freshports.dvl=#
The current idea for a table:
-- Table: public.package_notifications
-- DROP TABLE IF EXISTS public.package_notifications;
CREATE TABLE IF NOT EXISTS public.package_notifications
(
id bigint NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ),
abi_id integer NOT NULL,
package_set package_sets NOT NULL,
port_id integer NOT NULL,
action action NOT NULL,
version_previous text COLLATE pg_catalog."default",
version_current text COLLATE pg_catalog."default",
CONSTRAINT package_notifications_pkey PRIMARY KEY (id),
CONSTRAINT package_notifications_abi_id FOREIGN KEY (abi_id)
REFERENCES public.abi (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT package_notifications_port_id_fk FOREIGN KEY (port_id)
REFERENCES public.ports (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE
DEFERRABLE INITIALLY DEFERRED,
CONSTRAINT package_notifications_action_check CHECK (action = 'insert'::action AND version_previous IS NULL AND version_current IS NOT NULL OR action = 'update'::action AND version_previous IS NOT NULL AND version_current IS NOT NULL OR action = 'delete'::action AND version_previous IS NOT NULL AND version_current IS NULL) NOT VALID
)
TABLESPACE pg_default;
ALTER TABLE IF EXISTS public.package_notifications
OWNER to postgres;
COMMENT ON TABLE public.package_notifications
IS 'when a new repo is imported, we need to notify users. This table lets us build a list of what has change in this repo.
re: https://github.com/FreshPorts/freshports/issues/542';
COMMENT ON COLUMN public.package_notifications.action
IS 'Was this row the result of an insert, update, or a delete on the packages table?';
COMMENT ON CONSTRAINT package_notifications_action_check ON public.package_notifications
IS 'read the constraint as:
(action = ''insert''::action AND version_previous IS NULL AND version_current IS NOT NULL)
OR (action = ''update''::action AND version_previous IS NOT NULL AND version_current IS NOT NULL)
OR (action = ''delete''::action AND version_previous IS NOT NULL AND version_current IS NULL))
';
Subscriptions to a particular ABI are noted through this table:
--DROP TABLE IF EXISTS public.report_subscriptions_abi;
CREATE TABLE IF NOT EXISTS public.report_subscriptions_abi
(
user_id integer NOT NULL,
abi_id integer NOT NULL,
watch_list_id integer NOT NULL,
CONSTRAINT report_subscriptions_abi_user_abi_watch_pk UNIQUE NULLS NOT DISTINCT (user_id, abi_id, watch_list_id),
CONSTRAINT report_subscriptions_abi_abi_id FOREIGN KEY (abi_id)
REFERENCES public.abi (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT report_subscriptions_abi_user_id FOREIGN KEY (user_id)
REFERENCES public.users (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT report_subscriptions_abi_watch_list_id FOREIGN KEY (watch_list_id)
REFERENCES public.watch_list (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
TABLESPACE pg_default;
ALTER TABLE IF EXISTS public.report_subscriptions_abi
OWNER to postgres;
COMMENT ON TABLE public.report_subscriptions_abi
IS 'Records the ABI a given user follows - relates to package_notifications table
You can associate a given watch list with one or more ABI.';
COMMENT ON COLUMN public.report_subscriptions_abi.watch_list_id
IS 'We don''t need user_id, because watch_list_id will find us the user_id. We duplicate that information here because I think it''ll be useful in queries.';
COMMENT ON CONSTRAINT report_subscriptions_abi_user_abi_watch_pk ON public.report_subscriptions_abi
IS 'For a given user_id, abi_id, and watch_list_id must be unique.';
Adding the new report to the database:
INSERT INTO public.reports(
name, description, needs_frequency)
VALUES ('New Package Notification', 'Notification when a new package is available for something on your watch list.', false);
During a walk last night, this idea came to mind:
Add three more columns to the package_notifications
table:
action
(one of insert
, update
, or delete
)
version_previous
: text
version_current
: text
For insert
: version_previous
will be null; version_current
must be not null
For update
: both version_previous
and version_current
must be not null
For delete
: version_previous
must be null
, and version_current
must be null
Testing data:
insert into package_notifications(abi_id, package_set, port_id, action, version_previous, version_current)
values (27, 'latest', 101597, 'update', '13.0.3_1', '13.0.3_2');
insert into package_notifications(abi_id, package_set, port_id, action, version_previous, version_current)
values (27, 'latest', 101597, 'insert', null, '13.0.3_1');
insert into package_notifications(abi_id, package_set, port_id, action, version_previous, version_current)
values (27, 'latest', 101597, 'delete', '13.0.3_1', null);
-- failures
insert into package_notifications(abi_id, package_set, port_id, action, version_previous, version_current)
values (27, 'latest', 101597, 'update', '13.0.3_1', null);
insert into package_notifications(abi_id, package_set, port_id, action, version_previous, version_current)
values (27, 'latest', 101597, 'update', null, '13.0.3_1');
insert into package_notifications(abi_id, package_set, port_id, action, version_previous, version_current)
values (27, 'latest', 101597, 'insert', '13.0.3_1', null);
insert into package_notifications(abi_id, package_set, port_id, action, version_previous, version_current)
values (27, 'latest', 101597, 'delete', null, '13.0.3_1');
This is how the report subscriptions page will change:
first draft: selection of ABI for each watch list
This is the user-interface idea I had last night:
FreeBSD:14-amd64
, FreeBSD:13:amd64
)foo
, bar
, things
)ADD
ADD
will add the combinations of ABI
and watch list
to the list of package notifications
which now contains:
FreeBSD:14-amd64 foo
FreeBSD:14-amd64 things
** FreeBSD:13:amd64 bar
Here's some content: full listing at https://gist.github.com/dlangille/80e4b7169ac5d6c5d9cc2e9941f4b223
freshports.dvl=# select PA.category || '/' || PA.name as port, abi.name as abi, PN.package_set as set, pn.action,
pn.version_previous, pn.version_current from package_notifications PN join ports_active PA on PA.id = PN.port_id join
abi on ABI.id = PN.abi_id order by abi, set, port;
port | abi | set | action | version_previous | version_current
-----------------------------------------------------------+--------------------+-----------+--------+---------------------------+---------------------------
accessibility/kmag | FreeBSD:13:amd64 | latest | update | 23.08.4_1 | 23.08.4_2
accessibility/kmousetool | FreeBSD:13:amd64 | latest | update | 23.08.4 | 23.08.4_1
accessibility/kmouth | FreeBSD:13:amd64 | latest | update | 23.08.4 | 23.08.4_1
accessibility/py-speech-dispatcher | FreeBSD:13:amd64 | latest | update | 0.11.2_3 | 0.11.2_4
accessibility/speech-dispatcher | FreeBSD:13:amd64 | latest | update | 0.11.2_3 | 0.11.2_4
archivers/R-cran-zip | FreeBSD:13:amd64 | latest | update | 2.3.0_1 | 2.3.1
archivers/arc | FreeBSD:13:amd64 | latest | update | 5.21q | 5.21q_1
archivers/arj | FreeBSD:13:amd64 | latest | update | 3.10.22_11 | 3.10.22_12
archivers/ark | FreeBSD:13:amd64 | latest | update | 23.08.4 | 23.08.4_1
archivers/ark-devel | FreeBSD:13:amd64 | latest | insert | | 24.01.90_1
archivers/bzip2 | FreeBSD:13:amd64 | latest | update | 1.0.8 | 1.0.8_1
HTML as it stands now: https://gist.github.com/dlangille/9a8539c8edeb04a86046bfd5ba57692f
After great frustration with getting table elements correct, I have data coming in on a POST. From this I can code saves to the database.
And here we have the first entry in the subscription table:
[22:12 pg03 dvl ~/src/freshports/database-schema] % psql freshports.dvl
psql (16.1)
Type "help" for help.
freshports.dvl=# SELECT * FROM public.report_subscriptions_abi;
user_id | abi_id | watch_list_id
---------+--------+---------------
1 | 2 | 19901
(1 row)
freshports.dvl=#
I declare the first draft of the package report notification page completed.
Next: Sending out notifications based upon what the user has selected.
So, how do I test this, now that I have data and a subscription method. I work in reverse. Here's a list of 10 new packages. The original query is above
freshports.dvl=# select PA.category || '/' || PA.name as port, abi.name as abi, PN.package_set as set, pn.action,
pn.version_previous, pn.version_current from package_notifications PN join ports_active PA on PA.id = PN.port_id join
abi on ABI.id = PN.abi_id order by abi, set, port limit 10;
port | abi | set | action | version_previous | version_current
----------------------------+------------------+-----------+--------+------------------+-----------------
biology/ncbi-cxx-toolkit | FreeBSD:13:armv7 | quarterly | delete | 27.0.0_1 |
databases/rubygem-pg_query | FreeBSD:13:armv7 | quarterly | update | 4.2.3 | 5.1.0
devel/bossa | FreeBSD:13:armv7 | quarterly | update | 1.9.1 | 1.9.1_1
devel/gitaly | FreeBSD:13:armv7 | quarterly | update | 16.7.4 | 16.9.1
devel/gitlab-runner | FreeBSD:13:armv7 | quarterly | update | 16.7.0_1 | 16.9.0
devel/gitlab-shell | FreeBSD:13:armv7 | quarterly | update | 14.32.0 | 14.33.0
devel/hyprlang | FreeBSD:13:armv7 | quarterly | update | 0.4.0 | 0.4.1
devel/py-pygit2 | FreeBSD:13:armv7 | quarterly | update | 1.13.3 | 1.14.0
devel/rubygem-aws-sdk-core | FreeBSD:13:armv7 | quarterly | update | 3.190.1 | 3.191.2
devel/rubygem-aws-sdk-s3 | FreeBSD:13:armv7 | quarterly | update | 1.142.0 | 1.143.0
(10 rows)
freshports.dvl=#
I'm going to add several of those ports to a watch list. Then subscribe to FreeBSD:13:armv7
....
Oh I see a flaw in my UI. I don't have quarterly/latest
available. For now, the report will only work on quarterly
.
I visit /pkg_upload.php
and add these three ports to my watch list:
biology/ncbi-cxx-toolkit
databases/rubygem-pg_query
devel/py-pygit2
You'd think that would be easy to do. But it appears to be broken. Oh wait, it seems to be a page refresh issue. Perhaps I need a cache-buster on that URL?
Here I am, with those three ports on a watch lists (named knew
, after this host.
Next, write a query which combines these tables:
watch_list_elements
report_subscriptions_abi
package_notifications
Let's start with the report notifications query and go from there. This is in svn.int.unixathome.org/freshports-1/ingress/scripts/branches/git/report-notification.pl
select watch_list.id as watch_list_id,
watch_list.name as watch_list_name,
users.id as user_id,
users.email as email,
categories.name as category,
element.name as port,
to_char(commit_log.commit_date + SystemTimeAdjust(), 'DD Mon YYYY') as commit_date,
commit_log.description as commit_message,
commit_log.committer as comitter,
commit_log.id as commit_log_id
from commit_log,
watch_list_element,
watch_list,
users,
ports,
categories,
element,
commit_log_ports,
report_frequency,
report_subscriptions
where commit_log.date_added >= '$LastSent'
and commit_log.id = commit_log_ports.commit_log_id
and watch_list_element.element_id = ports.element_id
and watch_list_element.watch_list_id = watch_list.id
and users.id = watch_list.user_id
and report_frequency.frequency = '$Frequency'
and length(users.email) > 0
and users.emailbouncecount = 0
and ports.id = commit_log_ports.port_id
and ports.category_id = categories.id
and ports.element_id = element.id
and users.id = report_subscriptions.user_id
and report_subscriptions.report_id = $ReportID
and report_frequency.id = report_subscriptions.report_frequency_id
order by watch_list_id, watch_list_name, user_id, category, port, commit_date";
Here's is my first draft, and it's super fast. It runs in 0.112s
freshports.dvl=# select WL.id as watch_list_id,
WL.name as watch_list_name,
U.id as user_id,
U.email as email,
C.name as category,
E.name as port,
abi.name as abi,
PN.package_set as set,
PN.action,
PN.version_previous,
PN.version_current
from package_notifications PN
join ports P on P.id = PN.port_id
JOIN element E on E.id = P.element_id
JOIN categories C on C.id = P.category_id
JOIN watch_list_element WLE on E.id = WLE.element_id
JOIN watch_list WL on WL.id = WLE.watch_list_id
JOIN users U on U.id = WL.user_id and length(U.email) > 0 and U.emailbouncecount = 0
JOIN report_subscriptions RS on U.id = RS.user_id and RS.report_id = 7
JOIN report_subscriptions_abi RSA on RSA.abi_id = PN.abi_id AND PN.package_set = 'quarterly'
JOIN abi on abi.id = PN.abi_id
order by watch_list_id, watch_list_name, user_id, category, port;
watch_list_id | watch_list_name | user_id | email | category | port | abi | set | action | version_previous | version_current
---------------+-----------------+---------+-----------------+-----------+------------------+------------------+-----------+--------+------------------+-----------------
19904 | knew | 1 | dvl@FreeBSD.org | biology | ncbi-cxx-toolkit | FreeBSD:13:armv7 | quarterly | delete | 27.0.0_1 |
19904 | knew | 1 | dvl@FreeBSD.org | databases | rubygem-pg_query | FreeBSD:13:armv7 | quarterly | update | 4.2.3 | 5.1.0
19904 | knew | 1 | dvl@FreeBSD.org | devel | py-pygit2 | FreeBSD:13:armv7 | quarterly | update | 1.13.3 | 1.14.0
(3 rows)
freshports.dvl=#
Next, add this query to a new script, and create the outgoing email.
So here's the first email, and I have yet to figure out why it has duplicated information.
From: "FreshPorts Watch Daemon" <FreshPorts-Watch@FreshPorts.org>
To: dvl@FreeBSD.org
Subject: FreshPorts FreeBSD:13:armv7 notification - knew
X-FreshPorts-WatchListSet: latest
Auto-Submitted: auto-generated
X-FreshPorts-WatchListName: knew
X-FreshPorts-WatchListABI: FreeBSD:13:armv7
Precedence: bulk
Message-ID: <1709766967.1838191.mx5.messagingengine.com.3D36C14F@generated.messagingengine.com>
biology/ncbi-cxx-toolkit
FreeBSD:13:armv7
delete: old version: 27.0.0_1
https://dvl.freshports.org/biology/ncbi-cxx-toolkit/
biology/ncbi-cxx-toolkit
FreeBSD:13:armv7
delete: old version: 27.0.0_1
https://dvl.freshports.org/biology/ncbi-cxx-toolkit/
databases/rubygem-pg_query
FreeBSD:13:armv7
update: new version: 5.1.0
old version: 4.2.3
https://dvl.freshports.org/databases/rubygem-pg_query/
biology/ncbi-cxx-toolkit
FreeBSD:13:armv7
delete: old version: 27.0.0_1
https://dvl.freshports.org/biology/ncbi-cxx-toolkit/
biology/ncbi-cxx-toolkit
FreeBSD:13:armv7
delete: old version: 27.0.0_1
https://dvl.freshports.org/biology/ncbi-cxx-toolkit/
databases/rubygem-pg_query
FreeBSD:13:armv7
update: new version: 5.1.0
old version: 4.2.3
https://dvl.freshports.org/databases/rubygem-pg_query/
devel/py-pygit2
FreeBSD:13:armv7
update: new version: 1.14.0
old version: 1.13.3
https://dvl.freshports.org/devel/py-pygit2/
biology/ncbi-cxx-toolkit
FreeBSD:13:armv7
delete: old version: 27.0.0_1
https://dvl.freshports.org/biology/ncbi-cxx-toolkit/
biology/ncbi-cxx-toolkit
FreeBSD:13:armv7
delete: old version: 27.0.0_1
https://dvl.freshports.org/biology/ncbi-cxx-toolkit/
databases/rubygem-pg_query
FreeBSD:13:armv7
update: new version: 5.1.0
old version: 4.2.3
https://dvl.freshports.org/databases/rubygem-pg_query/
biology/ncbi-cxx-toolkit
FreeBSD:13:armv7
delete: old version: 27.0.0_1
https://dvl.freshports.org/biology/ncbi-cxx-toolkit/
biology/ncbi-cxx-toolkit
FreeBSD:13:armv7
delete: old version: 27.0.0_1
https://dvl.freshports.org/biology/ncbi-cxx-toolkit/
databases/rubygem-pg_query
FreeBSD:13:armv7
update: new version: 5.1.0
old version: 4.2.3
https://dvl.freshports.org/databases/rubygem-pg_query/
devel/py-pygit2
FreeBSD:13:armv7
update: new version: 1.14.0
old version: 1.13.3
https://dvl.freshports.org/devel/py-pygit2/
This message was generated by the FreshPorts Watch Daemon and highlights
new packages that are available for your selected ports on FreeBSD:13:armv7
for the watch list: knew.
If we were really on it, we'd have the package set too: latest/quarterly.
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
Please refer to https://dvl.freshports.org/watch.php for details.
--=20
You are receiving this message as part of the service you joined at
https://dvl.freshports.org/ - You can unsubscribe at
https://dvl.freshports.org/report-subscriptions.php
If a problem occurs, please send details to postmaster@FreshPorts.org
Here we go. This is looking sane.
new packages on FreeBSD:13:armv7 quarterly
biology/ncbi-cxx-toolkit
delete: old version: 27.0.0_1
https://dvl.freshports.org/biology/ncbi-cxx-toolkit/
databases/rubygem-pg_query
update: new version: 5.1.0
old version: 4.2.3
https://dvl.freshports.org/databases/rubygem-pg_query/
devel/py-pygit2
update: new version: 1.14.0
old version: 1.13.3
https://dvl.freshports.org/devel/py-pygit2/
This message was generated by the FreshPorts Watch Daemon and highlights
new packages that are available for your selected ports on FreeBSD:13:armv7
for the watch list: knew.
If we were really on it, we'd have the package set too: latest/quarterly.
======================================
Please refer to https://dvl.freshports.org/watch.php for details.
--
You are receiving this message as part of the service you joined at
https://dvl.freshports.org/ - You can unsubscribe at
https://dvl.freshports.org/report-subscriptions.php
Next: Invoke that script after processing a package import.
Package set has been added to the UI.
The notification email now handles package_set
properly.
new packages on FreeBSD:13:armv7 -> quarterly
biology/ncbi-cxx-toolkit
delete: old version: 27.0.0_1
https://dvl.freshports.org/biology/ncbi-cxx-toolkit/
databases/rubygem-pg_query
update: new version: 5.1.0
old version: 4.2.3
https://dvl.freshports.org/databases/rubygem-pg_query/
devel/py-pygit2
update: new version: 1.14.0
old version: 1.13.3
https://dvl.freshports.org/devel/py-pygit2/
This message was generated by the FreshPorts Watch Daemon and highlights
new packages that are available for your selected ports on FreeBSD:13:armv7
for the watch list: knew.
======================================
Please refer to https://dvl.freshports.org/watch.php for details.
--
You are receiving this message as part of the service you joined at
https://dvl.freshports.org/ - You can unsubscribe at
https://dvl.freshports.org/report-subscriptions.php
If a problem occurs, please send details to [postmaster@FreshPorts.org](mailto:postmaster@FreshPorts.org)
Next: Invoke that script after processing a package import.
This remains the next step. FYI: this is babysitting week. The two monsters are in 'school' right now. Time for a shower.
Now we wait for the next built repo.
[19:37 dvl-ingress01 dvl ~/scripts] % svn di job-waiting.pl
Index: job-waiting.pl
===================================================================
--- job-waiting.pl (revision 6043)
+++ job-waiting.pl (working copy)
@@ -72,6 +72,9 @@
# we should put a max loop in here. Loop 100 times, then stop
#
my $JobFound;
+my $NumLoops = 0;
+
+
do {
$JobFound = 0;
# one job might create another, so we keeping looping until they are all cleared.
@@ -81,8 +84,24 @@
FreshPorts::Utilities::Report('notice', "$flag exists. About to run $script");
`$FreshPorts::Config::ScriptDir/$script`;
FreshPorts::Utilities::Report('notice', "Finished running $script");
+ if ($script == 'UpdatePackagesFromRawPackages.py') {
+ # after importing packages, we need to send out notices
+ #
+ UpdatePackagesFromRawPackages.py
+ `$FreshPorts::Config::ScriptDir/report-notification-packages.pl`;
+ }
} else {
FreshPorts::Utilities::Report('notice', "flag '$flag' not set. no work for $script");
}
}
-} until (!$JobFound);
+
+ $NumLoops++;
+
+ # If we're going throught there more than 50 times, a script has forgotten to remove a flag
+ # or is erroring out.
+
+} until (!$JobFound || $NumLoops > 50);
+
+if ($JobFound) {
+ FreshPorts::Utilities::Report('err', "job-waiting.pl seems to be looping. Best check the logs.");
+}
[19:40 dvl-ingress01 dvl ~/scripts] %
This new feature is now available on dev
for testing by users:
This work has been merged into main
.
Currently Freshports can send out email notifications when there are changes to ports on your watch lists but there are cases where the ports don't make it into the actual repo for up to a week or more. I think it would be nice if you could instead set it to monitor the actual ABI repository (eg. FreeBSD:13:amd64, FreeBSD:14:amd64, etc) and get email notifications when there are changes there.