123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- # Licensed to Elasticsearch under one or more contributor
- # license agreements. See the NOTICE file distributed with
- # this work for additional information regarding copyright
- # ownership. Elasticsearch licenses this file to you under
- # the Apache License, Version 2.0 (the "License"); you may
- # not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing,
- # software distributed under the License is distributed on
- # an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- # either express or implied. See the License for the specific
- # language governing permissions and limitations under the License.
- # Prepare a release
- #
- # 1. Update the Version.java to remove the snapshot bit
- # 2. Remove the -SNAPSHOT suffix in all pom.xml files
- #
- # USAGE:
- #
- # python3 ./dev-tools/prepare-release.py
- #
- # Note: Ensure the script is run from the elasticsearch top level directory
- #
- import fnmatch
- import argparse
- from prepare_release_update_documentation import update_reference_docs
- import subprocess
- import tempfile
- import re
- import os
- import shutil
- VERSION_FILE = 'core/src/main/java/org/elasticsearch/Version.java'
- POM_FILE = 'pom.xml'
- MAIL_TEMPLATE = """
- Hi all
- The new release candidate for %(version)s based on this commit[1] is now available, including the x-plugins, and RPM/deb repos:
- - ZIP [2]
- - tar.gz [3]
- - RPM [4]
- - deb [5]
- Plugins can be installed as follows,
- bin/plugin -Des.plugins.staging=true install cloud-aws
- The same goes for the x-plugins:
- bin/plugin -Des.plugins.staging=true install license
- bin/plugin -Des.plugins.staging=true install shield
- bin/plugin -Des.plugins.staging=true install watcher
- To install the deb from an APT repo:
- APT line sources.list line:
- deb http://download.elasticsearch.org/elasticsearch/staging/%(version)s-%(hash)s/repos/elasticsearch/%(major_minor_version)s/debian/ stable main
- To install the RPM, create a YUM file like:
- /etc/yum.repos.d/elasticsearch.repo
- containing:
- [elasticsearch-2.0]
- name=Elasticsearch repository for packages
- baseurl=http://download.elasticsearch.org/elasticsearch/staging/%(version)s-%(hash)s/repos/elasticsearch/%(major_minor_version)s/centos
- gpgcheck=1
- gpgkey=http://packages.elastic.co/GPG-KEY-elasticsearch
- enabled=1
- To smoke-test the release please run:
- python3 -B ./dev-tools/smoke_tests_rc.py --version %(version)s --hash %(hash)s --plugins license,shield,watcher
- NOTE: this script requires JAVA_HOME to point to a Java 7 Runtime
- [1] https://github.com/elastic/elasticsearch/commit/%(hash)s
- [2] http://download.elasticsearch.org/elasticsearch/staging/%(version)s-%(hash)s/org/elasticsearch/distribution/zip/elasticsearch/%(version)s/elasticsearch-%(version)s.zip
- [3] http://download.elasticsearch.org/elasticsearch/staging/%(version)s-%(hash)s/org/elasticsearch/distribution/tar/elasticsearch/%(version)s/elasticsearch-%(version)s.tar.gz
- [4] http://download.elasticsearch.org/elasticsearch/staging/%(version)s-%(hash)s/org/elasticsearch/distribution/rpm/elasticsearch/%(version)s/elasticsearch-%(version)s.rpm
- [5] http://download.elasticsearch.org/elasticsearch/staging/%(version)s-%(hash)s/org/elasticsearch/distribution/deb/elasticsearch/%(version)s/elasticsearch-%(version)s.deb
- """
- VERBOSE=True
- def run(command, env_vars=None, verbose=VERBOSE):
- if env_vars:
- for key, value in env_vars.items():
- os.putenv(key, value)
- if not verbose:
- command = '%s >> /dev/null 2>&1' % (command)
- if os.system(command):
- raise RuntimeError(' FAILED: %s' % (command))
- def ensure_checkout_is_clean():
- # Make sure no local mods:
- s = subprocess.check_output('git diff --shortstat', shell=True).decode('utf-8')
- if len(s) > 0:
- raise RuntimeError('git diff --shortstat is non-empty got:\n%s' % s)
- # Make sure no untracked files:
- s = subprocess.check_output('git status', shell=True).decode('utf-8', errors='replace')
- if 'Untracked files:' in s:
- if 'dev-tools/__pycache__/' in s:
- print('*** NOTE: invoke python with -B to prevent __pycache__ directories ***')
- raise RuntimeError('git status shows untracked files got:\n%s' % s)
- # Make sure we have all changes from origin:
- if 'is behind' in s:
- raise RuntimeError('git status shows not all changes pulled from origin; try running "git pull origin" in this branch got:\n%s' % (s))
- # Make sure we no local unpushed changes (this is supposed to be a clean area):
- if 'is ahead' in s:
- raise RuntimeError('git status shows local commits; try running "git fetch origin", "git checkout ", "git reset --hard origin/" in this branch got:\n%s' % (s))
- # Reads the given file and applies the
- # callback to it. If the callback changed
- # a line the given file is replaced with
- # the modified input.
- def process_file(file_path, line_callback):
- fh, abs_path = tempfile.mkstemp()
- modified = False
- with open(abs_path,'w', encoding='utf-8') as new_file:
- with open(file_path, encoding='utf-8') as old_file:
- for line in old_file:
- new_line = line_callback(line)
- modified = modified or (new_line != line)
- new_file.write(new_line)
- os.close(fh)
- if modified:
- #Remove original file
- os.remove(file_path)
- #Move new file
- shutil.move(abs_path, file_path)
- return True
- else:
- # nothing to do - just remove the tmp file
- os.remove(abs_path)
- return False
- # Moves the Version.java file from a snapshot to a release
- def remove_version_snapshot(version_file, release):
- # 1.0.0.Beta1 -> 1_0_0_Beta1
- release = release.replace('.', '_')
- release = release.replace('-', '_')
- pattern = 'new Version(V_%s_ID, true' % (release)
- replacement = 'new Version(V_%s_ID, false' % (release)
- def callback(line):
- return line.replace(pattern, replacement)
- processed = process_file(version_file, callback)
- if not processed:
- raise RuntimeError('failed to remove snapshot version for %s' % (release))
- def rename_local_meta_files(path):
- for root, _, file_names in os.walk(path):
- for file_name in fnmatch.filter(file_names, 'maven-metadata-local.xml*'):
- full_path = os.path.join(root, file_name)
- os.rename(full_path, os.path.join(root, file_name.replace('-local', '')))
- # Checks the pom.xml for the release version.
- # This method fails if the pom file has no SNAPSHOT version set ie.
- # if the version is already on a release version we fail.
- # Returns the next version string ie. 0.90.7
- def find_release_version():
- with open('pom.xml', encoding='utf-8') as file:
- for line in file:
- match = re.search(r'<version>(.+)-SNAPSHOT</version>', line)
- if match:
- return match.group(1)
- raise RuntimeError('Could not find release version in branch')
- if __name__ == "__main__":
- parser = argparse.ArgumentParser(description='Builds and publishes a Elasticsearch Release')
- parser.add_argument('--deploy', '-d', dest='deploy', action='store_true',
- help='Installs and Deploys the release on a sonartype staging repository.')
- parser.add_argument('--skipDocCheck', '-c', dest='skip_doc_check', action='store_false',
- help='Skips any checks for pending documentation changes')
- parser.add_argument('--push-s3', '-p', dest='push', action='store_true',
- help='Pushes artifacts to the S3 staging area')
- parser.add_argument('--install_only', '-i', dest='install_only', action='store_true',
- help='Only runs a maven install to skip the remove deployment step')
- parser.add_argument('--gpg-key', '-k', dest='gpg_key', default="D88E42B4",
- help='Allows you to specify a different gpg_key to be used instead of the default release key')
- parser.add_argument('--verbose', '-b', dest='verbose',
- help='Runs the script in verbose mode')
- parser.set_defaults(deploy=False)
- parser.set_defaults(skip_doc_check=False)
- parser.set_defaults(push=False)
- parser.set_defaults(install_only=False)
- parser.set_defaults(verbose=False)
- args = parser.parse_args()
- install_and_deploy = args.deploy
- skip_doc_check = args.skip_doc_check
- push = args.push
- gpg_key = args.gpg_key
- install_only = args.install_only
- VERBOSE = args.verbose
- ensure_checkout_is_clean()
- release_version = find_release_version()
- if not re.match('(\d+\.\d+)\.*',release_version):
- raise RuntimeError('illegal release version format: %s' % (release_version))
- major_minor_version = re.match('(\d+\.\d+)\.*',release_version).group(1)
- print('*** Preparing release version: [%s]' % release_version)
- if not skip_doc_check:
- print('*** Check for pending documentation changes')
- pending_files = update_reference_docs(release_version)
- if pending_files:
- raise RuntimeError('pending coming[%s] documentation changes found in %s' % (release_version, pending_files))
- run('cd dev-tools && mvn versions:set -DnewVersion=%s -DgenerateBackupPoms=false' % (release_version))
- run('cd rest-api-spec && mvn versions:set -DnewVersion=%s -DgenerateBackupPoms=false' % (release_version))
- run('mvn versions:set -DnewVersion=%s -DgenerateBackupPoms=false' % (release_version))
- remove_version_snapshot(VERSION_FILE, release_version)
- print('*** Done removing snapshot version. DO NOT COMMIT THIS, WHEN CREATING A RELEASE CANDIDATE.')
- shortHash = subprocess.check_output('git log --pretty=format:"%h" -n 1', shell=True).decode('utf-8')
- localRepo = '/tmp/elasticsearch-%s-%s' % (release_version, shortHash)
- localRepoElasticsearch = localRepo + '/org/elasticsearch'
- if os.path.exists(localRepoElasticsearch):
- print('clean local repository %s' % localRepoElasticsearch)
- shutil.rmtree(localRepoElasticsearch)
- if install_only:
- mvn_target = 'install'
- else:
- mvn_target = 'deploy'
- install_command = 'mvn clean %s -Prelease -Dskip.integ.tests=true -Dgpg.key="%s" -Dpackaging.rpm.rpmbuild=/usr/bin/rpmbuild -Drpm.sign=true -Dmaven.repo.local=%s -Dno.commit.pattern="\\bno(n|)commit\\b" -Dforbidden.test.signatures=""' % (mvn_target, gpg_key, localRepo)
- clean_repo_command = 'find %s -name _remote.repositories -exec rm {} \;' % (localRepoElasticsearch)
- rename_metadata_files_command = 'for i in $(find %s -name "maven-metadata-local.xml*") ; do mv "$i" "${i/-local/}" ; done' % (localRepoElasticsearch)
- s3_sync_command = 's3cmd sync %s s3://download.elasticsearch.org/elasticsearch/staging/%s-%s/org/' % (localRepoElasticsearch, release_version, shortHash)
- s3_bucket_sync_to = 'download.elasticsearch.org/elasticsearch/staging/%s-%s/repos' % (release_version, shortHash)
- if install_and_deploy:
- for cmd in [install_command, clean_repo_command]:
- run(cmd)
- rename_local_meta_files(localRepoElasticsearch)
- else:
- print('')
- print('*** To create a release candidate run: ')
- print(' %s' % (install_command))
- print(' 1. Remove all _remote.repositories: %s' % (clean_repo_command))
- print(' 2. Rename all maven metadata files: %s' % (rename_metadata_files_command))
- if push:
- run(s3_sync_command)
- print('Use rpm-s3/deb-s3 to push into repositories at %s' % s3_bucket_sync_to)
- else:
- print('')
- print('*** To push a release candidate to s3 run: ')
- print(' 1. Sync %s into S3 bucket' % (localRepoElasticsearch))
- print (' %s' % (s3_sync_command))
- print(' 2. Create repositories: ')
- print(' Use rpm-s3/deb-s3 to push into repositories at %s' % s3_bucket_sync_to)
- print('')
- print('NOTE: the above mvn command will promt you several times for the GPG passphrase of the key you specified you can alternatively pass it via -Dgpg.passphrase=yourPassPhrase')
- print(' since RPM signing doesn\'t support gpg-agents the recommended way to set the password is to add a release profile to your settings.xml:')
- print("""
- <profiles>
- <profile>
- <id>release</id>
- <properties>
- <gpg.passphrase>YourPasswordGoesHere</gpg.passphrase>
- </properties>
- </profile>
- </profiles>
- """)
- print('NOTE: Running s3cmd might require you to create a config file with your credentials, if the s3cmd does not support suppliying them via the command line!')
- print('*** Once the release is deployed and published send out the following mail to dev@elastic.co:')
- string_format_dict = {'version' : release_version, 'hash': shortHash, 'major_minor_version' : major_minor_version}
- print(MAIL_TEMPLATE % string_format_dict)
- print('To publish the release and the repo on S3 execute the following commands:')
- print(' s3cmd cp --recursive s3://download.elasticsearch.org/elasticsearch/staging/%(version)s-%(hash)s/repos/elasticsearch/%(major_minor_version)s/ s3://packages.elasticsearch.org/elasticsearch/%(major_minor_version)s' % string_format_dict)
- print(' s3cmd cp --recursive s3://download.elasticsearch.org/elasticsearch/staging/%(version)s-%(hash)s/org/ s3://download.elasticsearch.org/elasticsearch/release/org' % string_format_dict)
- print('Now go ahead and tag the release:')
- print(' git tag -a v%(version)s %(hash)s' % string_format_dict)
- print(' git push origin v%(version)s' % string_format_dict )
|