def DeleteDeployTag(self, gh): """ Delete a temporary tag (github release) that was used to deploy a new release. """ user = self.buildSetup.gitConfig["user"] repo = self.buildSetup.gitConfig["repo"] deploy_tag_name = environ.get('APPVEYOR_REPO_TAG_NAME') if not deploy_tag_name: return deploy_tag = None page = 1 while page > 0: rc, data = gh.repos[user][repo].git.refs.tags.get(per_page=100, page=page) page = NextPage(gh) if rc == 200: for tag in data: if tag['ref'].rsplit('/', 1)[1] == deploy_tag_name: deploy_tag = tag page = 0 break if deploy_tag and deploy_tag['object']['type'] == 'commit': rc, data = gh.repos[user][repo].releases.tags[deploy_tag_name].get( per_page=100, page=page) if rc == 200: rc, data2 = gh.repos[user][repo].releases[data['id']].delete() if rc == 204: print "deploy github release deleted" if deploy_tag: rc, data = gh.repos[user][repo].git.refs.tags[ deploy_tag_name].delete() if rc == 204: print "deploy tag deleted"
def DoTask(self): buildSetup = self.buildSetup appVer = buildSetup.appVersion gitConfig = buildSetup.gitConfig token = gitConfig["token"] user = gitConfig["user"] repo = gitConfig["repo"] branch = gitConfig["branch"] ref = 'heads/{0}'.format(branch) setupFile = 'EventGhost_{0}_Setup.exe'.format(appVer) setupPath = join(buildSetup.outputDir, setupFile) chglogFile = "CHANGELOG.md" chglogPath = join(buildSetup.outputDir, chglogFile) print "reading changelog" try: f = open(chglogPath, 'r') except IOError: print "ERROR: couldn't read changelog file ({0}).".format(chglogFile) return else: changelog = f.read() f.close() print "loading setup file" try: f = open(setupPath, 'rb') except IOError: print "ERROR: '{0}' not found.".format(setupFile) return else: setupFileContent = f.read() f.close() gh = GitHub(token=token) print "getting release info" releaseExists = False page = 1 while page > 0: rc, data = gh.repos[user][repo].releases.get( sha=branch, per_page=100, page=page ) page = NextPage(gh) if rc == 200: for rel in data: if rel['name'][1:] == appVer: app = wx.GetApp() win = app.GetTopWindow() dlg = wx.MessageDialog( win, caption="Information", message="Found an existing GitHub release matching" " 'v{0}'\nOverwrite it?".format(appVer), style=wx.YES_NO ) if dlg.ShowModal() == wx.ID_NO: return releaseId = rel["id"] uploadUrl = str(rel['upload_url'][:-13]) releaseExists = True print "getting branch info" rc, data = gh.repos[user][repo].branches[branch].get() if rc != 200: print "ERROR: couldn't get branch info." return commitSha = data['commit']['sha'] rc, data = gh.repos[user][repo].contents[chglogFile].get(ref=branch) if rc == 200: remoteChangelog = base64.decodestring(data["content"]) else: remoteChangelog = None if changelog != remoteChangelog: print "getting commit referenced by branch" rc, data = gh.repos[user][repo].git.commits[commitSha].get() if rc != 200: print "ERROR: couldn't get commit info." return treeSha = data['tree']['sha'] print "getting tree" rc, data = gh.repos[user][repo].git.trees[treeSha].get() if rc != 200: print "ERROR: couldn't get tree info." return blob = None print "getting blob for {0}".format(chglogFile) for entry in data['tree']: if entry['path'] == chglogFile and entry['type'] == 'blob': blob = entry break if blob is None: print "ERROR: couldn't get blob info." return print "posting new changelog" body = { 'content': changelog, 'encoding': 'utf-8' } rc, data = gh.repos[user][repo].git.blobs.post(body=body) if rc != 201: print "ERROR: couldn't post new changelog contents." return print "posting tree" newblob = { 'path': blob['path'], 'mode': blob['mode'], 'type': blob['type'], 'sha': data['sha'] } body = { 'tree': [newblob], 'base_tree': treeSha } rc, data = gh.repos[user][repo].git.trees.post(body=body) if rc != 201: print "ERROR: couldn't post new tree." return newTreeSha = data['sha'] print "creating commit for changelog update" body = { 'message': "Add changelog for v{0}".format(appVer), 'tree': newTreeSha, 'parents': [commitSha] } rc, data = gh.repos[user][repo].git.commits.post(body=body) if rc != 201: print "ERROR: couldn't create commit for changelog update." return newCommitSha = data['sha'] print "updating reference for branch to new commit" body = {'sha': newCommitSha} rc, data = gh.repos[user][repo].git.refs[ref].patch(body=body) if rc != 200: print "ERROR: couldn't update reference ({0}) with new commit.".format(ref) return if not releaseExists: print "extracting changelog for this release" relChglog = '' chgLines = changelog.splitlines(True) try: for i in range(1, len(chgLines)): if chgLines[i].startswith("## "): break else: relChglog += chgLines[i] except IndexError: pass relChglog = relChglog.strip() print "creating release" body = {'tag_name': 'v{0}'.format(appVer), 'target_commitish': newCommitSha, 'name': 'v{0}'.format(appVer), 'body': relChglog, #'draft': False, 'prerelease': ("-" in self.buildSetup.appVersion) } rc, data = gh.repos[user][repo].releases.post(body=body) if rc != 201: print "ERROR: couldn't create a release on GitHub." return uploadUrl = str(data['upload_url'][:-13]) else: print 'deleting existing asset' rc, data = gh.repos[user][repo].releases[releaseId].assets.get() if rc == 200: for asset in data: if asset["name"] == setupFile: rc, data = gh.repos[user][repo].releases.\ assets[asset["id"]].delete() if rc != 204: print "ERROR: couldn't delete existing asset." return break print "uploading setup file" url = uploadUrl + '?name={0}'.format(setupFile) headers = {'content-type': 'application/octet-stream', 'authorization': 'Token {0}'.format(token), 'accept': 'application/vnd.github.v3+json', 'user-agent': 'agithub/v2.0'} conn = http.client.HTTPSConnection('uploads.github.com') conn.request('POST', url, setupFileContent, headers) response = conn.getresponse() status = response.status conn.close() if status != 201: print "ERROR: couldn't upload installer file to GitHub." return
def DoTask(self): if not self.buildSetup.gitConfig["token"]: print "WARNING: Skipping changelog build due to invalid token." return buildSetup = self.buildSetup changelog_path = join(buildSetup.outputDir, "CHANGELOG.md") copy2(join(buildSetup.sourceDir, "CHANGELOG.md"), changelog_path) token = buildSetup.gitConfig["token"] user = buildSetup.gitConfig["user"] repo = buildSetup.gitConfig["repo"] branch = buildSetup.gitConfig["branch"] gh = GitHub(token=token) # fetch all tags from github page = 1 all_tags = [] while page > 0: rc, data = gh.repos[user][repo].git.refs.tags.get(sha=branch, per_page=100, page=page) if rc != 200: raise BuildError(data["message"]) all_tags.extend(data) page = NextPage(gh) # get release date for tags for tag in all_tags: tag["pulls"] = [] if tag["object"]["type"] == "commit": rc, data = gh.repos[user][repo].git.commits[ tag["object"]["sha"]].get(sha=branch) tag["release_date"] = data["committer"]["date"] elif tag["object"]["type"] == "tag": rc, data = gh.repos[user][repo].git.tags[ tag["object"]["sha"]].get(sha=branch) tag["release_date"] = data["tagger"]["date"] else: raise BuildError("unknown tag type") # the search api is marked as beta, # thats why we have to do the following: old_accept = gh.client.default_headers["accept"] gh.client.default_headers[ "accept"] = 'application/vnd.github.cloak-preview' # get merge commits since last release page = 1 future_release = [] while page > 0: rc, data = gh.search.issues.get( sha=branch, per_page=100, page=page, q=('type:pr merged:>' + all_tags[-1]["release_date"] + ' user:'******' repo:' + repo + ' -label:internal'), ) data["items"].reverse() future_release.extend(data["items"]) page = NextPage(gh) # undo the beta api patch gh.client.default_headers["accept"] = old_accept # now filter and group the pull requests title_notice = "Important changes for plugin developers" title_enhancement = "Enhancements" title_bug = "Fixed bugs" title_other = "Other changes" prs = OrderedDict() prs[title_notice] = [] prs[title_enhancement] = [] prs[title_bug] = [] prs[title_other] = [] for pr in future_release: labels = [l["name"] for l in pr["labels"]] if "notice" in labels: prs[title_notice].append(pr) elif "enhancement" in labels: prs[title_enhancement].append(pr) elif any(lbl in ["bug", "bugfix"] for lbl in labels): prs[title_bug].append(pr) else: prs[title_other].append(pr) # prepare the grouped output buildDate = strftime("%Y-%m-%d", localtime(buildSetup.buildTime)) releaseUrl = "https://github.com/{0}/{1}/releases/tag/v{2}".format( user, repo, buildSetup.appVersion) changes = dict( md=[ "## [{0}]({1}) ({2})\n".format(buildSetup.appVersion, releaseUrl, buildDate) ], bb=[ "[size=150][b][url={0}]{1}[/url] ({2})[/b][/size]\n".format( releaseUrl, buildSetup.appVersion, buildDate) ]) print "## {0} ({1})".format(buildSetup.appVersion, buildDate) for title, items in prs.iteritems(): if items: changes['md'].append("\n**{0}:**\n\n".format(title)) changes['bb'].append("\n[b]{0}:[/b]\n[list]\n".format(title)) print "\n{0}:\n".format(title) for pr in items: changes['md'].append( u"* {0} [\#{1}]({2}) ([{3}]({4}))\n".format( EscapeMarkdown(pr["title"]), pr["number"], pr["html_url"], EscapeMarkdown(pr["user"]["login"]), pr["user"]["html_url"], )) changes['bb'].append( u"[*] {0} [url={1}]{2}[/url] " u"([url={3}]{4}[/url])\n".format( pr["title"], pr["html_url"], pr["number"], pr["user"]["html_url"], EscapeMarkdown(pr["user"]["login"]))) print u"* {0} #{1} ({2})".format( pr["title"], pr["number"], pr["user"]["login"], ) changes['bb'].append("[/list]") if len(changes['md']) == 1: text = "\nOnly minor changes in this release.\n" changes['md'].append(text) changes['bb'].append(text) print text.strip() # write a changelog file in bbcode for the news section in forum changes['bb'].append( u"\n\n[size=110][url=https://github.com/EventGhost/EventGhost/" u"releases/download/v{0}/EventGhost_{0}_Setup.exe]Download now" u"[/url][/size]\n".format(buildSetup.appVersion)) try: fn = join(buildSetup.outputDir, "CHANGELOG_THIS_RELEASE.bb") out = codecs.open(fn, "w", "utf8") out.writelines(changes['bb']) out.close() except: pass # write a file with current changes in markdown for release description try: fn = join(buildSetup.outputDir, "CHANGELOG_THIS_RELEASE.md") out = codecs.open(fn, "w", "utf8") out.writelines(changes['md'][1:]) out.close() except: pass # and now the full changelog file (in markdown format) # read the existing changelog... try: infile = codecs.open(changelog_path, "r", "utf8") except IOError: old_changes = '' else: old_changes = infile.read() infile.close() # ... and put the new changelog on top try: outfile = codecs.open(changelog_path, "w+", "utf8") except IOError: import sys import wx parent = wx.GetApp().GetTopWindow() msg = "CHANGELOG.md couldn't be written.\n({0})".format( sys.exc_value) dlg = wx.MessageDialog(parent, msg, caption="Error", style=wx.OK | wx.ICON_ERROR) dlg.ShowModal() else: outfile.writelines(changes['md']) if old_changes: outfile.write('\n\n') outfile.write(old_changes) outfile.close()
def DoTask(self): buildSetup = self.buildSetup appVer = "v" + buildSetup.appVersion gitConfig = buildSetup.gitConfig token = gitConfig["token"] user = gitConfig["user"] repo = gitConfig["repo"] branch = gitConfig["branch"] setupFile = 'EventGhost_{0}_Setup.exe'.format(buildSetup.appVersion) setupPath = join(buildSetup.outputDir, setupFile) self.chglogFile = "CHANGELOG.md" self.chglogShort = "CHANGELOG_THIS_RELEASE.md" chglogPath = join(buildSetup.outputDir, self.chglogFile) print "reading changelog" try: f = open(chglogPath, 'r') except IOError: print "ERROR: couldn't read changelog file ({0}).".format( self.chglogFile) return else: changelog = f.read() f.close() print "loading installer file" try: f = open(setupPath, 'rb') except IOError: print "ERROR: '{0}' not found.".format(setupFile) return else: setupFileContent = f.read() f.close() gh = GitHub(token=token) # delete a temporary tag that were used to deploy a release if IsCIBuild(): # when we are on CI, we only get here, # when a deploy tag was created self.DeleteDeployTag(gh) branch = gitConfig["branch"] = 'master' print "getting release info" releaseExists = False releaseId = None uploadUrl = None page = 1 while page > 0: rc, data = gh.repos[user][repo].releases.get(sha=branch, per_page=100, page=page) page = NextPage(gh) if rc == 200: for rel in data: if rel['name'] == appVer: msg = ("Found an existing GitHub release matching" " '{0}'".format(appVer)) if IsCIBuild(): raise BuildError(msg) app = wx.GetApp() win = app.GetTopWindow() dlg = wx.MessageDialog(win, caption="Information", message=msg + "\nOverwrite it?", style=wx.YES_NO) if dlg.ShowModal() == wx.ID_NO: return releaseId = rel["id"] uploadUrl = str(rel['upload_url'][:-13]) releaseExists = True page = 0 break print "getting branch info" rc, data = gh.repos[user][repo].branches[branch].get() if rc != 200: raise BuildError("ERROR: couldn't get branch info.") commitSha = data['commit']['sha'] # if not uploadUrl: # uploadUrl = str(data['upload_url'][:-13]) rc, data = gh.repos[user][repo].contents[self.chglogFile].get( ref=branch) if rc == 200: remoteChangelog = base64.decodestring(data["content"]) else: remoteChangelog = None newCommitSha = None if changelog != remoteChangelog: newCommitSha = self.CommitChangelog(gh, commitSha, changelog) if not releaseExists: print "reading changelog for this release" try: f = open(join(buildSetup.outputDir, self.chglogShort), 'r') except IOError: print "ERROR: couldn't read changelog file ({0}).".format( self.chglogShort) relChglog = "" else: relChglog = f.read().strip() f.close() print "creating release" body = dict(tag_name=appVer, target_commitish=newCommitSha, name=appVer, body=relChglog, draft=False, prerelease=("-" in self.buildSetup.appVersion)) rc, data = gh.repos[user][repo].releases.post(body=body) if rc != 201: raise BuildError("ERROR: couldn't create a release on GitHub.") uploadUrl = str(data['upload_url'][:-13]) else: print 'deleting existing asset' rc, data = gh.repos[user][repo].releases[releaseId].assets.get() if rc == 200: for asset in data: if asset["name"] == setupFile: rc, data = gh.repos[user][repo].releases.\ assets[asset["id"]].delete() if rc != 204: print "ERROR: couldn't delete existing asset." return break print "uploading setup file" url = uploadUrl + '?name={0}'.format(setupFile) headers = { 'content-type': 'application/octet-stream', 'authorization': 'Token {0}'.format(token), 'accept': 'application/vnd.github.v3+json', 'user-agent': 'agithub/v2.0' } conn = http.client.HTTPSConnection('uploads.github.com') conn.request('POST', url, setupFileContent, headers) response = conn.getresponse() status = response.status conn.close() if status != 201: raise BuildError( "ERROR: couldn't upload installer file to GitHub.")
def DoTask(self): if not self.buildSetup.gitConfig["token"]: print "WARNING: Skipping changelog build due to invalid token." return buildSetup = self.buildSetup changelog_path = join(buildSetup.outputDir, "CHANGELOG.md") copy2( join(buildSetup.sourceDir, "CHANGELOG.md"), changelog_path ) token = buildSetup.gitConfig["token"] user = buildSetup.gitConfig["user"] repo = buildSetup.gitConfig["repo"] branch = buildSetup.gitConfig["branch"] gh = GitHub(token=token) rc, data = gh.repos[user][repo].git.refs.tags.get() if rc != 200: raise BuildError("Couldn't get tags, probably due to invalid token.") to_commits = [i["object"]["sha"] for i in data] # get commits since last release page = 1 included_prs = [] item = {} while page > 0: rc, data = gh.repos[user][repo].pulls.get( state="closed", base="master", sha=branch, per_page=100, page=page ) # rc, data = gh.repos[user][repo].commits.get( # sha=branch, # per_page=100, # page=page # ) if rc != 200: raise BuildError("Couldn't get commits.") for item in data: if item['merge_commit_sha'] in to_commits: break included_prs.append(item["number"]) # try: # msg = item['commit']['message'].splitlines()[0] # if msg.startswith("Merge pull request #"): # included_prs.append(int(msg.split()[3][1:])) # except IndexError: # pass if item['merge_commit_sha'] in to_commits: break page = NextPage(gh) # now filter and group the pull requests page = 1 pulls = [] while page > 0: rc, data = gh.search.issues.get( q='type:pr is:merged ' '-label:internal ' # 'user:{0} ' 'repo:{0}/{1}'.format(user, repo), sort="created", order="asc", per_page=100, page=page ) if rc != 200: raise BuildError("Couldn't get additional info.") elif data.get("incomplete_results") == True: raise BuildError("Incomplete search result.") pulls.extend(data["items"]) page = NextPage(gh) title_notice = "Important changes for plugin developers" title_enhancement = "Enhancements" title_bug = "Fixed bugs" title_other = "Other changes" prs = OrderedDict() prs[title_notice] = [] prs[title_enhancement] = [] prs[title_bug] = [] prs[title_other] = [] for pr in pulls: if not pr["number"] in included_prs: continue labels = [l["name"] for l in pr["labels"]] if "internal" in labels: # This pull request should not be listed in changelog continue elif "notice" in labels: prs[title_notice].append(pr) elif "enhancement" in labels: prs[title_enhancement].append(pr) elif "bug" in labels: prs[title_bug].append(pr) else: prs[title_other].append(pr) # prepare the grouped output buildDate = strftime("%Y-%m-%d", localtime(buildSetup.buildTime)) releaseUrl = "https://github.com/{0}/{1}/releases/tag/v{2}".format( user, repo, buildSetup.appVersion ) changes = ["## [{0}]({1}) ({2})\n".format( buildSetup.appVersion, releaseUrl, buildDate, )] print "## {0} ({1})".format(buildSetup.appVersion, buildDate) for title, items in prs.iteritems(): if items: changes.append("\n**{0}:**\n\n".format(title)) print "\n{0}:\n".format(title) for pr in items: changes.append("* {0} [\#{1}]({2}) ([{3}]({4}))\n".format( EscapeMarkdown(pr["title"]), pr["number"], pr["html_url"], EscapeMarkdown(pr["user"]["login"]), pr["user"]["html_url"], )) print "* {0} #{1} ({2})".format( pr["title"], pr["number"], pr["user"]["login"], ) if len(changes) == 1: text = "\nOnly minor changes in this release.\n" changes.append(text) print text.strip() # read the existing changelog... try: infile = open(changelog_path, "r") except IOError: old_changes = '' else: old_changes = infile.read() infile.close() # ... and put the new changelog on top try: outfile = open(changelog_path, "w+") except IOError: import sys import wx parent = wx.GetApp().GetTopWindow() msg = "CHANGELOG.md couldn't be written.\n({0})".format( sys.exc_value ) dlg = wx.MessageDialog(parent, msg, caption="Error", style=wx.OK | wx.ICON_ERROR) dlg.ShowModal() else: outfile.writelines(changes) if old_changes: outfile.write('\n\n') outfile.write(old_changes) outfile.close()