maintain pwd when sourcing generated scripts

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'@
@[if disabled]
but disabled since the package is blacklisted (or not whitelisted) in the configuration file@
@[end if]@
@ </description>
  <keepDependencies>false</keepDependencies>
  <properties>
@(SNIPPET(
    'property_log-rotator',
    days_to_keep=100,
    num_to_keep=100,
))@
@[if github_url]@
@(SNIPPET(
    'property_github-project',
    project_url=github_url,
))@
@[end if]@
@[if job_priority is not None]@
@(SNIPPET(
    'property_job-priority',
    priority=job_priority,
))@
@[end if]@
@(SNIPPET(
    'property_requeue-job',
))@
@{
parameters = [
    {
        'type': 'boolean',
        'name': 'skip_cleanup',
        'description': 'Skip cleanup of catkin build artifacts as well as rosdoc index',
    },
]
if pull_request:
    parameters += [
        {
            'type': 'string',
            'name': 'sha1',
        },
    ]
}@
@(SNIPPET(
    'property_parameters-definition',
    parameters=parameters,
))@
  </properties>
@[if not pull_request]@
@(SNIPPET(
    'scm',
    repo_spec=source_repo_spec,
    path='catkin_workspace/src/%s' % source_repo_spec.name,
    git_ssh_credential_id=git_ssh_credential_id,
))@
@[else]@
@(SNIPPET(
    'scm_git',
    url=source_repo_spec.url,
    refspec='+refs/pull/*:refs/remotes/origin/pr/*',
    branch_name='${sha1}',
    relative_target_dir='catkin_workspace/src/%s' % source_repo_spec.name,
    git_ssh_credential_id=git_ssh_credential_id,
    merge_branch=source_repo_spec.version,
))@
@[end if]@
  <scmCheckoutRetryCount>2</scmCheckoutRetryCount>
  <assignedNode>@(node_label)</assignedNode>
  <canRoam>false</canRoam>
  <disabled>@('true' if disabled else 'false')</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers>
@[if not pull_request]@
@(SNIPPET(
    'trigger_poll',
    spec='H * * * *',
))@
@[end if]@
@[if pull_request]@
@(SNIPPET(
    'trigger_github-pull-request-builder',
    github_orgunit=github_orgunit,
    branch_name=source_repo_spec.version,
    job_name=job_name,
))@
@[end if]@
  </triggers>
  <concurrentBuild>true</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_system-groovy_check-free-disk-space',
))@
@(SNIPPET(
    'builder_shell_docker-info',
))@
@(SNIPPET(
    'builder_check-docker',
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
))@
@(SNIPPET(
    'builder_shell_clone-ros-buildfarm',
    ros_buildfarm_repository=ros_buildfarm_repository,
    wrapper_scripts=wrapper_scripts,
))@
@(SNIPPET(
    'builder_shell_key-files',
    script_generating_key_files=script_generating_key_files,
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'rm -fr $WORKSPACE/docker_generating_dockers',
        'mkdir -p $WORKSPACE/docker_generating_dockers',
        '',
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_generating_dockers/docker.cid > $WORKSPACE/docker_generating_dockers/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        '# generate Dockerfile, build and run it',
        '# generating the Dockerfiles for the actual devel tasks',
        'echo "# BEGIN SECTION: Generate Dockerfile - devel tasks"',
        'export TZ="%s"' % timezone,
        'export PYTHONPATH=$WORKSPACE/ros_buildfarm:$PYTHONPATH',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/devel/run_devel_job.py' +
        ' --rosdistro-index-url ' + rosdistro_index_url +
        ' ' + rosdistro_name +
        ' ' + source_build_name +
        ' ' + source_repo_spec.name +
        ' ' + os_name +
        ' ' + os_code_name +
        ' ' + arch +
        ' ' + ' '.join(repository_args) +
        ' --dockerfile-dir $WORKSPACE/docker_generating_dockers',
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - generating devel tasks"',
        'cd $WORKSPACE/docker_generating_dockers',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t devel_task_generation.%s_%s .' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - generating devel tasks"',
        'rm -fr $WORKSPACE/docker_build_and_install',
        'rm -fr $WORKSPACE/docker_build_and_test',
        'mkdir -p $WORKSPACE/docker_build_and_install',
        'mkdir -p $WORKSPACE/docker_build_and_test',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_generating_dockers/docker.cid' +
        ' -e=HOME=/home/buildfarm' +
        ' -e=TRAVIS=$TRAVIS' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace:ro' +
        ' -v $WORKSPACE/docker_build_and_install:/tmp/docker_build_and_install' +
        ' -v $WORKSPACE/docker_build_and_test:/tmp/docker_build_and_test' +
        ' -v ~/.ccache:/home/buildfarm/.ccache' +
        (' -v $HOME/.ssh/known_hosts:/etc/ssh/ssh_known_hosts:ro' +
         ' -v $SSH_AUTH_SOCK:/tmp/ssh_auth_sock' +
         ' -e SSH_AUTH_SOCK=/tmp/ssh_auth_sock' if git_ssh_credential_id else '') +
        ' devel_task_generation.%s_%s' % (rosdistro_name, source_repo_spec.name.lower()),
        'cd -',  # restore pwd when used in scripts
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_build_and_install/docker.cid > $WORKSPACE/docker_build_and_install/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - build and install"',
        '# build and run build_and_install Dockerfile',
        'cd $WORKSPACE/docker_build_and_install',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t devel_build_and_install.%s_%s .' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - build and install"',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_build_and_install/docker.cid' +
        ' -e=TRAVIS=$TRAVIS' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace' +
        ' devel_build_and_install.%s_%s' % (rosdistro_name, source_repo_spec.name.lower()),
        'cd -',  # restore pwd when used in scripts
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_build_and_test/docker.cid > $WORKSPACE/docker_build_and_test/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - build and test"',
        '# build and run build_and_test Dockerfile',
        'cd $WORKSPACE/docker_build_and_test',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t devel_build_and_test.%s_%s .' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - build and test"',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_build_and_test/docker.cid' +
        ' -e=TRAVIS=$TRAVIS' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace' +
        ' devel_build_and_test.%s_%s' % (rosdistro_name, source_repo_spec.name.lower()),
        'cd -',  # restore pwd when used in scripts
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'if [ "$skip_cleanup" = "false" ]; then',
        'echo "# BEGIN SECTION: Clean up to save disk space on slaves"',
        'rm -fr catkin_workspace/build_isolated',
        'rm -fr catkin_workspace/devel_isolated',
        'rm -fr catkin_workspace/install_isolated',
        'echo "# END SECTION"',
        'fi',
    ]),
))@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_warnings',
    unstable_threshold=0 if notify_compiler_warnings else '',
))@
@(SNIPPET(
    'publisher_xunit',
    pattern='catkin_workspace/test_results/**/*.xml',
))@
@[if notify_maintainers]@
@(SNIPPET(
    'publisher_groovy-postbuild_maintainer-notification',
))@
@[end if]@
@(SNIPPET(
    'publisher_mailer',
    recipients=notify_emails,
    dynamic_recipients=maintainer_emails,
    send_to_individuals=notify_committers,
))@
  </publishers>
  <buildWrappers>
@[if timeout_minutes is not None]@
@(SNIPPET(
    'build-wrapper_build-timeout',
    timeout_minutes=timeout_minutes,
))@
@[end if]@
@(SNIPPET(
    'build-wrapper_timestamper',
))@
@{
credential_ids = []
if git_ssh_credential_id:
    credential_ids.append(git_ssh_credential_id)
}@
@(SNIPPET(
    'build-wrapper_ssh-agent',
    credential_ids=credential_ids,
))@
  </buildWrappers>
</project>

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'@
@[if disabled]
but disabled since the package is blacklisted (or not whitelisted) in the configuration file@
@[end if]@
@ </description>
  <keepDependencies>false</keepDependencies>
  <properties>
@(SNIPPET(
    'property_log-rotator',
    days_to_keep=100,
    num_to_keep=100,
))@
@[if github_url]@
@(SNIPPET(
    'property_github-project',
    project_url=github_url,
))@
@[end if]@
@[if job_priority is not None]@
@(SNIPPET(
    'property_job-priority',
    priority=job_priority,
))@
@[end if]@
@(SNIPPET(
    'property_requeue-job',
))@
@{
parameters = [
    {
        'type': 'boolean',
        'name': 'skip_cleanup',
        'description': 'Skip cleanup of catkin build artifacts as well as rosdoc index',
    },
]
if pull_request:
    parameters += [
        {
            'type': 'string',
            'name': 'sha1',
        },
    ]
}@
@(SNIPPET(
    'property_parameters-definition',
    parameters=parameters,
))@
  </properties>
@[if not pull_request]@
@(SNIPPET(
    'scm',
    repo_spec=source_repo_spec,
    path='catkin_workspace/src/%s' % source_repo_spec.name,
    git_ssh_credential_id=git_ssh_credential_id,
))@
@[else]@
@(SNIPPET(
    'scm_git',
    url=source_repo_spec.url,
    refspec='+refs/pull/*:refs/remotes/origin/pr/*',
    branch_name='${sha1}',
    relative_target_dir='catkin_workspace/src/%s' % source_repo_spec.name,
    git_ssh_credential_id=git_ssh_credential_id,
    merge_branch=source_repo_spec.version,
))@
@[end if]@
  <scmCheckoutRetryCount>2</scmCheckoutRetryCount>
  <assignedNode>@(node_label)</assignedNode>
  <canRoam>false</canRoam>
  <disabled>@('true' if disabled else 'false')</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers>
@[if not pull_request]@
@(SNIPPET(
    'trigger_poll',
    spec='H * * * *',
))@
@[end if]@
@[if pull_request]@
@(SNIPPET(
    'trigger_github-pull-request-builder',
    github_orgunit=github_orgunit,
    branch_name=source_repo_spec.version,
    job_name=job_name,
))@
@[end if]@
  </triggers>
  <concurrentBuild>true</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_system-groovy_check-free-disk-space',
))@
@(SNIPPET(
    'builder_shell_docker-info',
))@
@(SNIPPET(
    'builder_check-docker',
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
))@
@(SNIPPET(
    'builder_shell_clone-ros-buildfarm',
    ros_buildfarm_repository=ros_buildfarm_repository,
    wrapper_scripts=wrapper_scripts,
))@
@(SNIPPET(
    'builder_shell_key-files',
    script_generating_key_files=script_generating_key_files,
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'rm -fr $WORKSPACE/docker_generating_dockers',
        'mkdir -p $WORKSPACE/docker_generating_dockers',
        '',
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_generating_dockers/docker.cid > $WORKSPACE/docker_generating_dockers/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        '# generate Dockerfile, build and run it',
        '# generating the Dockerfiles for the actual devel tasks',
        'echo "# BEGIN SECTION: Generate Dockerfile - devel tasks"',
        'export TZ="%s"' % timezone,
        'export PYTHONPATH=$WORKSPACE/ros_buildfarm:$PYTHONPATH',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/devel/run_devel_job.py' +
        ' --rosdistro-index-url ' + rosdistro_index_url +
        ' ' + rosdistro_name +
        ' ' + source_build_name +
        ' ' + source_repo_spec.name +
        ' ' + os_name +
        ' ' + os_code_name +
        ' ' + arch +
        ' ' + ' '.join(repository_args) +
        ' --dockerfile-dir $WORKSPACE/docker_generating_dockers',
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - generating devel tasks"',
        'cd $WORKSPACE/docker_generating_dockers',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t devel_task_generation.%s_%s .' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - generating devel tasks"',
        'rm -fr $WORKSPACE/docker_build_and_install',
        'rm -fr $WORKSPACE/docker_build_and_test',
        'mkdir -p $WORKSPACE/docker_build_and_install',
        'mkdir -p $WORKSPACE/docker_build_and_test',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_generating_dockers/docker.cid' +
        ' -e=HOME=/home/buildfarm' +
        ' -e=TRAVIS=$TRAVIS' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace:ro' +
        ' -v $WORKSPACE/docker_build_and_install:/tmp/docker_build_and_install' +
        ' -v $WORKSPACE/docker_build_and_test:/tmp/docker_build_and_test' +
        ' -v ~/.ccache:/home/buildfarm/.ccache' +
        (' -v $HOME/.ssh/known_hosts:/etc/ssh/ssh_known_hosts:ro' +
         ' -v $SSH_AUTH_SOCK:/tmp/ssh_auth_sock' +
         ' -e SSH_AUTH_SOCK=/tmp/ssh_auth_sock' if git_ssh_credential_id else '') +
        ' devel_task_generation.%s_%s' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_build_and_install/docker.cid > $WORKSPACE/docker_build_and_install/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - build and install"',
        '# build and run build_and_install Dockerfile',
        'cd $WORKSPACE/docker_build_and_install',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t devel_build_and_install.%s_%s .' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - build and install"',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_build_and_install/docker.cid' +
        ' -e=TRAVIS=$TRAVIS' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace' +
        ' devel_build_and_install.%s_%s' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_build_and_test/docker.cid > $WORKSPACE/docker_build_and_test/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - build and test"',
        '# build and run build_and_test Dockerfile',
        'cd $WORKSPACE/docker_build_and_test',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t devel_build_and_test.%s_%s .' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - build and test"',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_build_and_test/docker.cid' +
        ' -e=TRAVIS=$TRAVIS' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace' +
        ' devel_build_and_test.%s_%s' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'if [ "$skip_cleanup" = "false" ]; then',
        'echo "# BEGIN SECTION: Clean up to save disk space on slaves"',
        'rm -fr catkin_workspace/build_isolated',
        'rm -fr catkin_workspace/devel_isolated',
        'rm -fr catkin_workspace/install_isolated',
        'echo "# END SECTION"',
        'fi',
    ]),
))@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_warnings',
    unstable_threshold=0 if notify_compiler_warnings else '',
))@
@(SNIPPET(
    'publisher_xunit',
    pattern='catkin_workspace/test_results/**/*.xml',
))@
@[if notify_maintainers]@
@(SNIPPET(
    'publisher_groovy-postbuild_maintainer-notification',
))@
@[end if]@
@(SNIPPET(
    'publisher_mailer',
    recipients=notify_emails,
    dynamic_recipients=maintainer_emails,
    send_to_individuals=notify_committers,
))@
  </publishers>
  <buildWrappers>
@[if timeout_minutes is not None]@
@(SNIPPET(
    'build-wrapper_build-timeout',
    timeout_minutes=timeout_minutes,
))@
@[end if]@
@(SNIPPET(
    'build-wrapper_timestamper',
))@
@{
credential_ids = []
if git_ssh_credential_id:
    credential_ids.append(git_ssh_credential_id)
}@
@(SNIPPET(
    'build-wrapper_ssh-agent',
    credential_ids=credential_ids,
))@
  </buildWrappers>
</project>


Don't set an empty ssh-agent wrapper on devel jobs. (#528)

Setting the ssh agent build wrapper without a credential id causes
changes to devel job config from the Jenkins UI to add the first
configured ssh key credential. In addition to noise when reconfiguring,
manual config edits could also unintentionally expose an ssh key to a devel
job.

This is an example of a devel job before and after manual
reconfiguration.

```diff
--- previous-config.xml       2018-03-22 06:49:23.427691589 -0700
+++ current-config.xml        2018-03-22 06:49:54.783617638 -0700
@@ -464,9 +464,10 @@
     </hudson.plugins.build__timeout.BuildTimeoutWrapper>
     <hudson.plugins.timestamper.TimestamperBuildWrapper plugin="timestamper@1.8.8"/>
     <com.cloudbees.jenkins.plugins.sshagent.SSHAgentBuildWrapper plugin="ssh-agent@1.15">
+      <credentialIds>
+        <string>1e7d4696-7fd4-4bc6-8c87-ebc7b6ce16e5</string>
+      </credentialIds>
       <ignoreMissing>false</ignoreMissing>
     </com.cloudbees.jenkins.plugins.sshagent.SSHAgentBuildWrapper>
   </buildWrappers>
 </project>
 ```

With this change the job template no longer adds the ssh agent build
wrapper to the job unless it is actually needed.

```diff
--- remote config
+++ new config
@@ -468,5 +467,0 @@
-    <com.cloudbees.jenkins.plugins.sshagent.SSHAgentBuildWrapper plugin="ssh-agent@1.15">
-      <credentialIds>
-      </credentialIds>
-      <ignoreMissing>false</ignoreMissing>
-    </com.cloudbees.jenkins.plugins.sshagent.SSHAgentBuildWrapper>
```

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'@
@[if disabled]
but disabled since the package is blacklisted (or not whitelisted) in the configuration file@
@[end if]@
@ </description>
  <keepDependencies>false</keepDependencies>
  <properties>
@(SNIPPET(
    'property_log-rotator',
    days_to_keep=730,
    num_to_keep=100,
))@
@[if github_url]@
@(SNIPPET(
    'property_github-project',
    project_url=github_url,
))@
@[end if]@
@[if job_priority is not None]@
@(SNIPPET(
    'property_job-priority',
    priority=job_priority,
))@
@[end if]@
@(SNIPPET(
    'property_requeue-job',
))@
@{
parameters = [
    {
        'type': 'boolean',
        'name': 'skip_cleanup',
        'description': 'Skip cleanup of catkin build artifacts as well as rosdoc index',
    },
]
if pull_request:
    parameters += [
        {
            'type': 'string',
            'name': 'sha1',
        },
    ]
}@
@(SNIPPET(
    'property_parameters-definition',
    parameters=parameters,
))@
  </properties>
@[if not pull_request]@
@(SNIPPET(
    'scm',
    repo_spec=source_repo_spec,
    path='catkin_workspace/src/%s' % source_repo_spec.name,
    git_ssh_credential_id=git_ssh_credential_id,
))@
@[else]@
@(SNIPPET(
    'scm_git',
    url=source_repo_spec.url,
    refspec='+refs/pull/*:refs/remotes/origin/pr/*',
    branch_name='${sha1}',
    relative_target_dir='catkin_workspace/src/%s' % source_repo_spec.name,
    git_ssh_credential_id=git_ssh_credential_id,
    merge_branch=source_repo_spec.version,
))@
@[end if]@
  <scmCheckoutRetryCount>2</scmCheckoutRetryCount>
  <assignedNode>@(node_label)</assignedNode>
  <canRoam>false</canRoam>
  <disabled>@('true' if disabled else 'false')</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers>
@[if not pull_request]@
@(SNIPPET(
    'trigger_poll',
    spec='H * * * *',
))@
@[end if]@
@[if pull_request]@
@(SNIPPET(
    'trigger_github-pull-request-builder',
    github_orgunit=github_orgunit,
    branch_name=source_repo_spec.version,
    job_name=job_name,
))@
@[end if]@
  </triggers>
  <concurrentBuild>true</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_system-groovy_check-free-disk-space',
))@
@(SNIPPET(
    'builder_shell_docker-info',
))@
@(SNIPPET(
    'builder_check-docker',
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
))@
@(SNIPPET(
    'builder_shell_clone-ros-buildfarm',
    ros_buildfarm_repository=ros_buildfarm_repository,
    wrapper_scripts=wrapper_scripts,
))@
@(SNIPPET(
    'builder_shell_key-files',
    script_generating_key_files=script_generating_key_files,
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'rm -fr $WORKSPACE/docker_generating_dockers',
        'mkdir -p $WORKSPACE/docker_generating_dockers',
        '',
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_generating_dockers/docker.cid > $WORKSPACE/docker_generating_dockers/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        '# generate Dockerfile, build and run it',
        '# generating the Dockerfiles for the actual devel tasks',
        'echo "# BEGIN SECTION: Generate Dockerfile - devel tasks"',
        'export TZ="%s"' % timezone,
        'export PYTHONPATH=$WORKSPACE/ros_buildfarm:$PYTHONPATH',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/devel/run_devel_job.py' +
        ' --rosdistro-index-url ' + rosdistro_index_url +
        ' ' + rosdistro_name +
        ' ' + source_build_name +
        ' ' + source_repo_spec.name +
        ' ' + os_name +
        ' ' + os_code_name +
        ' ' + arch +
        ' ' + ' '.join(repository_args) +
        ' --dockerfile-dir $WORKSPACE/docker_generating_dockers',
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - generating devel tasks"',
        'cd $WORKSPACE/docker_generating_dockers',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t devel_task_generation.%s_%s .' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - generating devel tasks"',
        'rm -fr $WORKSPACE/docker_build_and_install',
        'rm -fr $WORKSPACE/docker_build_and_test',
        'mkdir -p $WORKSPACE/docker_build_and_install',
        'mkdir -p $WORKSPACE/docker_build_and_test',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_generating_dockers/docker.cid' +
        ' -e=HOME=/home/buildfarm' +
        ' -e=TRAVIS=$TRAVIS' +
        ' -e=ROS_BUILDFARM_PULL_REQUEST_BRANCH=$ROS_BUILDFARM_PULL_REQUEST_BRANCH' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace:ro' +
        ' -v $WORKSPACE/docker_build_and_install:/tmp/docker_build_and_install' +
        ' -v $WORKSPACE/docker_build_and_test:/tmp/docker_build_and_test' +
        ' -v ~/.ccache:/home/buildfarm/.ccache' +
        (' -v $HOME/.ssh/known_hosts:/etc/ssh/ssh_known_hosts:ro' +
         ' -v $SSH_AUTH_SOCK:/tmp/ssh_auth_sock' +
         ' -e SSH_AUTH_SOCK=/tmp/ssh_auth_sock' if git_ssh_credential_id else '') +
        ' devel_task_generation.%s_%s' % (rosdistro_name, source_repo_spec.name.lower()),
        'cd -',  # restore pwd when used in scripts
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_build_and_install/docker.cid > $WORKSPACE/docker_build_and_install/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - build and install"',
        '# build and run build_and_install Dockerfile',
        'cd $WORKSPACE/docker_build_and_install',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t devel_build_and_install.%s_%s .' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - build and install"',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_build_and_install/docker.cid' +
        ' -e=TRAVIS=$TRAVIS' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace' +
        ' devel_build_and_install.%s_%s' % (rosdistro_name, source_repo_spec.name.lower()),
        'cd -',  # restore pwd when used in scripts
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_build_and_test/docker.cid > $WORKSPACE/docker_build_and_test/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - build and test"',
        '# build and run build_and_test Dockerfile',
        'cd $WORKSPACE/docker_build_and_test',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t devel_build_and_test.%s_%s .' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - build and test"',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_build_and_test/docker.cid' +
        ' -e=TRAVIS=$TRAVIS' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace' +
        ' devel_build_and_test.%s_%s' % (rosdistro_name, source_repo_spec.name.lower()),
        'cd -',  # restore pwd when used in scripts
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'if [ "$skip_cleanup" = "false" ]; then',
        'echo "# BEGIN SECTION: Clean up to save disk space on agents"',
        'rm -fr catkin_workspace/build_isolated',
        'rm -fr catkin_workspace/devel_isolated',
        'rm -fr catkin_workspace/install_isolated',
        'echo "# END SECTION"',
        'fi',
    ]),
))@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_warnings',
    unstable_threshold=0 if notify_compiler_warnings else '',
))@
@(SNIPPET(
    'publisher_xunit',
    pattern='catkin_workspace/test_results/**/*.xml',
))@
@[if not pull_request or notify_pull_requests]@
@[ if notify_maintainers]@
@(SNIPPET(
    'publisher_groovy-postbuild_maintainer-notification',
))@
@[ end if]@
@(SNIPPET(
    'publisher_mailer',
    recipients=notify_emails,
    dynamic_recipients=maintainer_emails,
    send_to_individuals=notify_committers,
))@
@[end if]@
  </publishers>
  <buildWrappers>
@[if timeout_minutes is not None]@
@(SNIPPET(
    'build-wrapper_build-timeout',
    timeout_minutes=timeout_minutes,
))@
@[end if]@
@(SNIPPET(
    'build-wrapper_timestamper',
))@
@[if git_ssh_credential_id]@
@(SNIPPET(
    'build-wrapper_ssh-agent',
    credential_ids=[git_ssh_gredential_id],
))@
@[end if]@
  </buildWrappers>
</project>

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'@
@[if disabled]
but disabled since the package is blacklisted (or not whitelisted) in the configuration file@
@[end if]@
@ </description>
  <keepDependencies>false</keepDependencies>
  <properties>
@(SNIPPET(
    'property_log-rotator',
    days_to_keep=730,
    num_to_keep=100,
))@
@[if github_url]@
@(SNIPPET(
    'property_github-project',
    project_url=github_url,
))@
@[end if]@
@[if job_priority is not None]@
@(SNIPPET(
    'property_job-priority',
    priority=job_priority,
))@
@[end if]@
@(SNIPPET(
    'property_requeue-job',
))@
@{
parameters = [
    {
        'type': 'boolean',
        'name': 'skip_cleanup',
        'description': 'Skip cleanup of catkin build artifacts as well as rosdoc index',
    },
]
if pull_request:
    parameters += [
        {
            'type': 'string',
            'name': 'sha1',
        },
    ]
}@
@(SNIPPET(
    'property_parameters-definition',
    parameters=parameters,
))@
  </properties>
@[if not pull_request]@
@(SNIPPET(
    'scm',
    repo_spec=source_repo_spec,
    path='catkin_workspace/src/%s' % source_repo_spec.name,
    git_ssh_credential_id=git_ssh_credential_id,
))@
@[else]@
@(SNIPPET(
    'scm_git',
    url=source_repo_spec.url,
    refspec='+refs/pull/*:refs/remotes/origin/pr/*',
    branch_name='${sha1}',
    relative_target_dir='catkin_workspace/src/%s' % source_repo_spec.name,
    git_ssh_credential_id=git_ssh_credential_id,
    merge_branch=source_repo_spec.version,
))@
@[end if]@
  <scmCheckoutRetryCount>2</scmCheckoutRetryCount>
  <assignedNode>@(node_label)</assignedNode>
  <canRoam>false</canRoam>
  <disabled>@('true' if disabled else 'false')</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers>
@[if not pull_request]@
@(SNIPPET(
    'trigger_poll',
    spec='H * * * *',
))@
@[end if]@
@[if pull_request]@
@(SNIPPET(
    'trigger_github-pull-request-builder',
    github_orgunit=github_orgunit,
    branch_name=source_repo_spec.version,
    job_name=job_name,
))@
@[end if]@
  </triggers>
  <concurrentBuild>true</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_system-groovy_check-free-disk-space',
))@
@(SNIPPET(
    'builder_shell_docker-info',
))@
@(SNIPPET(
    'builder_check-docker',
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
))@
@(SNIPPET(
    'builder_shell_clone-ros-buildfarm',
    ros_buildfarm_repository=ros_buildfarm_repository,
    wrapper_scripts=wrapper_scripts,
))@
@(SNIPPET(
    'builder_shell_key-files',
    script_generating_key_files=script_generating_key_files,
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'rm -fr $WORKSPACE/docker_generating_dockers',
        'mkdir -p $WORKSPACE/docker_generating_dockers',
        '',
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_generating_dockers/docker.cid > $WORKSPACE/docker_generating_dockers/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        '# generate Dockerfile, build and run it',
        '# generating the Dockerfiles for the actual devel tasks',
        'echo "# BEGIN SECTION: Generate Dockerfile - devel tasks"',
        'export TZ="%s"' % timezone,
        'export PYTHONPATH=$WORKSPACE/ros_buildfarm:$PYTHONPATH',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/devel/run_devel_job.py' +
        ' --rosdistro-index-url ' + rosdistro_index_url +
        ' ' + rosdistro_name +
        ' ' + source_build_name +
        ' ' + source_repo_spec.name +
        ' ' + os_name +
        ' ' + os_code_name +
        ' ' + arch +
        ' ' + ' '.join(repository_args) +
        ' --dockerfile-dir $WORKSPACE/docker_generating_dockers',
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - generating devel tasks"',
        'cd $WORKSPACE/docker_generating_dockers',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t devel_task_generation.%s_%s .' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - generating devel tasks"',
        'rm -fr $WORKSPACE/docker_build_and_install',
        'rm -fr $WORKSPACE/docker_build_and_test',
        'mkdir -p $WORKSPACE/docker_build_and_install',
        'mkdir -p $WORKSPACE/docker_build_and_test',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_generating_dockers/docker.cid' +
        ' -e=HOME=/home/buildfarm' +
        ' -e=TRAVIS=$TRAVIS' +
        ' -e=ROS_BUILDFARM_PULL_REQUEST_BRANCH=$ROS_BUILDFARM_PULL_REQUEST_BRANCH' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace:ro' +
        ' -v $WORKSPACE/docker_build_and_install:/tmp/docker_build_and_install' +
        ' -v $WORKSPACE/docker_build_and_test:/tmp/docker_build_and_test' +
        ' -v ~/.ccache:/home/buildfarm/.ccache' +
        (' -v $HOME/.ssh/known_hosts:/etc/ssh/ssh_known_hosts:ro' +
         ' -v $SSH_AUTH_SOCK:/tmp/ssh_auth_sock' +
         ' -e SSH_AUTH_SOCK=/tmp/ssh_auth_sock' if git_ssh_credential_id else '') +
        ' devel_task_generation.%s_%s' % (rosdistro_name, source_repo_spec.name.lower()),
        'cd -',  # restore pwd when used in scripts
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_build_and_install/docker.cid > $WORKSPACE/docker_build_and_install/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - build and install"',
        '# build and run build_and_install Dockerfile',
        'cd $WORKSPACE/docker_build_and_install',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t devel_build_and_install.%s_%s .' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - build and install"',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_build_and_install/docker.cid' +
        ' -e=TRAVIS=$TRAVIS' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace' +
        ' devel_build_and_install.%s_%s' % (rosdistro_name, source_repo_spec.name.lower()),
        'cd -',  # restore pwd when used in scripts
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_build_and_test/docker.cid > $WORKSPACE/docker_build_and_test/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - build and test"',
        '# build and run build_and_test Dockerfile',
        'cd $WORKSPACE/docker_build_and_test',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t devel_build_and_test.%s_%s .' % (rosdistro_name, source_repo_spec.name.lower()),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - build and test"',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_build_and_test/docker.cid' +
        ' -e=TRAVIS=$TRAVIS' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace' +
        ' devel_build_and_test.%s_%s' % (rosdistro_name, source_repo_spec.name.lower()),
        'cd -',  # restore pwd when used in scripts
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'if [ "$skip_cleanup" = "false" ]; then',
        'echo "# BEGIN SECTION: Clean up to save disk space on agents"',
        'rm -fr catkin_workspace/build_isolated',
        'rm -fr catkin_workspace/devel_isolated',
        'rm -fr catkin_workspace/install_isolated',
        'echo "# END SECTION"',
        'fi',
    ]),
))@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_warnings',
    unstable_threshold=0 if notify_compiler_warnings else '',
))@
@(SNIPPET(
    'publisher_xunit',
    pattern='catkin_workspace/test_results/**/*.xml',
))@
@[if not pull_request or notify_pull_requests]@
@[ if notify_maintainers]@
@(SNIPPET(
    'publisher_groovy-postbuild_maintainer-notification',
))@
@[ end if]@
@(SNIPPET(
    'publisher_mailer',
    recipients=notify_emails,
    dynamic_recipients=maintainer_emails,
    send_to_individuals=notify_committers,
))@
@[end if]@
  </publishers>
  <buildWrappers>
@[if timeout_minutes is not None]@
@(SNIPPET(
    'build-wrapper_build-timeout',
    timeout_minutes=timeout_minutes,
))@
@[end if]@
@(SNIPPET(
    'build-wrapper_timestamper',
))@
@{
credential_ids = []
if git_ssh_credential_id:
    credential_ids.append(git_ssh_credential_id)
}@
@(SNIPPET(
    'build-wrapper_ssh-agent',
    credential_ids=credential_ids,
))@
  </buildWrappers>
</project>


fixed ROS_ROOT after moving env hooks around

# generated from ros/env-hooks/10.ros.sh.em

# scrub old ROS bin dirs, to avoid accidentally finding the wrong executables
export PATH=`python -c "import os; print(os.pathsep.join([x for x in \"$PATH\".split(os.pathsep) if not any([d for d in ['cturtle', 'diamondback', 'electric', 'fuerte'] if d in x])]))"`

export ROS_DISTRO=groovy

# python function to generate ROS package path based on all workspaces
PYTHON_CODE_BUILD_ROS_PACKAGE_PATH=$(cat <<EOF
from __future__ import print_function
import os
env_name = 'CMAKE_PREFIX_PATH'
paths = [path for path in os.environ[env_name].split(os.pathsep)] if env_name in os.environ and os.environ[env_name] != '' else []
workspaces = [path for path in paths if os.path.exists(os.path.join(path, '.CATKIN_WORKSPACE'))]
paths = []
for workspace in workspaces:
    filename = os.path.join(workspace, '.CATKIN_WORKSPACE')
    data = ''
    with open(filename) as f:
        data = f.read()
    if data == '':
        paths.append(os.path.join(workspace, 'share'))
        paths.append(os.path.join(workspace, 'stacks'))
    else:
        for source_path in data.split(';'):
            paths.append(source_path)
print(os.pathsep.join(paths))
EOF
)
export ROS_PACKAGE_PATH=`python -c "$PYTHON_CODE_BUILD_ROS_PACKAGE_PATH"`

@[if BUILDSPACE]@
# env variables in buildspace
export ROS_ROOT=@(CMAKE_CURRENT_SOURCE_DIR)/../..
export ROS_ETC_DIR=@(CATKIN_BUILD_PREFIX)/@(CATKIN_GLOBAL_ETC_DESTINATION)/ros
@[else]@
# env variables in installspace
export ROS_ROOT=@(CMAKE_INSTALL_PREFIX)/@(CATKIN_GLOBAL_SHARE_DESTINATION)/ros
export ROS_ETC_DIR=@(CMAKE_INSTALL_PREFIX)/@(CATKIN_PACKAGE_ETC_DESTINATION)
@[end if]@

# generated from ros/env-hooks/10.ros.sh.em

# scrub old ROS bin dirs, to avoid accidentally finding the wrong executables
export PATH=`python -c "import os; print(os.pathsep.join([x for x in \"$PATH\".split(os.pathsep) if not any([d for d in ['cturtle', 'diamondback', 'electric', 'fuerte'] if d in x])]))"`

export ROS_DISTRO=groovy

# python function to generate ROS package path based on all workspaces
PYTHON_CODE_BUILD_ROS_PACKAGE_PATH=$(cat <<EOF
from __future__ import print_function
import os
env_name = 'CMAKE_PREFIX_PATH'
paths = [path for path in os.environ[env_name].split(os.pathsep)] if env_name in os.environ and os.environ[env_name] != '' else []
workspaces = [path for path in paths if os.path.exists(os.path.join(path, '.CATKIN_WORKSPACE'))]
paths = []
for workspace in workspaces:
    filename = os.path.join(workspace, '.CATKIN_WORKSPACE')
    data = ''
    with open(filename) as f:
        data = f.read()
    if data == '':
        paths.append(os.path.join(workspace, 'share'))
        paths.append(os.path.join(workspace, 'stacks'))
    else:
        for source_path in data.split(';'):
            paths.append(source_path)
print(os.pathsep.join(paths))
EOF
)
export ROS_PACKAGE_PATH=`python -c "$PYTHON_CODE_BUILD_ROS_PACKAGE_PATH"`

@[if BUILDSPACE]@
# env variables in buildspace
export ROS_ROOT=@(CMAKE_CURRENT_SOURCE_DIR)/../..
export ROS_ETC_DIR=@(CATKIN_BUILD_PREFIX)/@(CATKIN_GLOBAL_ETC_DESTINATION)/ros
@[else]@
# env variables in installspace
export ROS_ROOT=@(CMAKE_INSTALL_PREFIX)/@(CATKIN_PACKAGE_SHARE_DESTINATION)
export ROS_ETC_DIR=@(CMAKE_INSTALL_PREFIX)/@(CATKIN_PACKAGE_ETC_DESTINATION)
@[end if]@


more tests

more tests

describe "rest", ->

  adapter = null
  session = null

  beforeEach ->
    require('./_shared').setupRest.apply(this)
    adapter = @adapter
    session = @session

  context 'simple model with errors', ->

    beforeEach ->
      class @Post extends Ep.Model
        title: Ep.attr('string')
        category: Ep.attr('string')
      @App.Post = @Post

      @container.register 'model:post', @Post

    context 'on create', ->
      it 'handles validation errors', ->
        adapter.r['PUT:/posts/1'] = ->
          throw status: 422, responseText: JSON.stringify(errors: {title: 'title is too short'})

        session.merge @Post.create(id: "1", title: 'test')
        session.load('post', 1).then (post) ->
          expect(post.title).to.eq('test')
          post.title = ''
          session.flush().then null, ->
            expect(post.hasErrors).to.be.true
            expect(post.title).to.eq('')
            expect(post.errors.title).to.eq('title is too short')
            expect(adapter.h).to.eql(['PUT:/posts/1'])

      it 'handles payload with error properties', ->
        adapter.r['PUT:/posts/1'] = ->
          throw status: 422, responseText: JSON.stringify(post: {id: 1, title: 'test', errors: {title: 'title is too short'}})

        session.merge @Post.create(id: "1", title: 'test')
        session.load('post', 1).then (post) ->
          expect(post.title).to.eq('test')
          post.title = ''
          session.flush().then null, ->
            expect(post.hasErrors).to.be.true
            expect(post.title).to.eq('')
            expect(post.errors.title).to.eq('title is too short')
            expect(adapter.h).to.eql(['PUT:/posts/1'])

      it 'merges payload with error properties and higher rev', ->
        adapter.r['PUT:/posts/1'] = ->
          throw status: 422, responseText: JSON.stringify(post: {id: 1, title: '', category: 'new', rev: 10, errors: {title: 'title is too short'}})

        session.merge @Post.create(id: "1", title: 'test')
        session.load('post', 1).then (post) ->
          expect(post.title).to.eq('test')
          post.title = ''
          session.flush().then null, ->
            expect(post.hasErrors).to.be.true
            expect(post.title).to.eq('')
            expect(post.category).to.eq('new')
            expect(post.errors.title).to.eq('title is too short')
            expect(adapter.h).to.eql(['PUT:/posts/1'])

      it 'merges payload with error and latest client changes against latest client version', ->
        adapter.r['PUT:/posts/1'] = (url, type, hash) ->
          throw status: 422, responseText: JSON.stringify(post: {id: 1, title: 'Something', client_rev: hash.data.post.client_rev, errors: {title: 'cannot be empty'}})

        session.merge @Post.create(id: "1", title: 'test')
        session.load('post', 1).then (post) ->
          expect(post.title).to.eq('test')
          post.title = ''
          session.flush().then null, ->
            expect(post.hasErrors).to.be.true
            expect(post.title).to.eq('Something')
            expect(adapter.h).to.eql(['PUT:/posts/1'])

      it 'empty errors object should deserialize without errors', ->
        adapter.r['PUT:/posts/1'] = ->
          post: {id: 1, title: '', errors: {}}

        session.merge @Post.create(id: "1", title: 'test')
        session.load('post', 1).then (post) ->
          expect(post.title).to.eq('test')
          post.title = ''
          session.flush().then null, ->
            expect(post.hasErrors).to.be.false
            expect(post.title).to.eq('')
            expect(adapter.h).to.eql(['PUT:/posts/1'])

    context 'on create', ->
      it 'handles error', ->
        adapter.r['POST:/posts'] = ->
          throw status: 422, responseText: JSON.stringify(errors: {title: 'is lamerz'})

        post = session.create 'post', title: 'errorz'
        session.flush().then null, ->
          expect(post.errors.title).to.eq('is lamerz')

      it 'merges payload with latest client changes against latest client version', ->
        adapter.r['POST:/posts'] = (url, type, hash) ->
          throw status: 422, responseText: JSON.stringify(post: {title: 'Something', client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev, errors: {title: 'cannot be empty'}})

        post = session.create 'post', title: ''
        session.flush().then null, ->
          expect(post.title).to.eq('Something')

      it 'succeeds after retry', ->
        adapter.r['POST:/posts'] = ->
          throw status: 422, responseText: JSON.stringify(errors: {title: 'is lamerz'})

        post = session.create 'post', title: 'errorz'
        session.flush().then null, ->
          expect(post.errors.title).to.eq('is lamerz')
          adapter.r['POST:/posts'] = (url, type, hash) ->
            post: {title: 'linkbait', id: 1, client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev}
          session.title = 'linkbait'
          session.flush().then ->
            expect(post.title).to.eq('linkbait')
            expect(adapter.h).to.eql(['POST:/posts', 'POST:/posts'])

      it 'succeeds after retry when failure merged data', ->
        adapter.r['POST:/posts'] = (url, type, hash) ->
          throw status: 422, responseText: JSON.stringify(post: {title: 'Something', client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev, errors: {title: 'is lamerz'}})

        post = session.create 'post', title: 'errorz'
        session.flush().then null, ->
          expect(post.title).to.eq('Something')
          adapter.r['POST:/posts'] = (url, type, hash) ->
            post: {title: 'linkbait', id: 1, client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev}
          session.title = 'linkbait'
          session.flush().then ->
            expect(post.title).to.eq('linkbait')
            expect(adapter.h).to.eql(['POST:/posts', 'POST:/posts'])

      context 'in child session', ->

        it 'merges payload with latest client changes against latest client version', ->
          adapter.r['POST:/posts'] = (url, type, hash) ->
            throw status: 422, responseText: JSON.stringify(post: {title: 'Something', client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev, errors: {title: 'cannot be empty'}})

          session = session.newSession()
          post = session.create 'post', title: ''
          session.flush().then null, ->
            expect(post.title).to.eq('Something')

        it 'succeeds after retry', ->
          adapter.r['POST:/posts'] = ->
            throw status: 422, responseText: JSON.stringify(errors: {title: 'is lamerz'})

          session = session.newSession()
          post = session.create 'post', title: 'errorz'
          session.flush().then null, ->
            expect(post.errors.title).to.eq('is lamerz')
            adapter.r['POST:/posts'] = (url, type, hash) ->
              post: {title: 'linkbait', id: 1, client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev}
            session.title = 'linkbait'
            session.flush().then ->
              expect(post.title).to.eq('linkbait')
              expect(adapter.h).to.eql(['POST:/posts', 'POST:/posts'])

        it 'succeeds after retry when failure merged data', ->
          adapter.r['POST:/posts'] = (url, type, hash) ->
            throw status: 422, responseText: JSON.stringify(post: {title: 'Something', client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev, errors: {title: 'is lamerz'}})

          session = session.newSession()
          post = session.create 'post', title: 'errorz'
          session.flush().then null, ->
            expect(post.title).to.eq('Something')
            adapter.r['POST:/posts'] = (url, type, hash) ->
              post: {title: 'linkbait', id: 1, client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev}
            session.title = 'linkbait'
            session.flush().then ->
              expect(post.title).to.eq('linkbait')
              expect(adapter.h).to.eql(['POST:/posts', 'POST:/posts'])

    context 'on load', ->
      [401, 403, 404].forEach (errorCode) ->

        it "handles #{errorCode}", ->
          adapter.r['GET:/posts/1'] = ->
            throw status: errorCode

          session.load('post', 1).then null, (post) ->
            expect(post.hasErrors).to.eq.true
            expect(post.errors.status).to.eq(errorCode)
            expect(adapter.h).to.eql(['GET:/posts/1'])

describe "rest", ->

  adapter = null
  session = null

  beforeEach ->
    require('./_shared').setupRest.apply(this)
    adapter = @adapter
    session = @session

  context 'simple model with errors', ->

    beforeEach ->
      class @Post extends Ep.Model
        title: Ep.attr('string')
        category: Ep.attr('string')
      @App.Post = @Post

      @container.register 'model:post', @Post

    context 'on create', ->
      it 'handles validation errors', ->
        adapter.r['PUT:/posts/1'] = ->
          throw status: 422, responseText: JSON.stringify(errors: {title: 'title is too short'})

        session.merge @Post.create(id: "1", title: 'test')
        session.load('post', 1).then (post) ->
          expect(post.title).to.eq('test')
          post.title = ''
          session.flush().then null, ->
            expect(post.hasErrors).to.be.true
            expect(post.title).to.eq('')
            expect(post.errors.title).to.eq('title is too short')
            expect(adapter.h).to.eql(['PUT:/posts/1'])

      it 'handles payload with error properties', ->
        adapter.r['PUT:/posts/1'] = ->
          throw status: 422, responseText: JSON.stringify(post: {id: 1, title: 'test', errors: {title: 'title is too short'}})

        session.merge @Post.create(id: "1", title: 'test')
        session.load('post', 1).then (post) ->
          expect(post.title).to.eq('test')
          post.title = ''
          session.flush().then null, ->
            expect(post.hasErrors).to.be.true
            expect(post.title).to.eq('')
            expect(post.errors.title).to.eq('title is too short')
            expect(adapter.h).to.eql(['PUT:/posts/1'])

      it 'merges payload with error properties and higher rev', ->
        adapter.r['PUT:/posts/1'] = ->
          throw status: 422, responseText: JSON.stringify(post: {id: 1, title: '', category: 'new', rev: 10, errors: {title: 'title is too short'}})

        session.merge @Post.create(id: "1", title: 'test')
        session.load('post', 1).then (post) ->
          expect(post.title).to.eq('test')
          post.title = ''
          session.flush().then null, ->
            expect(post.hasErrors).to.be.true
            expect(post.title).to.eq('')
            expect(post.category).to.eq('new')
            expect(post.errors.title).to.eq('title is too short')
            expect(adapter.h).to.eql(['PUT:/posts/1'])

      it 'merges payload with error and latest client changes against latest client version', ->
        adapter.r['PUT:/posts/1'] = (url, type, hash) ->
          throw status: 422, responseText: JSON.stringify(post: {id: 1, title: 'Something', client_rev: hash.data.post.client_rev, errors: {title: 'cannot be empty'}})

        session.merge @Post.create(id: "1", title: 'test')
        session.load('post', 1).then (post) ->
          expect(post.title).to.eq('test')
          post.title = ''
          session.flush().then null, ->
            expect(post.hasErrors).to.be.true
            expect(post.title).to.eq('Something')
            expect(adapter.h).to.eql(['PUT:/posts/1'])

      it 'empty errors object should deserialize without errors', ->
        adapter.r['PUT:/posts/1'] = ->
          post: {id: 1, title: '', errors: {}}

        session.merge @Post.create(id: "1", title: 'test')
        session.load('post', 1).then (post) ->
          expect(post.title).to.eq('test')
          post.title = ''
          session.flush().then null, ->
            expect(post.hasErrors).to.be.false
            expect(post.title).to.eq('')
            expect(adapter.h).to.eql(['PUT:/posts/1'])

    context 'on create', ->
      it 'handles error', ->
        adapter.r['POST:/posts'] = ->
          throw status: 422, responseText: JSON.stringify(errors: {title: 'is lamerz'})

        post = session.create 'post', title: 'errorz'
        session.flush().then null, ->
          expect(post.errors.title).to.eq('is lamerz')

      it 'merges payload with latest client changes against latest client version', ->
        adapter.r['POST:/posts'] = (url, type, hash) ->
          throw status: 422, responseText: JSON.stringify(post: {title: 'Something', client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev, errors: {title: 'cannot be empty'}})

        post = session.create 'post', title: ''
        session.flush().then null, ->
          expect(post.title).to.eq('Something')

      it 'succeeds after retry', ->
        adapter.r['POST:/posts'] = ->
          throw status: 422, responseText: JSON.stringify(errors: {title: 'is lamerz'})

        post = session.create 'post', title: 'errorz'
        session.flush().then null, ->
          expect(post.errors.title).to.eq('is lamerz')
          adapter.r['POST:/posts'] = (url, type, hash) ->
            post: {title: 'linkbait', id: 1, client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev}
          session.title = 'linkbait'
          session.flush().then ->
            expect(post.title).to.eq('linkbait')
            expect(adapter.h).to.eql(['POST:/posts', 'POST:/posts'])

      it 'succeeds after retry when failure merged data', ->
        adapter.r['POST:/posts'] = (url, type, hash) ->
          throw status: 422, responseText: JSON.stringify(post: {title: 'Something', client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev, errors: {title: 'is lamerz'}})

        post = session.create 'post', title: 'errorz'
        session.flush().then null, ->
          expect(post.title).to.eq('Something')
          adapter.r['POST:/posts'] = (url, type, hash) ->
            post: {title: 'linkbait', id: 1, client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev}
          session.title = 'linkbait'
          session.flush().then ->
            #expect(post.title).to.eq('linkbait')
            expect(adapter.h).to.eql(['POST:/posts', 'POST:/posts'])

      context 'in child session', ->

        it 'merges payload with latest client changes against latest client version', ->
          adapter.r['POST:/posts'] = (url, type, hash) ->
            throw status: 422, responseText: JSON.stringify(post: {title: 'Something', client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev, errors: {title: 'cannot be empty'}})

          session = session.newSession()
          post = session.create 'post', title: ''
          session.flush().then null, ->
            expect(post.title).to.eq('Something')

        it 'succeeds after retry', ->
          adapter.r['POST:/posts'] = ->
            throw status: 422, responseText: JSON.stringify(errors: {title: 'is lamerz'})

          session = session.newSession()
          post = session.create 'post', title: 'errorz'
          session.flush().then null, ->
            expect(post.errors.title).to.eq('is lamerz')
            adapter.r['POST:/posts'] = (url, type, hash) ->
              post: {title: 'linkbait', id: 1, client_id: hash.data.post.client_id, client_rev: hash.data.post.client_rev}
            session.title = 'linkbait'
            session.flush().then ->
              expect(post.title).to.eq('linkbait')
              expect(adapter.h).to.eql(['POST:/posts', 'POST:/posts'])

    context 'on load', ->
      [401, 403, 404].forEach (errorCode) ->

        it "handles #{errorCode}", ->
          adapter.r['GET:/posts/1'] = ->
            throw status: errorCode

          session.load('post', 1).then null, (post) ->
            expect(post.hasErrors).to.eq.true
            expect(post.errors.status).to.eq(errorCode)
            expect(adapter.h).to.eql(['GET:/posts/1'])


set ros_version in legacy platform.yaml.em template

Signed-off-by: Mikael Arguedas <a16f295d1139a1392bb19262902c32cf3e999ce4@gmail.com>

%YAML 1.1
# ROS Dockerfile database
---
platform:
    os_name: $os_name
    os_code_name: $os_code_name
    rosdistro_name: $release_name
    user_name: ros
    maintainer_name:
    arch: amd64
    type: distribution
    version: False
    release: ros
    ros_version: 1


prevent user to set None to msg fields (#169)

# generated from rosidl_generator_py/resource/_msg.py.em
# generated code does not contain a copyright notice

import logging
import traceback

class Metaclass(type):
    """Metaclass of message '@(spec.base_type.type)'."""

    _CONVERT_FROM_PY = None
    _CONVERT_TO_PY = None
    _TYPE_SUPPORT = None

    __constants = {
@[for constant in spec.constants]@
        '@(constant.name)': @constant_value_to_py(constant.type, constant.value),
@[end for]@
    }

    @@classmethod
    def __import_type_support__(cls):
        __type_support_importable = False
        try:
            import rclpy
            from rosidl_generator_py import import_type_support
            __type_support_importable = True
        except ImportError:
            logger = logging.getLogger('rosidl_generator_py.@(spec.base_type.type)')
            logger.debug(
                'Failed to import needed modules for type support:\n' + traceback.format_exc())

        if __type_support_importable:
            rclpy_implementation = rclpy._rclpy.rclpy_get_rmw_implementation_identifier()
            module = import_type_support(
                '@(package_name)', rclpy_implementation)
            cls._CONVERT_FROM_PY = module.convert_from_py_@(module_name)
            cls._CONVERT_TO_PY = module.convert_to_py_@(module_name)
            cls._TYPE_SUPPORT = module.type_support_@(module_name)
@{
importable_typesupports = {}
for field in spec.fields:
    if not field.type.is_primitive_type():
        key = '%s.msg.%s' % (field.type.pkg_name, field.type.type)
        if key not in importable_typesupports:
            importable_typesupports[key] = [field.type.pkg_name, field.type.type]
for key in sorted(importable_typesupports.keys()):
    (pkg_name, field_name) = importable_typesupports[key]
    print('%sfrom %s.msg import %s' % (' ' * 4 * 3, pkg_name, field_name))
    print('%sif %s.__class__._TYPE_SUPPORT is None:' % (' ' * 4 * 3, field_name))
    print('%s%s.__class__.__import_type_support__()' % (' ' * 4 * 4, field_name))
}@

    @@classmethod
    def __prepare__(cls, name, bases, **kwargs):
        # list constant names here so that they appear in the help text of
        # the message class under "Data and other attributes defined here:"
        # as well as populate each message instance
        return {
@[for constant in spec.constants]@
            '@(constant.name)': cls.__constants['@(constant.name)'],
@[end for]@
@[for field in spec.fields]@
@[  if field.default_value]@
            '@(field.name.upper())__DEFAULT': @value_to_py(field.type, field.default_value),
@[  end if]@
@[end for]@
        }
@[for constant in spec.constants]@

    @@property
    def @(constant.name)(self):
        """Message constant '@(constant.name)'."""
        return Metaclass.__constants['@(constant.name)']
@[end for]@
@[for field in spec.fields]@
@[  if field.default_value]@

    @@property
    def @(field.name.upper())__DEFAULT(cls):
        """Default value for message field '@(field.name)'."""
        return @value_to_py(field.type, field.default_value)
@[  end if]@
@[end for]@

class @(spec.base_type.type)(metaclass=Metaclass):
@[if not spec.constants]@
    """Message class '@(spec.base_type.type)'."""
@[else]@
    """
    Message class '@(spec.base_type.type)'.

    Constants:
@[  for constant in spec.constants]@
      @(constant.name)
@[  end for]@
    """
@[end if]@

    __slots__ = [
@[for field in spec.fields]@
        '_@(field.name)',
@[end for]@
    ]
@
@[if len(spec.fields) > 0]@

    def __init__(self, **kwargs):
        assert all(['_' + key in self.__slots__ for key in kwargs.keys()]), \
            "Invalid arguments passed to constructor: %r" % kwargs.keys()
@[  for field in spec.fields]@
@[    if field.default_value]@
        self.@(field.name) = kwargs.get(
            '@(field.name)', @(spec.base_type.type).@(field.name.upper())__DEFAULT)
@[    else]@
@[      if not field.type.is_primitive_type() and (not field.type.is_array or field.type.array_size)]@
        from @(field.type.pkg_name).msg import @(field.type.type)
@[      end if]@
@[      if field.type.array_size and not field.type.is_upper_bound]@
@[        if field.type.type == 'byte']@
        self.@(field.name) = kwargs.get(
            '@(field.name)',
            list([bytes([0]) for x in range(@(field.type.array_size))])
        )
@[        elif field.type.type == 'char']@
        self.@(field.name) = kwargs.get(
            '@(field.name)',
            list([chr(0) for x in range(@(field.type.array_size))])
        )
@[        else]@
        self.@(field.name) = kwargs.get(
            '@(field.name)',
            list([@(get_python_type(field.type))() for x in range(@(field.type.array_size))])
        )
@[        end if]@
@[      elif field.type.is_array]@
        self.@(field.name) = kwargs.get('@(field.name)', list())
@[      elif field.type.type == 'byte']@
        self.@(field.name) = kwargs.get('@(field.name)', bytes([0]))
@[      elif field.type.type == 'char']@
        self.@(field.name) = kwargs.get('@(field.name)', chr(0))
@[      else]@
        self.@(field.name) = kwargs.get('@(field.name)', @(get_python_type(field.type))())
@[      end if]@
@[    end if]@
@[  end for]@
@[end if]@

    def __repr__(self):
        typename = self.__class__.__module__.split('.')
        typename.pop()
        typename.append(self.__class__.__name__)
        args = [s[1:] + '=' + repr(getattr(self, s, None)) for s in self.__slots__]
        return '%s(%s)' % ('.'.join(typename), ', '.join(args))
@[for field in spec.fields]@

    @@property
    def @(field.name)(self):
        """Message field '@(field.name)'."""
        return self._@(field.name)

    @@@(field.name).setter
    def @(field.name)(self, value):
@[  if not field.type.is_primitive_type()]@
        from @(field.type.pkg_name).msg import @(field.type.type)
@[  end if]@
@[  if field.type.is_array]@
        from collections import Sequence
        from collections import Set
        from collections import UserList
        from collections import UserString
@[  elif field.type.string_upper_bound]@
        from collections import UserString
@[  elif field.type.type == 'byte']@
        from collections import ByteString
@[  elif field.type.type in ['char']]@
        from collections import UserString
@[  end if]@
        assert \
@[  if field.type.is_array]@
            ((isinstance(value, Sequence) or
              isinstance(value, Set) or
              isinstance(value, UserList)) and
             not isinstance(value, str) and
             not isinstance(value, UserString) and
@[    if field.type.array_size]@
@[      if field.type.is_upper_bound]@
             len(value) <= @(field.type.array_size) and
@[      else]@
             len(value) == @(field.type.array_size) and
@[      end if]@
@[    end if]@
             all([isinstance(v, @(get_python_type(field.type))) for v in value]))
@[  elif field.type.string_upper_bound]@
            ((isinstance(value, str) or isinstance(value, UserString)) and
             len(value) <= @(field.type.string_upper_bound))
@[  elif not field.type.is_primitive_type()]@
            isinstance(value, @(field.type.type))
@[  elif field.type.type == 'byte']@
            ((isinstance(value, bytes) or isinstance(value, ByteString)) and
             len(value) == 1)
@[  elif field.type.type == 'char']@
            ((isinstance(value, str) or isinstance(value, UserString)) and
             len(value) == 1)
@[  elif field.type.type in [
        'bool',
        'float32', 'float64',
        'int8', 'uint8',
        'int16', 'uint16',
        'int32', 'uint32',
        'int64', 'uint64',
        'string',
    ]]@
            isinstance(value, @(get_python_type(field.type)))
@[  else]@
            False
@[  end if]@
        self._@(field.name) = value
@[end for]@

# generated from rosidl_generator_py/resource/_msg.py.em
# generated code does not contain a copyright notice

import logging
import traceback

class Metaclass(type):
    """Metaclass of message '@(spec.base_type.type)'."""

    _CONVERT_FROM_PY = None
    _CONVERT_TO_PY = None
    _TYPE_SUPPORT = None

    __constants = {
@[for constant in spec.constants]@
        '@(constant.name)': @constant_value_to_py(constant.type, constant.value),
@[end for]@
    }

    @@classmethod
    def __import_type_support__(cls):
        __type_support_importable = False
        try:
            import rclpy
            from rosidl_generator_py import import_type_support
            __type_support_importable = True
        except ImportError:
            logger = logging.getLogger('rosidl_generator_py.@(spec.base_type.type)')
            logger.debug(
                'Failed to import needed modules for type support:\n' + traceback.format_exc())

        if __type_support_importable:
            rclpy_implementation = rclpy._rclpy.rclpy_get_rmw_implementation_identifier()
            module = import_type_support(
                '@(package_name)', rclpy_implementation)
            cls._CONVERT_FROM_PY = module.convert_from_py_@(module_name)
            cls._CONVERT_TO_PY = module.convert_to_py_@(module_name)
            cls._TYPE_SUPPORT = module.type_support_@(module_name)
@{
importable_typesupports = {}
for field in spec.fields:
    if not field.type.is_primitive_type():
        key = '%s.msg.%s' % (field.type.pkg_name, field.type.type)
        if key not in importable_typesupports:
            importable_typesupports[key] = [field.type.pkg_name, field.type.type]
for key in sorted(importable_typesupports.keys()):
    (pkg_name, field_name) = importable_typesupports[key]
    print('%sfrom %s.msg import %s' % (' ' * 4 * 3, pkg_name, field_name))
    print('%sif %s.__class__._TYPE_SUPPORT is None:' % (' ' * 4 * 3, field_name))
    print('%s%s.__class__.__import_type_support__()' % (' ' * 4 * 4, field_name))
}@

    @@classmethod
    def __prepare__(cls, name, bases, **kwargs):
        # list constant names here so that they appear in the help text of
        # the message class under "Data and other attributes defined here:"
        # as well as populate each message instance
        return {
@[for constant in spec.constants]@
            '@(constant.name)': cls.__constants['@(constant.name)'],
@[end for]@
@[for field in spec.fields]@
@[  if field.default_value]@
            '@(field.name.upper())__DEFAULT': @value_to_py(field.type, field.default_value),
@[  end if]@
@[end for]@
        }
@[for constant in spec.constants]@

    @@property
    def @(constant.name)(self):
        """Message constant '@(constant.name)'."""
        return Metaclass.__constants['@(constant.name)']
@[end for]@
@[for field in spec.fields]@
@[  if field.default_value]@

    @@property
    def @(field.name.upper())__DEFAULT(cls):
        """Default value for message field '@(field.name)'."""
        return @value_to_py(field.type, field.default_value)
@[  end if]@
@[end for]@

class @(spec.base_type.type)(metaclass=Metaclass):
@[if not spec.constants]@
    """Message class '@(spec.base_type.type)'."""
@[else]@
    """
    Message class '@(spec.base_type.type)'.

    Constants:
@[  for constant in spec.constants]@
      @(constant.name)
@[  end for]@
    """
@[end if]@

    __slots__ = [
@[for field in spec.fields]@
        '_@(field.name)',
@[end for]@
    ]
@
@[if len(spec.fields) > 0]@

    def __init__(self, **kwargs):
        assert all(['_' + key in self.__slots__ for key in kwargs.keys()]), \
            "Invalid arguments passed to constructor: %r" % kwargs.keys()
@[  for field in spec.fields]@
@[    if field.default_value]@
        self.@(field.name) = kwargs.get(
            '@(field.name)', @(spec.base_type.type).@(field.name.upper())__DEFAULT)
@[    else]@
@[      if not field.type.is_primitive_type() and (not field.type.is_array or field.type.array_size)]@
        from @(field.type.pkg_name).msg import @(field.type.type)
@[      end if]@
@[      if field.type.array_size and not field.type.is_upper_bound]@
@[        if field.type.type == 'byte']@
        self.@(field.name) = kwargs.get(
            '@(field.name)',
            list([bytes([0]) for x in range(@(field.type.array_size))])
        )
@[        elif field.type.type == 'char']@
        self.@(field.name) = kwargs.get(
            '@(field.name)',
            list([chr(0) for x in range(@(field.type.array_size))])
        )
@[        else]@
        self.@(field.name) = kwargs.get(
            '@(field.name)',
            list([@(get_python_type(field.type))() for x in range(@(field.type.array_size))])
        )
@[        end if]@
@[      elif field.type.is_array]@
        self.@(field.name) = kwargs.get('@(field.name)', list())
@[      elif field.type.type == 'byte']@
        self.@(field.name) = kwargs.get('@(field.name)', bytes([0]))
@[      elif field.type.type == 'char']@
        self.@(field.name) = kwargs.get('@(field.name)', chr(0))
@[      else]@
        self.@(field.name) = kwargs.get('@(field.name)', @(get_python_type(field.type))())
@[      end if]@
@[    end if]@
@[  end for]@
@[end if]@

    def __repr__(self):
        typename = self.__class__.__module__.split('.')
        typename.pop()
        typename.append(self.__class__.__name__)
        args = [s[1:] + '=' + repr(getattr(self, s, None)) for s in self.__slots__]
        return '%s(%s)' % ('.'.join(typename), ', '.join(args))
@[for field in spec.fields]@

    @@property
    def @(field.name)(self):
        """Message field '@(field.name)'."""
        return self._@(field.name)

    @@@(field.name).setter
    def @(field.name)(self, value):
@[  if not field.type.is_primitive_type()]@
        from @(field.type.pkg_name).msg import @(field.type.type)
@[  end if]@
@[  if field.type.is_array]@
        from collections import Sequence
        from collections import Set
        from collections import UserList
        from collections import UserString
@[  elif field.type.string_upper_bound]@
        from collections import UserString
@[  elif field.type.type == 'byte']@
        from collections import ByteString
@[  elif field.type.type in ['char']]@
        from collections import UserString
@[  end if]@
        assert isinstance(value, type(None)) or \
@[  if field.type.is_array]@
            ((isinstance(value, Sequence) or
              isinstance(value, Set) or
              isinstance(value, UserList)) and
             not isinstance(value, str) and
             not isinstance(value, UserString) and
@[    if field.type.array_size]@
@[      if field.type.is_upper_bound]@
             len(value) <= @(field.type.array_size) and
@[      else]@
             len(value) == @(field.type.array_size) and
@[      end if]@
@[    end if]@
             all([isinstance(v, @(get_python_type(field.type))) for v in value]))
@[  elif field.type.string_upper_bound]@
            ((isinstance(value, str) or isinstance(value, UserString)) and
             len(value) <= @(field.type.string_upper_bound))
@[  elif not field.type.is_primitive_type()]@
            isinstance(value, @(field.type.type))
@[  elif field.type.type == 'byte']@
            ((isinstance(value, bytes) or isinstance(value, ByteString)) and
             len(value) == 1)
@[  elif field.type.type == 'char']@
            ((isinstance(value, str) or isinstance(value, UserString)) and
             len(value) == 1)
@[  elif field.type.type in [
        'bool',
        'float32', 'float64',
        'int8', 'uint8',
        'int16', 'uint16',
        'int32', 'uint32',
        'int64', 'uint64',
        'string',
    ]]@
            isinstance(value, @(get_python_type(field.type)))
@[  else]@
            False
@[  end if]@
        self._@(field.name) = value
@[end for]@


fixed some comments

fixed some comments


Update ci_launcher job description (#96)

* Update ci_launcher job description

* Mention connext won't be enabled on aarch64 ever

<?xml version='1.0' encoding='UTF-8'?>
<project>
  <actions/>
  <description>
    Launches a build of the CI job for each supported platform with the same set of parameters.
    Use of RTI Connext will always be disabled on aarch64 jobs.
  </description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <com.sonyericsson.rebuild.RebuildSettings plugin="rebuild@@1.25">
      <autoRebuild>false</autoRebuild>
      <rebuildDisabled>false</rebuildDisabled>
    </com.sonyericsson.rebuild.RebuildSettings>
    <org.jenkinsci.plugins.requeuejob.RequeueJobProperty plugin="jobrequeue@@1.1">
      <requeueJob>true</requeueJob>
    </org.jenkinsci.plugins.requeuejob.RequeueJobProperty>
@(SNIPPET(
    'property_parameter-definition',
    ci_scripts_default_branch=ci_scripts_default_branch,
    default_repos_url=default_repos_url,
    supplemental_repos_url=supplemental_repos_url,
    use_connext_default=use_connext_default,
    disable_connext_static_default=disable_connext_static_default,
    disable_connext_dynamic_default=disable_connext_dynamic_default,
    use_osrf_connext_debs_default=use_osrf_connext_debs_default,
    use_fastrtps_default=use_fastrtps_default,
    use_opensplice_default=use_opensplice_default,
    cmake_build_type=cmake_build_type,
    ament_build_args_default=ament_build_args_default,
    ament_test_args_default=ament_test_args_default,
    enable_c_coverage_default=enable_c_coverage_default,
))@
  </properties>
  <scm class="hudson.scm.NullSCM"/>
  <assignedNode>@(label_expression)</assignedNode>
  <canRoam>false</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders>
    <hudson.plugins.groovy.SystemGroovy plugin="groovy@@2.0">
      <source class="hudson.plugins.groovy.StringSystemScriptSource">
        <script plugin="script-security@@1.27">
          <script>// PREDICT TRIGGERED BUILDS AND GENERATE MARKDOWN FOR BUILD STATUS

import jenkins.model.Jenkins

def predict_build_number(job_name) {
  p = Jenkins.instance.getItemByFullName(job_name)
  if (!p) {
    return 0
  }
  build_number = p.getNextBuildNumber()
  if (p.isInQueue()) {
    // assuming the queued builds don't get cancelled
    for (item in Jenkins.instance.getQueue().getItems()) {
      if (item.task.getName() == job_name) {
        build_number += 1
      }
    }
  }
  return build_number
}

build_numbers = [:]
@[for os_name in os_specific_data.keys()]@
build_numbers["@(os_name)"] = predict_build_number("ci_@(os_name)")
@[end for]@

for (item in build_numbers) {
  name = item.key[0].toUpperCase() + item.key[1..-1].toLowerCase()
  if (name == "Osx") {
    name = "macOS"
  }
  job_name = "ci_${item.key}"
  build_number = item.value
  println "* ${name} [![Build Status](http://ci.ros2.org/buildStatus/icon?job=${job_name}&build=${build_number})](http://ci.ros2.org/job/${job_name}/${build_number}/)"
}
</script>
          <sandbox>false</sandbox>
        </script>
      </source>
    </hudson.plugins.groovy.SystemGroovy>
  </builders>
  <publishers>
@[for os_name, os_data in os_specific_data.items()]@
    <hudson.plugins.parameterizedtrigger.BuildTrigger plugin="parameterized-trigger@@2.33">
      <configs>
        <hudson.plugins.parameterizedtrigger.BuildTriggerConfig>
          <configs>
            <hudson.plugins.parameterizedtrigger.CurrentBuildParameters/>
@[if os_name == 'linux-aarch64']@
            <hudson.plugins.parameterizedtrigger.BooleanParameters>
              <configs>
                <hudson.plugins.parameterizedtrigger.BooleanParameterConfig>
                  <name>CI_USE_CONNEXT</name>
                  <value>false</value>
                </hudson.plugins.parameterizedtrigger.BooleanParameterConfig>
              </configs>
            </hudson.plugins.parameterizedtrigger.BooleanParameters>
@[end if]@
          </configs>
          <projects>@(os_data['job_name'])</projects>
          <condition>SUCCESS</condition>
          <triggerWithNoParameters>false</triggerWithNoParameters>
          <triggerFromChildProjects>false</triggerFromChildProjects>
        </hudson.plugins.parameterizedtrigger.BuildTriggerConfig>
      </configs>
    </hudson.plugins.parameterizedtrigger.BuildTrigger>
@[end for]@
  </publishers>
  <buildWrappers/>
</project>

<?xml version='1.0' encoding='UTF-8'?>
<project>
  <actions/>
  <description>
    Note: The Windows jobs ignore the middleware selection flags.
  </description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <com.sonyericsson.rebuild.RebuildSettings plugin="rebuild@@1.25">
      <autoRebuild>false</autoRebuild>
      <rebuildDisabled>false</rebuildDisabled>
    </com.sonyericsson.rebuild.RebuildSettings>
    <org.jenkinsci.plugins.requeuejob.RequeueJobProperty plugin="jobrequeue@@1.1">
      <requeueJob>true</requeueJob>
    </org.jenkinsci.plugins.requeuejob.RequeueJobProperty>
@(SNIPPET(
    'property_parameter-definition',
    ci_scripts_default_branch=ci_scripts_default_branch,
    default_repos_url=default_repos_url,
    supplemental_repos_url=supplemental_repos_url,
    use_connext_default=use_connext_default,
    disable_connext_static_default=disable_connext_static_default,
    disable_connext_dynamic_default=disable_connext_dynamic_default,
    use_osrf_connext_debs_default=use_osrf_connext_debs_default,
    use_fastrtps_default=use_fastrtps_default,
    use_opensplice_default=use_opensplice_default,
    cmake_build_type=cmake_build_type,
    ament_build_args_default=ament_build_args_default,
    ament_test_args_default=ament_test_args_default,
    enable_c_coverage_default=enable_c_coverage_default,
))@
  </properties>
  <scm class="hudson.scm.NullSCM"/>
  <assignedNode>@(label_expression)</assignedNode>
  <canRoam>false</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders>
    <hudson.plugins.groovy.SystemGroovy plugin="groovy@@2.0">
      <source class="hudson.plugins.groovy.StringSystemScriptSource">
        <script plugin="script-security@@1.27">
          <script>// PREDICT TRIGGERED BUILDS AND GENERATE MARKDOWN FOR BUILD STATUS

import jenkins.model.Jenkins

def predict_build_number(job_name) {
  p = Jenkins.instance.getItemByFullName(job_name)
  if (!p) {
    return 0
  }
  build_number = p.getNextBuildNumber()
  if (p.isInQueue()) {
    // assuming the queued builds don't get cancelled
    for (item in Jenkins.instance.getQueue().getItems()) {
      if (item.task.getName() == job_name) {
        build_number += 1
      }
    }
  }
  return build_number
}

build_numbers = [:]
@[for os_name in os_specific_data.keys()]@
build_numbers["@(os_name)"] = predict_build_number("ci_@(os_name)")
@[end for]@

for (item in build_numbers) {
  name = item.key[0].toUpperCase() + item.key[1..-1].toLowerCase()
  if (name == "Osx") {
    name = "macOS"
  }
  job_name = "ci_${item.key}"
  build_number = item.value
  println "* ${name} [![Build Status](http://ci.ros2.org/buildStatus/icon?job=${job_name}&build=${build_number})](http://ci.ros2.org/job/${job_name}/${build_number}/)"
}
</script>
          <sandbox>false</sandbox>
        </script>
      </source>
    </hudson.plugins.groovy.SystemGroovy>
  </builders>
  <publishers>
@[for os_name, os_data in os_specific_data.items()]@
    <hudson.plugins.parameterizedtrigger.BuildTrigger plugin="parameterized-trigger@@2.33">
      <configs>
        <hudson.plugins.parameterizedtrigger.BuildTriggerConfig>
          <configs>
            <hudson.plugins.parameterizedtrigger.CurrentBuildParameters/>
@[if os_name == 'linux-aarch64']@
            <hudson.plugins.parameterizedtrigger.BooleanParameters>
              <configs>
                <hudson.plugins.parameterizedtrigger.BooleanParameterConfig>
                  <name>CI_USE_CONNEXT</name>
                  <value>false</value>
                </hudson.plugins.parameterizedtrigger.BooleanParameterConfig>
              </configs>
            </hudson.plugins.parameterizedtrigger.BooleanParameters>
@[end if]@
          </configs>
          <projects>@(os_data['job_name'])</projects>
          <condition>SUCCESS</condition>
          <triggerWithNoParameters>false</triggerWithNoParameters>
          <triggerFromChildProjects>false</triggerFromChildProjects>
        </hudson.plugins.parameterizedtrigger.BuildTriggerConfig>
      </configs>
    </hudson.plugins.parameterizedtrigger.BuildTrigger>
@[end for]@
  </publishers>
  <buildWrappers/>
</project>


use higher prio for import_upstream job

use higher prio for import_upstream job

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'</description>
  <keepDependencies>false</keepDependencies>
  <properties>
@(SNIPPET(
    'property_log-rotator',
    days_to_keep=100,
    num_to_keep=100,
))@
@(SNIPPET(
    'property_job-priority',
    priority=20,
))@
@(SNIPPET(
    'property_requeue-job',
))@
@(SNIPPET(
    'property_parameters-definition',
    parameters=[
        {
            'type': 'string',
            'name': 'config_file',
            'description': 'A specific yaml file or files to use as reprepro_config arguments. The default is the ros_bootstrap config. If unset it defaults to aggregating all yaml files in the directory defined by upstream_config in reprepro-updater.ini(not recommended)',
            'default_value': '/home/jenkins-slave/reprepro_config/ros_bootstrap.yaml',
        },
        {
            'type': 'boolean',
            'name': 'EXECUTE_IMPORT',
            'description': 'If this is not true, it will only do a dry run and print the expect import, but not execute.',
            'default_value': 'false',
        },
    ],
))@
  </properties>
@(SNIPPET(
    'scm_git',
    url='https://github.com/ros-infrastructure/reprepro-updater.git',
    branch_name='refactor',
    relative_target_dir='reprepro-updater',
    refspec=None,
))@
  <scmCheckoutRetryCount>2</scmCheckoutRetryCount>
  <assignedNode>building_repository</assignedNode>
  <canRoam>false</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'echo "# BEGIN SECTION: import debian packages"',
        'if [ "$EXECUTE_IMPORT" = "true" ]; then',
        '  export COMMIT_ARG="--commit"',
        'fi',
        'export PYTHONPATH=$WORKSPACE/reprepro-updater/src:$PYTHONPATH',
        'echo "# BEGIN SUBSECTION: import debian packages for ubuntu_building"',
        'python -u $WORKSPACE/reprepro-updater/scripts/import_upstream.py ubuntu_building $config_file $COMMIT_ARG',
        'echo "# END SUBSECTION"',
        'echo "# BEGIN SUBSECTION: import debian packages for ubuntu_testing"',
        'python -u $WORKSPACE/reprepro-updater/scripts/import_upstream.py ubuntu_testing $config_file $COMMIT_ARG',
        'echo "# END SUBSECTION"',
        'echo "# BEGIN SUBSECTION: import debian packages for ubuntu_main"',
        'python -u $WORKSPACE/reprepro-updater/scripts/import_upstream.py ubuntu_main $config_file $COMMIT_ARG',
        'echo "# END SUBSECTION"',
        'echo "# END SECTION"',
    ]),
))@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_mailer',
    recipients=recipients,
    dynamic_recipients=[],
    send_to_individuals=False,
))@
  </publishers>
  <buildWrappers>
@(SNIPPET(
    'build-wrapper_timestamper',
))@
  </buildWrappers>
</project>

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'</description>
  <keepDependencies>false</keepDependencies>
  <properties>
@(SNIPPET(
    'property_log-rotator',
    days_to_keep=100,
    num_to_keep=100,
))@
@(SNIPPET(
    'property_job-priority',
    priority=-1,
))@
@(SNIPPET(
    'property_requeue-job',
))@
@(SNIPPET(
    'property_parameters-definition',
    parameters=[
        {
            'type': 'string',
            'name': 'config_file',
            'description': 'A specific yaml file or files to use as reprepro_config arguments. The default is the ros_bootstrap config. If unset it defaults to aggregating all yaml files in the directory defined by upstream_config in reprepro-updater.ini(not recommended)',
            'default_value': '/home/jenkins-slave/reprepro_config/ros_bootstrap.yaml',
        },
        {
            'type': 'boolean',
            'name': 'EXECUTE_IMPORT',
            'description': 'If this is not true, it will only do a dry run and print the expect import, but not execute.',
            'default_value': 'false',
        },
    ],
))@
  </properties>
@(SNIPPET(
    'scm_git',
    url='https://github.com/ros-infrastructure/reprepro-updater.git',
    branch_name='refactor',
    relative_target_dir='reprepro-updater',
    refspec=None,
))@
  <scmCheckoutRetryCount>2</scmCheckoutRetryCount>
  <assignedNode>building_repository</assignedNode>
  <canRoam>false</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'echo "# BEGIN SECTION: import debian packages"',
        'if [ "$EXECUTE_IMPORT" = "true" ]; then',
        '  export COMMIT_ARG="--commit"',
        'fi',
        'export PYTHONPATH=$WORKSPACE/reprepro-updater/src:$PYTHONPATH',
        'echo "# BEGIN SUBSECTION: import debian packages for ubuntu_building"',
        'python -u $WORKSPACE/reprepro-updater/scripts/import_upstream.py ubuntu_building $config_file $COMMIT_ARG',
        'echo "# END SUBSECTION"',
        'echo "# BEGIN SUBSECTION: import debian packages for ubuntu_testing"',
        'python -u $WORKSPACE/reprepro-updater/scripts/import_upstream.py ubuntu_testing $config_file $COMMIT_ARG',
        'echo "# END SUBSECTION"',
        'echo "# BEGIN SUBSECTION: import debian packages for ubuntu_main"',
        'python -u $WORKSPACE/reprepro-updater/scripts/import_upstream.py ubuntu_main $config_file $COMMIT_ARG',
        'echo "# END SUBSECTION"',
        'echo "# END SECTION"',
    ]),
))@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_mailer',
    recipients=recipients,
    dynamic_recipients=[],
    send_to_individuals=False,
))@
  </publishers>
  <buildWrappers>
@(SNIPPET(
    'build-wrapper_timestamper',
))@
  </buildWrappers>
</project>