def docker_requirements_generate(requirements_in='requirements-in.txt'):

    if not os.path.exists(requirements_in):
        requirements_in = os.path.join(BASE_FOLDER + "/../../",
                                       requirements_in)
        assert os.path.exists(requirements_in)

    requirements = read_file(requirements_in)

    logger.info(f'Generate requirements for Docker image')

    new = {}
    logger.info(requirements)
    for name, version in requirements.items():
        # alas-webapp is for windows only
        if name == 'alas-webapp':
            continue
        if name == 'opencv-python':
            name = 'opencv-python-headless'
            version = None
        # if name == 'numpy':
        #     version = None
        new[name] = version

    write_file(os.path.join(BASE_FOLDER, f'./requirements.txt'), data=new)
예제 #2
0
    def iter_process_by_name(self, name):
        """
        Args:
            name (str): process name, such as 'alas.exe'

        Yields:
            str, str, str: executable_path, process_name, process_id
        """
        try:
            from win32com.client import GetObject
        except ModuleNotFoundError:
            logger.info('pywin32 not installed, skip')
            return False

        try:
            wmi = GetObject('winmgmts:')
            processes = wmi.InstancesOf('Win32_Process')
            for p in processes:
                executable_path = p.Properties_["ExecutablePath"].Value
                process_name = p.Properties_("Name").Value
                process_id = p.Properties_["ProcessID"].Value

                if executable_path is not None and process_name == name and process_id != self.self_pid:
                    executable_path = executable_path.replace(r'\\',
                                                              '/').replace(
                                                                  '\\', '/')
                    for folder in self.alas_folder:
                        if folder in executable_path:
                            yield executable_path, process_name, process_id
        except Exception as e:
            # Possible exception
            # pywintypes.com_error: (-2147217392, 'OLE error 0x80041010', None, None)
            logger.info(str(e))
            return False
예제 #3
0
    def pip_install(self):
        logger.hr('Update Dependencies', 0)

        if not self.InstallDependencies:
            logger.info('InstallDependencies is disabled, skip')
            return

        logger.hr('Check Python', 1)
        self.execute(f'"{self.python}" --version')

        arg = []
        if self.PypiMirror:
            mirror = self.PypiMirror
            arg += ['-i', mirror]
            # Trust http mirror
            if 'http:' in mirror:
                arg += ['--trusted-host', urlparse(mirror).hostname]

        # Don't update pip, just leave it.
        # logger.hr('Update pip', 1)
        # self.execute(f'"{self.pip}" install --upgrade pip{arg}')
        arg += ['--disable-pip-version-check']

        logger.hr('Update Dependencies', 1)
        arg = ' ' + ' '.join(arg) if arg else ''
        self.execute(f'{self.pip} install -r {self.requirements_file}{arg}')
예제 #4
0
    def app_update(self):
        logger.hr(f'Update app.asar', 0)

        if not self.AutoUpdate:
            logger.info('AutoUpdate is disabled, skip')
            return False

        return self.app_asar_replace(os.getcwd())
예제 #5
0
def show_fix_tip(module):
    logger.info(f"""
    To fix this:
    1. Open console.bat
    2. Execute the following commands:
        pip uninstall -y {module}
        pip install --no-cache-dir {module}
    3. Re-open Alas.exe
    """)
예제 #6
0
    def show_config(self):
        logger.hr("Show deploy config", 1)
        for k, v in self.config.items():
            if k in ("Password", "SSHUser"):
                continue
            if self.config_template[k] == v:
                continue
            logger.info(f"{k}: {v}")

        logger.info(f"Rest of the configs are the same as default")
예제 #7
0
 def kill_by_name(self, name):
     """
     Args:
         name (str): Process name
     """
     logger.hr(f'Kill {name}', 1)
     for row in self.iter_process_by_name(name):
         logger.info(' '.join(map(str, row)))
         self.execute(f'taskkill /f /pid {row[2]}',
                      allow_failure=True,
                      output=False)
예제 #8
0
    def adb_kill(self):
        # self._execute([self.adb_binary, 'devices'])
        # self._execute([self.adb_binary, 'kill-server'])

        # Just kill it, because some adb don't obey.
        logger.info('Kill all known ADB')
        for exe in [
                # Most emulator use this
                'adb.exe',
                # NoxPlayer 夜神模拟器
                'nox_adb.exe',
                # MumuPlayer MuMu模拟器
                'adb_server.exe',
                # Bluestacks 蓝叠模拟器
                'HD-Adb.exe'
        ]:
            ret_code = self._execute(['taskkill', '/f', '/im', exe],
                                     output=False)
            if ret_code == 0:
                logger.info(f'Task {exe} killed')
            elif ret_code == 128:
                logger.info(f'Task {exe} not found')
            else:
                logger.info(
                    f'Error occurred when killing task {exe}, return code {ret_code}'
                )
예제 #9
0
    def execute(self, command, allow_failure=False, output=True):
        """
        Args:
            command (str):
            allow_failure (bool):
            output(bool):

        Returns:
            bool: If success.
                Terminate installation if failed to execute and not allow_failure.
        """
        command = command.replace(r"\\", "/").replace("\\",
                                                      "/").replace('"', '"')
        if not output:
            command = command + ' >nul 2>nul'
        logger.info(command)
        error_code = os.system(command)
        if error_code:
            if allow_failure:
                logger.info(f"[ allowed failure ], error_code: {error_code}")
                return False
            else:
                logger.info(f"[ failure ], error_code: {error_code}")
                self.show_error(command, error_code)
                raise ExecutionError
        else:
            logger.info(f"[ success ]")
            return True
예제 #10
0
    def git_install(self):
        logger.hr('Update Alas', 0)

        if not self.AutoUpdate:
            logger.info('AutoUpdate is disabled, skip')
            return

        self.git_repository_init(
            repo=self.Repository,
            source='origin',
            branch=self.Branch,
            proxy=self.GitProxy,
            keep_changes=self.KeepLocalChanges,
        )
예제 #11
0
    def emulators(self):
        """
        Returns:
            list: List of installed emulators on current computer.
        """
        emulators = []
        for emulator in self.SUPPORTED_EMULATORS:
            try:
                serial = emulator.serial
                emulators.append(emulator)
            except FileNotFoundError:
                continue
            if len(serial):
                logger.info(
                    f'Emulator {emulator.name} found, instances: {serial}')

        return emulators
예제 #12
0
    def devices(self):
        """
        Returns:
            list[str]: Connected devices in adb
        """
        result = self._execute([self.adb_binary, 'devices']).decode()
        devices = []
        for line in result.replace('\r\r\n', '\n').replace('\r\n',
                                                           '\n').split('\n'):
            if line.startswith('List') or '\t' not in line:
                continue
            serial, status = line.split('\t')
            if status == 'device':
                devices.append(serial)

        logger.info(f'Devices: {devices}')
        return devices
예제 #13
0
    def adb_install(self):
        logger.hr('Start ADB service', 0)

        emulator = EmulatorConnect(adb=self.adb)
        if self.ReplaceAdb:
            logger.hr('Replace ADB', 1)
            emulator.adb_replace()
        elif self.AutoConnect:
            logger.hr('ADB Connect', 1)
            emulator.brute_force_connect()

        if self.InstallUiautomator2:
            logger.hr('Uiautomator2 Init', 1)
            try:
                import adbutils
            except ModuleNotFoundError as e:
                message = str(e)
                for module in ['apkutils2', 'progress']:
                    # ModuleNotFoundError: No module named 'apkutils2'
                    # ModuleNotFoundError: No module named 'progress.bar'
                    if module in message:
                        show_fix_tip(module)
                        exit(1)

            # Remove global proxies, or uiautomator2 will go through it
            for k in list(os.environ.keys()):
                if k.lower().endswith('_proxy'):
                    del os.environ[k]

            from uiautomator2.init import Initer
            for device in adbutils.adb.iter_device():
                init = Initer(device, loglevel=logging.DEBUG)
                init.set_atx_agent_addr('127.0.0.1:7912')
                try:
                    init.install()
                except AssertionError:
                    logger.info(
                        f'AssertionError when installing uiautomator2 on device {device.serial}'
                    )
                    logger.info(
                        'If you are using BlueStacks or LD player or WSA, '
                        'please enable ADB in the settings of your emulator')
                    exit(1)
                init._device.shell(["rm", "/data/local/tmp/minicap"])
                init._device.shell(["rm", "/data/local/tmp/minicap.so"])
예제 #14
0
def aidlux_requirements_generate(requirements_in='requirements-in.txt'):
    logger.info('aidlux_requirements_generate')
    requirements = read_file(requirements_in)
    for aidlux in iter_version():
        logger.info(f'Generate requirements for AidLux {aidlux}')
        pre_installed = read_file(
            os.path.join(BASE_FOLDER, f'./{aidlux}/pre-installed.txt'))
        new = {}
        for name, version in requirements.items():
            # alas-webapp is for windows only
            if name == 'alas-webapp':
                continue
            version = pre_installed.get(name, version)
            # Having conflicts with numpy, let pip to decide
            if name == 'numpy':
                version = None
            new[name] = version

        write_file(os.path.join(BASE_FOLDER, f'./{aidlux}/requirements.txt'),
                   data=new)
예제 #15
0
    def adb_replace(self, adb):
        """
        Backup the adb in emulator folder to xxx.bak, replace it with your adb.
        Need to call `adb kill-server` before replacing.

        Args:
            adb (str): Absolute path to adb.exe
        """
        for ori, bak in zip(self.adb_binary, self.adb_backup):
            logger.info(f'Replacing {ori}')
            if os.path.exists(ori):
                if filecmp.cmp(adb, ori, shallow=True):
                    logger.info(f'{adb} is same as {ori}, skip')
                else:
                    logger.info(f'{ori} -----> {bak}')
                    shutil.move(ori, bak)
                    logger.info(f'{adb} -----> {ori}')
                    shutil.copy(adb, ori)
            else:
                logger.info(f'{ori} not exists, skip')
예제 #16
0
 def adb_recover(self):
     """ Revert adb replacement """
     for ori in self.adb_binary:
         logger.info(f'Recovering {ori}')
         bak = f'{ori}.bak'
         if os.path.exists(bak):
             logger.info(f'Delete {ori}')
             if os.path.exists(ori):
                 os.remove(ori)
             logger.info(f'{bak} -----> {ori}')
             shutil.move(bak, ori)
         else:
             logger.info(f'Not exists {bak}, skip')
예제 #17
0
 def show_error(self, command=None, error_code=None):
     logger.hr("Update failed", 0)
     self.show_config()
     logger.info("")
     logger.info(f"Last command: {command}\nerror_code: {error_code}")
     logger.info("Please check your deploy settings in config/deploy.yaml "
                 "and re-open Alas.exe")
예제 #18
0
 def _execute(self, cmd, timeout=10, output=True):
     """
     Returns:
         Object: Stdout(str) of cmd if output,
                 return code(int) of cmd if not output.
     """
     if not output:
         cmd.extend(['>nul', '2>nul'])
     logger.info(' '.join(cmd))
     process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
     try:
         stdout, stderr = process.communicate(timeout=timeout)
         ret_code = process.returncode
     except subprocess.TimeoutExpired:
         process.kill()
         stdout, stderr = process.communicate()
         ret_code = 1
         logger.info(f'TimeoutExpired, stdout={stdout}, stderr={stderr}')
     if output:
         return stdout
     else:
         return ret_code
예제 #19
0
    def git_repository_init(self,
                            repo,
                            source='origin',
                            branch='master',
                            proxy='',
                            keep_changes=False):
        logger.hr('Git Init', 1)
        self.execute(f'"{self.git}" init')

        logger.hr('Set Git Proxy', 1)
        if proxy:
            self.execute(f'"{self.git}" config --local http.proxy {proxy}')
            self.execute(f'"{self.git}" config --local https.proxy {proxy}')
        else:
            self.execute(f'"{self.git}" config --local --unset http.proxy',
                         allow_failure=True)
            self.execute(f'"{self.git}" config --local --unset https.proxy',
                         allow_failure=True)

        logger.hr('Set Git Repository', 1)
        if not self.execute(f'"{self.git}" remote set-url {source} {repo}',
                            allow_failure=True):
            self.execute(f'"{self.git}" remote add {source} {repo}')

        logger.hr('Fetch Repository Branch', 1)
        self.execute(f'"{self.git}" fetch {source} {branch}')

        logger.hr('Pull Repository Branch', 1)
        # Remove git lock
        lock_file = './.git/index.lock'
        if os.path.exists(lock_file):
            logger.info(f'Lock file {lock_file} exists, removing')
            os.remove(lock_file)
        if keep_changes:
            if self.execute(f'"{self.git}" stash', allow_failure=True):
                self.execute(f'"{self.git}" pull --ff-only {source} {branch}')
                if self.execute(f'"{self.git}" stash pop', allow_failure=True):
                    pass
                else:
                    # No local changes to existing files, untracked files not included
                    logger.info(
                        'Stash pop failed, there seems to be no local changes, skip instead'
                    )
            else:
                logger.info(
                    'Stash failed, this may be the first installation, drop changes instead'
                )
                self.execute(f'"{self.git}" reset --hard {source}/{branch}')
                self.execute(f'"{self.git}" pull --ff-only {source} {branch}')
        else:
            self.execute(f'"{self.git}" reset --hard {source}/{branch}')
            self.execute(f'"{self.git}" pull --ff-only {source} {branch}')

        logger.hr('Show Version', 1)
        self.execute(f'"{self.git}" log --no-merges -1')
예제 #20
0
    def app_asar_replace(folder, path='./toolkit/WebApp/resources/app.asar'):
        """
        Args:
            folder (str): Path to AzurLaneAutoScript
            path (str): Path from AzurLaneAutoScript to app.asar

        Returns:
            bool: If updated.
        """
        source = os.path.abspath(os.path.join(folder, path))
        logger.info(f'Old file: {source}')

        try:
            import alas_webapp
        except ImportError:
            logger.info(f'Dependency alas_webapp not exists, skip updating')
            return False

        update = alas_webapp.app_file()
        logger.info(f'New version: {alas_webapp.__version__}')
        logger.info(f'New file: {update}')

        if os.path.exists(source):
            if filecmp.cmp(source, update, shallow=True):
                logger.info('app.asar is already up to date')
                return False
            else:
                logger.info(f'Copy {update} -----> {source}')
                os.remove(source)
                shutil.copy(update, source)
                return True
        else:
            logger.info(f'{source} not exists, skip updating')
            return False
import os
from deploy.logger import logger

BASE_FOLDER = os.path.dirname(os.path.abspath(__file__))
logger.info(BASE_FOLDER)


def read_file(file):
    out = {}
    with open(file, 'r', encoding='utf-8') as f:
        for line in f.readlines():
            res = [s.strip() for s in line.split('==')]
            if len(res) > 1:
                name, version = res
            else:
                name, version = res[0], None
            out[name] = version

    return out


def write_file(file, data):
    lines = []
    for name, version in data.items():
        if version:
            lines.append(f'{name}=={version}')
        else:
            lines.append(str(name))

    with open(file, 'w', encoding='utf-8', newline='') as f:
        f.write('\n'.join(lines))
예제 #22
0
        asyncio.run(connect())

        return self.devices()

    def adb_replace(self, adb=None):
        """
        Different version of ADB will kill each other when starting.
        Chinese emulators (NoxPlayer, LDPlayer, MemuPlayer, MuMuPlayer) use their own adb,
        instead of the one in system PATH, so when they start they kill the adb.exe Alas is using.
        Replacing the ADB in emulator is the simplest way to solve this.

        Args:
            adb (str): Absolute path to adb.exe
        """
        self.adb_kill()
        for emulator in self.emulators:
            emulator.adb_replace(adb if adb is not None else self.adb_binary)
        self.brute_force_connect()

    def adb_recover(self):
        """ Revert adb replacement """
        self.adb_kill()
        for emulator in self.emulators:
            emulator.adb_recover()
        self.brute_force_connect()


if __name__ == '__main__':
    emu = EmulatorConnect()
    logger.info(emu.brute_force_connect())