JFIF$        dd7 

Viewing File: /usr/bin/flatpak-bisect

#!/usr/bin/python3

import re
import argparse
import os
import gi
import json
import subprocess

gi.require_version('Flatpak', '1.0')
from gi.repository import Flatpak
from gi.repository import GLib

def get_bisection_data():
        return {'ref': None, 'good': None, 'bad': None,
                'refs': None, 'log': None, 'messages': None}

class Bisector():
    def load_cache(self):
        try:
            os.makedirs(os.path.join(GLib.get_user_cache_dir(), 'flatpak'))
        except FileExistsError:
            pass

        self.cache_path = os.path.join(GLib.get_user_cache_dir(),
                                       'flatpak', '%s-%s-bisect.status' % (
                                       self.name, self.branch))
        try:
            with open(self.cache_path, 'rb') as f:
                self.data = json.load(f)
        except FileNotFoundError:
            self.data = None

    def dump_data(self):
        with open(self.cache_path, 'w') as f:
            json.dump(self.data, f)

    def setup_flatpak_app(self):
        self.installation = Flatpak.Installation.new_user()
        kind = Flatpak.RefKind.APP
        if self.runtime:
            kind = Flatpak.RefKind.RUNTIME
        try:
            self.cref = self.installation.get_installed_ref(kind, self.name, None, self.branch, None)
        except GLib.Error as e:
            print("%s\n\nMake sure %s is installed as a "
                  "user (flatpak install --user) and specify `--runtime`"
                  " if it is a runtime." % (e, self.name))
            return -1
        return 0

    def run(self):
        self.name = self.name[0]
        self.load_cache()
        res = self.setup_flatpak_app()
        if res:
            return res

        try:
            func = getattr(self, self.subparser_name)
        except AttributeError:
            print('No action called %s' % self.subparser_name)

            return -1

        res = func()

        if self.data:
            self.dump_data()

        return res

    def set_reference_commits(self, set_name, check_name):
        if not self.data:
            print("You need to first start the bisection")
            return -1
        ref = self.cref.get_latest_commit()

        if self.data[check_name] == ref:
            print('Commit %s is already set as %s...' % (
                ref, check_name))
            return 1

        if ref not in self.data['refs']:
            print("%s is not a known commit." % ref)
            return -1

        print("Setting %s as %s commit" % (ref, set_name))
        self.data[set_name] = ref

        if self.data[set_name] and self.data[check_name]:
            x1 = self.data['refs'].index(self.data['good'])
            x2 = self.data['refs'].index(self.data['bad'])

            refs = self.data['refs'][x1:x2]
            if not refs:
                print("=========================="
                      "First bad commit is:\n%s"
                      "==========================" % self.data['message'][self.data['bad']])
                exit(0)
            ref = refs[int(len(refs) / 2)]
            if self.data['good'] == ref:
                print("\n==========================\n"
                      "First bad commit is:\n\n%s"
                      "==========================" % self.data['messages'][self.data['bad']])
                exit(0)

            return self.checkout(ref)

        return -1

    def load_refs(self):
        repodir, refname = self.download_history()
        history = subprocess.check_output(['ostree', 'log', '--repo', repodir, refname]).decode()

        refs = []
        messages = {}
        message = ""
        _hash = ''
        for l in history.split('\n'):
            rehash = re.search('(?<=^commit )\w+', l)
            if rehash:
                if message:
                    messages[_hash] = message
                _hash = rehash.group(0)
                refs.insert(0, _hash)
                message = ""
            message += l + '\n'

        if message:
            messages[_hash] = message

        self.data['refs'] = refs
        self.data['log'] = history
        self.data['messages'] = messages

    def good(self):
        if not self.data['bad']:
            print("Set the bad commit first")
            exit(-1)
        return self.set_reference_commits('good', 'bad')

    def bad(self):
        return self.set_reference_commits('bad', 'good')

    def start(self):
        if self.data:
            print('Bisection already started')
            return -1

        print("Updating to %s latest commit" % self.name)
        self.reset(False)
        self.data = get_bisection_data()
        self.load_refs()

    def download_history(self):
        print("Getting history")
        appidir = os.path.abspath(os.path.join(self.cref.get_deploy_dir(), '..'))
        dirname = "app"
        if self.runtime:
            dirname = "runtime"
        appidir = appidir.split('/%s/' % dirname)
        repodir = os.path.join(appidir[0], 'repo')
        refname = self.cref.get_origin() + ':' + dirname + '/' + self.cref.get_name() + '/' + self.cref.get_arch() + '/' + self.cref.get_branch()
        # FIXME Getting `error: Exceeded maximum recursion` in ostree if using --depth=-1 (or > 250)
        subprocess.call(['ostree', 'pull', '--depth=250', '--commit-metadata-only', '--repo', repodir, refname])

        return repodir, refname

    def log(self):
        if self.data:
            cmd = ['echo', self.data['log']]
        else:
            repodir, refname = self.download_history()
            cmd = ['ostree', 'log', '--repo', repodir, refname]
        pager = os.environ.get('PAGER')
        if pager:
            stdout = subprocess.PIPE
        else:
            stdout = None
        p = subprocess.Popen(cmd, stdout=stdout)
        if pager:
            subprocess.check_call((pager), stdin=p.stdout)
        p.wait()

    def checkout(self, commit=None):
        if not commit:
            commit = self.commit[0]
        refname = self.cref.get_name() + '/' + self.cref.get_arch() + '/' + self.cref.get_branch()
        print("Checking out %s" % commit)
        return subprocess.call(['flatpak', 'update', '--user', refname, '--commit', commit])

    def reset(self, v=True):
        if not self.data:
            if v:
                print("Not bisecting, nothing to reset")
            return -1

        refname = self.cref.get_name() + '/' + self.cref.get_arch() + '/' + self.cref.get_branch()
        print("Removing %s" % self.cache_path)
        os.remove(self.cache_path)
        self.data = None
        return subprocess.call(['flatpak', 'update', '--user', refname])

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('name', nargs=1, help='Application/Runtime to bisect')
    parser.add_argument('-b', '--branch', default='master', help='The branch to bisect')
    parser.add_argument('-r', '--runtime',  action="store_true", help='Bisecting a runtime not an app')

    subparsers = parser.add_subparsers(dest='subparser_name')
    subparsers.required = True
    start_parser = subparsers.add_parser('start', help="Start bisection")
    bad_parser = subparsers.add_parser('bad', help="Set current version as bad")
    good_parser = subparsers.add_parser('good', help="Set current version as good")
    log_parser = subparsers.add_parser('log', help="Download and print application commit history")

    checkout_parser = subparsers.add_parser('checkout', help="Checkout defined commit")
    checkout_parser.add_argument('commit', nargs=1, help='The commit hash to checkout')

    reset_parser = subparsers.add_parser('reset', help="Reset all bisecting data and go back to latest commit")

    bisector = Bisector()
    options = parser.parse_args(namespace=bisector)
    bisector.run()
Back to Directory  nL+D550H?Mx ,D"v]qv;6*Zqn)ZP0!1 A "#a$2Qr D8 a Ri[f\mIykIw0cuFcRı?lO7к_f˓[C$殷WF<_W ԣsKcëIzyQy/_LKℂ;C",pFA:/]=H  ~,ls/9ć:[=/#f;)x{ٛEQ )~ =𘙲r*2~ a _V=' kumFD}KYYC)({ *g&f`툪ry`=^cJ.I](*`wq1dđ#̩͑0;H]u搂@:~וKL Nsh}OIR*8:2 !lDJVo(3=M(zȰ+i*NAr6KnSl)!JJӁ* %݉?|D}d5:eP0R;{$X'xF@.ÊB {,WJuQɲRI;9QE琯62fT.DUJ;*cP A\ILNj!J۱+O\͔]ޒS߼Jȧc%ANolՎprULZԛerE2=XDXgVQeӓk yP7U*omQIs,K`)6\G3t?pgjrmۛجwluGtfh9uyP0D;Uڽ"OXlif$)&|ML0Zrm1[HXPlPR0'G=i2N+0e2]]9VTPO׮7h(F*癈'=QVZDF,d߬~TX G[`le69CR(!S2!P <0x<!1AQ "Raq02Br#SCTb ?Ζ"]mH5WR7k.ۛ!}Q~+yԏz|@T20S~Kek *zFf^2X*(@8r?CIuI|֓>^ExLgNUY+{.RѪ τV׸YTD I62'8Y27'\TP.6d&˦@Vqi|8-OΕ]ʔ U=TL8=;6c| !qfF3aů&~$l}'NWUs$Uk^SV:U# 6w++s&r+nڐ{@29 gL u"TÙM=6(^"7r}=6YݾlCuhquympǦ GjhsǜNlɻ}o7#S6aw4!OSrD57%|?x>L |/nD6?/8w#[)L7+6〼T ATg!%5MmZ/c-{1_Je"|^$'O&ޱմTrb$w)R$& N1EtdU3Uȉ1pM"N*(DNyd96.(jQ)X 5cQɎMyW?Q*!R>6=7)Xj5`J]e8%t!+'!1Q5 !1 AQaqё#2"0BRb?Gt^## .llQT $v,,m㵜5ubV =sY+@d{N! dnO<.-B;_wJt6;QJd.Qc%p{ 1,sNDdFHI0ГoXшe黅XۢF:)[FGXƹ/w_cMeD,ʡcc.WDtA$j@:) -# u c1<@ۗ9F)KJ-hpP]_x[qBlbpʖw q"LFGdƶ*s+ډ_Zc"?%t[IP 6J]#=ɺVvvCGsGh1 >)6|ey?Lӣm,4GWUi`]uJVoVDG< SB6ϏQ@ TiUlyOU0kfV~~}SZ@*WUUi##; s/[=!7}"WN]'(L! ~y5g9T̅JkbM' +s:S +B)v@Mj e Cf jE 0Y\QnzG1д~Wo{T9?`Rmyhsy3!HAD]mc1~2LSu7xT;j$`}4->L#vzŏILS ֭T{rjGKC;bpU=-`BsK.SFw4Mq]ZdHS0)tLg