Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change auth method in releasepy: remove shared token and use Jenkins API tokens #1201

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
10 changes: 6 additions & 4 deletions check_releasepy.bash
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/bin/bash -e

export _RELEASEPY_DEBUG=1
export _RELEASEPY_TEST_CREDENTIALS=1

test_dir=$(mktemp -d)
export _RELEASEPY_TEST_RELEASE_REPO="${test_dir}/test-release"
mkdir -p ${_RELEASEPY_TEST_RELEASE_REPO}/{focal,jammy,ubuntu}/debian
Expand All @@ -25,7 +27,7 @@ exec_releasepy_test()
./release.py \
--dry-run \
--no-sanity-checks \
gz-foo 1.2.3 token ${test_params}
gz-foo 1.2.3 ${test_params}
}

exec_ignition_releasepy_test()
Expand All @@ -35,7 +37,7 @@ exec_ignition_releasepy_test()
./release.py \
--dry-run \
--no-sanity-checks \
ign-foo 1.2.3 token ${test_params}
ign-foo 1.2.3 ${test_params}
}

exec_ignition_gazebo_releasepy_test()
Expand All @@ -45,7 +47,7 @@ exec_ignition_gazebo_releasepy_test()
./release.py \
--dry-run \
--no-sanity-checks \
ign-gazebo 1.2.3 token ${test_params}
ign-gazebo 1.2.3 ${test_params}
}

exec_releasepy_with_real_gz()
Expand All @@ -56,7 +58,7 @@ exec_releasepy_with_real_gz()
--no-sanity-checks \
--source-repo-uri http://github.com/gazebosim/gz-common \
--source-repo-existing-ref http://github.com/gazebosim/gz-common/foo-tag \
"${gz_pkg}" "${major_version}.x.y" token
"${gz_pkg}" "${major_version}.x.y"
}

expect_job_run()
Expand Down
34 changes: 0 additions & 34 deletions jenkins-scripts/dsl/_configs_/GenericRemoteToken.groovy

This file was deleted.

2 changes: 0 additions & 2 deletions jenkins-scripts/dsl/_configs_/OSRFLinuxBackportPkg.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import javaposse.jobdsl.dsl.Job

/*
-> OSRFLinuxBase
-> GenericRemoteToken

Implements:
- priorioty 300
Expand All @@ -24,7 +23,6 @@ class OSRFLinuxBackportPkg
static void create(Job job)
{
OSRFLinuxBase.create(job)
GenericRemoteToken.create(job)

job.with
{
Expand Down
2 changes: 0 additions & 2 deletions jenkins-scripts/dsl/_configs_/OSRFLinuxBuildPkg.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import _configs_.Globals

/*
-> OSRFLinuxBuildPkgBase
-> GenericRemoteToken

Implements:
- priority 100
Expand All @@ -28,7 +27,6 @@ class OSRFLinuxBuildPkg
static void create(Job job, Map default_params = [:])
{
OSRFLinuxBuildPkgBase.create(job)
GenericRemoteToken.create(job)

job.with
{
Expand Down
1 change: 0 additions & 1 deletion jenkins-scripts/dsl/_configs_/OSRFSourceCreation.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ class OSRFSourceCreation
static void create(Job job, Map default_params = [:], Map default_hidden_params = [:])
{
OSRFLinuxBuildPkgBase.create(job)
GenericRemoteToken.create(job)
OSRFSourceCreation.addParameters(job, default_params)

def pkg_sources_dir="pkgs"
Expand Down
4 changes: 0 additions & 4 deletions jenkins-scripts/dsl/brew_release.dsl
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ void include_common_params(Job job)
// 1. BREW pull request SHA updater
def release_job = job("generic-release-homebrew_pull_request_updater")
OSRFUNIXBase.create(release_job)
GenericRemoteToken.create(release_job)

include_common_params(release_job)
release_job.with
Expand Down Expand Up @@ -127,8 +126,6 @@ OSRFBrewCompilationAnyGitHub.create(bottle_job_builder,
DISABLE_TESTS,
NO_SUPPORTED_BRANCHES,
DISABLE_GITHUB_INTEGRATION)
GenericRemoteToken.create(bottle_job_builder)

bottle_job_builder.with
{
wrappers {
Expand Down Expand Up @@ -243,7 +240,6 @@ bottle_job_builder.with
// 4. BREW bottle hash update
def bottle_job_hash_updater = job(bottle_hash_updater_job_name)
OSRFUNIXBase.create(bottle_job_hash_updater)
GenericRemoteToken.create(bottle_job_hash_updater)

include_common_params(bottle_job_hash_updater)
bottle_job_hash_updater.with
Expand Down
1 change: 0 additions & 1 deletion jenkins-scripts/dsl/gazebo_ros_pkgs.dsl
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,6 @@ bloom_debbuild_jobs.each { bloom_pkg ->

// Use the linux install as base
OSRFLinuxBuildPkgBase.create(build_pkg_job)
GenericRemoteToken.create(build_pkg_job)

build_pkg_job.with
{
Expand Down
1 change: 0 additions & 1 deletion jenkins-scripts/dsl/ros_gz_bridge.dsl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ bridge_packages.each { pkg ->

// Use the linux install as base
OSRFLinuxBuildPkgBase.create(build_pkg_job)
GenericRemoteToken.create(build_pkg_job)

build_pkg_job.with
{
Expand Down
98 changes: 88 additions & 10 deletions release.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,25 @@

from __future__ import print_function
from argparse import RawTextHelpFormatter
from configparser import ConfigParser
from typing import Tuple
from urllib3.exceptions import RequestError
from urllib3.util import make_headers
import subprocess
import sys
import tempfile
import os
import urllib.parse
import urllib.request
import urllib3
Copy link
Contributor

@scpeters scpeters Nov 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be helpful to document somewhere the python dependencies needed to run release.py. I am using a python venv, so with trial and error I installed the missing dependencies, but it could be clearer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or avoid using libraries outside of the python standard libraries if possible.

import argparse
import shutil
import venv

USAGE = 'release.py <package> <version> <jenkinstoken>'
USAGE = 'release.py <package> <version>'
try:
JENKINS_URL = os.environ['JENKINS_URL']
except KeyError:
JENKINS_URL = 'http://build.osrfoundation.org'
JENKINS_URL = 'https://build.osrfoundation.org'
JOB_NAME_PATTERN = '%s-debbuilder'
GENERIC_BREW_PULLREQUEST_JOB = 'generic-release-homebrew_pull_request_updater'

Expand Down Expand Up @@ -111,19 +114,18 @@ def parse_args(argv):
Script to handle the release process for the Gazebo devs.
Examples:
A) Generate source: local repository tag + call source job:
$ release.py <package> <version> <jenkins_token>
$ release.py <package> <version>
(auto calculate source-repo-uri from local directory)

B) Call builders: reuse existing tarball version + call build jobs:
$ release.py --source-tarball-uri <URL> <package> <version> <jenkins_token>
$ release.py --source-tarball-uri <URL> <package> <version>
(no call to source job, directly build jobs with tarball URL)

C) Nightly builds (linux)
$ release.py --source-repo-existing-ref <git_branch> --upload-to-repo nightly <URL> <package> <version> <jenkins_token>
$ release.py --source-repo-existing-ref <git_branch> --upload-to-repo nightly <URL> <package> <version>
""")
parser.add_argument('package', help='which package to release')
parser.add_argument('version', help='which version to release')
parser.add_argument('jenkins_token', help='secret token to allow access to Jenkins to start builds')
parser.add_argument('--dry-run', dest='dry_run', action='store_true', default=False,
help='dry-run; i.e., do actually run any of the commands')
parser.add_argument('-a', '--package-alias', dest='package_alias',
Expand Down Expand Up @@ -176,6 +178,50 @@ def parse_args(argv):

return args

#
# BEGIN: Credentials code copied from ros_buildfarm
#
def get_credentials(jenkins_url=None):
try:
if os.environ['_RELEASEPY_TEST_CREDENTIALS']:
return 'fake_user', 'fake_api_token'
except KeyError:
pass

config = ConfigParser()
config_file = get_credential_path()
if not os.path.exists(config_file):
print("Could not find credential file '%s'" % config_file,
file=sys.stderr)
return None, None

config.read(config_file)
section_name = None
if jenkins_url is not None and jenkins_url in config:
section_name = jenkins_url
if section_name is None and 'DEFAULT' in config:
section_name = 'DEFAULT'

if section_name is None or 'username' not in config[section_name] or \
'password' not in config[section_name]:
print(
"Could not find credentials for '%s' in file '%s'" %
(jenkins_url, config_file), file=sys.stderr)
return None, None
return config[section_name]['username'], config[section_name]['password']


def get_credential_path():
return os.path.join(
os.path.expanduser('~'), get_relative_credential_path())


def get_relative_credential_path():
return os.path.join('.buildfarm', 'jenkins.ini')

#
# END: Credentials code copied from ros_buildfarm
#

def get_release_repository_info(package):
github_url = "https://github.com/gazebo-release/" + package + "-release"
Expand Down Expand Up @@ -336,6 +382,13 @@ def sanity_checks(args, repo_dir):
sanity_check_sdformat_versions(args.package, args.version)
sanity_project_package_in_stable(args.version, args.upload_to_repository)

try:
if os.environ['_RELEASEPY_TEST_CREDENTIALS']:
pass
except KeyError:
check_credentials()
print_success("Jenkins credentials are good")

shutil.rmtree(repo_dir)


Expand Down Expand Up @@ -486,6 +539,20 @@ def generate_source_params(args):

return params

def build_credentials_header():
username, api_token = get_credentials(JENKINS_URL)
if not username:
exit(1)

return make_headers(basic_auth=f'{username}:{api_token}')

def check_credentials():
http = urllib3.PoolManager()
response = http.request('GET', JENKINS_URL, headers=build_credentials_header())
if response.status != 200:
print(f"Crendentials error: {response.status}: {response.reason}")
http.clear()
exit(1)

def call_jenkins_build(job_name, params, output_string,
search_description_help):
Expand All @@ -502,9 +569,21 @@ def call_jenkins_build(job_name, params, output_string,
job_name,
params_query)
print_only_dbg(f" -- {output_string}: {url}")
if not DRY_RUN:
urllib.request.urlopen(url)

if not DRY_RUN:
http = urllib3.PoolManager()
try :
response = http.request('POST', url , headers=build_credentials_header())
# 201 code is "created", it is the expected return of POST
if response.status != 201:
print(f"Error {response.status}: {response.reason}")
exit(1)
except RequestError as e:
print(f"An error occurred in the http request: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
finally:
http.clear()

def display_help_job_chain_for_source_calls(args):
# Encode the different ways using in the job descriptions to filter builds
Expand Down Expand Up @@ -707,7 +786,6 @@ def go(argv):
sanity_checks(args, repo_dir)

params = generate_source_params(args)
params['token'] = args.jenkins_token
params['PACKAGE'] = args.package
params['VERSION'] = args.version if not NIGHTLY else 'nightly'
params['RELEASE_REPO_BRANCH'] = args.release_repo_branch
Expand Down
Loading