1- import os
2- import subprocess
31from collections import defaultdict
42from datetime import datetime , timezone
5- from pathlib import Path
63from typing import Optional
74
85import click
9- import requests
10-
11- from . import ERROR_MSG_PREFIX , __version__
12- from .utils import (
13- NotRequiredIf ,
14- err ,
15- get_metadata_from_file ,
16- get_project_meta ,
17- glob_fragments ,
18- load_toml_config ,
19- nonceify ,
20- out ,
21- render_fragments ,
22- )
23-
24-
25- PR_ENDPOINT = "https://api.github.com/repos/discord-modmail/modmail/pulls/{number}"
26- BAD_RESPONSE = {
27- 404 : "Pull request not located! Please enter a valid number!" ,
28- 403 : "Rate limit has been hit! Please try again later!" ,
29- }
30-
31- TEMPLATE = """
32- # Please write your news content. When finished, save the file.
33- # In order to abort, exit without saving.
34- # Lines starting with \" #\" are ignored.
35-
36- """ .lstrip ()
37- NO_NEWS_PATH_ERROR = (
38- f"{ ERROR_MSG_PREFIX } `news/next/` doesn't exist.\n You are either in the wrong directory while"
39- " running this command (should be in the project root) or the path doesn't exist, if it "
40- "doesn't exist please create it and run this command again :) Happy change-logging!"
41- )
426
43- CONFIG = load_toml_config ()
44- SECTIONS = [_type for _type , _ in CONFIG .get ("types" ).items ()]
45-
46-
47- def save_news_fragment (ctx : click .Context , gh_pr : int , nonce : str , news_entry : str , news_type : str ) -> None :
48- """Save received changelog data to a news file."""
49- date = datetime .now (timezone .utc ).strftime ("%Y-%m-%d" )
50- path = Path (Path .cwd (), f"news/next/{ news_type } /{ date } .pr-{ gh_pr } .{ nonce } .md" )
51- if not path .parents [1 ].exists ():
52- err (NO_NEWS_PATH_ERROR , fg = "blue" )
53- ctx .exit (1 )
54- elif not path .parents [0 ].exists ():
55- make_news_type_dir = click .confirm (
56- f"Should I make the new type DIR for the news type at { path .parents [0 ]} "
57- )
58- if make_news_type_dir :
59- path .parents [0 ].mkdir (exist_ok = True )
60- elif path .exists ():
61- # The file exists
62- err (f"{ ERROR_MSG_PREFIX } { Path (os .path .relpath (path , start = Path .cwd ()))} already exists" )
63- ctx .exit (1 )
64-
65- text = str (news_entry )
66- with open (path , "wt" , encoding = "utf-8" ) as file :
67- file .write (text )
68-
69- # Add news fragment to git stage
70- subprocess .run (["git" , "add" , "--force" , path ]).check_returncode ()
71-
72- out (
73- f"All done! ✨ 🍰 ✨ Created news fragment at { Path (os .path .relpath (path , start = Path .cwd ()))} "
74- "\n You are now ready for commit!"
75- )
76-
77-
78- def validate_pull_request_number (
79- ctx : click .Context , _param : click .Parameter , value : Optional [int ]
80- ) -> Optional [int ]:
81- """Check if the given pull request number exists on the github repository."""
82- r = requests .get (PR_ENDPOINT .format (number = value ))
83- if r .status_code == 403 :
84- if r .headers .get ("X-RateLimit-Remaining" ) == "0" :
85- err (f"{ ERROR_MSG_PREFIX } Ratelimit reached, please retry in a few minutes." )
86- ctx .exit ()
87- err (f"{ ERROR_MSG_PREFIX } Cannot access pull request." )
88- ctx .exit ()
89- elif r .status_code in (404 , 410 ):
90- err (f"{ ERROR_MSG_PREFIX } PR not found." )
91- ctx .exit ()
92- elif r .status_code != 200 :
93- err (f"{ ERROR_MSG_PREFIX } Error while fetching issue, retry again after sometime." )
94- ctx .exit ()
95-
96- return value
7+ from . import __version__
8+ from .constants import *
9+ from .utils import *
9710
9811
9912@click .group (context_settings = dict (help_option_names = ["-h" , "--help" ]), invoke_without_command = True )
10013@click .version_option (version = __version__ )
101- def cli_main () -> None :
14+ @click .pass_context
15+ def cli_main (ctx : click .Context ) -> None :
10216 """
10317 Modmail News 📜🤖.
10418
@@ -107,7 +21,9 @@ def cli_main() -> None:
10721 contributors and maintainers to work with news files (changelogs) by automating
10822 the process of generating, compiling and validating them!
10923 """
110- ...
24+ if not ctx .args and not ctx .resilient_parsing and not ctx .command :
25+ click .echo (ctx .get_help ())
26+ ctx .exit ()
11127
11228
11329@cli_main .command ("add" )
@@ -131,24 +47,19 @@ def cli_main() -> None:
13147 prompt = True ,
13248)
13349@click .option (
134- "--pr-number " ,
50+ "--pr" ,
13551 type = int ,
13652 prompt = True ,
13753 callback = validate_pull_request_number ,
13854)
13955@click .pass_context
140- def cli_add_news (ctx : click .Context , message : str , editor : str , type : str , pr_number : int ) -> None :
56+ def cli_add_news (ctx : click .Context , message : str , editor : str , type : str , pr : int ) -> None :
14157 """Add a news entry 📜 to the current discord-modmail repo for your awesome change!"""
14258 if not message :
14359 message_notes = []
14460 while True :
14561 content = click .edit (
146- (
147- "# Please write your news content. When finished, save the file.\n "
148- "# In order to abort, exit without saving.\n "
149- "# Lines starting with '#' are ignored.\n "
150- "\n " .join (message_notes )
151- ),
62+ "\n " .join ((TEMPLATE , * message_notes )),
15263 editor = editor ,
15364 extension = "md" ,
15465 )
@@ -167,7 +78,7 @@ def cli_add_news(ctx: click.Context, message: str, editor: str, type: str, pr_nu
16778
16879 break
16980
170- save_news_fragment (ctx , pr_number , nonceify (message ), message , type )
81+ save_news_fragment (ctx , pr , nonceify (message ), message , type )
17182
17283
17384@cli_main .command ("build" )
@@ -192,7 +103,7 @@ def cli_build_news(ctx: click.Context, edit: Optional[bool], keep: bool) -> None
192103 for filename in filenames :
193104 if not filename .endswith (".md" ):
194105 continue
195- _file_metadata [filename ] = get_metadata_from_file (Path (filename ))
106+ _file_metadata [filename ] = get_metadata_from_news (Path (filename ))
196107
197108 # Group metadata according to news_type
198109 for path , fragment in _file_metadata .items ():
@@ -201,28 +112,16 @@ def cli_build_news(ctx: click.Context, edit: Optional[bool], keep: bool) -> None
201112 fragment ["path" ] = path
202113 file_metadata [news_type ].append (fragment )
203114
204- template = CONFIG ["core" ].get ("template" )
205- if not template :
206- template = Path (Path .cwd (), "scripts/news/template.md.jinja" )
207- else :
208- template = Path (Path .cwd (), f"scripts/news/{ template } " )
209-
210- if not template .exists ():
211- err (
212- f"{ ERROR_MSG_PREFIX } Template at { template .relative_to (Path .cwd ())} not found :(. Make sure "
213- f"your path is relative to `scripts/news`!"
214- )
215-
216115 name , version = get_project_meta ()
217116 version_news = render_fragments (
218- section_names = CONFIG [ "types" ] ,
219- template = template ,
117+ sections = SECTIONS ,
118+ template = TEMPLATE_FILE_PATH ,
220119 metadata = file_metadata ,
221120 wrap = True ,
222121 version_data = (name , version ),
223122 date = date ,
224123 )
225- news_path = Path (Path . cwd () , f"news/{ version } .md" )
124+ news_path = Path (REPO_ROOT , f"news/{ version } .md" )
226125
227126 with open (news_path , mode = "w" ) as file :
228127 file .write (version_news )
@@ -233,10 +132,9 @@ def cli_build_news(ctx: click.Context, edit: Optional[bool], keep: bool) -> None
233132 click .edit (filename = str (news_path ))
234133
235134 if not keep :
236- files = Path (Path .cwd (), "scripts/news/next" )
237- for news_fragment in files .glob ("*.md" ):
135+ for news_fragment in NEWS_NEXT .glob ("*/*.md" ):
238136 os .remove (news_fragment )
239- out ("🍰 Cleared existing `scripts/ news/next` news fragments!" )
137+ out ("🍰 Cleared existing `news/next` news fragments!" )
240138
241139
242140if __name__ == "__main__" :
0 commit comments