Skip to main content

Many python-automation scripts can be seperated into steps. This library supports this seperation.

Project description

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

Project details


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

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.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page