1- import github
2- from github import Github
3- import os ,sys ,platform ,base64 ,time
4-
5- # Intializing the Variables
6- BOT_TOKEN = os .environ .get ('CONCORE_BOT_TOKEN' , '' )
7- BOT_ACCOUNT = 'concore-bot' #bot account name
8- REPO_NAME = 'concore-studies' #study repo name
9- UPSTREAM_ACCOUNT = 'ControlCore-Project' #upstream account name
10- STUDY_NAME = sys .argv [1 ]
11- STUDY_NAME_PATH = sys .argv [2 ]
12- AUTHOR_NAME = sys .argv [3 ]
13- BRANCH_NAME = sys .argv [4 ]
14- PR_TITLE = sys .argv [5 ]
15- PR_BODY = sys .argv [6 ]
16-
17- # Defining Functions
18- def checkInputValidity ():
19- if not AUTHOR_NAME or not STUDY_NAME or not STUDY_NAME_PATH :
20- print ("Please Provide necessary Inputs" )
21- exit (1 )
22- if not os .path .isdir (STUDY_NAME_PATH ):
23- print ("Directory doesnot Exists.Invalid Path" )
24- exit (1 )
25-
26- def printPR (pr ):
27- print (f'Check your example here https://github.com/{ UPSTREAM_ACCOUNT } /{ REPO_NAME } /pulls/{ pr .number } ' ,end = "" )
28-
29- def anyOpenPR (upstream_repo ):
30- try :
31- prs = upstream_repo .get_pulls (state = 'open' , head = f'{ BOT_ACCOUNT } :{ BRANCH_NAME } ' )
32- return prs [0 ] if prs .totalCount > 0 else None
33- except Exception :
34- print ("Unable to fetch PR status. Try again later." )
35- exit (1 )
36-
37- def commitAndUpdateRef (repo ,tree_content ,commit ,branch ):
38- try :
39- new_tree = repo .create_git_tree (tree = tree_content ,base_tree = commit .commit .tree )
40- new_commit = repo .create_git_commit (f"Committing Study Named { STUDY_NAME } " ,new_tree ,[commit .commit ])
41- if len (repo .compare (base = commit .commit .sha ,head = new_commit .sha ).files ) == 0 :
42- print ("Your don't have any new changes.May be your example is already accepted.If this is not the case try with different fields." )
43- exit (1 )
44- ref = repo .get_git_ref ("heads/" + branch .name )
45- ref .edit (new_commit .sha ,True )
46- except Exception as e :
47- print ("failed to Upload your example.Please try after some time." ,end = "" )
48- exit (1 )
49-
50-
51- def appendBlobInTree (repo ,content ,file_path ,tree_content ):
52- blob = repo .create_git_blob (content ,'utf-8' )
53- tree_content .append ( github .InputGitTreeElement (path = file_path ,mode = "100644" ,type = "blob" ,sha = blob .sha ))
54-
55-
56- def runWorkflow (repo ,upstream_repo ):
57- openPR = anyOpenPR (upstream_repo )
58- if not openPR :
59- try :
60- repo .get_workflow ("pull_request.yml" ).create_dispatch (
61- ref = BRANCH_NAME ,
62- inputs = {'title' : f"[BOT]: { PR_TITLE } " , 'body' : PR_BODY , 'upstreamRepo' : UPSTREAM_ACCOUNT , 'botRepo' : BOT_ACCOUNT , 'repo' : REPO_NAME }
63- )
64- printPRStatus (upstream_repo )
65- except Exception as e :
66- print (f"Error triggering workflow. Try again later.\n ERROR: { e } " )
67- exit (1 )
68- else :
69- print (f"Successfully uploaded. Waiting for approval: https://github.com/{ UPSTREAM_ACCOUNT } /{ REPO_NAME } /pull/{ openPR .number } " )
70-
71- def printPRStatus (upstream_repo ):
72- attempts = 5
73- delay = 2
74- for i in range (attempts ):
75- print (f"Attempt: { i } " )
76- try :
77- latest_pr = upstream_repo .get_pulls (state = 'open' , sort = 'created' , direction = 'desc' )[0 ]
78- print (f"Check your example here: https://github.com/{ UPSTREAM_ACCOUNT } /{ REPO_NAME } /pull/{ latest_pr .number } " )
79- return
80- except Exception :
81- time .sleep (delay )
82- delay *= 2
83- print ("Uploaded successfully, but unable to fetch status." )
84-
85-
86- def isImageFile (filename ):
87- image_extensions = ['.jpeg' , '.jpg' , '.png' ,'.gif' ]
88- return any (filename .endswith (ext ) for ext in image_extensions )
89-
90- def remove_prefix (text , prefix ):
91- if text .startswith (prefix ):
92- return text [len (prefix ):]
93- return text
94-
95-
96- # Decode Github Token
97- def decode_token (encoded_token ):
98- decoded_bytes = encoded_token .encode ("ascii" )
99- convertedbytes = base64 .b64decode (decoded_bytes )
100- decoded_token = convertedbytes .decode ("ascii" )
101- return decoded_token
102-
103-
104- # check if directory path is Valid
105- checkInputValidity ()
106-
107-
108- # Authenticating Github with Access token
109- try :
110- BRANCH_NAME = AUTHOR_NAME .replace (" " , "_" ) + "_" + STUDY_NAME if BRANCH_NAME == "#" else BRANCH_NAME .replace (" " , "_" )
111- PR_TITLE = f"Contributing Study { STUDY_NAME } by { AUTHOR_NAME } " if PR_TITLE == "#" else PR_TITLE
112- PR_BODY = f"Study Name: { STUDY_NAME } \n Author Name: { AUTHOR_NAME } " if PR_BODY == "#" else PR_BODY
113- DIR_PATH = STUDY_NAME
114- DIR_PATH = DIR_PATH .replace (" " ,"_" )
115- g = Github (BOT_TOKEN )
116- repo = g .get_user (BOT_ACCOUNT ).get_repo (REPO_NAME )
117- upstream_repo = g .get_repo (f'{ UPSTREAM_ACCOUNT } /{ REPO_NAME } ' ) #controlcore-Project/concore-studies
118- base_ref = upstream_repo .get_branch (repo .default_branch )
119-
120- try :
121- repo .get_branch (BRANCH_NAME )
122- is_present = True
123- except github .GithubException :
124- print (f"No Branch is available with the name { BRANCH_NAME } " )
125- is_present = False
126- except Exception as e :
127- print ("Authentication failed" , end = "" )
128- exit (1 )
129-
130-
131- try :
132- if not is_present :
133- repo .create_git_ref (f"refs/heads/{ BRANCH_NAME } " , base_ref .commit .sha )
134- branch = repo .get_branch (BRANCH_NAME )
135- except Exception :
136- print ("Unable to create study. Try again later." )
137- exit (1 )
138-
139-
140- tree_content = []
141-
142- try :
143- for root , dirs , files in os .walk (STUDY_NAME_PATH ):
144- files = [f for f in files if not f [0 ] == '.' ]
145- for filename in files :
146- path = f"{ root } /{ filename } "
147- if isImageFile (filename ):
148- with open (file = path , mode = 'rb' ) as file :
149- image = file .read ()
150- content = base64 .b64encode (image ).decode ('utf-8' )
151- else :
152- with open (file = path , mode = 'r' ) as file :
153- content = file .read ()
154- file_path = f'{ DIR_PATH + remove_prefix (path ,STUDY_NAME_PATH )} '
155- if (platform .uname ()[0 ]== 'Windows' ): file_path = file_path .replace ("\\ " ,"/" )
156- appendBlobInTree (repo ,content ,file_path ,tree_content )
157- commitAndUpdateRef (repo ,tree_content ,base_ref .commit ,branch )
158- runWorkflow (repo ,upstream_repo )
159- except Exception as e :
160- print (e )
161- print ("Some error Occured.Please try again after some time." ,end = "" )
1+ import github
2+ from github import Github
3+ import os ,sys ,platform ,base64 ,time ,re
4+ import requests
5+
6+ # Intializing the Variables
7+ BOT_TOKEN = os .environ .get ('CONCORE_BOT_TOKEN' , '' )
8+
9+ # Fix 1: Fail fast if token is missing
10+ if not BOT_TOKEN :
11+ print ("Error: CONCORE_BOT_TOKEN environment variable is not set." )
12+ sys .exit (1 )
13+
14+ # Fix 2: Token format validation
15+ token_pattern = r"^(ghp_|github_pat_)[A-Za-z0-9_]{20,}$"
16+ if not re .match (token_pattern , BOT_TOKEN ):
17+ print ("Error: Invalid GitHub token format." )
18+ sys .exit (1 )
19+ BOT_ACCOUNT = 'concore-bot' #bot account name
20+ REPO_NAME = 'concore-studies' #study repo name
21+ UPSTREAM_ACCOUNT = 'ControlCore-Project' #upstream account name
22+ STUDY_NAME = sys .argv [1 ]
23+ STUDY_NAME_PATH = sys .argv [2 ]
24+ AUTHOR_NAME = sys .argv [3 ]
25+ BRANCH_NAME = sys .argv [4 ]
26+ PR_TITLE = sys .argv [5 ]
27+ PR_BODY = sys .argv [6 ]
28+
29+ # Defining Functions
30+ def checkInputValidity ():
31+ if not AUTHOR_NAME or not STUDY_NAME or not STUDY_NAME_PATH :
32+ print ("Please Provide necessary Inputs" )
33+ exit (1 )
34+ if not os .path .isdir (STUDY_NAME_PATH ):
35+ print ("Directory doesnot Exists.Invalid Path" )
36+ exit (1 )
37+
38+ # Fix 5: Retry + backoff wrapper for GitHub API requests
39+ def github_request (method , url , headers = None , json = None , retries = 3 ):
40+ for attempt in range (retries ):
41+ try :
42+ response = requests .request (method , url , headers = headers , json = json , timeout = 30 )
43+ if response .status_code == 429 or response .status_code >= 500 :
44+ wait_time = 2 ** attempt
45+ time .sleep (wait_time )
46+ continue
47+ return response
48+ except requests .exceptions .ConnectionError :
49+ print ("Network error while contacting GitHub API." )
50+ sys .exit (1 )
51+ except requests .exceptions .Timeout :
52+ print ("GitHub API request timed out." )
53+ sys .exit (1 )
54+ print ("Error: GitHub API request failed after retries." )
55+ sys .exit (1 )
56+
57+ # Fix 4: Correct PR URL (singular 'pull' not 'pulls')
58+ def printPR (pr ):
59+ print (f'Check your example here https://github.com/{ UPSTREAM_ACCOUNT } /{ REPO_NAME } /pull/{ pr .number } ' ,end = "" )
60+
61+ def anyOpenPR (upstream_repo ):
62+ try :
63+ prs = upstream_repo .get_pulls (state = 'open' , head = f'{ BOT_ACCOUNT } :{ BRANCH_NAME } ' )
64+ return prs [0 ] if prs .totalCount > 0 else None
65+ except requests .exceptions .ConnectionError :
66+ print ("Network error while fetching PR status." )
67+ exit (1 )
68+ except requests .exceptions .Timeout :
69+ print ("Request timed out while fetching PR status." )
70+ exit (1 )
71+ except Exception :
72+ print ("Unable to fetch PR status. Try again later." )
73+ exit (1 )
74+
75+ def commitAndUpdateRef (repo ,tree_content ,commit ,branch ):
76+ try :
77+ new_tree = repo .create_git_tree (tree = tree_content ,base_tree = commit .commit .tree )
78+ new_commit = repo .create_git_commit (f"Committing Study Named { STUDY_NAME } " ,new_tree ,[commit .commit ])
79+ if len (repo .compare (base = commit .commit .sha ,head = new_commit .sha ).files ) == 0 :
80+ print ("Your don't have any new changes.May be your example is already accepted.If this is not the case try with different fields." )
81+ exit (1 )
82+ ref = repo .get_git_ref ("heads/" + branch .name )
83+ ref .edit (new_commit .sha ,True )
84+ except requests .exceptions .HTTPError as e :
85+ print (f"GitHub API error: { e .response .status_code } " )
86+ exit (1 )
87+ except Exception :
88+ print ("Failed to upload your example. Please try after some time." ,end = "" )
89+ exit (1 )
90+
91+
92+ def appendBlobInTree (repo ,content ,file_path ,tree_content ):
93+ blob = repo .create_git_blob (content ,'utf-8' )
94+ tree_content .append ( github .InputGitTreeElement (path = file_path ,mode = "100644" ,type = "blob" ,sha = blob .sha ))
95+
96+
97+ def runWorkflow (repo ,upstream_repo ):
98+ openPR = anyOpenPR (upstream_repo )
99+ if not openPR :
100+ try :
101+ repo .get_workflow ("pull_request.yml" ).create_dispatch (
102+ ref = BRANCH_NAME ,
103+ inputs = {'title' : f"[BOT]: { PR_TITLE } " , 'body' : PR_BODY , 'upstreamRepo' : UPSTREAM_ACCOUNT , 'botRepo' : BOT_ACCOUNT , 'repo' : REPO_NAME }
104+ )
105+ printPRStatus (upstream_repo )
106+ except requests .exceptions .HTTPError as e :
107+ print (f"GitHub API error while triggering workflow: { e .response .status_code } " )
108+ exit (1 )
109+ except Exception :
110+ print ("Error triggering workflow. Try again later." )
111+ exit (1 )
112+ else :
113+ print (f"Successfully uploaded. Waiting for approval: https://github.com/{ UPSTREAM_ACCOUNT } /{ REPO_NAME } /pull/{ openPR .number } " )
114+
115+ def printPRStatus (upstream_repo ):
116+ attempts = 5
117+ delay = 2
118+ for i in range (attempts ):
119+ print (f"Attempt: { i } " )
120+ try :
121+ latest_pr = upstream_repo .get_pulls (state = 'open' , sort = 'created' , direction = 'desc' )[0 ]
122+ print (f"Check your example here: https://github.com/{ UPSTREAM_ACCOUNT } /{ REPO_NAME } /pull/{ latest_pr .number } " )
123+ return
124+ except Exception :
125+ time .sleep (delay )
126+ delay *= 2
127+ print ("Uploaded successfully, but unable to fetch status." )
128+
129+
130+ def isImageFile (filename ):
131+ image_extensions = ['.jpeg' , '.jpg' , '.png' ,'.gif' ]
132+ return any (filename .endswith (ext ) for ext in image_extensions )
133+
134+ def remove_prefix (text , prefix ):
135+ if text .startswith (prefix ):
136+ return text [len (prefix ):]
137+ return text
138+
139+
140+ # Fix 9: Removed unused decode_token() function
141+
142+ # check if directory path is Valid
143+ checkInputValidity ()
144+
145+
146+ # Authenticating Github with Access token
147+ try :
148+ BRANCH_NAME = AUTHOR_NAME .replace (" " , "_" ) + "_" + STUDY_NAME if BRANCH_NAME == "#" else BRANCH_NAME .replace (" " , "_" )
149+ PR_TITLE = f"Contributing Study { STUDY_NAME } by { AUTHOR_NAME } " if PR_TITLE == "#" else PR_TITLE
150+ PR_BODY = f"Study Name: { STUDY_NAME } \n Author Name: { AUTHOR_NAME } " if PR_BODY == "#" else PR_BODY
151+ DIR_PATH = STUDY_NAME
152+ DIR_PATH = DIR_PATH .replace (" " ,"_" )
153+ g = Github (BOT_TOKEN )
154+ repo = g .get_user (BOT_ACCOUNT ).get_repo (REPO_NAME )
155+ upstream_repo = g .get_repo (f'{ UPSTREAM_ACCOUNT } /{ REPO_NAME } ' ) #controlcore-Project/concore-studies
156+ base_ref = upstream_repo .get_branch (repo .default_branch )
157+
158+ try :
159+ repo .get_branch (BRANCH_NAME )
160+ is_present = True
161+ except github .GithubException :
162+ print (f"No Branch is available with the name { BRANCH_NAME } " )
163+ is_present = False
164+ except requests .exceptions .ConnectionError :
165+ print ("Network error during GitHub authentication." )
166+ exit (1 )
167+ except requests .exceptions .Timeout :
168+ print ("GitHub authentication request timed out." )
169+ exit (1 )
170+ except Exception :
171+ print ("Authentication failed" , end = "" )
172+ exit (1 )
173+
174+
175+ try :
176+ if not is_present :
177+ repo .create_git_ref (f"refs/heads/{ BRANCH_NAME } " , base_ref .commit .sha )
178+ branch = repo .get_branch (BRANCH_NAME )
179+ except Exception :
180+ print ("Unable to create study. Try again later." )
181+ exit (1 )
182+
183+
184+ tree_content = []
185+
186+ try :
187+ for root , dirs , files in os .walk (STUDY_NAME_PATH ):
188+ files = [f for f in files if not f [0 ] == '.' ]
189+ for filename in files :
190+ path = f"{ root } /{ filename } "
191+ if isImageFile (filename ):
192+ with open (file = path , mode = 'rb' ) as file :
193+ image = file .read ()
194+ content = base64 .b64encode (image ).decode ('utf-8' )
195+ else :
196+ with open (file = path , mode = 'r' ) as file :
197+ content = file .read ()
198+ file_path = f'{ DIR_PATH + remove_prefix (path ,STUDY_NAME_PATH )} '
199+ if (platform .uname ()[0 ]== 'Windows' ): file_path = file_path .replace ("\\ " ,"/" )
200+ appendBlobInTree (repo ,content ,file_path ,tree_content )
201+ commitAndUpdateRef (repo ,tree_content ,base_ref .commit ,branch )
202+ runWorkflow (repo ,upstream_repo )
203+ except requests .exceptions .HTTPError as e :
204+ print (f"GitHub API error: { e .response .status_code } " )
205+ exit (1 )
206+ except requests .exceptions .ConnectionError :
207+ print ("Network error while uploading study." )
208+ exit (1 )
209+ except requests .exceptions .Timeout :
210+ print ("Request timed out while uploading study." )
211+ exit (1 )
212+ except Exception :
213+ print ("Some error occurred. Please try again after some time." ,end = "" )
162214 exit (1 )
0 commit comments