def __init__(self): """Initialize weather instance.""" self.api_key = get_config("libweather.api_key") if self.api_key is None: raise AssertionError("An API key is needed") self.base_unit = get_config("libweather.temp_unit", "metric") self.temp_unit = TEMP_UNITS.get(self.base_unit, "K") self.wind_unit = WIND_UNITS.get(self.base_unit, "km/h")
def user_is_admin(user_id): """ Check if the given user ID is in the list of the approved user IDs. """ if user_id not in get_config("libadmin.approved_user_ids", []): LOGI(f"Access denied to user {user_id}") return False LOGI(f"Access granted to user {user_id}") return True
def ci(update: Update, context: CallbackContext): if not user_is_approved(update.message.from_user.id): update.message.reply_text("Error: You are not authorized to use CI function of this bot.\n" "Ask to who host this bot to add you to the authorized people list") return if get_config("ci.channel_id") is None: update.message.reply_text("Error: CI channel or user ID not defined") LOGE("CI channel or user ID not defined") return parser = CIParser(prog="/ci") parser.set_output(update.message.reply_text) parser.add_argument('project', help='CI project', nargs='?', default=None,) parser.add_argument('-s', '--status', action='store_true', help='show queue status') args, project_args = parser.parse_known_args(context.args) if args.status: update.message.reply_text(queue_manager.get_formatted_queue_list()) return if args.project is None: parser.error("Please specify a project") try: project_class = import_module(f"homebot.modules.ci.projects.{args.project}", package="Project").Project except (ModuleNotFoundError, AttributeError): update.message.reply_text("Error: Project script not found") return except Exception as e: text = "Error: Error while importing project:" text += format_exception(e) update.message.reply_text(text) LOGE(text) return try: project = project_class(update, context, project_args) except Exception as e: text = "Error: Project class initialization failed:\n" text += format_exception(e) update.message.reply_text(text) LOGE(text) return workflow = Workflow(project) queue_manager.put(workflow) update.message.reply_text("Workflow added to the queue") LOGI("Workflow added to the queue")
def __init__(self): """Initialize the uploader variables.""" self.method = get_config("libupload.method") self.destination_path_base = Path(get_config("libupload.base_dir")) self.host = get_config("libupload.host") self.port = get_config("libupload.port") self.server = self.host if self.port is None else f"{self.host}:{self.port}" self.username = get_config("libupload.username") self.password = get_config("libupload.password") if self.method not in ALLOWED_METHODS: raise NotImplementedError("Upload method not valid")
def main(): updater = HomeBot(get_config("bot.api_token")) LOGI(f"HomeBot started, version {__version__}") LOGI(f"Bot username: @{updater.bot.get_me().username}") updater.start_polling()
from homebot.core.config import get_config from homebot.modules.ci.artifacts import Artifacts from telegram.error import TimedOut, RetryAfter from time import sleep chat_id = get_config("ci.channel_id") class PostManager: def __init__(self, project, device: str, artifacts: Artifacts): """Initialize PostManager class.""" self.project = project self.device = device self.artifacts = artifacts self.base_message_text = self.get_base_message_text() self.message = self.project.context.bot.send_message( chat_id, self.base_message_text) def get_base_message_text(self): text = f"🛠CI | {self.project.name} {self.project.version} ({self.project.android_version})\n" text += f"Device: {self.device}\n" text += f"Lunch flavor: {self.project.lunch_prefix}_{self.device}-{self.project.lunch_suffix}\n" text += "\n" return text def update(self, status: str): text = self.base_message_text text += f"Status: {status}\n" text += "\n" if self.artifacts.artifacts: text += self.artifacts.get_readable_artifacts_list()
def build(self): status_message = self.update.message.reply_text("Downloading file...") # Download file tempdir = TemporaryDirectory() path = Path(tempdir.name) url = self.parsed_args.url file = path / "recovery.img" open(file, 'wb').write(requests.get(url, allow_redirects=True).content) # Generate device tree status_message.edit_text("Generating device tree...") try: devicetree = DeviceTree(path / "working", recovery_image=file) except Exception as e: status_message.edit_text("Device tree generation failed\n" f"Error: {e}") return try: build_description = devicetree.build_prop_reader.get_prop(BUILD_DESCRIPTION, "build description") branch = build_description.replace(" ", "-") except AssertionError: status_message.edit_text("Failed to get build description prop, using date as a branch") today = date.today() build_description = None branch = f"{today.year}-{today.month}-{today.day}" # Upload to GitHub status_message.edit_text("Pushing to GitHub...") gh_username = get_config("ci.github_username") gh_token = get_config("ci.github_token") gh_org_name = get_config("ci.twrpdtgen.github_org") repo_name = f"android_device_{devicetree.manufacturer}_{devicetree.codename}" git_repo_url = f"https://{gh_username}:{gh_token}@github.com/{gh_org_name}/{repo_name}" # Get organization try: gh = Github(gh_token) gh_org = gh.get_organization(gh_org_name) except GithubException as error: status_message.edit_text(f"Failed to get organization\n" f"Error: {error}") return # Create repo if needed status_message.edit_text("Creating repo if needed...") try: devicetree_repo = gh_org.create_repo(name=repo_name, private=False, auto_init=False) except GithubException as error: if error.status != 422: status_message.edit_text("Repo creation failed\n" f"Error: {error.status} {error}") return devicetree_repo = gh_org.get_repo(name=repo_name) status_message.edit_text("Pushing...") try: devicetree.git_repo.git.push(git_repo_url, f"HEAD:refs/heads/{branch}") devicetree_repo.edit(default_branch=branch) except GitCommandError: status_message.edit_text("Error: Push to remote failed!") return status_message.edit_text("Done") channel_id = get_config("ci.twrpdtgen.channel_id") self.context.bot.send_message(channel_id, "TWRP device tree generated\n" f"Codename: {devicetree.codename}\n" f"Manufacturer: {devicetree.manufacturer}\n" f"Build description: {build_description}\n" f"Device tree: {devicetree_repo.html_url}/tree/{branch}", disable_web_page_preview=True)
def build(self): project_dir = Path( f"{get_config('ci.main_dir', '')}/{self.name}-{self.version}") device_out_dir = project_dir / "out" / "target" / "product" / self.parsed_args.device artifacts = Artifacts(device_out_dir, self.artifacts) post_manager = PostManager(self, self.parsed_args.device, artifacts) if self.parsed_args.clean is True: clean_type = "clean" elif self.parsed_args.installclean is True: clean_type = "installclean" else: clean_type = "none" post_manager.update("Building") command = [ bot_path / "modules" / "ci" / "projects" / "aosp" / "tools" / "building.sh", "--sources", project_dir, "--lunch_prefix", self.lunch_prefix, "--lunch_suffix", self.lunch_suffix, "--build_target", self.build_target, "--clean", clean_type, "--device", self.parsed_args.device ] last_edit = datetime.now() process = subprocess.Popen(command, encoding="UTF-8", stdout=subprocess.PIPE, stderr=subprocess.STDOUT) while True: output = process.stdout.readline() if output == '' and process.poll() is not None: break if not output: continue now = datetime.now() if (now - last_edit).seconds < 150: continue result = re.search(r"\[ +([0-9]+% [0-9]+/[0-9]+)\]", output.strip()) if result is None: continue result_split = str(result.group(1)).split() if len(result_split) != 2: continue percentage, targets = re.split(" +", result.group(1)) post_manager.update(f"Building: {percentage} ({targets})") last_edit = now returncode = process.poll() # Process return code build_result = ERROR_CODES.get(returncode, "Build failed: Unknown error") post_manager.update(build_result) needs_logs_upload = NEEDS_LOGS_UPLOAD.get(returncode, False) if needs_logs_upload != False: log_file = open(project_dir / needs_logs_upload, "rb") self.context.bot.send_document(get_config("ci.channel_id"), log_file) log_file.close() if returncode != SUCCESS or get_config("ci.upload_artifacts", False) is not True: return # Upload artifacts try: uploader = Uploader() except Exception as e: post_manager.update(f"{build_result}\n" f"Upload failed: {type(e)}: {e}") return artifacts.update() post_manager.update(build_result) for artifact in artifacts.artifacts: artifact.status = STATUS_UPLOADING post_manager.update(build_result) try: uploader.upload( artifact.path, Path(self.category) / self.parsed_args.device / self.name / self.android_version) except Exception as e: artifact.status = f"{STATUS_NOT_UPLOADED}: {type(e)}: {e}" LOGE(f"Error while uploading artifact {artifact.name}:\n" f"{format_exception(e)}") else: artifact.status = STATUS_UPLOADED post_manager.update(build_result)