Repository navigation
Bug 2086124 - [HACKBOT] Structured metadata on comments #2819
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
5305100
05a5f15
88e1652
d63923d
820e1f1
66b3a9a
605a83c
892758f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1869,8 +1869,7 @@ a.lightbox-icon.markdown { | |
| .comment .comment-actions > [role="group"] { | ||
| justify-content: flex-end; | ||
| flex-wrap: wrap; | ||
| gap: 4px 0; | ||
| width: 128px; | ||
| width: 64px; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this narrows the action group and drops the row gap for every comment on small screens, not just hackbot ones. tag/reply/spinner (and private for insiders) now wrap two per row with no spacing. could this move to hackbot.css scoped to hackbot comments, or is the global change intended |
||
| } | ||
|
|
||
| .comment .comment-actions > [role="group"] .comment-private { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # This Source Code Form is subject to the terms of the Mozilla Public | ||
| # License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| # file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
| # | ||
| # This Source Code Form is "Incompatible With Secondary Licenses", as | ||
| # defined by the Mozilla Public License, v. 2.0. | ||
|
|
||
| package Bugzilla::Extension::Hackbot; | ||
|
|
||
| use 5.10.1; | ||
| use strict; | ||
| use warnings; | ||
|
|
||
| use constant NAME => 'Hackbot'; | ||
|
|
||
| use constant REQUIRED_MODULES => []; | ||
|
|
||
| use constant OPTIONAL_MODULES => []; | ||
|
|
||
| __PACKAGE__->NAME; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| # This Source Code Form is subject to the terms of the Mozilla Public | ||
| # License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| # file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
| # | ||
| # This Source Code Form is "Incompatible With Secondary Licenses", as | ||
| # defined by the Mozilla Public License, v. 2.0. | ||
|
|
||
| package Bugzilla::Extension::Hackbot; | ||
|
|
||
| use 5.10.1; | ||
| use strict; | ||
| use warnings; | ||
|
|
||
| use base qw(Bugzilla::Extension); | ||
|
|
||
| use Bugzilla::Extension::Hackbot::Metadata qw(parse_front_matter); | ||
| use Bugzilla::User; | ||
|
|
||
| our $VERSION = '1'; | ||
|
|
||
| BEGIN { | ||
| *Bugzilla::Comment::hackbot = \&_comment_hackbot; | ||
| } | ||
|
|
||
| # The ID of the account named by the hackbot_user param, or 0 when it is unset | ||
| # or names nobody. Bugzilla::User::login_to_id doesn't cache a miss, and this | ||
| # runs for every comment that is read. | ||
| sub _hackbot_user_id { | ||
| my $login = Bugzilla->params->{hackbot_user} or return 0; | ||
| my $cache = Bugzilla->request_cache->{hackbot_user_ids} ||= {}; | ||
| return $cache->{$login} //= Bugzilla::User::login_to_id($login) || 0; | ||
| } | ||
|
|
||
| # Anyone can type a front matter block into a comment, so only the Hackbot | ||
| # account's comments are parsed, and only plain ones: the text of other types | ||
| # (attachments, duplicates) is formatted without the comment_body_full hook, so | ||
| # their block would stay visible. Returns the same list as parse_front_matter. | ||
| # A page reads each comment several times (header, badge, tags, text), so the | ||
| # result is kept on the comment for as long as its text is unchanged. | ||
| sub _parse_comment { | ||
| my ($comment) = @_; | ||
| my $user_id = _hackbot_user_id() or return; | ||
| return if ($comment->{who} // 0) != $user_id || $comment->type; | ||
|
|
||
| my $body = $comment->body; | ||
| my $parsed = $comment->{hackbot_front_matter}; | ||
| if (!$parsed || $parsed->[0] ne $body) { | ||
| $parsed = $comment->{hackbot_front_matter} = [$body, parse_front_matter($body)]; | ||
| } | ||
| return @$parsed[1 .. $#$parsed]; | ||
| } | ||
|
|
||
| sub _comment_hackbot { | ||
| my ($comment) = @_; | ||
| my ($metadata) = _parse_comment($comment); | ||
| return $metadata; | ||
| } | ||
|
|
||
| sub comment_body_full { | ||
| my ($self, $args) = @_; | ||
| my (undef, $body) = _parse_comment($args->{comment}); | ||
| ${$args->{body}} = $body if defined $body; | ||
|
kyoshino marked this conversation as resolved.
Resolved
|
||
| } | ||
|
|
||
| sub config_add_panels { | ||
| my ($self, $args) = @_; | ||
| $args->{panel_modules}->{Hackbot} = 'Bugzilla::Extension::Hackbot::Config'; | ||
| } | ||
|
|
||
| __PACKAGE__->NAME; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # This Source Code Form is subject to the terms of the Mozilla Public | ||
| # License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| # file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
| # | ||
| # This Source Code Form is "Incompatible With Secondary Licenses", as | ||
| # defined by the Mozilla Public License, v. 2.0. | ||
|
|
||
| package Bugzilla::Extension::Hackbot::Config; | ||
|
|
||
| use 5.10.1; | ||
| use strict; | ||
| use warnings; | ||
|
|
||
| use Bugzilla::Config::Common; | ||
| use Bugzilla::User; | ||
|
|
||
| our $sortkey = 1310; | ||
|
|
||
| sub get_param_list { | ||
| my ($class) = @_; | ||
|
|
||
| return ({ | ||
| name => 'hackbot_user', | ||
| type => 't', | ||
| default => '', | ||
| checker => \&check_hackbot_user, | ||
| }); | ||
| } | ||
|
|
||
| # Empty is valid: it turns the extension off. | ||
| sub check_hackbot_user { | ||
| my ($login) = @_; | ||
| return '' if $login eq '' || Bugzilla::User::login_to_id($login); | ||
| return "There is no user named '$login'."; | ||
| } | ||
|
|
||
| 1; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| # This Source Code Form is subject to the terms of the Mozilla Public | ||
| # License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| # file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
| # | ||
| # This Source Code Form is "Incompatible With Secondary Licenses", as | ||
| # defined by the Mozilla Public License, v. 2.0. | ||
|
|
||
| package Bugzilla::Extension::Hackbot::Metadata; | ||
|
|
||
| use 5.10.1; | ||
| use strict; | ||
| use warnings; | ||
|
|
||
| use base qw(Exporter); | ||
|
|
||
| our @EXPORT_OK = qw(parse_front_matter); | ||
|
|
||
| # The block hackbot-api puts at the top of a comment: | ||
| # | ||
| # --- | ||
| # hackbot: | ||
| # run: 550e8400-e29b-41d4-a716-446655440000 | ||
| # action: 3f2a9c1e7b4d4f0a8e6c2d1b5a7f9e30 | ||
| # agent: bug-fix | ||
| # url: https://hackbot.moz.tools/runs/550e8400-e29b-41d4-a716-446655440000 | ||
| # --- | ||
| # | ||
| # It is only ever generated, so this reads that one shape rather than YAML in | ||
| # general. Anything else is an ordinary comment that happens to start with a | ||
| # rule, and unknown keys are skipped so that fields can be added. The /a flag | ||
| # keeps the character classes to ASCII. | ||
| my $FRONT_MATTER | ||
| = qr/\A---\nhackbot:\n((?: [[:lower:]_]+: [^\n]*\n)+)---(?:\n+|\z)(.*)\z/as; | ||
| my $FIELD = qr/^ ([[:lower:]_]+): ([^\n]*?)\s*$/am; | ||
| my $IDENTIFIER = qr/\A[[:alnum:]._-]+\z/a; | ||
|
|
||
| # Returns the metadata hash and the comment text without the block, or an empty | ||
| # list when the text doesn't start with a valid block. The url key is left out | ||
| # unless it is an https URL, because it ends up in a link's href. | ||
| sub parse_front_matter { | ||
| my ($text) = @_; | ||
|
|
||
| my ($block, $body) = ($text // '') =~ $FRONT_MATTER or return; | ||
|
|
||
| my %fields = $block =~ /$FIELD/g; | ||
|
|
||
| my %metadata; | ||
| foreach my $key (qw(run action agent)) { | ||
| return unless ($fields{$key} // '') =~ $IDENTIFIER; | ||
| $metadata{$key} = $fields{$key}; | ||
| } | ||
| $metadata{url} = $fields{url} if ($fields{url} // '') =~ m{\Ahttps://\S+\z}; | ||
|
|
||
| return (\%metadata, $body); | ||
| } | ||
|
|
||
| 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with this hook the rest
textfield (and show.xml thetext, webhook/pulse body) can now omit part of the stored text. docs/en/md/api/core/v1/comment.md still saystextis the full body. worth a line there saying extensions may hide parts oftextandraw_textalways has the stored text