2011-06-04 06:37:48 -04:00
|
|
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
|
# contributor license agreements. See the NOTICE file distributed with
|
|
|
|
# this work for additional information regarding copyright ownership.
|
|
|
|
# The ASF 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.
|
|
|
|
|
2014-08-28 04:01:50 -04:00
|
|
|
import argparse
|
2011-06-04 06:37:48 -04:00
|
|
|
import os
|
2013-04-26 08:03:13 -04:00
|
|
|
import zipfile
|
2012-09-26 14:33:46 -04:00
|
|
|
import codecs
|
2012-04-06 09:33:54 -04:00
|
|
|
import tarfile
|
2012-09-25 17:08:51 -04:00
|
|
|
import zipfile
|
2012-04-06 09:33:54 -04:00
|
|
|
import threading
|
2012-09-24 08:31:22 -04:00
|
|
|
import traceback
|
2013-04-21 09:26:54 -04:00
|
|
|
import datetime
|
|
|
|
import time
|
2012-04-06 09:33:54 -04:00
|
|
|
import subprocess
|
|
|
|
import signal
|
2011-06-04 06:37:48 -04:00
|
|
|
import shutil
|
|
|
|
import hashlib
|
2012-08-03 17:18:14 -04:00
|
|
|
import http.client
|
2011-06-04 06:37:48 -04:00
|
|
|
import re
|
2012-08-03 17:18:14 -04:00
|
|
|
import urllib.request, urllib.error, urllib.parse
|
|
|
|
import urllib.parse
|
2011-06-04 06:37:48 -04:00
|
|
|
import sys
|
2012-08-03 17:18:14 -04:00
|
|
|
import html.parser
|
2012-03-19 13:36:27 -04:00
|
|
|
from collections import defaultdict
|
|
|
|
import xml.etree.ElementTree as ET
|
|
|
|
import filecmp
|
|
|
|
import platform
|
2012-03-22 11:24:44 -04:00
|
|
|
import checkJavaDocs
|
2012-06-08 06:18:09 -04:00
|
|
|
import checkJavadocLinks
|
2012-09-25 17:08:51 -04:00
|
|
|
import io
|
2012-09-26 11:55:27 -04:00
|
|
|
import codecs
|
2014-08-28 04:01:50 -04:00
|
|
|
import textwrap
|
2014-08-30 11:48:16 -04:00
|
|
|
from collections import namedtuple
|
2011-06-04 06:37:48 -04:00
|
|
|
|
|
|
|
# This tool expects to find /lucene and /solr off the base URL. You
|
2012-03-19 13:36:27 -04:00
|
|
|
# must have a working gpg, tar, unzip in your path. This has been
|
|
|
|
# tested on Linux and on Cygwin under Windows 7.
|
2011-06-04 06:37:48 -04:00
|
|
|
|
2012-09-24 12:33:39 -04:00
|
|
|
cygwin = platform.system().lower().startswith('cygwin')
|
|
|
|
cygwinWindowsRoot = os.popen('cygpath -w /').read().strip().replace('\\','/') if cygwin else ''
|
|
|
|
|
2012-04-08 14:22:14 -04:00
|
|
|
def unshortenURL(url):
|
2012-08-03 17:18:14 -04:00
|
|
|
parsed = urllib.parse.urlparse(url)
|
2012-04-08 14:22:14 -04:00
|
|
|
if parsed[0] in ('http', 'https'):
|
2012-08-03 17:18:14 -04:00
|
|
|
h = http.client.HTTPConnection(parsed.netloc)
|
2012-04-08 14:22:14 -04:00
|
|
|
h.request('HEAD', parsed.path)
|
|
|
|
response = h.getresponse()
|
2012-09-24 08:31:22 -04:00
|
|
|
if int(response.status/100) == 3 and response.getheader('Location'):
|
2012-04-08 14:22:14 -04:00
|
|
|
return response.getheader('Location')
|
|
|
|
return url
|
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
# TODO
|
|
|
|
# + verify KEYS contains key that signed the release
|
|
|
|
# + make sure changes HTML looks ok
|
|
|
|
# - verify license/notice of all dep jars
|
|
|
|
# - check maven
|
|
|
|
# - check JAR manifest version
|
|
|
|
# - check license/notice exist
|
|
|
|
# - check no "extra" files
|
|
|
|
# - make sure jars exist inside bin release
|
|
|
|
# - run "ant test"
|
|
|
|
# - make sure docs exist
|
|
|
|
# - use java5 for lucene/modules
|
|
|
|
|
|
|
|
reHREF = re.compile('<a href="(.*?)">(.*?)</a>')
|
|
|
|
|
2012-09-26 14:33:46 -04:00
|
|
|
# Set to False to avoid re-downloading the packages...
|
|
|
|
FORCE_CLEAN = True
|
2011-06-04 06:37:48 -04:00
|
|
|
|
|
|
|
def getHREFs(urlString):
|
|
|
|
|
|
|
|
# Deref any redirects
|
|
|
|
while True:
|
2012-08-03 17:18:14 -04:00
|
|
|
url = urllib.parse.urlparse(urlString)
|
2015-12-07 16:21:05 -05:00
|
|
|
if url.scheme == "http":
|
|
|
|
h = http.client.HTTPConnection(url.netloc)
|
|
|
|
elif url.scheme == "https":
|
|
|
|
h = http.client.HTTPSConnection(url.netloc)
|
|
|
|
else:
|
|
|
|
raise RuntimeError("Unknown protocol: %s" % url.scheme)
|
2016-02-12 16:39:35 -05:00
|
|
|
h.request('HEAD', url.path)
|
2011-06-04 06:37:48 -04:00
|
|
|
r = h.getresponse()
|
|
|
|
newLoc = r.getheader('location')
|
|
|
|
if newLoc is not None:
|
|
|
|
urlString = newLoc
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
|
|
|
|
links = []
|
2012-09-24 08:31:22 -04:00
|
|
|
try:
|
2015-02-10 13:09:53 -05:00
|
|
|
html = load(urlString)
|
2012-09-24 08:31:22 -04:00
|
|
|
except:
|
|
|
|
print('\nFAILED to open url %s' % urlString)
|
2012-09-24 11:25:13 -04:00
|
|
|
traceback.print_exc()
|
2012-09-24 08:31:22 -04:00
|
|
|
raise
|
|
|
|
|
|
|
|
for subUrl, text in reHREF.findall(html):
|
2012-08-03 17:18:14 -04:00
|
|
|
fullURL = urllib.parse.urljoin(urlString, subUrl)
|
2011-06-04 06:37:48 -04:00
|
|
|
links.append((text, fullURL))
|
|
|
|
return links
|
|
|
|
|
2012-03-19 13:36:27 -04:00
|
|
|
def download(name, urlString, tmpDir, quiet=False):
|
2013-04-21 09:26:54 -04:00
|
|
|
startTime = time.time()
|
2011-06-04 06:37:48 -04:00
|
|
|
fileName = '%s/%s' % (tmpDir, name)
|
2012-09-26 14:33:46 -04:00
|
|
|
if not FORCE_CLEAN and os.path.exists(fileName):
|
2012-03-19 13:36:27 -04:00
|
|
|
if not quiet and fileName.find('.asc') == -1:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' already done: %.1f MB' % (os.path.getsize(fileName)/1024./1024.))
|
2011-06-04 06:37:48 -04:00
|
|
|
return
|
|
|
|
try:
|
2015-02-10 09:40:27 -05:00
|
|
|
attemptDownload(urlString, fileName)
|
2013-04-20 06:54:55 -04:00
|
|
|
except Exception as e:
|
2015-02-10 09:40:27 -05:00
|
|
|
print('Retrying download of url %s after exception: %s' % (urlString, e))
|
|
|
|
try:
|
|
|
|
attemptDownload(urlString, fileName)
|
|
|
|
except Exception as e:
|
|
|
|
raise RuntimeError('failed to download url "%s"' % urlString) from e
|
|
|
|
if not quiet and fileName.find('.asc') == -1:
|
|
|
|
t = time.time()-startTime
|
|
|
|
sizeMB = os.path.getsize(fileName)/1024./1024.
|
|
|
|
print(' %.1f MB in %.2f sec (%.1f MB/sec)' % (sizeMB, t, sizeMB/t))
|
2013-04-20 06:54:55 -04:00
|
|
|
|
2015-02-10 09:40:27 -05:00
|
|
|
def attemptDownload(urlString, fileName):
|
|
|
|
fIn = urllib.request.urlopen(urlString)
|
|
|
|
fOut = open(fileName, 'wb')
|
|
|
|
success = False
|
|
|
|
try:
|
|
|
|
while True:
|
|
|
|
s = fIn.read(65536)
|
|
|
|
if s == b'':
|
|
|
|
break
|
|
|
|
fOut.write(s)
|
|
|
|
fOut.close()
|
|
|
|
fIn.close()
|
|
|
|
success = True
|
|
|
|
finally:
|
|
|
|
fIn.close()
|
|
|
|
fOut.close()
|
|
|
|
if not success:
|
|
|
|
os.remove(fileName)
|
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
def load(urlString):
|
2015-02-10 13:09:53 -05:00
|
|
|
try:
|
|
|
|
content = urllib.request.urlopen(urlString).read().decode('utf-8')
|
|
|
|
except Exception as e:
|
|
|
|
print('Retrying download of url %s after exception: %s' % (urlString, e))
|
|
|
|
content = urllib.request.urlopen(urlString).read().decode('utf-8')
|
|
|
|
return content
|
2012-09-25 17:08:51 -04:00
|
|
|
|
|
|
|
def noJavaPackageClasses(desc, file):
|
|
|
|
with zipfile.ZipFile(file) as z2:
|
|
|
|
for name2 in z2.namelist():
|
|
|
|
if name2.endswith('.class') and (name2.startswith('java/') or name2.startswith('javax/')):
|
2012-09-26 10:51:36 -04:00
|
|
|
raise RuntimeError('%s contains sheisty class "%s"' % (desc, name2))
|
2012-09-25 17:08:51 -04:00
|
|
|
|
2012-09-26 14:33:46 -04:00
|
|
|
def decodeUTF8(bytes):
|
|
|
|
return codecs.getdecoder('UTF-8')(bytes)[0]
|
|
|
|
|
|
|
|
MANIFEST_FILE_NAME = 'META-INF/MANIFEST.MF'
|
|
|
|
NOTICE_FILE_NAME = 'META-INF/NOTICE.txt'
|
|
|
|
LICENSE_FILE_NAME = 'META-INF/LICENSE.txt'
|
|
|
|
|
2016-02-11 15:19:43 -05:00
|
|
|
def checkJARMetaData(desc, jarFile, gitRevision, version):
|
2012-09-26 14:33:46 -04:00
|
|
|
|
2015-07-28 15:04:21 -04:00
|
|
|
with zipfile.ZipFile(jarFile, 'r') as z:
|
2012-09-26 14:33:46 -04:00
|
|
|
for name in (MANIFEST_FILE_NAME, NOTICE_FILE_NAME, LICENSE_FILE_NAME):
|
2015-07-28 15:04:21 -04:00
|
|
|
try:
|
|
|
|
# The Python docs state a KeyError is raised ... so this None
|
|
|
|
# check is just defensive:
|
|
|
|
if z.getinfo(name) is None:
|
2015-07-27 14:12:45 -04:00
|
|
|
raise RuntimeError('%s is missing %s' % (desc, name))
|
2015-07-28 15:04:21 -04:00
|
|
|
except KeyError:
|
|
|
|
raise RuntimeError('%s is missing %s' % (desc, name))
|
|
|
|
|
|
|
|
s = decodeUTF8(z.read(MANIFEST_FILE_NAME))
|
2012-09-26 14:33:46 -04:00
|
|
|
|
2015-07-28 15:04:21 -04:00
|
|
|
for verify in (
|
|
|
|
'Specification-Vendor: The Apache Software Foundation',
|
|
|
|
'Implementation-Vendor: The Apache Software Foundation',
|
|
|
|
# Make sure 1.8 compiler was used to build release bits:
|
2016-05-20 16:20:39 -04:00
|
|
|
'X-Compile-Source-JDK: 8',
|
2016-08-21 04:48:59 -04:00
|
|
|
# Make sure 1.8 or 1.9 ant was used to build release bits: (this will match 1.8.x, 1.9.x)
|
|
|
|
('Ant-Version: Apache Ant 1.8', 'Ant-Version: Apache Ant 1.9'),
|
2015-07-28 15:04:21 -04:00
|
|
|
# Make sure .class files are 1.8 format:
|
2016-05-20 16:20:39 -04:00
|
|
|
'X-Compile-Target-JDK: 8',
|
2015-07-28 15:04:21 -04:00
|
|
|
'Specification-Version: %s' % version,
|
|
|
|
# Make sure the release was compiled with 1.8:
|
|
|
|
'Created-By: 1.8'):
|
2016-08-21 04:48:59 -04:00
|
|
|
if type(verify) is not tuple:
|
|
|
|
verify = (verify,)
|
|
|
|
for x in verify:
|
|
|
|
if s.find(x) != -1:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
if len(verify) == 1:
|
|
|
|
raise RuntimeError('%s is missing "%s" inside its META-INF/MANIFEST.MF' % (desc, verify[0]))
|
|
|
|
else:
|
|
|
|
raise RuntimeError('%s is missing one of "%s" inside its META-INF/MANIFEST.MF' % (desc, verify))
|
2015-07-28 15:04:21 -04:00
|
|
|
|
2016-02-11 15:19:43 -05:00
|
|
|
if gitRevision != 'skip':
|
|
|
|
# Make sure this matches the version and git revision we think we are releasing:
|
|
|
|
# TODO: LUCENE-7023: is it OK that Implementation-Version's value now spans two lines?
|
|
|
|
verifyRevision = 'Implementation-Version: %s %s' % (version, gitRevision)
|
2015-07-28 15:04:21 -04:00
|
|
|
if s.find(verifyRevision) == -1:
|
2016-02-11 15:19:43 -05:00
|
|
|
raise RuntimeError('%s is missing "%s" inside its META-INF/MANIFEST.MF (wrong git revision?)' % \
|
2015-07-27 14:12:45 -04:00
|
|
|
(desc, verifyRevision))
|
|
|
|
|
2015-07-28 15:04:21 -04:00
|
|
|
notice = decodeUTF8(z.read(NOTICE_FILE_NAME))
|
|
|
|
license = decodeUTF8(z.read(LICENSE_FILE_NAME))
|
2015-07-27 14:12:45 -04:00
|
|
|
|
2015-07-28 15:04:21 -04:00
|
|
|
justFileName = os.path.split(desc)[1]
|
|
|
|
|
|
|
|
if justFileName.lower().find('solr') != -1:
|
|
|
|
if SOLR_LICENSE is None:
|
|
|
|
raise RuntimeError('BUG in smokeTestRelease!')
|
|
|
|
if SOLR_NOTICE is None:
|
|
|
|
raise RuntimeError('BUG in smokeTestRelease!')
|
|
|
|
if notice != SOLR_NOTICE:
|
|
|
|
raise RuntimeError('%s: %s contents doesn\'t match main NOTICE.txt' % \
|
|
|
|
(desc, NOTICE_FILE_NAME))
|
|
|
|
if license != SOLR_LICENSE:
|
|
|
|
raise RuntimeError('%s: %s contents doesn\'t match main LICENSE.txt' % \
|
|
|
|
(desc, LICENSE_FILE_NAME))
|
|
|
|
else:
|
|
|
|
if LUCENE_LICENSE is None:
|
|
|
|
raise RuntimeError('BUG in smokeTestRelease!')
|
|
|
|
if LUCENE_NOTICE is None:
|
|
|
|
raise RuntimeError('BUG in smokeTestRelease!')
|
|
|
|
if notice != LUCENE_NOTICE:
|
|
|
|
raise RuntimeError('%s: %s contents doesn\'t match main NOTICE.txt' % \
|
|
|
|
(desc, NOTICE_FILE_NAME))
|
|
|
|
if license != LUCENE_LICENSE:
|
|
|
|
raise RuntimeError('%s: %s contents doesn\'t match main LICENSE.txt' % \
|
|
|
|
(desc, LICENSE_FILE_NAME))
|
2012-09-26 14:33:46 -04:00
|
|
|
|
2012-09-26 10:51:36 -04:00
|
|
|
def normSlashes(path):
|
|
|
|
return path.replace(os.sep, '/')
|
|
|
|
|
2016-02-11 15:19:43 -05:00
|
|
|
|
|
|
|
def checkAllJARs(topDir, project, gitRevision, version, tmpDir, baseURL):
|
2013-10-11 12:58:36 -04:00
|
|
|
print(' verify JAR metadata/identity/no javax.* or java.* classes...')
|
|
|
|
if project == 'solr':
|
|
|
|
luceneDistFilenames = dict()
|
|
|
|
for file in getBinaryDistFiles('lucene', tmpDir, version, baseURL):
|
|
|
|
luceneDistFilenames[os.path.basename(file)] = file
|
2012-09-26 10:51:36 -04:00
|
|
|
for root, dirs, files in os.walk(topDir):
|
|
|
|
|
|
|
|
normRoot = normSlashes(root)
|
|
|
|
|
2014-10-31 21:22:01 -04:00
|
|
|
if project == 'solr' and normRoot.endswith('/server/lib'):
|
2012-09-26 10:51:36 -04:00
|
|
|
# Solr's example intentionally ships servlet JAR:
|
|
|
|
continue
|
|
|
|
|
2012-09-25 17:08:51 -04:00
|
|
|
for file in files:
|
|
|
|
if file.lower().endswith('.jar'):
|
2012-09-26 10:51:36 -04:00
|
|
|
if project == 'solr':
|
2014-07-18 13:10:03 -04:00
|
|
|
if (normRoot.endswith('/contrib/dataimporthandler-extras/lib') and (file.startswith('javax.mail-') or file.startswith('activation-'))) or (normRoot.endswith('/test-framework/lib') and file.startswith('jersey-')):
|
2012-09-26 10:51:36 -04:00
|
|
|
print(' **WARNING**: skipping check of %s/%s: it has javax.* classes' % (root, file))
|
|
|
|
continue
|
2013-05-16 13:18:59 -04:00
|
|
|
else:
|
|
|
|
if normRoot.endswith('/replicator/lib') and file.startswith('javax.servlet'):
|
|
|
|
continue
|
2012-09-25 17:08:51 -04:00
|
|
|
fullPath = '%s/%s' % (root, file)
|
|
|
|
noJavaPackageClasses('JAR file "%s"' % fullPath, fullPath)
|
2012-09-26 14:33:46 -04:00
|
|
|
if file.lower().find('lucene') != -1 or file.lower().find('solr') != -1:
|
2016-02-11 15:19:43 -05:00
|
|
|
checkJARMetaData('JAR file "%s"' % fullPath, fullPath, gitRevision, version)
|
2013-10-11 12:58:36 -04:00
|
|
|
if project == 'solr' and file.lower().find('lucene') != -1:
|
|
|
|
jarFilename = os.path.basename(file)
|
|
|
|
if jarFilename not in luceneDistFilenames:
|
|
|
|
raise RuntimeError('Artifact %s is not present in Lucene binary distribution' % fullPath)
|
|
|
|
identical = filecmp.cmp(fullPath, luceneDistFilenames[jarFilename], shallow=False)
|
|
|
|
if not identical:
|
|
|
|
raise RuntimeError('Artifact %s is not identical to %s in Lucene binary distribution'
|
|
|
|
% (fullPath, luceneDistFilenames[jarFilename]))
|
|
|
|
|
2012-09-26 10:51:36 -04:00
|
|
|
|
2012-04-08 14:22:14 -04:00
|
|
|
def checkSigs(project, urlString, version, tmpDir, isSigned):
|
2011-06-04 06:37:48 -04:00
|
|
|
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' test basics...')
|
2011-06-04 06:37:48 -04:00
|
|
|
ents = getDirEntries(urlString)
|
|
|
|
artifact = None
|
|
|
|
keysURL = None
|
|
|
|
changesURL = None
|
|
|
|
mavenURL = None
|
2012-04-08 14:22:14 -04:00
|
|
|
expectedSigs = []
|
|
|
|
if isSigned:
|
|
|
|
expectedSigs.append('asc')
|
|
|
|
expectedSigs.extend(['md5', 'sha1'])
|
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
artifacts = []
|
|
|
|
for text, subURL in ents:
|
|
|
|
if text == 'KEYS':
|
|
|
|
keysURL = subURL
|
|
|
|
elif text == 'maven/':
|
|
|
|
mavenURL = subURL
|
|
|
|
elif text.startswith('changes'):
|
|
|
|
if text not in ('changes/', 'changes-%s/' % version):
|
|
|
|
raise RuntimeError('%s: found %s vs expected changes-%s/' % (project, text, version))
|
|
|
|
changesURL = subURL
|
|
|
|
elif artifact == None:
|
|
|
|
artifact = text
|
|
|
|
artifactURL = subURL
|
|
|
|
if project == 'solr':
|
2013-01-12 12:51:57 -05:00
|
|
|
expected = 'solr-%s' % version
|
2011-06-04 06:37:48 -04:00
|
|
|
else:
|
|
|
|
expected = 'lucene-%s' % version
|
|
|
|
if not artifact.startswith(expected):
|
|
|
|
raise RuntimeError('%s: unknown artifact %s: expected prefix %s' % (project, text, expected))
|
|
|
|
sigs = []
|
|
|
|
elif text.startswith(artifact + '.'):
|
|
|
|
sigs.append(text[len(artifact)+1:])
|
|
|
|
else:
|
|
|
|
if sigs != expectedSigs:
|
|
|
|
raise RuntimeError('%s: artifact %s has wrong sigs: expected %s but got %s' % (project, artifact, expectedSigs, sigs))
|
|
|
|
artifacts.append((artifact, artifactURL))
|
|
|
|
artifact = text
|
|
|
|
artifactURL = subURL
|
|
|
|
sigs = []
|
|
|
|
|
|
|
|
if sigs != []:
|
|
|
|
artifacts.append((artifact, artifactURL))
|
|
|
|
if sigs != expectedSigs:
|
|
|
|
raise RuntimeError('%s: artifact %s has wrong sigs: expected %s but got %s' % (project, artifact, expectedSigs, sigs))
|
|
|
|
|
|
|
|
if project == 'lucene':
|
|
|
|
expected = ['lucene-%s-src.tgz' % version,
|
|
|
|
'lucene-%s.tgz' % version,
|
|
|
|
'lucene-%s.zip' % version]
|
|
|
|
else:
|
2013-01-12 12:51:57 -05:00
|
|
|
expected = ['solr-%s-src.tgz' % version,
|
|
|
|
'solr-%s.tgz' % version,
|
|
|
|
'solr-%s.zip' % version]
|
2011-06-04 06:37:48 -04:00
|
|
|
|
|
|
|
actual = [x[0] for x in artifacts]
|
|
|
|
if expected != actual:
|
|
|
|
raise RuntimeError('%s: wrong artifacts: expected %s but got %s' % (project, expected, actual))
|
|
|
|
|
|
|
|
if keysURL is None:
|
|
|
|
raise RuntimeError('%s is missing KEYS' % project)
|
|
|
|
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' get KEYS')
|
2011-06-04 06:37:48 -04:00
|
|
|
download('%s.KEYS' % project, keysURL, tmpDir)
|
|
|
|
|
|
|
|
keysFile = '%s/%s.KEYS' % (tmpDir, project)
|
|
|
|
|
|
|
|
# Set up clean gpg world; import keys file:
|
|
|
|
gpgHomeDir = '%s/%s.gpg' % (tmpDir, project)
|
|
|
|
if os.path.exists(gpgHomeDir):
|
|
|
|
shutil.rmtree(gpgHomeDir)
|
2012-08-03 17:18:14 -04:00
|
|
|
os.makedirs(gpgHomeDir, 0o700)
|
2011-06-04 06:37:48 -04:00
|
|
|
run('gpg --homedir %s --import %s' % (gpgHomeDir, keysFile),
|
2013-05-21 06:40:30 -04:00
|
|
|
'%s/%s.gpg.import.log' % (tmpDir, project))
|
2011-06-04 06:37:48 -04:00
|
|
|
|
|
|
|
if mavenURL is None:
|
|
|
|
raise RuntimeError('%s is missing maven' % project)
|
|
|
|
|
2012-09-24 00:40:14 -04:00
|
|
|
if changesURL is None:
|
|
|
|
raise RuntimeError('%s is missing changes-%s' % (project, version))
|
|
|
|
testChanges(project, version, changesURL)
|
2011-06-04 06:37:48 -04:00
|
|
|
|
|
|
|
for artifact, urlString in artifacts:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' download %s...' % artifact)
|
2011-06-04 06:37:48 -04:00
|
|
|
download(artifact, urlString, tmpDir)
|
|
|
|
verifyDigests(artifact, urlString, tmpDir)
|
|
|
|
|
2012-04-08 14:22:14 -04:00
|
|
|
if isSigned:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' verify sig')
|
2012-04-08 14:22:14 -04:00
|
|
|
# Test sig (this is done with a clean brand-new GPG world)
|
|
|
|
download(artifact + '.asc', urlString + '.asc', tmpDir)
|
|
|
|
sigFile = '%s/%s.asc' % (tmpDir, artifact)
|
|
|
|
artifactFile = '%s/%s' % (tmpDir, artifact)
|
|
|
|
logFile = '%s/%s.%s.gpg.verify.log' % (tmpDir, project, artifact)
|
|
|
|
run('gpg --homedir %s --verify %s %s' % (gpgHomeDir, sigFile, artifactFile),
|
|
|
|
logFile)
|
2014-12-24 00:48:58 -05:00
|
|
|
# Forward any GPG warnings, except the expected one (since it's a clean world)
|
2012-08-03 17:49:24 -04:00
|
|
|
f = open(logFile, encoding='UTF-8')
|
2012-04-08 14:22:14 -04:00
|
|
|
for line in f.readlines():
|
|
|
|
if line.lower().find('warning') != -1 \
|
|
|
|
and line.find('WARNING: This key is not certified with a trusted signature') == -1:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' GPG: %s' % line.strip())
|
2012-04-08 14:22:14 -04:00
|
|
|
f.close()
|
2011-11-21 08:54:41 -05:00
|
|
|
|
2012-04-08 14:22:14 -04:00
|
|
|
# Test trust (this is done with the real users config)
|
|
|
|
run('gpg --import %s' % (keysFile),
|
2013-05-21 06:40:30 -04:00
|
|
|
'%s/%s.gpg.trust.import.log' % (tmpDir, project))
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' verify trust')
|
2012-04-08 14:22:14 -04:00
|
|
|
logFile = '%s/%s.%s.gpg.trust.log' % (tmpDir, project, artifact)
|
|
|
|
run('gpg --verify %s %s' % (sigFile, artifactFile), logFile)
|
|
|
|
# Forward any GPG warnings:
|
2012-08-03 17:49:24 -04:00
|
|
|
f = open(logFile, encoding='UTF-8')
|
2012-04-08 14:22:14 -04:00
|
|
|
for line in f.readlines():
|
|
|
|
if line.lower().find('warning') != -1:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' GPG: %s' % line.strip())
|
2012-04-08 14:22:14 -04:00
|
|
|
f.close()
|
2011-06-04 06:37:48 -04:00
|
|
|
|
|
|
|
def testChanges(project, version, changesURLString):
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' check changes HTML...')
|
2011-06-04 06:37:48 -04:00
|
|
|
changesURL = None
|
|
|
|
for text, subURL in getDirEntries(changesURLString):
|
|
|
|
if text == 'Changes.html':
|
|
|
|
changesURL = subURL
|
|
|
|
|
|
|
|
if changesURL is None:
|
|
|
|
raise RuntimeError('did not see Changes.html link from %s' % changesURLString)
|
|
|
|
|
|
|
|
s = load(changesURL)
|
2011-06-28 16:51:05 -04:00
|
|
|
checkChangesContent(s, version, changesURL, project, True)
|
2011-06-04 06:37:48 -04:00
|
|
|
|
2011-06-28 16:51:05 -04:00
|
|
|
def testChangesText(dir, version, project):
|
|
|
|
"Checks all CHANGES.txt under this dir."
|
|
|
|
for root, dirs, files in os.walk(dir):
|
|
|
|
|
|
|
|
# NOTE: O(N) but N should be smallish:
|
|
|
|
if 'CHANGES.txt' in files:
|
|
|
|
fullPath = '%s/CHANGES.txt' % root
|
2012-03-22 11:24:44 -04:00
|
|
|
#print 'CHECK %s' % fullPath
|
2012-08-03 17:49:24 -04:00
|
|
|
checkChangesContent(open(fullPath, encoding='UTF-8').read(), version, fullPath, project, False)
|
2013-04-19 13:38:46 -04:00
|
|
|
|
|
|
|
reChangesSectionHREF = re.compile('<a id="(.*?)".*?>(.*?)</a>', re.IGNORECASE)
|
2014-05-19 16:17:58 -04:00
|
|
|
reUnderbarNotDashHTML = re.compile(r'<li>(\s*(LUCENE|SOLR)_\d\d\d\d+)')
|
|
|
|
reUnderbarNotDashTXT = re.compile(r'\s+((LUCENE|SOLR)_\d\d\d\d+)', re.MULTILINE)
|
2011-06-28 16:51:05 -04:00
|
|
|
def checkChangesContent(s, version, name, project, isHTML):
|
2016-11-17 12:54:33 -05:00
|
|
|
currentVersionTuple = versionToTuple(version, name)
|
2011-06-28 16:51:05 -04:00
|
|
|
|
|
|
|
if isHTML and s.find('Release %s' % version) == -1:
|
|
|
|
raise RuntimeError('did not see "Release %s" in %s' % (version, name))
|
|
|
|
|
2014-05-19 16:17:58 -04:00
|
|
|
if isHTML:
|
|
|
|
r = reUnderbarNotDashHTML
|
|
|
|
else:
|
|
|
|
r = reUnderbarNotDashTXT
|
|
|
|
|
|
|
|
m = r.search(s)
|
|
|
|
if m is not None:
|
|
|
|
raise RuntimeError('incorrect issue (_ instead of -) in %s: %s' % (name, m.group(1)))
|
|
|
|
|
2011-06-28 16:51:05 -04:00
|
|
|
if s.lower().find('not yet released') != -1:
|
|
|
|
raise RuntimeError('saw "not yet released" in %s' % name)
|
|
|
|
|
|
|
|
if not isHTML:
|
|
|
|
if project == 'lucene':
|
|
|
|
sub = 'Lucene %s' % version
|
|
|
|
else:
|
|
|
|
sub = version
|
|
|
|
|
|
|
|
if s.find(sub) == -1:
|
2012-06-08 06:18:09 -04:00
|
|
|
# benchmark never seems to include release info:
|
2011-06-28 16:51:05 -04:00
|
|
|
if name.find('/benchmark/') == -1:
|
|
|
|
raise RuntimeError('did not see "%s" in %s' % (sub, name))
|
2012-04-08 13:15:54 -04:00
|
|
|
|
2013-04-19 13:38:46 -04:00
|
|
|
if isHTML:
|
2016-11-17 12:54:33 -05:00
|
|
|
# Make sure that a section only appears once under each release,
|
|
|
|
# and that each release is not greater than the current version
|
2013-04-19 13:38:46 -04:00
|
|
|
seenIDs = set()
|
|
|
|
seenText = set()
|
|
|
|
|
|
|
|
release = None
|
|
|
|
for id, text in reChangesSectionHREF.findall(s):
|
|
|
|
if text.lower().startswith('release '):
|
|
|
|
release = text[8:].strip()
|
|
|
|
seenText.clear()
|
2016-11-17 12:54:33 -05:00
|
|
|
releaseTuple = versionToTuple(release, name)
|
|
|
|
if releaseTuple > currentVersionTuple:
|
|
|
|
raise RuntimeError('Future release %s is greater than %s in %s' % (release, version, name))
|
2013-04-19 13:38:46 -04:00
|
|
|
if id in seenIDs:
|
|
|
|
raise RuntimeError('%s has duplicate section "%s" under release "%s"' % (name, text, release))
|
|
|
|
seenIDs.add(id)
|
|
|
|
if text in seenText:
|
|
|
|
raise RuntimeError('%s has duplicate section "%s" under release "%s"' % (name, text, release))
|
|
|
|
seenText.add(text)
|
|
|
|
|
2016-11-17 12:54:33 -05:00
|
|
|
|
|
|
|
reVersion = re.compile(r'(\d+)\.(\d+)(?:\.(\d+))?\s*(-alpha|-beta|final|RC\d+)?\s*(?:\[.*\])?', re.IGNORECASE)
|
|
|
|
def versionToTuple(version, name):
|
|
|
|
versionMatch = reVersion.match(version)
|
|
|
|
if versionMatch is None:
|
|
|
|
raise RuntimeError('Version %s in %s cannot be parsed' % (version, name))
|
|
|
|
versionTuple = versionMatch.groups()
|
|
|
|
while versionTuple[-1] is None or versionTuple[-1] == '':
|
|
|
|
versionTuple = versionTuple[:-1]
|
|
|
|
if versionTuple[-1].lower() == '-alpha':
|
|
|
|
versionTuple = versionTuple[:-1] + ('0',)
|
|
|
|
elif versionTuple[-1].lower() == '-beta':
|
|
|
|
versionTuple = versionTuple[:-1] + ('1',)
|
|
|
|
elif versionTuple[-1].lower() == 'final':
|
|
|
|
versionTuple = versionTuple[:-2] + ('100',)
|
|
|
|
elif versionTuple[-1].lower()[:2] == 'rc':
|
|
|
|
versionTuple = versionTuple[:-2] + (versionTuple[-1][2:],)
|
|
|
|
return versionTuple
|
|
|
|
|
|
|
|
|
2012-04-08 13:15:54 -04:00
|
|
|
reUnixPath = re.compile(r'\b[a-zA-Z_]+=(?:"(?:\\"|[^"])*"' + '|(?:\\\\.|[^"\'\\s])*' + r"|'(?:\\'|[^'])*')" \
|
|
|
|
+ r'|(/(?:\\.|[^"\'\s])*)' \
|
|
|
|
+ r'|("/(?:\\.|[^"])*")' \
|
|
|
|
+ r"|('/(?:\\.|[^'])*')")
|
|
|
|
|
|
|
|
def unix2win(matchobj):
|
|
|
|
if matchobj.group(1) is not None: return cygwinWindowsRoot + matchobj.group()
|
|
|
|
if matchobj.group(2) is not None: return '"%s%s' % (cygwinWindowsRoot, matchobj.group().lstrip('"'))
|
|
|
|
if matchobj.group(3) is not None: return "'%s%s" % (cygwinWindowsRoot, matchobj.group().lstrip("'"))
|
|
|
|
return matchobj.group()
|
|
|
|
|
|
|
|
def cygwinifyPaths(command):
|
|
|
|
# The problem: Native Windows applications running under Cygwin
|
|
|
|
# (e.g. Ant, which isn't available as a Cygwin package) can't
|
|
|
|
# handle Cygwin's Unix-style paths. However, environment variable
|
|
|
|
# values are automatically converted, so only paths outside of
|
|
|
|
# environment variable values should be converted to Windows paths.
|
|
|
|
# Assumption: all paths will be absolute.
|
|
|
|
if '; ant ' in command: command = reUnixPath.sub(unix2win, command)
|
|
|
|
return command
|
|
|
|
|
2012-11-08 09:00:28 -05:00
|
|
|
def printFileContents(fileName):
|
|
|
|
|
|
|
|
# Assume log file was written in system's default encoding, but
|
|
|
|
# even if we are wrong, we replace errors ... the ASCII chars
|
|
|
|
# (which is what we mostly care about eg for the test seed) should
|
|
|
|
# still survive:
|
|
|
|
txt = codecs.open(fileName, 'r', encoding=sys.getdefaultencoding(), errors='replace').read()
|
|
|
|
|
|
|
|
# Encode to our output encoding (likely also system's default
|
|
|
|
# encoding):
|
|
|
|
bytes = txt.encode(sys.stdout.encoding, errors='replace')
|
|
|
|
|
|
|
|
# Decode back to string and print... we should hit no exception here
|
|
|
|
# since all errors have been replaced:
|
|
|
|
print(codecs.getdecoder(sys.stdout.encoding)(bytes)[0])
|
|
|
|
print()
|
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
def run(command, logFile):
|
2012-04-08 13:15:54 -04:00
|
|
|
if cygwin: command = cygwinifyPaths(command)
|
2011-06-04 06:37:48 -04:00
|
|
|
if os.system('%s > %s 2>&1' % (command, logFile)):
|
2012-03-19 13:36:27 -04:00
|
|
|
logPath = os.path.abspath(logFile)
|
2012-09-26 11:55:27 -04:00
|
|
|
print('\ncommand "%s" failed:' % command)
|
2012-11-08 09:00:28 -05:00
|
|
|
printFileContents(logFile)
|
2012-03-19 13:36:27 -04:00
|
|
|
raise RuntimeError('command "%s" failed; see log file %s' % (command, logPath))
|
2011-06-04 06:37:48 -04:00
|
|
|
|
|
|
|
def verifyDigests(artifact, urlString, tmpDir):
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' verify md5/sha1 digests')
|
2011-06-04 06:37:48 -04:00
|
|
|
md5Expected, t = load(urlString + '.md5').strip().split()
|
|
|
|
if t != '*'+artifact:
|
|
|
|
raise RuntimeError('MD5 %s.md5 lists artifact %s but expected *%s' % (urlString, t, artifact))
|
|
|
|
|
|
|
|
sha1Expected, t = load(urlString + '.sha1').strip().split()
|
|
|
|
if t != '*'+artifact:
|
|
|
|
raise RuntimeError('SHA1 %s.sha1 lists artifact %s but expected *%s' % (urlString, t, artifact))
|
|
|
|
|
|
|
|
m = hashlib.md5()
|
|
|
|
s = hashlib.sha1()
|
2012-08-03 17:49:24 -04:00
|
|
|
f = open('%s/%s' % (tmpDir, artifact), 'rb')
|
2011-06-04 06:37:48 -04:00
|
|
|
while True:
|
|
|
|
x = f.read(65536)
|
2012-08-03 17:49:24 -04:00
|
|
|
if len(x) == 0:
|
2011-06-04 06:37:48 -04:00
|
|
|
break
|
|
|
|
m.update(x)
|
|
|
|
s.update(x)
|
|
|
|
f.close()
|
|
|
|
md5Actual = m.hexdigest()
|
|
|
|
sha1Actual = s.hexdigest()
|
|
|
|
if md5Actual != md5Expected:
|
|
|
|
raise RuntimeError('MD5 digest mismatch for %s: expected %s but got %s' % (artifact, md5Expected, md5Actual))
|
|
|
|
if sha1Actual != sha1Expected:
|
|
|
|
raise RuntimeError('SHA1 digest mismatch for %s: expected %s but got %s' % (artifact, sha1Expected, sha1Actual))
|
2012-03-19 13:36:27 -04:00
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
def getDirEntries(urlString):
|
2012-08-07 16:58:46 -04:00
|
|
|
if urlString.startswith('file:/') and not urlString.startswith('file://'):
|
|
|
|
# stupid bogus ant URI
|
|
|
|
urlString = "file:///" + urlString[6:]
|
|
|
|
|
2012-04-08 14:22:14 -04:00
|
|
|
if urlString.startswith('file://'):
|
|
|
|
path = urlString[7:]
|
|
|
|
if path.endswith('/'):
|
|
|
|
path = path[:-1]
|
2012-09-22 21:28:16 -04:00
|
|
|
if cygwin: # Convert Windows path to Cygwin path
|
|
|
|
path = re.sub(r'^/([A-Za-z]):/', r'/cygdrive/\1/', path)
|
2012-04-08 14:22:14 -04:00
|
|
|
l = []
|
|
|
|
for ent in os.listdir(path):
|
|
|
|
entPath = '%s/%s' % (path, ent)
|
|
|
|
if os.path.isdir(entPath):
|
|
|
|
entPath += '/'
|
|
|
|
ent += '/'
|
|
|
|
l.append((ent, 'file://%s' % entPath))
|
|
|
|
l.sort()
|
|
|
|
return l
|
|
|
|
else:
|
|
|
|
links = getHREFs(urlString)
|
|
|
|
for i, (text, subURL) in enumerate(links):
|
|
|
|
if text == 'Parent Directory' or text == '..':
|
|
|
|
return links[(i+1):]
|
2011-06-04 06:37:48 -04:00
|
|
|
|
2016-02-11 15:19:43 -05:00
|
|
|
def unpackAndVerify(java, project, tmpDir, artifact, gitRevision, version, testArgs, baseURL):
|
2011-06-04 06:37:48 -04:00
|
|
|
destDir = '%s/unpack' % tmpDir
|
|
|
|
if os.path.exists(destDir):
|
|
|
|
shutil.rmtree(destDir)
|
|
|
|
os.makedirs(destDir)
|
|
|
|
os.chdir(destDir)
|
2012-09-25 15:09:58 -04:00
|
|
|
print(' unpack %s...' % artifact)
|
2011-06-04 06:37:48 -04:00
|
|
|
unpackLogFile = '%s/%s-unpack-%s.log' % (tmpDir, project, artifact)
|
|
|
|
if artifact.endswith('.tar.gz') or artifact.endswith('.tgz'):
|
|
|
|
run('tar xzf %s/%s' % (tmpDir, artifact), unpackLogFile)
|
|
|
|
elif artifact.endswith('.zip'):
|
|
|
|
run('unzip %s/%s' % (tmpDir, artifact), unpackLogFile)
|
|
|
|
|
|
|
|
# make sure it unpacks to proper subdir
|
|
|
|
l = os.listdir(destDir)
|
2013-01-12 12:51:57 -05:00
|
|
|
expected = '%s-%s' % (project, version)
|
2011-06-04 06:37:48 -04:00
|
|
|
if l != [expected]:
|
|
|
|
raise RuntimeError('unpack produced entries %s; expected only %s' % (l, expected))
|
|
|
|
|
|
|
|
unpackPath = '%s/%s' % (destDir, expected)
|
2016-02-11 15:19:43 -05:00
|
|
|
verifyUnpacked(java, project, artifact, unpackPath, gitRevision, version, testArgs, tmpDir, baseURL)
|
2016-02-12 16:39:35 -05:00
|
|
|
return unpackPath
|
2011-06-04 06:37:48 -04:00
|
|
|
|
2012-09-26 14:33:46 -04:00
|
|
|
LUCENE_NOTICE = None
|
|
|
|
LUCENE_LICENSE = None
|
|
|
|
SOLR_NOTICE = None
|
|
|
|
SOLR_LICENSE = None
|
|
|
|
|
2016-02-11 15:19:43 -05:00
|
|
|
def verifyUnpacked(java, project, artifact, unpackPath, gitRevision, version, testArgs, tmpDir, baseURL):
|
2012-09-26 14:33:46 -04:00
|
|
|
global LUCENE_NOTICE
|
|
|
|
global LUCENE_LICENSE
|
|
|
|
global SOLR_NOTICE
|
|
|
|
global SOLR_LICENSE
|
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
os.chdir(unpackPath)
|
|
|
|
isSrc = artifact.find('-src') != -1
|
2014-09-11 08:55:42 -04:00
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
l = os.listdir(unpackPath)
|
2012-10-18 08:34:08 -04:00
|
|
|
textFiles = ['LICENSE', 'NOTICE', 'README']
|
2011-06-04 06:37:48 -04:00
|
|
|
if project == 'lucene':
|
2012-10-18 08:34:08 -04:00
|
|
|
textFiles.extend(('JRE_VERSION_MIGRATION', 'CHANGES', 'MIGRATE', 'SYSTEM_REQUIREMENTS'))
|
2011-06-04 06:37:48 -04:00
|
|
|
if isSrc:
|
|
|
|
textFiles.append('BUILD')
|
2014-09-11 08:55:42 -04:00
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
for fileName in textFiles:
|
2017-06-30 17:22:37 -04:00
|
|
|
fileNameTxt = fileName + '.txt'
|
|
|
|
fileNameMd = fileName + '.md'
|
|
|
|
if fileNameTxt in l:
|
|
|
|
l.remove(fileNameTxt)
|
|
|
|
elif fileNameMd in l:
|
|
|
|
l.remove(fileNameMd)
|
|
|
|
else:
|
|
|
|
raise RuntimeError('file "%s".[txt|md] is missing from artifact %s' % (fileName, artifact))
|
2011-06-04 06:37:48 -04:00
|
|
|
|
2012-09-26 14:33:46 -04:00
|
|
|
if project == 'lucene':
|
|
|
|
if LUCENE_NOTICE is None:
|
2012-09-29 10:26:53 -04:00
|
|
|
LUCENE_NOTICE = open('%s/NOTICE.txt' % unpackPath, encoding='UTF-8').read()
|
2012-09-26 14:33:46 -04:00
|
|
|
if LUCENE_LICENSE is None:
|
2012-09-29 10:26:53 -04:00
|
|
|
LUCENE_LICENSE = open('%s/LICENSE.txt' % unpackPath, encoding='UTF-8').read()
|
2012-09-26 14:33:46 -04:00
|
|
|
else:
|
|
|
|
if SOLR_NOTICE is None:
|
2012-09-29 10:26:53 -04:00
|
|
|
SOLR_NOTICE = open('%s/NOTICE.txt' % unpackPath, encoding='UTF-8').read()
|
2012-09-26 14:33:46 -04:00
|
|
|
if SOLR_LICENSE is None:
|
2012-09-29 10:26:53 -04:00
|
|
|
SOLR_LICENSE = open('%s/LICENSE.txt' % unpackPath, encoding='UTF-8').read()
|
2012-09-26 14:33:46 -04:00
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
if not isSrc:
|
2012-06-08 05:46:28 -04:00
|
|
|
# TODO: we should add verifyModule/verifySubmodule (e.g. analysis) here and recurse through
|
2011-06-04 06:37:48 -04:00
|
|
|
if project == 'lucene':
|
2012-06-08 05:46:28 -04:00
|
|
|
expectedJARs = ()
|
2011-06-04 06:37:48 -04:00
|
|
|
else:
|
|
|
|
expectedJARs = ()
|
|
|
|
|
|
|
|
for fileName in expectedJARs:
|
|
|
|
fileName += '.jar'
|
|
|
|
if fileName not in l:
|
|
|
|
raise RuntimeError('%s: file "%s" is missing from artifact %s' % (project, fileName, artifact))
|
|
|
|
l.remove(fileName)
|
|
|
|
|
|
|
|
if project == 'lucene':
|
2012-06-08 05:46:28 -04:00
|
|
|
# TODO: clean this up to not be a list of modules that we must maintain
|
2016-02-29 17:15:16 -05:00
|
|
|
extras = ('analysis', 'backward-codecs', 'benchmark', 'classification', 'codecs', 'core', 'demo', 'docs', 'expressions', 'facet', 'grouping', 'highlighter', 'join', 'memory', 'misc', 'queries', 'queryparser', 'replicator', 'sandbox', 'spatial', 'spatial-extras', 'spatial3d', 'suggest', 'test-framework', 'licenses')
|
2011-06-04 06:37:48 -04:00
|
|
|
if isSrc:
|
2016-05-16 19:31:18 -04:00
|
|
|
extras += ('build.xml', 'common-build.xml', 'module-build.xml', 'top-level-ivy-settings.xml', 'default-nested-ivy-settings.xml', 'ivy-versions.properties', 'ivy-ignore-conflicts.properties', 'version.properties', 'tools', 'site')
|
2011-06-04 06:37:48 -04:00
|
|
|
else:
|
|
|
|
extras = ()
|
|
|
|
|
2012-08-03 18:48:27 -04:00
|
|
|
# TODO: if solr, verify lucene/licenses, solr/licenses are present
|
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
for e in extras:
|
|
|
|
if e not in l:
|
|
|
|
raise RuntimeError('%s: %s missing from artifact %s' % (project, e, artifact))
|
|
|
|
l.remove(e)
|
|
|
|
|
|
|
|
if project == 'lucene':
|
|
|
|
if len(l) > 0:
|
|
|
|
raise RuntimeError('%s: unexpected files/dirs in artifact %s: %s' % (project, artifact, l))
|
|
|
|
|
|
|
|
if isSrc:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' make sure no JARs/WARs in src dist...')
|
2012-04-06 09:33:54 -04:00
|
|
|
lines = os.popen('find . -name \\*.jar').readlines()
|
|
|
|
if len(lines) != 0:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' FAILED:')
|
2012-04-06 09:33:54 -04:00
|
|
|
for line in lines:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' %s' % line.strip())
|
2012-04-06 09:33:54 -04:00
|
|
|
raise RuntimeError('source release has JARs...')
|
|
|
|
lines = os.popen('find . -name \\*.war').readlines()
|
|
|
|
if len(lines) != 0:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' FAILED:')
|
2012-04-06 09:33:54 -04:00
|
|
|
for line in lines:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' %s' % line.strip())
|
2012-04-06 09:33:54 -04:00
|
|
|
raise RuntimeError('source release has WARs...')
|
|
|
|
|
2017-04-28 09:58:02 -04:00
|
|
|
# Can't run documentation-lint in lucene src, because dev-tools is missing
|
|
|
|
validateCmd = 'ant validate' if project == 'lucene' else 'ant validate documentation-lint';
|
|
|
|
print(' run "%s"' % validateCmd)
|
|
|
|
java.run_java8(validateCmd, '%s/validate.log' % unpackPath)
|
2012-04-06 09:33:54 -04:00
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
if project == 'lucene':
|
2014-11-20 17:47:21 -05:00
|
|
|
print(" run tests w/ Java 8 and testArgs='%s'..." % testArgs)
|
|
|
|
java.run_java8('ant clean test %s' % testArgs, '%s/test.log' % unpackPath)
|
|
|
|
java.run_java8('ant jar', '%s/compile.log' % unpackPath)
|
|
|
|
testDemo(java.run_java8, isSrc, version, '1.8')
|
2012-10-26 00:53:34 -04:00
|
|
|
|
2014-11-20 17:47:21 -05:00
|
|
|
print(' generate javadocs w/ Java 8...')
|
|
|
|
java.run_java8('ant javadocs', '%s/javadocs.log' % unpackPath)
|
2012-10-26 00:53:34 -04:00
|
|
|
checkJavadocpathFull('%s/build/docs' % unpackPath)
|
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
else:
|
2012-09-06 18:45:52 -04:00
|
|
|
os.chdir('solr')
|
2011-11-21 10:45:07 -05:00
|
|
|
|
2014-11-20 17:47:21 -05:00
|
|
|
print(" run tests w/ Java 8 and testArgs='%s'..." % testArgs)
|
|
|
|
java.run_java8('ant clean test -Dtests.slow=false %s' % testArgs, '%s/test.log' % unpackPath)
|
2014-08-30 11:48:16 -04:00
|
|
|
|
2011-11-21 10:45:07 -05:00
|
|
|
# test javadocs
|
2014-11-20 17:47:21 -05:00
|
|
|
print(' generate javadocs w/ Java 8...')
|
|
|
|
java.run_java8('ant clean javadocs', '%s/javadocs.log' % unpackPath)
|
2012-10-26 00:53:34 -04:00
|
|
|
checkJavadocpathFull('%s/solr/build/docs' % unpackPath, False)
|
2011-11-21 10:45:07 -05:00
|
|
|
|
2014-11-20 17:47:21 -05:00
|
|
|
print(' test solr example w/ Java 8...')
|
2016-03-08 14:36:04 -05:00
|
|
|
java.run_java8('ant clean server', '%s/antexample.log' % unpackPath)
|
2014-11-20 17:47:21 -05:00
|
|
|
testSolrExample(unpackPath, java.java8_home, True)
|
2014-08-30 11:48:16 -04:00
|
|
|
|
|
|
|
os.chdir('..')
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' check NOTICE')
|
2012-04-06 09:33:54 -04:00
|
|
|
testNotice(unpackPath)
|
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
else:
|
2012-09-26 10:51:36 -04:00
|
|
|
|
2016-02-11 15:19:43 -05:00
|
|
|
checkAllJARs(os.getcwd(), project, gitRevision, version, tmpDir, baseURL)
|
2014-09-11 08:55:42 -04:00
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
if project == 'lucene':
|
2014-11-20 17:47:21 -05:00
|
|
|
testDemo(java.run_java8, isSrc, version, '1.8')
|
2014-08-30 11:48:16 -04:00
|
|
|
|
|
|
|
print(' check Lucene\'s javadoc JAR')
|
|
|
|
checkJavadocpath('%s/docs' % unpackPath)
|
2012-09-25 17:08:51 -04:00
|
|
|
|
2012-04-06 09:33:54 -04:00
|
|
|
else:
|
2014-11-20 17:47:21 -05:00
|
|
|
print(' copying unpacked distribution for Java 8 ...')
|
|
|
|
java8UnpackPath = '%s-java8' % unpackPath
|
|
|
|
if os.path.exists(java8UnpackPath):
|
|
|
|
shutil.rmtree(java8UnpackPath)
|
|
|
|
shutil.copytree(unpackPath, java8UnpackPath)
|
|
|
|
os.chdir(java8UnpackPath)
|
|
|
|
print(' test solr example w/ Java 8...')
|
|
|
|
testSolrExample(java8UnpackPath, java.java8_home, False)
|
2012-09-25 15:09:58 -04:00
|
|
|
|
|
|
|
os.chdir(unpackPath)
|
2011-06-04 06:37:48 -04:00
|
|
|
|
2011-06-28 16:51:05 -04:00
|
|
|
testChangesText('.', version, project)
|
|
|
|
|
2014-09-11 08:55:42 -04:00
|
|
|
if project == 'lucene' and isSrc:
|
|
|
|
print(' confirm all releases have coverage in TestBackwardsCompatibility')
|
2016-01-10 14:42:37 -05:00
|
|
|
confirmAllReleasesAreTestedForBackCompat(version, unpackPath)
|
2014-09-11 08:55:42 -04:00
|
|
|
|
|
|
|
|
2012-04-06 09:33:54 -04:00
|
|
|
def testNotice(unpackPath):
|
2012-08-03 17:49:24 -04:00
|
|
|
solrNotice = open('%s/NOTICE.txt' % unpackPath, encoding='UTF-8').read()
|
|
|
|
luceneNotice = open('%s/lucene/NOTICE.txt' % unpackPath, encoding='UTF-8').read()
|
2012-04-06 09:33:54 -04:00
|
|
|
|
|
|
|
expected = """
|
|
|
|
=========================================================================
|
|
|
|
== Apache Lucene Notice ==
|
|
|
|
=========================================================================
|
|
|
|
|
|
|
|
""" + luceneNotice + """---
|
|
|
|
"""
|
|
|
|
|
|
|
|
if solrNotice.find(expected) == -1:
|
|
|
|
raise RuntimeError('Solr\'s NOTICE.txt does not have the verbatim copy, plus header/footer, of Lucene\'s NOTICE.txt')
|
|
|
|
|
2012-08-20 20:16:43 -04:00
|
|
|
def readSolrOutput(p, startupEvent, failureEvent, logFile):
|
2012-04-06 09:33:54 -04:00
|
|
|
f = open(logFile, 'wb')
|
|
|
|
try:
|
|
|
|
while True:
|
2013-04-01 18:04:05 -04:00
|
|
|
line = p.stdout.readline()
|
2012-08-03 18:48:27 -04:00
|
|
|
if len(line) == 0:
|
2012-11-08 09:00:28 -05:00
|
|
|
p.poll()
|
|
|
|
if not startupEvent.isSet():
|
|
|
|
failureEvent.set()
|
|
|
|
startupEvent.set()
|
2012-04-06 09:33:54 -04:00
|
|
|
break
|
|
|
|
f.write(line)
|
|
|
|
f.flush()
|
2012-11-08 09:00:28 -05:00
|
|
|
#print('SOLR: %s' % line.strip())
|
|
|
|
if not startupEvent.isSet():
|
2015-04-22 07:35:31 -04:00
|
|
|
if line.find(b'Started ServerConnector@') != -1 and line.find(b'{HTTP/1.1}{0.0.0.0:8983}') != -1:
|
2012-11-08 09:00:28 -05:00
|
|
|
startupEvent.set()
|
|
|
|
elif p.poll() is not None:
|
|
|
|
failureEvent.set()
|
|
|
|
startupEvent.set()
|
|
|
|
break
|
2012-08-20 20:16:43 -04:00
|
|
|
except:
|
|
|
|
print()
|
|
|
|
print('Exception reading Solr output:')
|
|
|
|
traceback.print_exc()
|
|
|
|
failureEvent.set()
|
2012-11-08 09:00:28 -05:00
|
|
|
startupEvent.set()
|
2012-04-06 09:33:54 -04:00
|
|
|
finally:
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
def testSolrExample(unpackPath, javaPath, isSrc):
|
2016-03-08 14:36:04 -05:00
|
|
|
# test solr using some examples it comes with
|
2012-04-06 09:33:54 -04:00
|
|
|
logFile = '%s/solr-example.log' % unpackPath
|
2014-10-31 21:22:01 -04:00
|
|
|
if isSrc:
|
|
|
|
os.chdir(unpackPath+'/solr')
|
2016-11-08 05:42:35 -05:00
|
|
|
subprocess.call(['chmod','+x',unpackPath+'/solr/bin/solr', unpackPath+'/solr/bin/solr.cmd', unpackPath+'/solr/bin/solr.in.cmd'])
|
2014-10-31 21:22:01 -04:00
|
|
|
else:
|
|
|
|
os.chdir(unpackPath)
|
|
|
|
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' start Solr instance (log=%s)...' % logFile)
|
2012-04-06 09:33:54 -04:00
|
|
|
env = {}
|
|
|
|
env.update(os.environ)
|
|
|
|
env['JAVA_HOME'] = javaPath
|
|
|
|
env['PATH'] = '%s/bin:%s' % (javaPath, env['PATH'])
|
2014-11-18 15:30:37 -05:00
|
|
|
|
2014-11-10 15:57:39 -05:00
|
|
|
# Stop Solr running on port 8983 (in case a previous run didn't shutdown cleanly)
|
2014-11-18 15:30:37 -05:00
|
|
|
try:
|
2016-11-08 05:42:35 -05:00
|
|
|
if not cygwin:
|
|
|
|
subprocess.call(['bin/solr','stop','-p','8983'])
|
|
|
|
else:
|
|
|
|
subprocess.call('env "PATH=`cygpath -S -w`:$PATH" bin/solr.cmd stop -p 8983', shell=True)
|
2014-11-18 15:30:37 -05:00
|
|
|
except:
|
|
|
|
print(' Stop failed due to: '+sys.exc_info()[0])
|
|
|
|
|
2015-08-14 12:00:12 -04:00
|
|
|
print(' Running techproducts example on port 8983 from %s' % unpackPath)
|
2012-04-06 09:33:54 -04:00
|
|
|
try:
|
2016-11-08 05:42:35 -05:00
|
|
|
if not cygwin:
|
|
|
|
runExampleStatus = subprocess.call(['bin/solr','-e','techproducts'])
|
|
|
|
else:
|
|
|
|
runExampleStatus = subprocess.call('env "PATH=`cygpath -S -w`:$PATH" bin/solr.cmd -e techproducts', shell=True)
|
|
|
|
|
2015-08-14 12:00:12 -04:00
|
|
|
if runExampleStatus != 0:
|
|
|
|
raise RuntimeError('Failed to run the techproducts example, check log for previous errors.')
|
2013-03-31 17:30:31 -04:00
|
|
|
|
2014-10-31 21:22:01 -04:00
|
|
|
os.chdir('example')
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' test utf8...')
|
2014-10-31 21:22:01 -04:00
|
|
|
run('sh ./exampledocs/test_utf8.sh http://localhost:8983/solr/techproducts', 'utf8.log')
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' run query...')
|
2015-02-10 13:09:53 -05:00
|
|
|
s = load('http://localhost:8983/solr/techproducts/select/?q=video')
|
2017-07-24 18:36:29 -04:00
|
|
|
if s.find('"numFound":3,"start":0') == -1:
|
2012-08-03 17:18:14 -04:00
|
|
|
print('FAILED: response is:\n%s' % s)
|
2012-04-06 09:33:54 -04:00
|
|
|
raise RuntimeError('query on solr example instance failed')
|
2017-08-20 11:30:15 -04:00
|
|
|
s = load('http://localhost:8983/api/cores')
|
2017-07-24 18:36:29 -04:00
|
|
|
if s.find('"status":0,') == -1:
|
2017-03-10 22:30:52 -05:00
|
|
|
print('FAILED: response is:\n%s' % s)
|
|
|
|
raise RuntimeError('query api v2 on solr example instance failed')
|
2012-04-06 09:33:54 -04:00
|
|
|
finally:
|
|
|
|
# Stop server:
|
2014-10-31 21:22:01 -04:00
|
|
|
print(' stop server using: bin/solr stop -p 8983')
|
|
|
|
if isSrc:
|
|
|
|
os.chdir(unpackPath+'/solr')
|
|
|
|
else:
|
|
|
|
os.chdir(unpackPath)
|
2016-11-08 05:42:35 -05:00
|
|
|
|
|
|
|
if not cygwin:
|
|
|
|
subprocess.call(['bin/solr','stop','-p','8983'])
|
|
|
|
else:
|
|
|
|
subprocess.call('env "PATH=`cygpath -S -w`:$PATH" bin/solr.cmd stop -p 8983', shell=True)
|
2012-04-06 09:33:54 -04:00
|
|
|
|
2014-10-31 21:22:01 -04:00
|
|
|
if isSrc:
|
|
|
|
os.chdir(unpackPath+'/solr')
|
|
|
|
else:
|
|
|
|
os.chdir(unpackPath)
|
2012-04-06 09:33:54 -04:00
|
|
|
|
2012-10-26 00:53:34 -04:00
|
|
|
# the weaker check: we can use this on java6 for some checks,
|
|
|
|
# but its generated HTML is hopelessly broken so we cannot run
|
|
|
|
# the link checking that checkJavadocpathFull does.
|
2012-09-06 18:45:52 -04:00
|
|
|
def checkJavadocpath(path, failOnMissing=True):
|
2012-06-08 06:18:09 -04:00
|
|
|
# check for level='package'
|
|
|
|
# we fail here if its screwed up
|
2012-09-06 18:45:52 -04:00
|
|
|
if failOnMissing and checkJavaDocs.checkPackageSummaries(path, 'package'):
|
2012-06-08 06:18:09 -04:00
|
|
|
raise RuntimeError('missing javadocs package summaries!')
|
|
|
|
|
|
|
|
# now check for level='class'
|
|
|
|
if checkJavaDocs.checkPackageSummaries(path):
|
2012-04-06 09:33:54 -04:00
|
|
|
# disabled: RM cannot fix all this, see LUCENE-3887
|
|
|
|
# raise RuntimeError('javadoc problems')
|
2012-08-03 17:18:14 -04:00
|
|
|
print('\n***WARNING***: javadocs want to fail!\n')
|
2012-06-08 06:18:09 -04:00
|
|
|
|
2012-10-26 00:53:34 -04:00
|
|
|
# full checks
|
|
|
|
def checkJavadocpathFull(path, failOnMissing=True):
|
|
|
|
# check for missing, etc
|
|
|
|
checkJavadocpath(path, failOnMissing)
|
|
|
|
|
|
|
|
# also validate html/check for broken links
|
2012-06-08 06:18:09 -04:00
|
|
|
if checkJavadocLinks.checkAll(path):
|
|
|
|
raise RuntimeError('broken javadocs links found!')
|
2012-03-22 11:24:44 -04:00
|
|
|
|
2014-08-30 11:48:16 -04:00
|
|
|
def testDemo(run_java, isSrc, version, jdk):
|
2012-10-27 13:42:45 -04:00
|
|
|
if os.path.exists('index'):
|
2012-10-26 00:53:34 -04:00
|
|
|
shutil.rmtree('index') # nuke any index from any previous iteration
|
|
|
|
|
|
|
|
print(' test demo with %s...' % jdk)
|
2012-04-08 13:15:54 -04:00
|
|
|
sep = ';' if cygwin else ':'
|
2011-06-04 06:37:48 -04:00
|
|
|
if isSrc:
|
2012-06-08 09:25:04 -04:00
|
|
|
cp = 'build/core/classes/java{0}build/demo/classes/java{0}build/analysis/common/classes/java{0}build/queryparser/classes/java'.format(sep)
|
2012-03-19 13:36:27 -04:00
|
|
|
docsDir = 'core/src'
|
2011-06-04 06:37:48 -04:00
|
|
|
else:
|
2012-06-08 06:18:09 -04:00
|
|
|
cp = 'core/lucene-core-{0}.jar{1}demo/lucene-demo-{0}.jar{1}analysis/common/lucene-analyzers-common-{0}.jar{1}queryparser/lucene-queryparser-{0}.jar'.format(version, sep)
|
2011-06-04 06:37:48 -04:00
|
|
|
docsDir = 'docs'
|
2014-08-30 11:48:16 -04:00
|
|
|
run_java('java -cp "%s" org.apache.lucene.demo.IndexFiles -index index -docs %s' % (cp, docsDir), 'index.log')
|
|
|
|
run_java('java -cp "%s" org.apache.lucene.demo.SearchFiles -index index -query lucene' % cp, 'search.log')
|
2011-06-04 06:37:48 -04:00
|
|
|
reMatchingDocs = re.compile('(\d+) total matching documents')
|
2012-08-03 17:49:24 -04:00
|
|
|
m = reMatchingDocs.search(open('search.log', encoding='UTF-8').read())
|
2011-06-04 06:37:48 -04:00
|
|
|
if m is None:
|
|
|
|
raise RuntimeError('lucene demo\'s SearchFiles found no results')
|
|
|
|
else:
|
|
|
|
numHits = int(m.group(1))
|
|
|
|
if numHits < 100:
|
|
|
|
raise RuntimeError('lucene demo\'s SearchFiles found too few results: %s' % numHits)
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' got %d hits for query "lucene"' % numHits)
|
2014-07-28 15:48:09 -04:00
|
|
|
print(' checkindex with %s...' % jdk)
|
2014-08-30 11:48:16 -04:00
|
|
|
run_java('java -ea -cp "%s" org.apache.lucene.index.CheckIndex index' % cp, 'checkindex.log')
|
2014-07-28 15:48:09 -04:00
|
|
|
s = open('checkindex.log').read()
|
|
|
|
m = re.search(r'^\s+version=(.*?)$', s, re.MULTILINE)
|
|
|
|
if m is None:
|
|
|
|
raise RuntimeError('unable to locate version=NNN output from CheckIndex; see checkindex.log')
|
|
|
|
actualVersion = m.group(1)
|
|
|
|
if removeTrailingZeros(actualVersion) != removeTrailingZeros(version):
|
|
|
|
raise RuntimeError('wrong version from CheckIndex: got "%s" but expected "%s"' % (actualVersion, version))
|
2012-03-19 13:36:27 -04:00
|
|
|
|
2014-07-28 15:48:09 -04:00
|
|
|
def removeTrailingZeros(version):
|
|
|
|
return re.sub(r'(\.0)*$', '', version)
|
2012-03-19 13:36:27 -04:00
|
|
|
|
2016-02-12 16:39:35 -05:00
|
|
|
def checkMaven(solrSrcUnpackPath, baseURL, tmpDir, gitRevision, version, isSigned):
|
2012-03-19 13:36:27 -04:00
|
|
|
POMtemplates = defaultdict()
|
2016-02-12 16:39:35 -05:00
|
|
|
getPOMtemplates(solrSrcUnpackPath, POMtemplates, tmpDir)
|
2013-10-11 12:58:36 -04:00
|
|
|
print(' download artifacts')
|
2012-03-19 13:36:27 -04:00
|
|
|
artifacts = {'lucene': [], 'solr': []}
|
|
|
|
for project in ('lucene', 'solr'):
|
2015-04-06 23:46:42 -04:00
|
|
|
artifactsURL = '%s/%s/maven/org/apache/%s/' % (baseURL, project, project)
|
2012-03-19 13:36:27 -04:00
|
|
|
targetDir = '%s/maven/org/apache/%s' % (tmpDir, project)
|
|
|
|
if not os.path.exists(targetDir):
|
|
|
|
os.makedirs(targetDir)
|
|
|
|
crawl(artifacts[project], artifactsURL, targetDir)
|
2012-08-03 17:18:14 -04:00
|
|
|
print()
|
2012-03-19 13:36:27 -04:00
|
|
|
verifyPOMperBinaryArtifact(artifacts, version)
|
|
|
|
verifyArtifactPerPOMtemplate(POMtemplates, artifacts, tmpDir, version)
|
|
|
|
verifyMavenDigests(artifacts)
|
2013-10-09 13:57:31 -04:00
|
|
|
checkJavadocAndSourceArtifacts(artifacts, version)
|
2012-03-19 13:36:27 -04:00
|
|
|
verifyDeployedPOMsCoordinates(artifacts, version)
|
2012-04-08 14:22:14 -04:00
|
|
|
if isSigned:
|
|
|
|
verifyMavenSigs(baseURL, tmpDir, artifacts)
|
2012-03-19 13:36:27 -04:00
|
|
|
|
2013-10-11 12:58:36 -04:00
|
|
|
distFiles = getBinaryDistFilesForMavenChecks(tmpDir, version, baseURL)
|
|
|
|
checkIdenticalMavenArtifacts(distFiles, artifacts, version)
|
2012-03-19 13:36:27 -04:00
|
|
|
|
2016-02-12 16:39:35 -05:00
|
|
|
checkAllJARs('%s/maven/org/apache/lucene' % tmpDir, 'lucene', gitRevision, version, tmpDir, baseURL)
|
|
|
|
checkAllJARs('%s/maven/org/apache/solr' % tmpDir, 'solr', gitRevision, version, tmpDir, baseURL)
|
2012-09-26 14:33:46 -04:00
|
|
|
|
2013-10-11 12:58:36 -04:00
|
|
|
def getBinaryDistFilesForMavenChecks(tmpDir, version, baseURL):
|
2016-02-12 16:39:35 -05:00
|
|
|
# TODO: refactor distribution unpacking so that it only happens once per distribution per smoker run
|
2013-10-11 12:58:36 -04:00
|
|
|
distFiles = defaultdict()
|
2012-03-19 13:36:27 -04:00
|
|
|
for project in ('lucene', 'solr'):
|
2013-10-11 12:58:36 -04:00
|
|
|
distFiles[project] = getBinaryDistFiles(project, tmpDir, version, baseURL)
|
|
|
|
return distFiles
|
|
|
|
|
|
|
|
def getBinaryDistFiles(project, tmpDir, version, baseURL):
|
|
|
|
distribution = '%s-%s.tgz' % (project, version)
|
|
|
|
if not os.path.exists('%s/%s' % (tmpDir, distribution)):
|
|
|
|
distURL = '%s/%s/%s' % (baseURL, project, distribution)
|
|
|
|
print(' download %s...' % distribution, end=' ')
|
|
|
|
download(distribution, distURL, tmpDir)
|
|
|
|
destDir = '%s/unpack-%s-getBinaryDistFiles' % (tmpDir, project)
|
|
|
|
if os.path.exists(destDir):
|
|
|
|
shutil.rmtree(destDir)
|
|
|
|
os.makedirs(destDir)
|
|
|
|
os.chdir(destDir)
|
|
|
|
print(' unpack %s...' % distribution)
|
|
|
|
unpackLogFile = '%s/unpack-%s-getBinaryDistFiles.log' % (tmpDir, distribution)
|
|
|
|
run('tar xzf %s/%s' % (tmpDir, distribution), unpackLogFile)
|
|
|
|
distributionFiles = []
|
|
|
|
for root, dirs, files in os.walk(destDir):
|
|
|
|
distributionFiles.extend([os.path.join(root, file) for file in files])
|
2012-03-19 13:36:27 -04:00
|
|
|
return distributionFiles
|
|
|
|
|
2013-10-09 13:57:31 -04:00
|
|
|
def checkJavadocAndSourceArtifacts(artifacts, version):
|
2013-10-11 12:58:36 -04:00
|
|
|
print(' check for javadoc and sources artifacts...')
|
2012-03-19 13:36:27 -04:00
|
|
|
for project in ('lucene', 'solr'):
|
|
|
|
for artifact in artifacts[project]:
|
2013-10-09 13:57:31 -04:00
|
|
|
if artifact.endswith(version + '.jar'):
|
2012-03-19 13:36:27 -04:00
|
|
|
javadocJar = artifact[:-4] + '-javadoc.jar'
|
|
|
|
if javadocJar not in artifacts[project]:
|
|
|
|
raise RuntimeError('missing: %s' % javadocJar)
|
|
|
|
sourcesJar = artifact[:-4] + '-sources.jar'
|
|
|
|
if sourcesJar not in artifacts[project]:
|
|
|
|
raise RuntimeError('missing: %s' % sourcesJar)
|
|
|
|
|
2013-04-26 08:03:13 -04:00
|
|
|
def getZipFileEntries(fileName):
|
|
|
|
entries = []
|
|
|
|
with zipfile.ZipFile(fileName) as zf:
|
|
|
|
for zi in zf.infolist():
|
2013-04-26 16:24:22 -04:00
|
|
|
entries.append(zi.filename)
|
2013-04-26 08:03:13 -04:00
|
|
|
# Sort by name:
|
|
|
|
entries.sort()
|
|
|
|
return entries
|
|
|
|
|
2013-10-11 12:58:36 -04:00
|
|
|
def checkIdenticalMavenArtifacts(distFiles, artifacts, version):
|
|
|
|
print(' verify that Maven artifacts are same as in the binary distribution...')
|
2012-03-19 20:21:13 -04:00
|
|
|
reJarWar = re.compile(r'%s\.[wj]ar$' % version) # exclude *-javadoc.jar and *-sources.jar
|
2012-03-19 13:36:27 -04:00
|
|
|
for project in ('lucene', 'solr'):
|
|
|
|
distFilenames = dict()
|
2013-10-11 12:58:36 -04:00
|
|
|
for file in distFiles[project]:
|
2012-03-23 19:47:55 -04:00
|
|
|
baseName = os.path.basename(file)
|
|
|
|
distFilenames[baseName] = file
|
2012-03-19 13:36:27 -04:00
|
|
|
for artifact in artifacts[project]:
|
|
|
|
if reJarWar.search(artifact):
|
2013-10-09 13:57:31 -04:00
|
|
|
artifactFilename = os.path.basename(artifact)
|
|
|
|
if artifactFilename not in distFilenames:
|
|
|
|
raise RuntimeError('Maven artifact %s is not present in %s binary distribution'
|
|
|
|
% (artifact, project))
|
|
|
|
else:
|
2013-10-11 12:58:36 -04:00
|
|
|
identical = filecmp.cmp(artifact, distFilenames[artifactFilename], shallow=False)
|
|
|
|
if not identical:
|
|
|
|
raise RuntimeError('Maven artifact %s is not identical to %s in %s binary distribution'
|
|
|
|
% (artifact, distFilenames[artifactFilename], project))
|
2012-03-19 13:36:27 -04:00
|
|
|
|
|
|
|
def verifyMavenDigests(artifacts):
|
2013-10-11 12:58:36 -04:00
|
|
|
print(" verify Maven artifacts' md5/sha1 digests...")
|
2012-03-19 13:36:27 -04:00
|
|
|
reJarWarPom = re.compile(r'\.(?:[wj]ar|pom)$')
|
|
|
|
for project in ('lucene', 'solr'):
|
|
|
|
for artifactFile in [a for a in artifacts[project] if reJarWarPom.search(a)]:
|
|
|
|
if artifactFile + '.md5' not in artifacts[project]:
|
|
|
|
raise RuntimeError('missing: MD5 digest for %s' % artifactFile)
|
|
|
|
if artifactFile + '.sha1' not in artifacts[project]:
|
|
|
|
raise RuntimeError('missing: SHA1 digest for %s' % artifactFile)
|
2012-08-03 17:49:24 -04:00
|
|
|
with open(artifactFile + '.md5', encoding='UTF-8') as md5File:
|
2012-03-19 13:36:27 -04:00
|
|
|
md5Expected = md5File.read().strip()
|
2012-08-03 17:49:24 -04:00
|
|
|
with open(artifactFile + '.sha1', encoding='UTF-8') as sha1File:
|
2012-03-19 13:36:27 -04:00
|
|
|
sha1Expected = sha1File.read().strip()
|
|
|
|
md5 = hashlib.md5()
|
|
|
|
sha1 = hashlib.sha1()
|
2012-08-03 17:49:24 -04:00
|
|
|
inputFile = open(artifactFile, 'rb')
|
2012-03-19 13:36:27 -04:00
|
|
|
while True:
|
|
|
|
bytes = inputFile.read(65536)
|
2012-08-03 18:48:27 -04:00
|
|
|
if len(bytes) == 0:
|
|
|
|
break
|
2012-03-19 13:36:27 -04:00
|
|
|
md5.update(bytes)
|
|
|
|
sha1.update(bytes)
|
|
|
|
inputFile.close()
|
|
|
|
md5Actual = md5.hexdigest()
|
|
|
|
sha1Actual = sha1.hexdigest()
|
|
|
|
if md5Actual != md5Expected:
|
|
|
|
raise RuntimeError('MD5 digest mismatch for %s: expected %s but got %s'
|
|
|
|
% (artifactFile, md5Expected, md5Actual))
|
|
|
|
if sha1Actual != sha1Expected:
|
|
|
|
raise RuntimeError('SHA1 digest mismatch for %s: expected %s but got %s'
|
|
|
|
% (artifactFile, sha1Expected, sha1Actual))
|
|
|
|
|
|
|
|
def getPOMcoordinate(treeRoot):
|
|
|
|
namespace = '{http://maven.apache.org/POM/4.0.0}'
|
|
|
|
groupId = treeRoot.find('%sgroupId' % namespace)
|
|
|
|
if groupId is None:
|
|
|
|
groupId = treeRoot.find('{0}parent/{0}groupId'.format(namespace))
|
|
|
|
groupId = groupId.text.strip()
|
|
|
|
artifactId = treeRoot.find('%sartifactId' % namespace).text.strip()
|
|
|
|
version = treeRoot.find('%sversion' % namespace)
|
|
|
|
if version is None:
|
|
|
|
version = treeRoot.find('{0}parent/{0}version'.format(namespace))
|
|
|
|
version = version.text.strip()
|
|
|
|
packaging = treeRoot.find('%spackaging' % namespace)
|
|
|
|
packaging = 'jar' if packaging is None else packaging.text.strip()
|
|
|
|
return groupId, artifactId, packaging, version
|
|
|
|
|
|
|
|
def verifyMavenSigs(baseURL, tmpDir, artifacts):
|
2013-10-11 12:58:36 -04:00
|
|
|
print(' verify maven artifact sigs', end=' ')
|
2012-03-19 13:36:27 -04:00
|
|
|
for project in ('lucene', 'solr'):
|
|
|
|
keysFile = '%s/%s.KEYS' % (tmpDir, project)
|
|
|
|
if not os.path.exists(keysFile):
|
|
|
|
keysURL = '%s/%s/KEYS' % (baseURL, project)
|
|
|
|
download('%s.KEYS' % project, keysURL, tmpDir, quiet=True)
|
|
|
|
|
|
|
|
# Set up clean gpg world; import keys file:
|
|
|
|
gpgHomeDir = '%s/%s.gpg' % (tmpDir, project)
|
|
|
|
if os.path.exists(gpgHomeDir):
|
|
|
|
shutil.rmtree(gpgHomeDir)
|
2012-08-03 17:18:14 -04:00
|
|
|
os.makedirs(gpgHomeDir, 0o700)
|
2012-03-19 13:36:27 -04:00
|
|
|
run('gpg --homedir %s --import %s' % (gpgHomeDir, keysFile),
|
|
|
|
'%s/%s.gpg.import.log' % (tmpDir, project))
|
|
|
|
|
|
|
|
reArtifacts = re.compile(r'\.(?:pom|[jw]ar)$')
|
|
|
|
for artifactFile in [a for a in artifacts[project] if reArtifacts.search(a)]:
|
|
|
|
artifact = os.path.basename(artifactFile)
|
|
|
|
sigFile = '%s.asc' % artifactFile
|
|
|
|
# Test sig (this is done with a clean brand-new GPG world)
|
|
|
|
logFile = '%s/%s.%s.gpg.verify.log' % (tmpDir, project, artifact)
|
|
|
|
run('gpg --homedir %s --verify %s %s' % (gpgHomeDir, sigFile, artifactFile),
|
|
|
|
logFile)
|
2014-12-24 00:48:58 -05:00
|
|
|
# Forward any GPG warnings, except the expected one (since it's a clean world)
|
2012-08-03 17:49:24 -04:00
|
|
|
f = open(logFile, encoding='UTF-8')
|
2012-03-19 13:36:27 -04:00
|
|
|
for line in f.readlines():
|
|
|
|
if line.lower().find('warning') != -1 \
|
|
|
|
and line.find('WARNING: This key is not certified with a trusted signature') == -1 \
|
|
|
|
and line.find('WARNING: using insecure memory') == -1:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' GPG: %s' % line.strip())
|
2012-03-19 13:36:27 -04:00
|
|
|
f.close()
|
|
|
|
|
|
|
|
# Test trust (this is done with the real users config)
|
|
|
|
run('gpg --import %s' % keysFile,
|
|
|
|
'%s/%s.gpg.trust.import.log' % (tmpDir, project))
|
|
|
|
logFile = '%s/%s.%s.gpg.trust.log' % (tmpDir, project, artifact)
|
|
|
|
run('gpg --verify %s %s' % (sigFile, artifactFile), logFile)
|
|
|
|
# Forward any GPG warnings:
|
2012-08-03 17:49:24 -04:00
|
|
|
f = open(logFile, encoding='UTF-8')
|
2012-03-19 13:36:27 -04:00
|
|
|
for line in f.readlines():
|
|
|
|
if line.lower().find('warning') != -1 \
|
|
|
|
and line.find('WARNING: This key is not certified with a trusted signature') == -1 \
|
|
|
|
and line.find('WARNING: using insecure memory') == -1:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' GPG: %s' % line.strip())
|
2012-03-19 13:36:27 -04:00
|
|
|
f.close()
|
|
|
|
|
|
|
|
sys.stdout.write('.')
|
2012-08-03 17:18:14 -04:00
|
|
|
print()
|
2012-03-19 13:36:27 -04:00
|
|
|
|
|
|
|
def verifyPOMperBinaryArtifact(artifacts, version):
|
2013-10-11 12:58:36 -04:00
|
|
|
print(' verify that each binary artifact has a deployed POM...')
|
2012-03-19 13:36:27 -04:00
|
|
|
reBinaryJarWar = re.compile(r'%s\.[jw]ar$' % re.escape(version))
|
|
|
|
for project in ('lucene', 'solr'):
|
|
|
|
for artifact in [a for a in artifacts[project] if reBinaryJarWar.search(a)]:
|
|
|
|
POM = artifact[:-4] + '.pom'
|
|
|
|
if POM not in artifacts[project]:
|
|
|
|
raise RuntimeError('missing: POM for %s' % artifact)
|
|
|
|
|
|
|
|
def verifyDeployedPOMsCoordinates(artifacts, version):
|
|
|
|
"""
|
|
|
|
verify that each POM's coordinate (drawn from its content) matches
|
|
|
|
its filepath, and verify that the corresponding artifact exists.
|
|
|
|
"""
|
2013-10-11 12:58:36 -04:00
|
|
|
print(" verify deployed POMs' coordinates...")
|
2012-03-19 13:36:27 -04:00
|
|
|
for project in ('lucene', 'solr'):
|
|
|
|
for POM in [a for a in artifacts[project] if a.endswith('.pom')]:
|
|
|
|
treeRoot = ET.parse(POM).getroot()
|
|
|
|
groupId, artifactId, packaging, POMversion = getPOMcoordinate(treeRoot)
|
|
|
|
POMpath = '%s/%s/%s/%s-%s.pom' \
|
|
|
|
% (groupId.replace('.', '/'), artifactId, version, artifactId, version)
|
|
|
|
if not POM.endswith(POMpath):
|
|
|
|
raise RuntimeError("Mismatch between POM coordinate %s:%s:%s and filepath: %s"
|
|
|
|
% (groupId, artifactId, POMversion, POM))
|
|
|
|
# Verify that the corresponding artifact exists
|
|
|
|
artifact = POM[:-3] + packaging
|
|
|
|
if artifact not in artifacts[project]:
|
|
|
|
raise RuntimeError('Missing corresponding .%s artifact for POM %s' % (packaging, POM))
|
|
|
|
|
|
|
|
def verifyArtifactPerPOMtemplate(POMtemplates, artifacts, tmpDir, version):
|
2013-10-11 12:58:36 -04:00
|
|
|
print(' verify that there is an artifact for each POM template...')
|
2012-03-19 13:36:27 -04:00
|
|
|
namespace = '{http://maven.apache.org/POM/4.0.0}'
|
|
|
|
xpathPlugin = '{0}build/{0}plugins/{0}plugin'.format(namespace)
|
|
|
|
xpathSkipConfiguration = '{0}configuration/{0}skip'.format(namespace)
|
|
|
|
for project in ('lucene', 'solr'):
|
|
|
|
for POMtemplate in POMtemplates[project]:
|
|
|
|
treeRoot = ET.parse(POMtemplate).getroot()
|
|
|
|
skipDeploy = False
|
|
|
|
for plugin in treeRoot.findall(xpathPlugin):
|
|
|
|
artifactId = plugin.find('%sartifactId' % namespace).text.strip()
|
|
|
|
if artifactId == 'maven-deploy-plugin':
|
|
|
|
skip = plugin.find(xpathSkipConfiguration)
|
|
|
|
if skip is not None: skipDeploy = (skip.text.strip().lower() == 'true')
|
|
|
|
if not skipDeploy:
|
|
|
|
groupId, artifactId, packaging, POMversion = getPOMcoordinate(treeRoot)
|
|
|
|
# Ignore POMversion, since its value will not have been interpolated
|
|
|
|
artifact = '%s/maven/%s/%s/%s/%s-%s.%s' \
|
|
|
|
% (tmpDir, groupId.replace('.', '/'), artifactId,
|
|
|
|
version, artifactId, version, packaging)
|
|
|
|
if artifact not in artifacts['lucene'] and artifact not in artifacts['solr']:
|
|
|
|
raise RuntimeError('Missing artifact %s' % artifact)
|
|
|
|
|
2016-02-12 16:39:35 -05:00
|
|
|
def getPOMtemplates(solrSrcUnpackPath, POMtemplates, tmpDir):
|
|
|
|
print(' find pom.xml.template files in the unpacked Solr source distribution')
|
2012-03-19 13:36:27 -04:00
|
|
|
allPOMtemplates = []
|
2016-02-12 16:39:35 -05:00
|
|
|
rePOMtemplate = re.compile(r'^pom\.xml\.template$')
|
|
|
|
for root, dirs, files in os.walk(solrSrcUnpackPath):
|
|
|
|
allPOMtemplates.extend([os.path.join(root, f) for f in files if rePOMtemplate.search(f)])
|
2012-03-19 20:21:13 -04:00
|
|
|
|
2012-09-22 21:28:16 -04:00
|
|
|
reLucenePOMtemplate = re.compile(r'.*/maven/lucene.*/pom\.xml\.template$')
|
|
|
|
POMtemplates['lucene'] = [p for p in allPOMtemplates if reLucenePOMtemplate.search(p)]
|
2012-03-19 13:36:27 -04:00
|
|
|
if POMtemplates['lucene'] is None:
|
2016-02-12 16:39:35 -05:00
|
|
|
raise RuntimeError('No Lucene POMs found at %s' % solrSrcUnpackPath)
|
2012-09-22 21:28:16 -04:00
|
|
|
reSolrPOMtemplate = re.compile(r'.*/maven/solr.*/pom\.xml\.template$')
|
|
|
|
POMtemplates['solr'] = [p for p in allPOMtemplates if reSolrPOMtemplate.search(p)]
|
2012-03-19 13:36:27 -04:00
|
|
|
if POMtemplates['solr'] is None:
|
2016-02-12 16:39:35 -05:00
|
|
|
raise RuntimeError('No Solr POMs found at %s' % solrSrcUnpackPath)
|
2012-03-19 13:36:27 -04:00
|
|
|
POMtemplates['grandfather'] = [p for p in allPOMtemplates if '/maven/pom.xml.template' in p]
|
2012-04-08 14:22:14 -04:00
|
|
|
if len(POMtemplates['grandfather']) == 0:
|
2016-02-12 16:39:35 -05:00
|
|
|
raise RuntimeError('No Lucene/Solr grandfather POM found at %s' % solrSrcUnpackPath)
|
2012-03-19 13:36:27 -04:00
|
|
|
|
|
|
|
def crawl(downloadedFiles, urlString, targetDir, exclusions=set()):
|
|
|
|
for text, subURL in getDirEntries(urlString):
|
|
|
|
if text not in exclusions:
|
|
|
|
path = os.path.join(targetDir, text)
|
|
|
|
if text.endswith('/'):
|
|
|
|
if not os.path.exists(path):
|
|
|
|
os.makedirs(path)
|
|
|
|
crawl(downloadedFiles, subURL, path, exclusions)
|
|
|
|
else:
|
2012-09-26 14:33:46 -04:00
|
|
|
if not os.path.exists(path) or FORCE_CLEAN:
|
2012-03-19 13:36:27 -04:00
|
|
|
download(text, subURL, targetDir, quiet=True)
|
|
|
|
downloadedFiles.append(path)
|
|
|
|
sys.stdout.write('.')
|
|
|
|
|
2014-08-30 11:48:16 -04:00
|
|
|
def make_java_config(parser, java8_home):
|
|
|
|
def _make_runner(java_home, version):
|
|
|
|
print('Java %s JAVA_HOME=%s' % (version, java_home))
|
|
|
|
if cygwin:
|
2016-11-08 05:42:35 -05:00
|
|
|
java_home = subprocess.check_output('cygpath -u "%s"' % java_home, shell=True).decode('utf-8').strip()
|
2014-08-30 11:48:16 -04:00
|
|
|
cmd_prefix = 'export JAVA_HOME="%s" PATH="%s/bin:$PATH" JAVACMD="%s/bin/java"' % \
|
|
|
|
(java_home, java_home, java_home)
|
|
|
|
s = subprocess.check_output('%s; java -version' % cmd_prefix,
|
|
|
|
shell=True, stderr=subprocess.STDOUT).decode('utf-8')
|
|
|
|
if s.find(' version "%s.' % version) == -1:
|
|
|
|
parser.error('got wrong version for java %s:\n%s' % (version, s))
|
|
|
|
def run_java(cmd, logfile):
|
|
|
|
run('%s; %s' % (cmd_prefix, cmd), logfile)
|
|
|
|
return run_java
|
2014-11-20 17:47:21 -05:00
|
|
|
java8_home = os.environ.get('JAVA_HOME')
|
|
|
|
if java8_home is None:
|
2014-08-30 11:48:16 -04:00
|
|
|
parser.error('JAVA_HOME must be set')
|
2014-11-20 17:47:21 -05:00
|
|
|
run_java8 = _make_runner(java8_home, '1.8')
|
2014-08-30 11:48:16 -04:00
|
|
|
|
2014-11-20 17:47:21 -05:00
|
|
|
jc = namedtuple('JavaConfig', 'run_java8 java8_home')
|
|
|
|
return jc(run_java8, java8_home)
|
2014-08-30 11:48:16 -04:00
|
|
|
|
2014-08-28 04:01:50 -04:00
|
|
|
version_re = re.compile(r'(\d+\.\d+\.\d+(-ALPHA|-BETA)?)')
|
2016-02-11 15:19:43 -05:00
|
|
|
revision_re = re.compile(r'rev([a-f\d]+)')
|
2014-08-28 04:01:50 -04:00
|
|
|
def parse_config():
|
|
|
|
epilogue = textwrap.dedent('''
|
|
|
|
Example usage:
|
2016-05-25 16:09:22 -04:00
|
|
|
python3 -u dev-tools/scripts/smokeTestRelease.py https://dist.apache.org/repos/dist/dev/lucene/lucene-solr-6.0.1-RC2-revc7510a0...
|
2014-08-28 04:01:50 -04:00
|
|
|
''')
|
|
|
|
description = 'Utility to test a release.'
|
|
|
|
parser = argparse.ArgumentParser(description=description, epilog=epilogue,
|
|
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
|
|
parser.add_argument('--tmp-dir', metavar='PATH',
|
|
|
|
help='Temporary directory to test inside, defaults to /tmp/smoke_lucene_$version_$revision')
|
|
|
|
parser.add_argument('--not-signed', dest='is_signed', action='store_false', default=True,
|
|
|
|
help='Indicates the release is not signed')
|
|
|
|
parser.add_argument('--revision',
|
2016-02-11 15:19:43 -05:00
|
|
|
help='GIT revision number that release was built with, defaults to that in URL')
|
2014-08-28 04:01:50 -04:00
|
|
|
parser.add_argument('--version', metavar='X.Y.Z(-ALPHA|-BETA)?',
|
|
|
|
help='Version of the release, defaults to that in URL')
|
2014-08-30 11:48:16 -04:00
|
|
|
parser.add_argument('--test-java8', metavar='JAVA8_HOME',
|
|
|
|
help='Path to Java8 home directory, to run tests with if specified')
|
2014-08-28 04:01:50 -04:00
|
|
|
parser.add_argument('url', help='Url pointing to release to test')
|
2014-08-30 11:48:16 -04:00
|
|
|
parser.add_argument('test_args', nargs=argparse.REMAINDER,
|
|
|
|
help='Arguments to pass to ant for testing, e.g. -Dwhat=ever.')
|
2014-08-28 04:01:50 -04:00
|
|
|
c = parser.parse_args()
|
|
|
|
|
|
|
|
if c.version is not None:
|
|
|
|
if not version_re.match(c.version):
|
|
|
|
parser.error('version "%s" does not match format X.Y.Z[-ALPHA|-BETA]' % c.version)
|
|
|
|
else:
|
|
|
|
version_match = version_re.search(c.url)
|
|
|
|
if version_match is None:
|
|
|
|
parser.error('Could not find version in URL')
|
|
|
|
c.version = version_match.group(1)
|
|
|
|
|
|
|
|
if c.revision is None:
|
|
|
|
revision_match = revision_re.search(c.url)
|
|
|
|
if revision_match is None:
|
|
|
|
parser.error('Could not find revision in URL')
|
|
|
|
c.revision = revision_match.group(1)
|
2016-02-11 15:19:43 -05:00
|
|
|
print('Revision: %s' % c.revision)
|
|
|
|
|
2014-08-30 11:48:16 -04:00
|
|
|
c.java = make_java_config(parser, c.test_java8)
|
2014-08-28 04:01:50 -04:00
|
|
|
|
|
|
|
if c.tmp_dir:
|
|
|
|
c.tmp_dir = os.path.abspath(c.tmp_dir)
|
|
|
|
else:
|
|
|
|
tmp = '/tmp/smoke_lucene_%s_%s' % (c.version, c.revision)
|
|
|
|
c.tmp_dir = tmp
|
|
|
|
i = 1
|
|
|
|
while os.path.exists(c.tmp_dir):
|
|
|
|
c.tmp_dir = tmp + '_%d' % i
|
|
|
|
i += 1
|
2012-09-25 17:37:31 -04:00
|
|
|
|
2014-08-28 04:01:50 -04:00
|
|
|
return c
|
2011-06-04 06:37:48 -04:00
|
|
|
|
2014-09-11 08:55:42 -04:00
|
|
|
reVersion1 = re.compile(r'\>(\d+)\.(\d+)\.(\d+)(-alpha|-beta)?/\<', re.IGNORECASE)
|
|
|
|
reVersion2 = re.compile(r'-(\d+)\.(\d+)\.(\d+)(-alpha|-beta)?\.', re.IGNORECASE)
|
|
|
|
|
|
|
|
def getAllLuceneReleases():
|
2015-02-10 13:09:53 -05:00
|
|
|
s = load('https://archive.apache.org/dist/lucene/java')
|
2014-09-11 08:55:42 -04:00
|
|
|
|
|
|
|
releases = set()
|
|
|
|
for r in reVersion1, reVersion2:
|
|
|
|
for tup in r.findall(s):
|
|
|
|
if tup[-1].lower() == '-alpha':
|
|
|
|
tup = tup[:3] + ('0',)
|
|
|
|
elif tup[-1].lower() == '-beta':
|
|
|
|
tup = tup[:3] + ('1',)
|
|
|
|
elif tup[-1] == '':
|
|
|
|
tup = tup[:3]
|
|
|
|
else:
|
|
|
|
raise RuntimeError('failed to parse version: %s' % tup[-1])
|
|
|
|
releases.add(tuple(int(x) for x in tup))
|
|
|
|
|
|
|
|
l = list(releases)
|
|
|
|
l.sort()
|
|
|
|
return l
|
|
|
|
|
2016-01-10 14:42:37 -05:00
|
|
|
def confirmAllReleasesAreTestedForBackCompat(smokeVersion, unpackPath):
|
2014-09-11 08:55:42 -04:00
|
|
|
|
|
|
|
print(' find all past Lucene releases...')
|
|
|
|
allReleases = getAllLuceneReleases()
|
2014-09-25 05:56:14 -04:00
|
|
|
#for tup in allReleases:
|
|
|
|
# print(' %s' % '.'.join(str(x) for x in tup))
|
2014-09-11 08:55:42 -04:00
|
|
|
|
|
|
|
testedIndices = set()
|
|
|
|
|
|
|
|
os.chdir(unpackPath)
|
|
|
|
|
2015-05-29 10:17:04 -04:00
|
|
|
print(' run TestBackwardsCompatibility..')
|
|
|
|
command = 'ant test -Dtestcase=TestBackwardsCompatibility -Dtests.verbose=true'
|
|
|
|
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
|
|
stdout, stderr = p.communicate()
|
|
|
|
if p.returncode is not 0:
|
|
|
|
# Not good: the test failed!
|
|
|
|
raise RuntimeError('%s failed:\n%s' % (command, stdout))
|
2016-11-08 05:42:35 -05:00
|
|
|
stdout = stdout.decode('utf-8',errors='replace').replace('\r\n','\n')
|
2015-05-29 10:17:04 -04:00
|
|
|
|
|
|
|
if stderr is not None:
|
|
|
|
# Should not happen since we redirected stderr to stdout:
|
|
|
|
raise RuntimeError('stderr non-empty')
|
|
|
|
|
|
|
|
reIndexName = re.compile(r'TEST: index[\s*=\s*](.*?)(-cfs|-nocfs)$', re.MULTILINE)
|
|
|
|
for name, cfsPart in reIndexName.findall(stdout):
|
|
|
|
# Fragile: decode the inconsistent naming schemes we've used in TestBWC's indices:
|
|
|
|
#print('parse name %s' % name)
|
|
|
|
tup = tuple(name.split('.'))
|
|
|
|
if len(tup) == 3:
|
|
|
|
# ok
|
|
|
|
tup = tuple(int(x) for x in tup)
|
|
|
|
elif tup == ('4', '0', '0', '1'):
|
|
|
|
# CONFUSING: this is the 4.0.0-alpha index??
|
|
|
|
tup = 4, 0, 0, 0
|
|
|
|
elif tup == ('4', '0', '0', '2'):
|
|
|
|
# CONFUSING: this is the 4.0.0-beta index??
|
|
|
|
tup = 4, 0, 0, 1
|
|
|
|
elif name == '5x-with-4x-segments':
|
|
|
|
# Mixed version test case; ignore it for our purposes because we only
|
|
|
|
# tally up the "tests single Lucene version" indices
|
|
|
|
continue
|
2016-03-07 10:38:04 -05:00
|
|
|
elif name == '5.0.0.singlesegment':
|
|
|
|
tup = 5, 0, 0
|
2015-05-29 10:17:04 -04:00
|
|
|
else:
|
|
|
|
raise RuntimeError('could not parse version %s' % name)
|
|
|
|
|
|
|
|
testedIndices.add(tup)
|
2014-09-11 08:55:42 -04:00
|
|
|
|
|
|
|
l = list(testedIndices)
|
|
|
|
l.sort()
|
|
|
|
if False:
|
|
|
|
for release in l:
|
|
|
|
print(' %s' % '.'.join(str(x) for x in release))
|
|
|
|
|
|
|
|
allReleases = set(allReleases)
|
|
|
|
|
|
|
|
for x in testedIndices:
|
|
|
|
if x not in allReleases:
|
|
|
|
# Curious: we test 1.9.0 index but it's not in the releases (I think it was pulled because of nasty bug?)
|
|
|
|
if x != (1, 9, 0):
|
|
|
|
raise RuntimeError('tested version=%s but it was not released?' % '.'.join(str(y) for y in x))
|
|
|
|
|
|
|
|
notTested = []
|
|
|
|
for x in allReleases:
|
|
|
|
if x not in testedIndices:
|
2016-01-10 14:42:37 -05:00
|
|
|
releaseVersion = '.'.join(str(y) for y in x)
|
|
|
|
if releaseVersion in ('1.4.3', '1.9.1', '2.3.1', '2.3.2'):
|
2014-09-11 08:55:42 -04:00
|
|
|
# Exempt the dark ages indices
|
|
|
|
continue
|
2016-01-10 14:42:37 -05:00
|
|
|
if x >= tuple(int(y) for y in smokeVersion.split('.')):
|
|
|
|
# Exempt versions not less than the one being smoke tested
|
|
|
|
print(' Backcompat testing not required for release %s because it\'s not less than %s'
|
|
|
|
% (releaseVersion, smokeVersion))
|
|
|
|
continue
|
2014-09-11 08:55:42 -04:00
|
|
|
notTested.append(x)
|
|
|
|
|
|
|
|
if len(notTested) > 0:
|
|
|
|
notTested.sort()
|
|
|
|
print('Releases that don\'t seem to be tested:')
|
|
|
|
failed = True
|
|
|
|
for x in notTested:
|
|
|
|
print(' %s' % '.'.join(str(y) for y in x))
|
|
|
|
raise RuntimeError('some releases are not tested by TestBackwardsCompatibility?')
|
|
|
|
else:
|
|
|
|
print(' success!')
|
|
|
|
|
2016-05-25 16:09:22 -04:00
|
|
|
def getScriptVersion():
|
|
|
|
topLevelDir = '../..' # Assumption: this script is in dev-tools/scripts/ of a checkout
|
|
|
|
m = re.compile(r'(.*)/').match(sys.argv[0]) # Get this script's directory
|
|
|
|
if m is not None and m.group(1) != '.':
|
|
|
|
origCwd = os.getcwd()
|
|
|
|
os.chdir(m.group(1))
|
|
|
|
os.chdir('../..')
|
|
|
|
topLevelDir = os.getcwd()
|
|
|
|
os.chdir(origCwd)
|
|
|
|
reBaseVersion = re.compile(r'version\.base\s*=\s*(\d+\.\d+)')
|
|
|
|
return reBaseVersion.search(open('%s/lucene/version.properties' % topLevelDir).read()).group(1)
|
|
|
|
|
2014-08-28 04:01:50 -04:00
|
|
|
def main():
|
|
|
|
c = parse_config()
|
2016-05-25 16:09:22 -04:00
|
|
|
|
|
|
|
scriptVersion = getScriptVersion()
|
|
|
|
if not c.version.startswith(scriptVersion + '.'):
|
|
|
|
raise RuntimeError('smokeTestRelease.py for %s.X is incompatible with a %s release.' % (scriptVersion, c.version))
|
|
|
|
|
2014-08-28 04:01:50 -04:00
|
|
|
print('NOTE: output encoding is %s' % sys.stdout.encoding)
|
2014-08-30 11:48:16 -04:00
|
|
|
smokeTest(c.java, c.url, c.revision, c.version, c.tmp_dir, c.is_signed, ' '.join(c.test_args))
|
2016-02-11 15:19:43 -05:00
|
|
|
|
|
|
|
def smokeTest(java, baseURL, gitRevision, version, tmpDir, isSigned, testArgs):
|
2012-04-08 14:22:14 -04:00
|
|
|
|
2013-04-21 09:26:54 -04:00
|
|
|
startTime = datetime.datetime.now()
|
|
|
|
|
2012-09-26 14:33:46 -04:00
|
|
|
if FORCE_CLEAN:
|
2011-06-04 06:37:48 -04:00
|
|
|
if os.path.exists(tmpDir):
|
|
|
|
raise RuntimeError('temp dir %s exists; please remove first' % tmpDir)
|
2012-04-06 09:33:54 -04:00
|
|
|
|
|
|
|
if not os.path.exists(tmpDir):
|
2011-06-04 06:37:48 -04:00
|
|
|
os.makedirs(tmpDir)
|
|
|
|
|
|
|
|
lucenePath = None
|
|
|
|
solrPath = None
|
2012-08-03 17:18:14 -04:00
|
|
|
print()
|
|
|
|
print('Load release URL "%s"...' % baseURL)
|
2012-04-08 14:22:14 -04:00
|
|
|
newBaseURL = unshortenURL(baseURL)
|
|
|
|
if newBaseURL != baseURL:
|
2012-08-03 17:18:14 -04:00
|
|
|
print(' unshortened: %s' % newBaseURL)
|
2012-04-08 14:22:14 -04:00
|
|
|
baseURL = newBaseURL
|
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
for text, subURL in getDirEntries(baseURL):
|
|
|
|
if text.lower().find('lucene') != -1:
|
|
|
|
lucenePath = subURL
|
|
|
|
elif text.lower().find('solr') != -1:
|
|
|
|
solrPath = subURL
|
|
|
|
|
|
|
|
if lucenePath is None:
|
|
|
|
raise RuntimeError('could not find lucene subdir')
|
|
|
|
if solrPath is None:
|
|
|
|
raise RuntimeError('could not find solr subdir')
|
|
|
|
|
2012-08-03 17:18:14 -04:00
|
|
|
print()
|
|
|
|
print('Test Lucene...')
|
2012-04-08 14:22:14 -04:00
|
|
|
checkSigs('lucene', lucenePath, version, tmpDir, isSigned)
|
2011-06-04 06:37:48 -04:00
|
|
|
for artifact in ('lucene-%s.tgz' % version, 'lucene-%s.zip' % version):
|
2016-02-11 15:19:43 -05:00
|
|
|
unpackAndVerify(java, 'lucene', tmpDir, artifact, gitRevision, version, testArgs, baseURL)
|
|
|
|
unpackAndVerify(java, 'lucene', tmpDir, 'lucene-%s-src.tgz' % version, gitRevision, version, testArgs, baseURL)
|
2011-06-04 06:37:48 -04:00
|
|
|
|
2012-08-03 17:18:14 -04:00
|
|
|
print()
|
|
|
|
print('Test Solr...')
|
2012-04-08 14:22:14 -04:00
|
|
|
checkSigs('solr', solrPath, version, tmpDir, isSigned)
|
2013-01-12 12:51:57 -05:00
|
|
|
for artifact in ('solr-%s.tgz' % version, 'solr-%s.zip' % version):
|
2016-02-11 15:19:43 -05:00
|
|
|
unpackAndVerify(java, 'solr', tmpDir, artifact, gitRevision, version, testArgs, baseURL)
|
2016-02-12 16:39:35 -05:00
|
|
|
solrSrcUnpackPath = unpackAndVerify(java, 'solr', tmpDir, 'solr-%s-src.tgz' % version,
|
|
|
|
gitRevision, version, testArgs, baseURL)
|
2011-06-04 06:37:48 -04:00
|
|
|
|
2012-09-26 14:33:46 -04:00
|
|
|
print()
|
2012-08-03 17:18:14 -04:00
|
|
|
print('Test Maven artifacts for Lucene and Solr...')
|
2016-02-12 16:39:35 -05:00
|
|
|
checkMaven(solrSrcUnpackPath, baseURL, tmpDir, gitRevision, version, isSigned)
|
2012-03-19 13:36:27 -04:00
|
|
|
|
2013-04-21 09:26:54 -04:00
|
|
|
print('\nSUCCESS! [%s]\n' % (datetime.datetime.now() - startTime))
|
2012-09-24 15:37:53 -04:00
|
|
|
|
2011-06-04 06:37:48 -04:00
|
|
|
if __name__ == '__main__':
|
2012-08-03 18:48:27 -04:00
|
|
|
try:
|
|
|
|
main()
|
2014-08-28 04:01:50 -04:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
print('Keyboard interrupt...exiting')
|
|
|
|
|