Ejemplo n.º 1
0
    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()
Ejemplo n.º 2
0
    def CommitChangelog(self, gh, commitSha, changelog):
        buildSetup = self.buildSetup
        appVer = "v" + buildSetup.appVersion
        gitConfig = buildSetup.gitConfig
        user = gitConfig["user"]
        repo = gitConfig["repo"]
        branch = gitConfig["branch"]
        ref = 'heads/{0}'.format(branch)

        print "getting commit referenced by branch"
        rc, data = gh.repos[user][repo].git.commits[commitSha].get()
        if rc != 200:
            raise BuildError("ERROR: couldn't get commit info.")
        treeSha = data['tree']['sha']

        print "getting tree"
        rc, data = gh.repos[user][repo].git.trees[treeSha].get()
        if rc != 200:
            raise BuildError("ERROR: couldn't get tree info.")

        blob = None
        print "getting blob for {0}".format(self.chglogFile)
        for entry in data['tree']:
            if entry['path'] == self.chglogFile and entry['type'] == 'blob':
                blob = entry
                break
        if blob is None:
            raise BuildError("ERROR: couldn't get blob info.")

        print "posting new changelog"
        body = dict(content=changelog, encoding='utf-8')
        rc, data = gh.repos[user][repo].git.blobs.post(body=body)
        if rc != 201:
            raise BuildError("ERROR: couldn't post new changelog contents.")

        print "posting tree"
        newblob = dict(path=blob['path'],
                       mode=blob['mode'],
                       type=blob['type'],
                       sha=data['sha'])
        body = dict(tree=[newblob], base_tree=treeSha)
        rc, data = gh.repos[user][repo].git.trees.post(body=body)
        if rc != 201:
            raise BuildError("ERROR: couldn't post new tree.")
        newTreeSha = data['sha']

        print "creating commit for changelog update"
        body = {
            'message': "Add changelog for {0}\n[skip appveyor]".format(appVer),
            'tree': newTreeSha,
            'parents': [commitSha]
        }
        rc, data = gh.repos[user][repo].git.commits.post(body=body)
        if rc != 201:
            raise BuildError(
                "ERROR: couldn't create commit for changelog update.")
        newCommitSha = data['sha']

        print "updating reference for branch to new commit"
        body = dict(sha=newCommitSha)
        rc, data = gh.repos[user][repo].git.refs[ref].patch(body=body)
        if rc != 200:
            raise BuildError("ERROR: couldn't update reference ({0}) "
                             "with new commit.".format(ref))

        return newCommitSha
Ejemplo n.º 3
0
    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.")
Ejemplo n.º 4
0
    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()