Skip to content

Commit

Permalink
v3.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Przybyszewski authored Jul 26, 2022
2 parents 37a0b0d + 0d792a0 commit f372c16
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 18 deletions.
10 changes: 3 additions & 7 deletions cogs/ecosystem/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,8 @@ async def config_show_command_group(self, ctx: GitBotContext) -> None:
await ctx.error(ctx.l.generic.nonexistent.qa)
return
lang: str = ctx.fmt('accessibility list locale', f'`{ctx.l.meta.localized_name.capitalize()}`')
user_str: str = ctx.fmt('qa list user', (f'[`{user["user"]}`](https://github.com/{user["user"]})'
if 'user' in user else f'`{ctx.l.config.show.base.item_not_set}`'))
org: str = ctx.fmt('qa list org', (f'[`{user["org"]}`](https://github.com/{user["org"]})'
if 'org' in user else f'`{ctx.l.config.show.base.item_not_set}`'))
repo: str = ctx.fmt('qa list repo', (f'[`{user["repo"]}`](https://github.com/{user["repo"]})'
if 'repo' in user else f'`{ctx.l.config.show.base.item_not_set}`'))
user_str, org, repo = (ctx.fmt(f'qa list {item}', f'[`{item}`](https://github.com/{item})' if item in user else
f'`{ctx.l.config.show.base.item_not_set}`') for item in ('user', 'org', 'repo'))
accessibility: list = ctx.l.config.show.base.accessibility.heading + '\n' + '\n'.join([lang])
qa: list = ctx.l.config.show.base.qa.heading + '\n' + '\n'.join([user_str, org, repo])
guild_str: str = ''
Expand All @@ -90,7 +86,7 @@ async def config_show_command_group(self, ctx: GitBotContext) -> None:
ac: AutomaticConversionSettings = Mgr.env.autoconv_default
else:
ac: AutomaticConversionSettings = {k: (v if k not in (_ac := guild.get('autoconv', {}))
else _ac[k]) for k, v in Mgr.env.autoconv_default.items()}
else _ac[k]) for k, v in Mgr.env.autoconv_default.items()}
codeblock: str = ctx.fmt('codeblock',
f'`{ctx.l.enum.generic.switch[str(ac["codeblock"])]}`')
lines: str = ctx.fmt('gh_lines',
Expand Down
49 changes: 43 additions & 6 deletions cogs/github/other/loc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import aiofiles
import shutil
import fnmatch
import subprocess
from discord.ext import commands
from typing import Optional
Expand All @@ -16,6 +17,9 @@


class LinesOfCode(commands.Cog):
# I know that using "perl" alone is insecure, but it will only be used in Windows dev environments
__perl_command_line__: str = '/bin/perl' if os.name != 'nt' else 'perl'

def __init__(self, bot: commands.Bot):
self.bot: commands.Bot = bot

Expand All @@ -35,11 +39,13 @@ async def lines_of_code_command(self, ctx: GitBotContext, repo: GitHubRepository
if not r:
await ctx.error(ctx.l.generic.nonexistent.repo.base)
return
processed: Optional[dict] = await self.process_repo(ctx, repo)
processed: Optional[tuple[dict, int | None]] | dict = await self.process_repo(ctx, repo)
if not processed:
await ctx.error(ctx.l.loc.file_too_big)
return
title: str = ctx.fmt('title', f'`{repo}`')
count: int | None = processed[1]
processed = processed[0]
embed: GitBotEmbed = GitBotEmbed(
color=0x00a6ff,
title=title,
Expand All @@ -52,11 +58,31 @@ async def lines_of_code_command(self, ctx: GitBotContext, repo: GitHubRepository
+ f'**{ctx.l.loc.stats.comments}:** {processed["SUM"]["comment"]}\n'
+ f'**{ctx.l.loc.stats.detailed}:**\n'
+ await self.prepare_result_sheet(processed)),
footer=ctx.l.loc.footer
footer=ctx.l.loc.footer.credit if not count
else (ctx.fmt('footer with_count plural', count) if count > 1 else ctx.fmt('footer with_count singular')),
)
await ctx.send(embed=embed)

async def process_repo(self, ctx: GitBotContext, repo: GitHubRepository) -> Optional[dict]:
@staticmethod
def remove_matches(directory: str, pattern: str) -> int:
Mgr.debug(f'Removing files matching pattern "{pattern}" from directory "{directory}"')
c_removed: int = 0
for root, dirs, files in os.walk(directory):
for f in files:
if fnmatch.fnmatch(f, pattern):
Mgr.debug(f'Removing file "{f}"')
c_removed += 1
os.remove(os.path.join(root, f))
for d in dirs:
if fnmatch.fnmatch(d, pattern):
Mgr.debug(f'Removing directory "{d}"')
c_removed += 1
shutil.rmtree(os.path.join(root, d))
Mgr.debug(f'Removed {c_removed} entries.')
return c_removed

@staticmethod
async def process_repo(ctx: GitBotContext, repo: GitHubRepository) -> Optional[tuple[dict, int | None]]:
if (not ctx.__nocache__) and (cached := Mgr.loc_cache.get(repo := repo.lower())):
return cached
tmp_zip_path: str = f'./tmp/{ctx.message.id}.zip'
Expand All @@ -70,20 +96,31 @@ async def process_repo(self, ctx: GitBotContext, repo: GitHubRepository) -> Opti
async with aiofiles.open(tmp_zip_path, 'wb') as fp:
await fp.write(files)
await Mgr.unzip_file(tmp_zip_path, tmp_dir_path)
output: dict = json.loads(subprocess.check_output(['/bin/perl', 'cloc.pl', '--json', tmp_dir_path]))
c_removed: int = 0
cfg: dict | None = await Mgr.get_repo_gitbot_config(repo)
if cfg and cfg.get('loc'):
if isinstance(cfg['loc'], dict) and (ignore := cfg['loc'].get('ignore')):
if isinstance(ignore, str):
ignore = [ignore]
c_removed: int = 0
for pattern in ignore:
c_removed += LinesOfCode.remove_matches(tmp_dir_path, pattern)
output: dict = json.loads(subprocess.check_output([LinesOfCode.__perl_command_line__, 'cloc.pl',
'--json', tmp_dir_path]))
except subprocess.CalledProcessError as e:
Mgr.debug(f'the CLOC script failed with exit code {e.returncode}')
else:
Mgr.loc_cache[repo] = output
return output
return output, c_removed
finally:
try:
shutil.rmtree(tmp_dir_path)
os.remove(tmp_zip_path)
except FileNotFoundError:
pass

async def prepare_result_sheet(self, data: dict) -> str:
@staticmethod
async def prepare_result_sheet(data: dict) -> str:
result: str = '```py\n{}```'
threshold: int = 15
for k, v in data.items():
Expand Down
4 changes: 2 additions & 2 deletions lib/api/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class GitHubAPI:
"""

def __init__(self, tokens: tuple, requester: str):
requester: str = requester + '; Python {v.major}.{v.minor}.{v.micro}'.format(v=version_info)
requester += '; Python {v.major}.{v.minor}.{v.micro}'.format(v=version_info)
self.__tokens: tuple = tokens
self.__token_cycle: cycle = cycle(t for t in self.__tokens if t is not None)
self.queries: DirProxy = DirProxy('./resources/queries/', ('.gql', '.graphql'))
Expand Down Expand Up @@ -107,7 +107,7 @@ async def get_repo_files(self, repo: GitHubRepository) -> list[dict]:
return []

@normalize_repository
async def get_tree_file(self, repo: GitHubRepository, path: str):
async def get_tree_file(self, repo: GitHubRepository, path: str) -> dict | list:
if repo.count('/') != 1:
return []
if path[0] == '/':
Expand Down
13 changes: 11 additions & 2 deletions lib/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import ast
import json
import dotenv
import base64
import discord
import zipfile
import os.path
Expand Down Expand Up @@ -46,7 +47,8 @@
from typing import Optional, Callable, Any, Reversible, Iterable, Type, TYPE_CHECKING, Generator
if TYPE_CHECKING:
from lib.structs.discord.context import GitBotContext
from lib.typehints import DictSequence, AnyDict, Identity, GitBotGuild, AutomaticConversionSettings, LocaleName, ReleaseFeedItemMention
from lib.api.github import GitHubAPI
from lib.typehints import DictSequence, AnyDict, Identity, GitBotGuild, AutomaticConversionSettings, LocaleName, ReleaseFeedItemMention, GitBotDotJSON


class Manager:
Expand All @@ -60,7 +62,7 @@ class Manager:
def __init__(self, github):
self.lib_root: str = os.path.dirname(os.path.abspath(__file__))
self.root_directory: str = self.lib_root[:self.lib_root.rindex(os.sep)]
self.git = github
self.git: 'GitHubAPI' = github
self.ses: aiohttp.ClientSession = self.git.ses
self._prepare_env()
self.bot_dev_name: str = f'gitbot ({"production" if self.env.production else "preview"})'
Expand All @@ -83,6 +85,13 @@ def __init__(self, github):
self.__fix_missing_locales()
self.__preprocess_locale_emojis()

async def get_repo_gitbot_config(self, repo: str) -> GitBotDotJSON | None:
gh_res: dict | None = await self.git.get_tree_file(repo, '.gitbot.json')
if not gh_res:
return
if gh_res['encoding'] == 'base64':
return json.loads(base64.decodebytes(bytes(gh_res['content'].encode('utf-8'))).decode('utf-8'))

def get_current_commit(self, short: bool = True) -> str:
"""
Get the current commit hash of the running bot instance.
Expand Down
1 change: 1 addition & 0 deletions lib/typehints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from lib.typehints.generic import *
from lib.typehints.locale.help import *
from lib.typehints.db.guild.release_feed import *
from lib.typehints.gitbot_dot_json import *

__all__: tuple = (
'DictSequence',
Expand Down
14 changes: 14 additions & 0 deletions lib/typehints/gitbot_dot_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from typing import TypedDict


class LOCGitBotJSONEntry(TypedDict):
ignore: str | list[str]


class ReleaseFeedGitBotJSONEntry(TypedDict):
ignore: str


class GitBotDotJSON(TypedDict, total=False):
loc: LOCGitBotJSONEntry
release_feed: ReleaseFeedGitBotJSONEntry
8 changes: 7 additions & 1 deletion resources/locale/en.locale.json
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,13 @@
"comments": "Comments",
"detailed": "Detailed"
},
"footer": "This command is powered by the CLOC CLI tool."
"footer": {
"with_count": {
"plural": "{0} entries matching .gitbot.json ignore rules were removed.",
"singular": "One entry matching .gitbot.json ignore rules was removed."
},
"credit": "This command is powered by the CLOC CLI tool."
}
},
"commits": {
"embed": {
Expand Down

0 comments on commit f372c16

Please sign in to comment.