Skip to content

Commit 99c24d0

Browse files
committed
add plugins doc
1 parent 7e865a8 commit 99c24d0

File tree

2 files changed

+135
-2
lines changed

2 files changed

+135
-2
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515

1616
# Comments Density Analyzer
1717

18-
A tool to analyze the comment density and quality in PHP source code files
19-
2018
You might want to use it to control in CI/CD spreading of todos and fixmes in the codebase.
2119

2220
Or you might want to spot simple (regular) comments, which might be there to explain some shitty code or be the commented out code
2321

2422
Or you might want to enforce some docblocks (I worked in companies, where each class and method were required to have docblock explaining purpose et al.)
2523

24+
All of this is possible by utilizing plugins system (see example in docs)
25+
2626
## Features
2727

2828
- **Multiple Comment Types**: Supports identification and analysis of several comment types including regular,
@@ -31,6 +31,7 @@ docblocks, TODOs, FIXMEs, and license information.
3131
- **Quality Check**: Set up a configuration file, and if thresholds aren't met, the exit code will be returned with the report.
3232
- **Configurable Reports**: Get results in either console or HTML file.
3333
- **Baseline**: Filter collected comments against a baseline to ignore old technical debt and focus on new issues.
34+
- **Plugins**: You can implement any logic for processing found comments in your plugins via simple interface
3435

3536
### Output Example
3637
![Output Example](./example_for_readme.png)

docs/examples/OpenIssue.php

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?php
2+
3+
namespace App\Main\Plugin;
4+
5+
use PhpToken;
6+
use SavinMikhail\CommentsDensity\AnalyzeComments\Analyzer\DTO\Output\CommentDTO;
7+
use SavinMikhail\CommentsDensity\AnalyzeComments\Analyzer\DTO\Output\Report;
8+
use SavinMikhail\CommentsDensity\AnalyzeComments\Comments\FixMeComment;
9+
use SavinMikhail\CommentsDensity\AnalyzeComments\Comments\TodoComment;
10+
use SavinMikhail\CommentsDensity\AnalyzeComments\Config\DTO\Config;
11+
use SavinMikhail\CommentsDensity\Plugin\PluginInterface;
12+
use Symfony\Component\HttpClient\HttpClient;
13+
use Symfony\Contracts\HttpClient\HttpClientInterface;
14+
15+
final readonly class OpenIssue implements PluginInterface
16+
{
17+
private const YOUTRACK_URL = 'https://yt';
18+
private const AUTHORIZATION_TOKEN = '';
19+
private const PROJECT_ID = '59-178';
20+
private const STAGE = 'Второй этап';
21+
private const BRANCH_NAME = 'develop';
22+
private const GITLAB_PROJECT_URL = 'https://gitlab/backend';
23+
24+
public function handle(Report $report, Config $config): void
25+
{
26+
$httpClient = HttpClient::create();
27+
28+
foreach ($report->comments as $comment) {
29+
if (!in_array($comment->commentType, [TodoComment::NAME, FixMeComment::NAME], true)) {
30+
continue;
31+
}
32+
33+
if ($this->hasIssueUrl($comment->content)) {
34+
continue;
35+
}
36+
37+
$draftId = $this->createDraft($httpClient, $comment);
38+
$issueId = $this->createIssueFromDraft($httpClient, $draftId);
39+
$issueUrl = self::YOUTRACK_URL . '/issue/' . $issueId;
40+
$this->updateCommentInFile($comment, $issueUrl);
41+
}
42+
}
43+
44+
private function buildDescription(CommentDTO $comment): string
45+
{
46+
$gitlabUrl = self::GITLAB_PROJECT_URL . '/-/blob/' . self::BRANCH_NAME . $comment->file . '?ref_type=heads#L' . $comment->line;
47+
return
48+
"**Comment**: $comment->content \n"
49+
. "**File**: $comment->file \n"
50+
. "**Line**: $comment->line \n"
51+
. "**Gitlab url**: $gitlabUrl \n";
52+
}
53+
54+
private function buildSummary(): string
55+
{
56+
return 'Сфера > ' . self::STAGE . ' > Back > Техдолг';
57+
}
58+
59+
private function createDraft(HttpClientInterface $httpClient, CommentDTO $comment): string
60+
{
61+
$response = $httpClient->request(
62+
'POST',
63+
self::YOUTRACK_URL . '/api/users/me/drafts?$top=-1&fields=$type,applicableActions(description,executing,id,name,userInputType),attachments($type,author(fullName,id,ringId),comment(id,visibility($type)),created,id,imageDimensions(height,width),issue(id,project(id,ringId),visibility($type)),mimeType,name,removed,size,thumbnailURL,url,visibility($type,implicitPermittedUsers($type,avatarUrl,banBadge,banned,email,fullName,id,isLocked,issueRelatedGroup(icon),login,name,online,profiles(general(trackOnlineStatus)),ringId),permittedGroups($type,allUsersGroup,icon,id,name,ringId),permittedUsers($type,avatarUrl,banBadge,banned,email,fullName,id,isLocked,issueRelatedGroup(icon),login,name,online,profiles(general(trackOnlineStatus)),ringId))),canAddPublicComment,canUpdateVisibility,comments(attachments($type,author(fullName,id,ringId),comment(id,visibility($type)),created,id,imageDimensions(height,width),issue(id,project(id,ringId),visibility($type)),mimeType,name,removed,size,thumbnailURL,url,visibility($type,implicitPermittedUsers($type,avatarUrl,banBadge,banned,email,fullName,id,isLocked,issueRelatedGroup(icon),login,name,online,profiles(general(trackOnlineStatus)),ringId),permittedGroups($type,allUsersGroup,icon,id,name,ringId),permittedUsers($type,avatarUrl,banBadge,banned,email,fullName,id,isLocked,issueRelatedGroup(icon),login,name,online,profiles(general(trackOnlineStatus)),ringId))),id),created,description,externalIssue(key,name,url),fields($type,hasStateMachine,id,isUpdatable,name,projectCustomField($type,bundle(id),canBeEmpty,emptyFieldText,field(fieldType(isMultiValue,valueType),id,localizedName,name,ordinal),id,isEstimation,isPublic,isSpentTime,ordinal,size),value($type,archived,avatarUrl,buildIntegration,buildLink,color(background,id),description,fullName,id,isResolved,localizedName,login,markdownText,minutes,name,presentation,ringId,text)),hasEmail,hiddenAttachmentsCount,id,idReadable,isDraft,links(direction,id,issuesSize,linkType(aggregation,directed,localizedName,localizedSourceToTarget,localizedTargetToSource,name,sourceToTarget,targetToSource,uid),trimmedIssues($type,comments($type),created,id,idReadable,isDraft,numberInProject,project(id,ringId),reporter(id),resolved,summary,voters(hasVote),votes,watchers(hasStar)),unresolvedIssuesSize),mentionedArticles(idReadable,summary),mentionedIssues(idReadable,resolved,summary),mentionedUsers($type,avatarUrl,banBadge,banned,canReadProfile,fullName,id,isLocked,login,name,ringId),messages,numberInProject,project($type,id,isDemo,leader(id),name,plugins(helpDeskSettings(enabled),timeTrackingSettings(enabled,estimate(field(id,name),id),timeSpent(field(id,name),id)),vcsIntegrationSettings(processors(enabled,migrationFailed,server(enabled,url),upsourceHubResourceKey,url))),ringId,shortName,team($type,allUsersGroup,icon,id,name,ringId)),reporter($type,avatarUrl,banBadge,banned,email,fullName,id,isLocked,issueRelatedGroup(icon),login,name,online,profiles(general(trackOnlineStatus)),ringId),resolved,summary,tags(color(id),id,isUpdatable,isUsable,name,owner(id),query),updated,updater($type,avatarUrl,banBadge,banned,email,fullName,id,isLocked,issueRelatedGroup(icon),login,name,online,profiles(general(trackOnlineStatus)),ringId),usesMarkdown,visibility($type,implicitPermittedUsers($type,avatarUrl,banBadge,banned,email,fullName,id,isLocked,issueRelatedGroup(icon),login,name,online,profiles(general(trackOnlineStatus)),ringId),permittedGroups($type,allUsersGroup,icon,id,name,ringId),permittedUsers($type,avatarUrl,banBadge,banned,email,fullName,id,isLocked,issueRelatedGroup(icon),login,name,online,profiles(general(trackOnlineStatus)),ringId)),voters(hasVote),votes,watchers(hasStar),widgets(base,indexPath,place,pluginName)',
64+
[
65+
'headers' => [
66+
'Content-Type' => 'application/json',
67+
'Authorization' => 'Bearer ' . self::AUTHORIZATION_TOKEN,
68+
],
69+
'json' => [
70+
'summary' => $this->buildSummary(),
71+
'description' => $this->buildDescription($comment),
72+
'project' => ['id' => self::PROJECT_ID],
73+
],
74+
]
75+
);
76+
$response = $response->toArray();
77+
return $response['id']; // looks like 68-107353
78+
}
79+
80+
private function createIssueFromDraft(HttpClientInterface $httpClient, string $draftId): string
81+
{
82+
$response = $httpClient->request(
83+
'POST',
84+
self::YOUTRACK_URL . "/api/issues?draftId={$draftId}&\$top=-1&fields=id,idReadable,numberInProject,messages",
85+
[
86+
'headers' => [
87+
'Content-Type' => 'application/json',
88+
'Authorization' => 'Bearer ' . self::AUTHORIZATION_TOKEN,
89+
],
90+
'json' => [
91+
'issueId' => $draftId,
92+
],
93+
]
94+
);
95+
$response = $response->toArray();
96+
return $response['idReadable']; // looks like backend-287
97+
}
98+
99+
private function hasIssueUrl(string $commentContent): bool
100+
{
101+
return (bool)preg_match('/https:\/\/yt\.kr\.digital\/issue\/\w+-\d+/', $commentContent);
102+
}
103+
104+
private function updateCommentInFile(CommentDTO $comment, string $issueUrl): void
105+
{
106+
$fileContent = file_get_contents($comment->file);
107+
$tokens = \PhpToken::tokenize($fileContent);
108+
$changed = false;
109+
foreach ($tokens as $token) {
110+
if ($token->line === $comment->line && $token->is([T_COMMENT, T_DOC_COMMENT]) ) {
111+
$token->text = rtrim($token->text) . " ($issueUrl)";
112+
$changed = true;
113+
}
114+
}
115+
if (!$changed) {
116+
return;
117+
}
118+
$this->save($comment->file, $tokens);
119+
}
120+
121+
/**
122+
* @param PhpToken[] $tokens
123+
*/
124+
private function save(string $file, array $tokens): void
125+
{
126+
$content = implode('', array_map(static fn(\PhpToken $token) => $token->text, $tokens));
127+
$res = file_put_contents($file, $content);
128+
if ($res === false) {
129+
throw new \Exception('failed to write to file: ' . $file);
130+
}
131+
}
132+
}

0 commit comments

Comments
 (0)