This repository was archived by the owner on Mar 25, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsyncPreAlpha.py
More file actions
351 lines (270 loc) · 9.97 KB
/
syncPreAlpha.py
File metadata and controls
351 lines (270 loc) · 9.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
import json
import os
import pdb
import time
import requests
from dotenv import load_dotenv
from tumblerLogging import getLogger
# get custom logger
logger = getLogger()
# TODO: don't show all os.system calls, instead only in logger
def _validDir(directory):
dirname = os.path.dirname(directory)
if not os.path.exists(dirname):
logger.error(
"Not a valid directory name, make sure you're not using ~ or any shorthand"
)
return 1
return 0
def _hubInstalled():
# check for hub
logger.info("Checking for hub")
if os.system("which hub"):
logger.error("Need to install github/hub to create PRs.")
return 1
return 0
def _cloneRock(rockDir):
# clone Rock if it doesn't exist in the current directory
if not os.path.exists(rockDir):
os.system("git clone https://github.com/NewSpring/Rock.git " + rockDir)
# switch to Rock directory
os.chdir(rockDir)
# check that it is a git directory
if "true" not in os.popen("git rev-parse --is-inside-work-tree").read():
logger.error(
"Directory is not valid or is corrupt. May need to delete and retry."
)
return 1
def _checkout(branch):
# checkout and update branch
logger.info("Updating {} branch".format(branch))
r = os.popen("git checkout {}".format(branch)).read()
if "up to date" not in r:
os.system("git pull")
def _cleanup(deleteRemote=True):
# delete the local and remote branches
_checkout("master")
if deleteRemote:
os.system("git push --delete origin sync-pre-alpha")
os.system("git branch -D sync-pre-alpha")
def _createPreAlpha():
# get Spark remote branches
if "SparkDevNetwork" not in os.popen("git remote").read():
# set up Spark as remote
logger.info("Adding Spark remote")
os.system(
"git remote add SparkDevNetwork https://github.com/SparkDevNetwork/Rock.git"
)
os.system("git fetch SparkDevNetwork")
# if sync-pre-alpha doesn't exist, create it
# if "sync-pre-alpha" not in os.popen("git branch").read():
logger.info("Creating new sync-pre-alpha from Spark")
os.system(
"git checkout -B sync-pre-alpha SparkDevNetwork/pre-alpha-release")
# push pre alpha branch
logger.info("Deleting remote sync-pre-alpha")
os.system("git push --delete origin sync-pre-alpha")
logger.info("Pushing sync-pre-alpha")
os.system("git push --set-upstream origin sync-pre-alpha")
def _getSHA():
# this will return the last commit SHA-1 hash
return os.popen("git rev-parse --short HEAD").read().strip("\n")
def _getBranch():
# this will get the current branch name
return os.popen("git rev-parse --abbrev-ref HEAD").read().strip("\n")
def _merge(destination, source):
_checkout(source)
_checkout(destination)
# check for changes
logger.info("Merging {} -> {}".format(source, destination))
if ("up to date" in os.popen(
'git merge {} -m "Merge from NewSpring/{}"'.format(
source, source)).read()):
logger.info("No changes to sync")
# merge source into destination
else:
# check for files to safely delete
conflicts = False
deletedBySource = (os.popen(
"git diff --name-only --diff-filter=UD").read().split("\n"))
logger.debug(deletedBySource)
if deletedBySource != [""]:
conflicts = True
for file in deletedBySource:
if not os.path.exists(file):
continue
logger.info("{}".format(file))
choice = input("Safe to delete files? (y/n) ")
if choice.lower() not in ["y", "yes"]:
logger.info("Fix conflicts manually")
return 1
logger.info("Deleting files...")
for file in deletedBySource:
os.system("git rm {}".format(file))
# run check again
if os.popen("git diff --diff-filter=U").read() != "":
logger.warning("Some can't be resolved. Fix conflicts manually.")
return 1
# merge commit
if conflicts:
logger.info("Committing merge conflict resolution")
os.system('git commit -am "Merge conflicts resolved"')
# push destination branch
logger.info("Pushing {} branch".format(destination))
os.system("git push --set-upstream origin {}".format(destination))
def _build(branch, authKey):
# POST request to start appveyor build
data = {
"accountName": "NewSpring",
"projectSlug": "rock",
"branch": branch
}
headers = {"Authorization": "Bearer " + authKey}
r = requests.post(
"https://ci.appveyor.com/api/builds", data=data, headers=headers)
def _buildingCheck(branch):
# wait until the current commit is the one being built
commit = _getSHA()
buildCommit = ""
start = round(time.perf_counter())
while commit not in buildCommit:
elapsed = round(time.perf_counter()) - start
if elapsed > 30:
logger.warning(
"\nTimeout. Branch '{}' not building.".format(branch))
return 1
print(
"\rVerifying automatic AppVeyor build ({}s)".format(elapsed),
end="")
r = requests.get(
"https://ci.appveyor.com/api/projects/NewSpring/rock/branch/{}".
format(branch))
try:
buildCommit = json.loads(r.text)["build"]["commitId"]
except KeyError:
logger.warning("\nBranch not found.")
return 1
print("")
logger.info("Current commit is building or has already been built")
return 0
def _buildStatus(branch):
# wait on Appveyor build to pass or fail
status = ""
start = round(time.perf_counter())
while status not in ["success", "failed"]:
elapsed = round(time.perf_counter()) - start
print("\rWaiting on AppVeyor build ({}s)".format(elapsed), end="")
r = requests.get(
"https://ci.appveyor.com/api/projects/NewSpring/rock/branch/{}".
format(branch))
status = json.loads(r.text)["build"]["status"]
print("")
if status == "success":
logger.info("{} branch build passed".format(branch))
return 0
logger.error("Build failed. Debug {} branch and run again.".format(branch))
return -1
def _getBuildVersion(branch, authKey):
headers = {"Authorization": "Bearer {}".format(authKey)}
r = requests.get(
"https://ci.appveyor.com/api/projects/NewSpring/rock/branch/{}".format(
branch),
headers=headers,
)
logger.debug(f"Build version response: {r}")
return json.loads(r.text)["build"]["version"]
def _deploy(branch, version, envID, authKey):
logger.debug(f"envID: {envID}")
logger.debug(f"authKey: {authKey}")
# get Appveyor environment name
headers = {"Authorization": "Bearer {}".format(authKey)}
r = requests.get(
"https://ci.appveyor.com/api/environments/{}/settings".format(envID),
headers=headers,
)
logger.debug(r.text)
envName = json.loads(r.text)["environment"]["name"]
# make POST request to trigger deployment
data = {
"environmentName": envName,
"accountName": "NewSpring",
"projectSlug": "rock",
"buildVersion": version,
"environmentVariables": {
"application_path": "D:\wwwroot"
},
}
r = requests.post(
"https://ci.appveyor.com/api/deployments", data=data, headers=headers)
logger.info("Deploying branch {} to {} started.".format(branch, envName))
logger.debug(r.text)
def _appveyorBuild(branch):
# run appveyor build on source branch if it's not automatic
if _buildingCheck(branch):
_build(branch, os.getenv("APPVEYOR_KEY"))
# after build passes, we can safely merge
if _buildStatus(branch):
return 1
def _safeMerge(destination, source):
# first merge destination branch into source so we can test the build
if _merge(source, destination):
return 1
# run appveyor build on source branch
if _appveyorBuild(source):
return 1
# merge source into destination
if _merge(destination, source):
return 1
# run appveyor build on destination branch
if _appveyorBuild(destination):
return 1
def _pr(base, head):
_checkout(head)
if os.system('hub pull-request -b {} -m "Merge from NewSpring/{}"'.format(
base, head)):
logger.error("Could not complete pull request")
return 1
logger.info(
"Created {} -> {} PR. Ready to merge and deploy {} manually.".format(
head, base, base))
def sync(rockDir="/tmp/Rock", safe=True, fast=False):
"""This will sync Rock pre-alpha with our alpha branch."""
logger.info("*****************")
logger.info("Syncing pre-alpha")
logger.info("*****************")
# load environment variables
load_dotenv()
# first make sure a valid directory was passed
if _validDir(rockDir):
return 1
# check that hub is installed
if _hubInstalled():
return 1
# if Rock doesn't exist, clone it
if _cloneRock(rockDir):
return 1
# checkout alpha branch
_createPreAlpha()
# stopping at alpha PR
if safe:
# if making the PR fails, delete the remote branch too
if _pr("alpha", "sync-pre-alpha"):
# _cleanup()
return 1
else:
# merge pre alpha into alpha after it builds successfully
if _safeMerge("alpha", "sync-pre-alpha"):
return 1
if fast:
if _safeMerge("beta", "alpha"):
return 1
_deploy(
destination,
_getBuildVersion(destination, os.getenv("APPVEYOR_KEY")),
os.getenv("APPVEYOR_BETA_ENV"),
os.getenv("APPVEYOR_KEY"),
)
else:
_pr("beta", "alpha")
# if alpha was made, don't delete remote pre-alpha branch
# _cleanup(not prAlpha)