Source code for scap.stage_train

# -*- coding: utf-8 -*-
"""
    scap.stage_train
    ~~~~~~~~
    Wrapper for all the manual steps performed before the deploy window on a Tuesday during a
    typical RelEng train deployment.

    Example usage: scap stage-train 1.38.0-wmf.20

    The above command:
    * Ensures you're in a tmux window
    * Ensures you've got a running ssh-agent
    * Checks for the new branch
    * Preps the new branch
    * Applies patches to the new branch
    * Deploys the new branch to testwikis
    * Cleans up out-of-date branches

    The behavior associated to stage-train used to live in the `tools/release`. The file history is
    still available in that repo and can be viewed with:
        git log -- bin/stage-train

    Copyright © 2014-2022 Wikimedia Foundation and Contributors.

    This file is part of Scap.

    Scap is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
import os
import sys

from scap import cli, utils, interaction

STAGE_SEQUENCE = [
    "clean",
    "prep",
    "patch",
    "testwikis",
]


[docs]@cli.command( "stage-train", help="Wraps manual steps required to stage a typical Tuesday deploy of the RelEng train" " deployment: Preps new version branch, applies patches and deploys to testwikis", affected_by_blocked_deployments=True, ) class StageTrain(cli.Application): """ Scap sub-command to stage a typical Tuesday deploy of the RelEng train deployment """
[docs] def __init__(self, exe_name): super().__init__(exe_name) self.logger = self.get_logger()
[docs] @cli.argument( "version", help="Wikiversion needing staging. Specify 'auto' to use the latest available wmf branch", ) @cli.argument( "--start-from", choices=STAGE_SEQUENCE, default=STAGE_SEQUENCE[0], help="stage to start from", ) @cli.argument("--dry-run", action="store_true", help="dry run") @cli.argument( "-y", "--yes", action="store_true", help="default to Yes for prompts, and don't check for tmux or screen running.", ) def main(self, *extra_args): def check_term_multplxr(): if os.environ.get("TMUX") is None and os.environ.get("STY") is None: utils.abort( "No tmux or screen process found. Try either:\n" '\tTMUX: tmux new-session -s "🚂🌈"\n' "\tSCREEN: screen -D -RR train" ) if self.arguments.version == "auto": self._setup_auto_mode() if not self.arguments.yes: check_term_multplxr() self._check_user_auth_sock() os.umask(self.config["umask"]) stages = STAGE_SEQUENCE[STAGE_SEQUENCE.index(self.arguments.start_from) :] for i, stage in enumerate(stages, start=1): self.logger.info( "----------------------------\n" "%s. Starting: %s" % (i, stage) ) getattr(self, "_" + stage)() self.logger.info("DONE!")
# Stage implementations def _prep(self): self._run(["prep", self.arguments.version]) def _patch(self): self._run( [ "apply-patches", "--abort-git-am-on-fail", "--train", self.arguments.version, ] ) def _testwikis(self): self._run(["deploy-promote", "--yes", "testwikis", self.arguments.version]) def _clean(self): self._run(["clean", "auto"]) # End stage implementations def _run(self, scap_cmd): scap_cmd_str = " ".join(scap_cmd) cmd_run_approved = ( self.arguments.yes or interaction.prompt_user_for_confirmation("Run `%s` now?" % scap_cmd_str) ) if cmd_run_approved: if self.arguments.dry_run: self.logger.info("Skipping `%s`, dry run only..." % scap_cmd_str) return self.scap_check_call(scap_cmd) def _setup_auto_mode(self): self.logger.info("Initializing stage-train auto mode") self.logger.info("Retrieving train information...") gerrit_latest_version = utils.get_current_train_version_from_gerrit( self.config["gerrit_url"] ) train_info = self.get_current_train_info() task = train_info["task"] status = train_info["status"] version = train_info["version"] if status != "open": self.logger.warn( "Phabricator task %s has status '%s'. Cancelling operation.", task, status, ) sys.exit(0) if version != gerrit_latest_version: utils.abort( "Phabricator task {} says the train version is '{}', but '{}' is the latest available in Gerrit.".format( task, version, gerrit_latest_version ) ) self.arguments.version = version self.logger.info("Using version %s", version)