Ejemplo n.º 1
0
def testBranchFlags():
    yaml_contents = dedent('''
        branch-master:build_shell_commands:
        - "master.sh"

        branch-milky_way:build_batch_commands:
        - "milky_way.bat"

        branch-with-hyphens-in-name:build_batch_commands:
        - "crazy.bat"
        ''')

    jd_file = JobsDoneJob.CreateFromYAML(yaml_contents,
                                         repository=Repository(
                                             url='https://space.git',
                                             branch='milky_way'))[0]
    assert jd_file.build_shell_commands is None
    assert jd_file.build_batch_commands == ['milky_way.bat']

    jd_file = JobsDoneJob.CreateFromYAML(yaml_contents,
                                         repository=Repository(
                                             url='https://space.git',
                                             branch='master'))[0]
    assert jd_file.build_shell_commands == ['master.sh']
    assert jd_file.build_batch_commands is None
Ejemplo n.º 2
0
def GetJobsFromDirectory(directory='.'):
    '''
    Looks in a directory for a jobs_done file and git repository information to create jobs.

    :param directory:
        Directory where we'll extract information to generate `JenkinsJob`s

    :return tuple(Repository,set(JenkinsJob))
        Repository information for the given directory, and jobs obtained from this directory.

        .. seealso:: GetJobsFromFile
    '''
    from jobs_done10.jobs_done_job import JOBS_DONE_FILENAME
    from jobs_done10.repository import Repository
    import os

    from subprocess import check_output
    url = check_output('git config --local --get remote.origin.url', shell=True, cwd=directory).strip().decode('UTF-8')
    branches = check_output('git branch', shell=True, cwd=directory).strip().decode('UTF-8')
    for branch in branches.splitlines():
        branch = branch.strip()
        if '*' in branch:  # Current branch
            branch = branch.split(' ', 1)[1]
            break
    else:
        raise RuntimeError('Error parsing output from git branch')

    repository = Repository(url=url, branch=branch)
    try:
        with io.open(os.path.join(directory, JOBS_DONE_FILENAME), encoding='utf-8') as f:
            jobs_done_file_contents = f.read()
    except IOError:
        jobs_done_file_contents = None

    return repository, GetJobsFromFile(repository, jobs_done_file_contents)
Ejemplo n.º 3
0
    def SetGit(self, git_options, git_xml=None):
        '''
        Sets git options

        :param dict git_options:
            Options that will be set in git

        :param None|xml_factory._xml_factory.XmlFactory git_xml:
            Target XmlFactory object to set options.
            If None, will use the main project's Xml (`self.git`)
        '''
        if git_xml is None:
            git_xml = self.git

        git_xml['configVersion'] = '2'

        def _Set(option, xml_path, default=None):
            value = git_options.pop(option, default)
            if value is not None:
                for xml_path in AsList(xml_path):
                    git_xml[xml_path] = value

        # Git branch option is set in many places
        branch_paths = [
            'branches/hudson.plugins.git.BranchSpec/name',  # Branch being built
            'extensions/hudson.plugins.git.extensions.impl.LocalBranch/localBranch',  # Checkout to local branch (GitPlugin 2.0+)
            'localBranch',  # Checkout to local branch (GitPlugin 1.5)
        ]

        # Set all options --------------------------------------------------------------------------
        # Try to obtain a default target_dir based on repository name
        if 'url' in git_options:
            from jobs_done10.repository import Repository
            repository = Repository(url=git_options['url'])
            _Set('target_dir', 'relativeTargetDir', default=repository.name)
        else:
            _Set('target_dir', 'relativeTargetDir')

        _Set('remote', 'userRemoteConfigs/hudson.plugins.git.UserRemoteConfig/name')
        _Set('refspec', 'userRemoteConfigs/hudson.plugins.git.UserRemoteConfig/refspec')
        _Set('url', 'userRemoteConfigs/hudson.plugins.git.UserRemoteConfig/url')
        _Set('branch', branch_paths)
        _Set('recursive_submodules', 'extensions/hudson.plugins.git.extensions.impl.SubmoduleOption/recursiveSubmodules')
        _Set('shallow_clone', 'extensions/hudson.plugins.git.extensions.impl.CloneOption/shallow')
        _Set('reference', 'extensions/hudson.plugins.git.extensions.impl.CloneOption/reference')
        _Set('timeout', 'extensions/hudson.plugins.git.extensions.impl.CloneOption/timeout')

        # just accessing attribute tag in the xml object will create a tag without text
        bool_options = [
            ('clean_checkout', 'extensions/hudson.plugins.git.extensions.impl.CleanCheckout', 'true'),
            ('lfs', 'extensions/hudson.plugins.git.extensions.impl.GitLFSPull', 'true'),
        ]
        for option_name, tag_path, default_value in bool_options:
            value = git_options.pop(option_name, default_value)
            if value == 'true':
                # noinspection PyStatementEffect
                git_xml[tag_path]


        self._CheckUnknownOptions('git', git_options)
Ejemplo n.º 4
0
def test_post(client, post_json_data, mocker, repo_info_json_data):
    contents = 'jobs_done yaml contents'

    new_jobs = ['new1-eden-master', 'new2-eden-master']
    updated_jobs = ['upd1-eden-master', 'upd2-eden-master']
    deleted_jobs = ['del1-eden-master', 'del2-eden-master']

    upload_mock = mocker.patch(
        'jobs_done10.generators.jenkins.UploadJobsFromFile',
        autospec=True,
        return_value=(new_jobs, updated_jobs, deleted_jobs))

    with requests_mock.Mocker() as m:
        stash_url = 'https://example.com/stash'
        project_key = 'ESSS'
        slug = 'eden'
        path = '.jobs_done.yaml'
        ref = '8522b06a7c330008814a522d0342be9a997a1460'
        m.get(
            f'{stash_url}/projects/{project_key}/repos/{slug}/raw/{path}?at={ref}',
            text=contents)

        m.get(f'{stash_url}/rest/api/1.0/projects/{project_key}/repos/{slug}',
              json=repo_info_json_data)

        response = client.post(json=post_json_data)
        assert response.status_code == 200
        assert response.mimetype == 'text/plain'
        assert response.data.decode('UTF-8') == dedent("""
            NEW - new1-eden-master
            NEW - new2-eden-master
            UPD - upd1-eden-master
            UPD - upd2-eden-master
            DEL - del1-eden-master
            DEL - del2-eden-master
        """).strip()

    assert upload_mock.call_count == 1
    args, kwargs = upload_mock.call_args
    assert args == ()
    assert kwargs == dict(
        repository=Repository(
            'ssh://[email protected]:7999/esss/eden.git',
            'stable-pwda11-master'),
        jobs_done_file_contents=contents,
        url='https://example.com/jenkins',
        username='******',
        password='******',
    )
Ejemplo n.º 5
0
def testJobGeneratorConfigurator():
    class MyGenerator(object):
        def SetRepository(self, repository):
            assert repository.url == 'http://repo.git'

        def SetMatrix(self, matrix, matrix_row):
            assert matrix == {'id': [1, 2, 3]}
            assert matrix_row == {'id': 1}

        def SetBuildBatchCommands(self, commands):
            assert commands == ['command']

        def Reset(self):
            pass

    jobs_done_job = JobsDoneJob()
    jobs_done_job.matrix = {'id': [1, 2, 3]}
    jobs_done_job.matrix_row = {'id': 1}
    jobs_done_job.repository = Repository(url='http://repo.git')

    generator = MyGenerator()

    # Test basic calls
    with ExpectedCalls(generator,
                       Reset=1,
                       SetRepository=1,
                       SetMatrix=1,
                       SetBuildBatchCommands=0):
        JobGeneratorConfigurator.Configure(generator, jobs_done_job)

    # Set some more values to jobs_done_job, and make sure it is called
    jobs_done_job.build_batch_commands = ['command']
    with ExpectedCalls(generator,
                       Reset=1,
                       SetRepository=1,
                       SetMatrix=1,
                       SetBuildBatchCommands=1):
        JobGeneratorConfigurator.Configure(generator, jobs_done_job)

    # Try calling a missing option
    jobs_done_job.boosttest_patterns = 'patterns'
    with pytest.raises(JobGeneratorAttributeError):
        JobGeneratorConfigurator.Configure(generator, jobs_done_job)
Ejemplo n.º 6
0
def process_jobs_done(data) -> flask.Response:
    """

    Example of a post event for a push:

       {"eventKey": "repo:refs_changed", "date": "2018-06-18T16:20:06-0300",
        "actor": {"name": "jenkins", "emailAddress": "*****@*****.**", "id": 2852,
                  "displayName": "jenkins", "active": true, "slug": "jenkins", "type": "NORMAL"},
        "repository": {"slug": "eden", "id": 2231, "name": "eden", "scmId": "git", "state": "AVAILABLE",
                       "statusMessage": "Available", "forkable": true,
                       "project": {"key": "ESSS", "id": 1, "name": "ESSS", "description": "Dev projects",
                                   "public": false, "type": "NORMAL"}, "public": false}, "changes": [
        {"ref": {"id": "refs/heads/stable-pwda11-master", "displayId": "stable-pwda11-master", "type": "BRANCH"},
         "refId": "refs/heads/stable-pwda11-master", "fromHash": "cd39f701ae0a729b73c57b7848fbd1f340a36514",
         "toHash": "8522b06a7c330008814a522d0342be9a997a1460", "type": "UPDATE"}]}
    """
    if data is None:
        # return a 200 response when no JSON data is posted; this is useful because
        # the "Test Connection" in Stash does just that, making it easy to verify we have
        # the correct version up.
        import pkg_resources

        version = pkg_resources.get_distribution('jobs_done10').version
        return app.response_class(response=f'jobs_done10 version {version}',
                                  status=200,
                                  mimetype='text/plain')

    if not isinstance(data, dict) or 'eventKey' not in data:
        raise RuntimeError(
            f'Invalid request json data: {pprint.pformat(data)}')

    app.logger.info(f'Received request:\n{pprint.pformat(data)}')

    stash_url = os.environ['JD_STASH_URL'].rstrip()
    stash_username = os.environ['JD_STASH_USERNAME']
    stash_password = os.environ['JD_STASH_PASSWORD']
    project_key = data['repository']['project']['key']
    slug = data['repository']['slug']

    all_new_jobs = []
    all_updated_jobs = []
    all_deleted_jobs = []
    lines = []
    for change in data['changes']:
        try:
            jobs_done_file_contents = get_file_contents(
                stash_url=stash_url,
                username=stash_username,
                password=stash_password,
                project_key=project_key,
                slug=slug,
                path='.jobs_done.yaml',
                ref=change['toHash'],
            )
        except IOError:
            jobs_done_file_contents = None

        from jobs_done10.generators import jenkins
        from jobs_done10.repository import Repository

        clone_url = get_clone_url(stash_url=stash_url,
                                  username=stash_username,
                                  password=stash_password,
                                  project_key=project_key,
                                  slug=slug)

        branch = change['ref']['id']
        prefix = 'refs/heads/'
        if not branch.startswith(prefix):
            lines.append(
                f'WARNING: ignoring branch {branch}: expected {prefix}')
            continue
        branch = branch[len(prefix):]
        repository = Repository(url=clone_url, branch=branch)
        jenkins_url = os.environ['JD_JENKINS_URL'].rstrip('/')
        jenkins_username = os.environ['JD_JENKINS_USERNAME']
        jenkins_password = os.environ['JD_JENKINS_PASSWORD']

        new_jobs, updated_jobs, deleted_jobs = jenkins.UploadJobsFromFile(
            repository=repository,
            jobs_done_file_contents=jobs_done_file_contents,
            url=jenkins_url,
            username=jenkins_username,
            password=jenkins_password,
        )
        all_new_jobs.extend(new_jobs)
        all_updated_jobs.extend(updated_jobs)
        all_deleted_jobs.extend(deleted_jobs)

    lines.extend(f'NEW - {x}' for x in all_new_jobs)
    lines.extend(f'UPD - {x}' for x in all_updated_jobs)
    lines.extend(f'DEL - {x}' for x in all_deleted_jobs)

    message = '\n'.join(lines)
    app.logger.info(message)
    return app.response_class(response=message,
                              status=200,
                              mimetype='text/plain')
Ejemplo n.º 7
0
def _process_jobs_done_request(payload) -> str:
    """
    Generate/update a Jenkins job from a request and returns a debug message
    """
    if not isinstance(payload, dict) or 'eventKey' not in payload:
        raise RuntimeError(
            f'Invalid request json data: {pprint.pformat(payload)}')

    app.logger.info(f'Received request:\n{pprint.pformat(payload)}')

    stash_url = os.environ['JD_STASH_URL'].rstrip()
    stash_username = os.environ['JD_STASH_USERNAME']
    stash_password = os.environ['JD_STASH_PASSWORD']
    project_key = payload['repository']['project']['key']
    slug = payload['repository']['slug']

    all_new_jobs = []
    all_updated_jobs = []
    all_deleted_jobs = []
    lines = []
    for change in payload['changes']:
        try:
            jobs_done_file_contents = get_file_contents(
                stash_url=stash_url,
                username=stash_username,
                password=stash_password,
                project_key=project_key,
                slug=slug,
                path='.jobs_done.yaml',
                ref=change['toHash'],
            )
        except IOError:
            jobs_done_file_contents = None

        from jobs_done10.generators import jenkins
        from jobs_done10.repository import Repository

        clone_url = get_clone_url(stash_url=stash_url,
                                  username=stash_username,
                                  password=stash_password,
                                  project_key=project_key,
                                  slug=slug)

        branch = change['ref']['id']
        prefix = 'refs/heads/'
        if not branch.startswith(prefix):
            lines.append(
                f'WARNING: ignoring branch {branch}: expected {prefix}')
            continue
        branch = branch[len(prefix):]
        repository = Repository(url=clone_url, branch=branch)
        jenkins_url = os.environ['JD_JENKINS_URL'].rstrip('/')
        jenkins_username = os.environ['JD_JENKINS_USERNAME']
        jenkins_password = os.environ['JD_JENKINS_PASSWORD']

        new_jobs, updated_jobs, deleted_jobs = jenkins.UploadJobsFromFile(
            repository=repository,
            jobs_done_file_contents=jobs_done_file_contents,
            url=jenkins_url,
            username=jenkins_username,
            password=jenkins_password,
        )
        all_new_jobs.extend(new_jobs)
        all_updated_jobs.extend(updated_jobs)
        all_deleted_jobs.extend(deleted_jobs)

    lines.extend(f'NEW - {x}' for x in all_new_jobs)
    lines.extend(f'UPD - {x}' for x in all_updated_jobs)
    lines.extend(f'DEL - {x}' for x in all_deleted_jobs)

    message = '\n'.join(lines)
    app.logger.info(message)
    return message
Ejemplo n.º 8
0
from textwrap import dedent

import pytest

from jobs_done10.jobs_done_job import (JobsDoneFileTypeError, JobsDoneJob,
                                       UnknownJobsDoneFileOption,
                                       UnmatchableConditionError)
from jobs_done10.repository import Repository

_REPOSITORY = Repository(url='https://space.git', branch='milky_way')


def testCreateJobsDoneJobFromYAML():
    yaml_contents = dedent('''
        junit_patterns:
        - "junit*.xml"

        boosttest_patterns:
        - "cpptest*.xml"

        display_name: "[{branch}] {planet}-{moon} {name}"

        label_expression: "planet-{planet}&&moon-{moon}"

        parameters:
        - choice:
            name: "PARAM"
            choices:
            - "choice_1"
            - "choice_2"
            description: "Description"