Skip to main content

task_in_steps

Many python-automation scripts can be seperated into steps. This library supports this seperation. You can define steps with classes. The steps are indepodent as they can implement can_skip which checks if the step can be skipped.

Example

class Config:

    def __init__(self):
        self.parser = argparse.ArgumentParser()
        self.parser.add_argument('--projectname', help='Name of the new project', required=True)
        self.parser.add_argument('--domain', help='Top-Level-Domain (without review.)', required=True)
        self.parser.add_argument('--template', help='A link to a GitHub-Repo, used as template', required=True)
        self.parser.add_argument('--username', help='LDAP Username', required=True)
        self.parser.add_argument('--password', help='LDAP Password (optional)', required=False)
        self.gerrit_port = 29418

    def load_args(self):
        args = self.parser.parse_args()
        self.project_name = args.projectname
        self.template_src = args.template
        self.gerrit_user = args.username
        self.password = args.password
        self.domain = args.domain
        self.gerrit_host = 'review.' + args.domain
        self.gerrit_url = f'http://{self.gerrit_host}'
        return self

if __name__ == "__main__":
    config = Config().load_args()

    run_steps(config, [
        CertificateOnThisMachine(),
        UploadSshKey(),
        ProjectFromTemplate(),
        CreateGerritProject(),
        ConfigureGerritGitHooks(),
        ConfigureGitPushToGerrit(),
        CreateJenkinsPipelines(),
        TriggerTemplateInit(),
        SendInitialCommit()
    ])

### The steps:

class CertificateOnThisMachine(Step):
    name = "SSH certificate on this machine"

    def __init__(self):
        home = os.path.expanduser('~')
        self.ssh_path = home + '/.ssh'

    def can_skip(self, config):
        return os.path.isfile(self.ssh_path + '/id_rsa.pub')

    def run(self, config):
        print('no ssh-certificate: generating one')
        os.system(f'ssh-keygen -t rsa -N '' -f {self.ssh_path}')
        return True


class UploadSshKey(Step):
    name = "Upload your SSH-Key"

    def can_skip(self, config):
        ssh_command = f'ssh -p {config.gerrit_port} {config.gerrit_user}@{config.gerrit_host} gerrit version'
        res = subprocess.run(ssh_command.split(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode
        return res == 0

    def run(self, config):
        home = os.path.expanduser('~')
        ssh_path = home + '/.ssh'
        print()
        print('You did not upload your SSH-Key.')
        print()
        print(f'1. Go to: http://{config.gerrit_host}/settings/#SSHKeys')
        print( '2. Paste this in, click "Add Key" and run again:')
        with open(f'{ssh_path}/id_rsa.pub', 'r') as file:
            print(file.read())
        return False

class ProjectFromTemplate(Step):
    name = "Create project from template"

    def can_skip(self, config):
        return os.path.isdir(f'./{config.project_name}')

    def run(self, config):
        os.system(f'git clone {config.template_src} {config.project_name}')
        os.chdir(f'./{config.project_name}')
        os.system(f'rm .git -rf')
        os.system(f'git init')
        os.chdir('..')
        return True


class CreateGerritProject(Step):
    name = 'Create Gerrit project'

    def can_skip(self, config):
        command = f'ssh -p {config.gerrit_port} {config.gerrit_user}@{config.gerrit_host} gerrit ls-projects'
        existing_projects = subprocess.check_output(command, shell=True).decode().strip().split('\n')
        return config.project_name in existing_projects

    def run(self, config):
        os.chdir(f'./{config.project_name}')
        os.system(f'ssh -p {config.gerrit_port} {config.gerrit_user}@{config.gerrit_host} gerrit create-project {config.project_name}')
        os.chdir('..')
        return True


class ConfigureGerritGitHooks(Step):
    name = 'Installing Gerrit Git-Hooks'

    def can_skip(self, config):
        path = f'./{config.project_name}/.git/hooks/commit-msg'
        if not os.path.isfile(path):
            return False
        with open(path, 'r') as file:
            content = file.read()
            return 'Change-Id:' in content

    def run(self, config):
        os.chdir(f'./{config.project_name}')
        os.system(f'scp -p -P {config.gerrit_port} {config.gerrit_user}@{config.gerrit_host}:hooks/commit-msg ".git/hooks/"')
        os.chdir('..')
        return True


class ConfigureGitPushToGerrit(Step):
    name = 'Configure "git push" to Gerrit'

    def can_skip(self, config):
        path = f'./{config.project_name}/.git/config'
        if not os.path.isfile(path):
            return False
        with open(path, 'r') as file:
            content = file.read()
            return 'push = refs/heads/master:refs/for/master' in content

    def run(self, config):
        path = f'./{config.project_name}/.git/config'
        git_config = f'''
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = ssh://{config.gerrit_user}@{config.gerrit_host}:{config.gerrit_port}/{config.project_name}
        fetch = +refs/heads/master:refs/remotes/origin/master
        push = refs/heads/master:refs/for/master
[branch "master"]
        remote = origin
        merge = refs/heads/master
'''
        with open(path, 'w') as f:
            f.write(git_config)
        return True

class CreateJenkinsPipelines(Step):
    name = 'Creating Jenkins-Pipelines'

    def can_skip(self, config):
        job1 = config.project_name + '-code'
        job2 = config.project_name + '-deploy'

        server = self._get_server(config)
        jobs = [job['name'] for job in server.get_all_jobs()]

        return job1 in jobs and job2 in jobs


    def run(self, config):
        self._create_pipeline(config.project_name, 'code', config)
        self._create_pipeline(config.project_name, 'deploy', config)
        return True

    def _get_server(self, config):
        if hasattr(self, 'server'):
            return self.server

        url = f'http://ci.{config.domain}'

        password = config.password
        if not password:
            print(f'Enter credentials for "{url}"')
            print(f'User: {config.gerrit_user}')
            password = getpass()

        self.server = Jenkins(
            url, 
            username=config.gerrit_user, 
            password=password)

        return self.server


    def _create_pipeline(self, project_name, pipeline_type, config):
        content = pkg_resources.resource_string('generate_gerrit_jenkins_project', f'jobs/{pipeline_type}.config.xml').decode()
        content = content.replace('helloworld', project_name)

        try:
            self._get_server(config).create_job(project_name + '-' + pipeline_type, content)
        except JenkinsException as e:
            print(e)
            raise

class TriggerTemplateInit(Step):
    name = 'Trigger Template Init'

    def can_skip(self, config):
        return not os.path.isfile(f'./{config.project_name}/init_template.py')

    def run(self, config):
        stdout, stderr, has_error = exec(f'python3 init_template.py', cwd=f'./{config.project_name}')
        if not has_error:
            exec(f'rm init_template.py', cwd=f'./{config.project_name}')
        return not has_error

class SendInitialCommit(Step):
    name = 'Send initial commit to gerrit'

    def can_skip(self, config):
        stdout, stderr, has_error = exec('git log', cwd=f'./{config.project_name}')
        return not has_error # git log returns error if there are not commits. Skip=True if no error returned

    def run(self, config):
        stdout, stderr, has_error1 = exec('git add .', cwd=f'./{config.project_name}')
        print(stdout, stderr)
        stdout, stderr, has_error2 = exec('git commit -aminit', cwd=f'./{config.project_name}')
        print(stdout, stderr)
        stdout, stderr, has_error3 = exec('git push origin master', cwd=f'./{config.project_name}')
        print(stdout, stderr)
        return True

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

task_in_steps-0.0.1.tar.gz (10.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

task_in_steps-0.0.1-py3-none-any.whl (9.1 kB view details)

Uploaded Python 3

File details

Details for the file task_in_steps-0.0.1.tar.gz.

File metadata

  • Download URL: task_in_steps-0.0.1.tar.gz
  • Upload date:
  • Size: 10.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.5.0 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.5

File hashes

Hashes for task_in_steps-0.0.1.tar.gz
Algorithm Hash digest
SHA256 2993ad537587383d622efc42c5d4971cf246e7181d6d3bdcabb81a6498b38c80
MD5 3d200a5420830478851f174a1290a1f3
BLAKE2b-256 043febfb9bee85ee6da6774606e3212ea30675563b956ad36005c9118245f549

See more details on using hashes here.

File details

Details for the file task_in_steps-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: task_in_steps-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 9.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.5.0 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.5

File hashes

Hashes for task_in_steps-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c0df603b3e4cc53ff78dabbd081249d75b80288ef7ecbbcb94a30008f69fbb6a
MD5 7933ec4deeddd1b0f7a0059d6fa62aa1
BLAKE2b-256 e4104981b1a3d917f5e3f2ba7f4862670c07109fd8788a4479e7dc71eb389771

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.0.1 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page