blob: 22bb96df639b5cddc70ef0e755fbb21ee6539526 [file] [log] [blame]
#!/bin/bash
# -*- indent-tabs-mode: nil; sh-indentation: 2; sh-basic-offset: 2 -*-
#
# 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.
#
set -e
set -u
set -o pipefail
SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [ "$#" -ne 4 ]; then
echo "Usage: $0 <version> <rc-num> <gpg-key-id> <artifact-dir>"
exit
fi
version=$1
rc=$2
gpg_key_id=$3
artifact_dir=$4
docker_image_name=apache-arrow/release-binary
gpg_agent_extra_socket="$(gpgconf --list-dirs agent-extra-socket)"
if [ $(uname) = "Darwin" ]; then
docker_uid=10000
docker_gid=10000
else
docker_uid=$(id -u)
docker_gid=$(id -g)
fi
docker_ssh_key="${SOURCE_DIR}/binary/id_rsa"
if [ -z "$artifact_dir" ]; then
echo "artifact_dir is empty"
exit 1
fi
if [ ! -e "$artifact_dir" ]; then
echo "$artifact_dir does not exist"
exit 1
fi
if [ ! -d "$artifact_dir" ]; then
echo "$artifact_dir is not a directory"
exit 1
fi
if [ -z "${BINTRAY_PASSWORD}" ]; then
echo "BINTRAY_PASSWORD is empty"
exit 1
fi
if ! jq --help > /dev/null 2>&1; then
echo "jq is required"
exit 1
fi
: ${BINTRAY_REPOSITORY:=apache/arrow}
: ${SOURCE_BINTRAY_REPOSITORY:=${BINTRAY_REPOSITORY}}
BINTRAY_DOWNLOAD_URL_BASE=https://dl.bintray.com
docker_run() {
docker \
run \
--rm \
--tty \
--interactive \
--user ${docker_uid}:${docker_gid} \
--volume "$PWD":/host \
${docker_image_name} \
bash -c "cd /host && $*"
}
docker_gpg_ssh() {
local ssh_port=$1
shift
local known_hosts_file=$(mktemp -t "arrow-binary-gpg-ssh-known-hosts.XXXXX")
local exit_code=
if ssh \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=${known_hosts_file} \
-i "${docker_ssh_key}" \
-p ${ssh_port} \
-R "/home/arrow/.gnupg/S.gpg-agent:${gpg_agent_extra_socket}" \
arrow@127.0.0.1 \
"$@"; then
exit_code=$?;
else
exit_code=$?;
fi
rm -f ${known_hosts_file}
return ${exit_code}
}
docker_run_gpg_ready() {
local container_id_dir=$(mktemp -d -t "arrow-binary-gpg-container.XXXXX")
local container_id_file=${container_id_dir}/id
docker \
run \
--rm \
--detach \
--cidfile ${container_id_file} \
--publish-all \
--volume "$PWD":/host \
${docker_image_name} \
bash -c "
if [ \$(id -u) -ne ${docker_uid} ]; then
usermod --uid ${docker_uid} arrow
chown -R arrow: ~arrow
fi
/usr/sbin/sshd -D
"
local container_id=$(cat ${container_id_file})
local ssh_port=$(docker port ${container_id} | grep -E -o '[0-9]+$')
# Wait for sshd available
while ! docker_gpg_ssh ${ssh_port} : > /dev/null 2>&1; do
sleep 0.1
done
gpg --export ${gpg_key_id} | docker_gpg_ssh ${ssh_port} gpg --import
docker_gpg_ssh ${ssh_port} "cd /host && $@"
docker kill ${container_id}
rm -rf ${container_id_dir}
}
bintray() {
local command=$1
shift
local path=$1
shift
local url=https://bintray.com/api/v1${path}
echo "${command} ${url}" 1>&2
curl \
--fail \
--basic \
--user "${BINTRAY_USER:-$USER}:${BINTRAY_PASSWORD}" \
--header "Content-Type: application/json" \
--request ${command} \
${url} \
"$@" | \
jq .
}
ensure_version() {
local version=$1
local rc=$2
local target=$3
local version_name=${version}-rc${rc}
if ! bintray \
GET \
/packages/${BINTRAY_REPOSITORY}/${target}-rc/versions/${version_name}; then
bintray \
POST /packages/${BINTRAY_REPOSITORY}/${target}-rc/versions \
--data-binary "
{
\"name\": \"${version_name}\",
\"desc\": \"Apache Arrow ${version} RC${rc}.\"
}
"
fi
}
download_files() {
local version=$1
local rc=$2
local target=$3
local version_name=${version}-rc${rc}
local files=$(
bintray \
GET /packages/${SOURCE_BINTRAY_REPOSITORY}/${target}-rc/versions/${version_name}/files | \
jq -r ".[].path")
local file=
for file in ${files}; do
mkdir -p "$(dirname ${file})"
curl \
--fail \
--location \
--output ${file} \
${BINTRAY_DOWNLOAD_URL_BASE}/${SOURCE_BINTRAY_REPOSITORY}/${file} &
done
}
delete_file() {
local version=$1
local rc=$2
local target=$3
local upload_path=$4
local version_name=${version}-rc${rc}
bintray \
DELETE /content/${BINTRAY_REPOSITORY}/${target}-rc/${upload_path}
}
upload_file() {
local version=$1
local rc=$2
local target=$3
local local_path=$4
local upload_path=$5
local version_name=${version}-rc${rc}
local sha256=$(shasum -a 256 ${local_path} | awk '{print $1}')
local request_path=/content/${BINTRAY_REPOSITORY}/${target}-rc/${version_name}/${target}-rc/${upload_path}
if ! bintray \
PUT ${request_path} \
--header "X-Bintray-Publish: 1" \
--header "X-Bintray-Override: 1" \
--header "X-Checksum-Sha2: ${sha256}" \
--data-binary "@${local_path}"; then
delete_file ${version} ${rc} ${target} ${upload_path}
bintray \
PUT ${request_path} \
--header "X-Bintray-Publish: 1" \
--header "X-Bintray-Override: 1" \
--header "X-Checksum-Sha2: ${sha256}" \
--data-binary "@${local_path}"
fi
}
sign_and_upload_file() {
local version=$1
local rc=$2
local target=$3
local local_path=$4
local upload_path=$5
local sha256=$(shasum -a 256 ${local_path} | awk '{print $1}')
local download_path=/${BINTRAY_REPOSITORY}/${target}-rc/${upload_path}
local source_upload=no
if ! curl \
--fail \
--head \
${BINTRAY_DOWNLOAD_URL_BASE}${download_path} | \
grep -q "^X-Checksum-Sha2: ${sha256}"; then
upload_file ${version} ${rc} ${target} ${local_path} ${upload_path}
source_upload=yes
fi
local suffix=
for suffix in asc sha512; do
pushd $(dirname ${local_path})
local local_path_base=$(basename ${local_path})
local output_dir=$(mktemp -d -t "arrow-binary-sign.XXXXX")
local output=${output_dir}/tmp.${suffix}
case $suffix in
asc)
gpg \
--local-user ${gpg_key_id} \
--detach-sig \
--output ${output} \
${local_path_base}
;;
sha*)
shasum \
--algorithm $(echo $suffix | sed -e 's/^sha//') \
${local_path_base} > ${output}
;;
esac
local need_upload=no
if [ "${source_upload}" = "yes" ]; then
need_upload=yes
elif ! curl \
--fail \
--head \
${BINTRAY_DOWNLOAD_URL_BASE}${download_path}.${suffix}; then
need_upload=yes
fi
if [ "${need_upload}" = "yes" ]; then
upload_file ${version} ${rc} ${target} ${output} ${upload_path}.${suffix}
fi
rm -rf ${output_dir}
popd
done
}
upload_deb_file() {
local version=$1
local rc=$2
local distribution=$3
local code_name=$4
local base_path=$5
case ${base_path} in
*.dsc|*.changes)
docker_run_gpg_ready debsign -k${gpg_key_id} --re-sign ${base_path}
;;
*.asc|*.sha256|*.sha512)
return
;;
esac
sign_and_upload_file \
${version} \
${rc} \
${distribution} \
${base_path} \
pool/${code_name}/main/a/apache-arrow/${base_path}
}
upload_deb() {
local version=$1
local rc=$2
local distribution=$3
local code_name=$4
local base_path=
for base_path in *; do
upload_deb_file ${version} ${rc} ${distribution} ${code_name} ${base_path} &
done
wait
}
upload_apt() {
local version=$1
local rc=$2
local distribution=$3
local tmp_dir=tmp/${distribution}
rm -rf ${tmp_dir}
mkdir -p ${tmp_dir}
pushd ${tmp_dir}
download_files ${version} ${rc} ${distribution}
wait
pushd ${distribution}-rc
local keyring_name=apache-arrow-keyring.gpg
rm -f ${keyring_name}
curl --fail https://dist.apache.org/repos/dist/dev/arrow/KEYS | \
gpg \
--no-default-keyring \
--keyring ./${keyring_name} \
--import - || : # XXX: Ignore gpg error
sign_and_upload_file \
${version} \
${rc} \
${distribution} \
${keyring_name} \
${keyring_name} &
local pool_code_name=
for pool_code_name in pool/*; do
local code_name=$(basename ${pool_code_name})
local dist=dists/${code_name}/main
rm -rf dists
mkdir -p ${dist}/{source,binary-amd64}
docker_run apt-ftparchive \
sources pool/${code_name} > \
dists/${code_name}/main/source/Sources
gzip --keep dists/${code_name}/main/source/Sources
xz --keep dists/${code_name}/main/source/Sources
docker_run apt-ftparchive \
packages pool/${code_name} > \
dists/${code_name}/main/binary-amd64/Packages
gzip --keep dists/${code_name}/main/binary-amd64/Packages
xz --keep dists/${code_name}/main/binary-amd64/Packages
docker_run apt-ftparchive \
contents pool/${code_name} > \
dists/${code_name}/main/Contents-amd64
gzip --keep dists/${code_name}/main/Contents-amd64
docker_run apt-ftparchive \
release \
-o "APT::FTPArchive::Release::Origin=Apache\\ Arrow" \
-o "APT::FTPArchive::Release::Label=Apache\\ Arrow" \
-o APT::FTPArchive::Release::Codename=${code_name} \
-o APT::FTPArchive::Release::Architectures=amd64 \
-o APT::FTPArchive::Release::Components=main \
dists/${code_name} > \
dists/${code_name}/Release
gpg \
--local-user ${gpg_key_id} \
--sign \
--detach-sign \
--armor \
--output dists/${code_name}/Release.gpg \
dists/${code_name}/Release
local path=
for path in $(find dists/${code_name}/ -type f); do
sign_and_upload_file \
${version} \
${rc} \
${distribution} \
${path} \
${path} &
done
wait
done
popd
popd
rm -rf ${tmp_dir}
}
upload_rpm_file() {
local version=$1
local rc=$2
local distribution=$3
local distribution_version=$4
local rpm_path=$5
local upload_path=${distribution_version}
case ${rpm_path} in
*.src.rpm)
upload_path=${upload_path}/Source/SPackages
;;
*)
upload_path=${upload_path}/x86_64/Packages
;;
esac
upload_path=${upload_path}/${rpm_path}
docker_run_gpg_ready rpm \
-D "_gpg_name\\ ${gpg_key_id}" \
--addsign \
${rpm_path}
sign_and_upload_file \
${version} \
${rc} \
${distribution} \
${rpm_path} \
${upload_path}
}
upload_rpm() {
local version=$1
local rc=$2
local distribution=$3
local distribution_version=$4
local rpm_path=
for rpm_path in *.rpm; do
upload_rpm_file \
${version} \
${rc} \
${distribution} \
${distribution_version} \
${rpm_path} &
done
wait
}
upload_yum() {
local version=$1
local rc=$2
local distribution=$3
local version_name=${version}-rc${rc}
local tmp_dir=tmp/${distribution}
rm -rf ${tmp_dir}
mkdir -p ${tmp_dir}
pushd ${tmp_dir}
download_files ${version} ${rc} ${distribution}
wait
pushd ${distribution}-rc
local keyring_name=RPM-GPG-KEY-apache-arrow
curl -o ${keyring_name} https://dist.apache.org/repos/dist/dev/arrow/KEYS
sign_and_upload_file \
${version} \
${rc} \
${distribution} \
${keyring_name} \
${keyring_name} &
local version_dir=
local arch_dir=
local repo_path=
for version_dir in $(find . -mindepth 1 -maxdepth 1 -type d); do
for arch_dir in ${version_dir}/*; do
mkdir -p ${arch_dir}/repodata/
docker_run createrepo ${arch_dir}
for repo_path in ${arch_dir}/repodata/*; do
sign_and_upload_file \
${version} \
${rc} \
${distribution} \
${repo_path} \
${repo_path} &
done
done
done
wait
popd
popd
rm -rf ${tmp_dir}
}
upload_python() {
local version=$1
local rc=$2
local target=python
local base_path=
for base_path in *; do
case ${base_path} in
*.asc|*.sha256|*.sha512)
continue
;;
esac
sign_and_upload_file \
${version} \
${rc} \
${target} \
${base_path} \
${version}-rc${rc}/${base_path} &
done
wait
}
docker build -t ${docker_image_name} ${SOURCE_DIR}/binary
chmod go-rwx "${docker_ssh_key}"
# By default upload all artifacts.
# To deactivate one category, deactivate the category and all of its dependents.
# To explicitly select one category, set UPLOAD_DEFAULT=0 UPLOAD_X=1.
: ${UPLOAD_DEFAULT:=1}
: ${UPLOAD_CENTOS_RPM:=${UPLOAD_DEFAULT}}
: ${UPLOAD_CENTOS_YUM:=${UPLOAD_DEFAULT}}
: ${UPLOAD_DEBIAN_APT:=${UPLOAD_DEFAULT}}
: ${UPLOAD_DEBIAN_DEB:=${UPLOAD_DEFAULT}}
: ${UPLOAD_PYTHON:=${UPLOAD_DEFAULT}}
: ${UPLOAD_UBUNTU_APT:=${UPLOAD_DEFAULT}}
: ${UPLOAD_UBUNTU_DEB:=${UPLOAD_DEFAULT}}
have_debian=no
have_ubuntu=no
have_centos=no
have_python=no
pushd "${artifact_dir}"
for dir in *; do
is_deb=no
is_rpm=no
is_python=no
case ${dir} in
debian-*)
distribution=debian
code_name=$(echo ${dir} | sed -e 's/^debian-//')
is_deb=yes
have_debian=yes
;;
ubuntu-*)
distribution=ubuntu
code_name=$(echo ${dir} | sed -e 's/^ubuntu-//')
is_deb=yes
have_ubuntu=yes
;;
centos-*)
distribution=centos
distribution_version=$(echo ${dir} | sed -e 's/^centos-//')
is_rpm=yes
have_centos=yes
;;
conda-*|wheel-*)
is_python=yes
have_python=yes
;;
esac
if [ ${is_deb} = "yes" ]; then
pushd ${dir}
case ${distribution} in
debian)
if [ ${UPLOAD_DEBIAN_DEB} -gt 0 ]; then
ensure_version ${version} ${rc} ${distribution}
upload_deb ${version} ${rc} ${distribution} ${code_name} &
fi
;;
ubuntu)
if [ ${UPLOAD_UBUNTU_DEB} -gt 0 ]; then
ensure_version ${version} ${rc} ${distribution}
upload_deb ${version} ${rc} ${distribution} ${code_name} &
fi
;;
esac
popd
elif [ ${is_rpm} = "yes" ]; then
pushd ${dir}
if [ ${UPLOAD_CENTOS_RPM} -gt 0 ]; then
ensure_version ${version} ${rc} ${distribution}
upload_rpm ${version} ${rc} ${distribution} ${distribution_version} &
fi
popd
elif [ ${is_python} = "yes" ]; then
pushd ${dir}
if [ ${UPLOAD_PYTHON} -gt 0 ]; then
ensure_version ${version} ${rc} python
upload_python ${version} ${rc} &
fi
popd
fi
done
wait
popd
if [ ${have_debian} = "yes" ]; then
if [ ${UPLOAD_DEBIAN_APT} -gt 0 ]; then
ensure_version ${version} ${rc} debian
upload_apt ${version} ${rc} debian
fi
fi
if [ ${have_ubuntu} = "yes" ]; then
if [ ${UPLOAD_UBUNTU_APT} -gt 0 ]; then
ensure_version ${version} ${rc} ubuntu
upload_apt ${version} ${rc} ubuntu
fi
fi
if [ ${have_centos} = "yes" ]; then
if [ ${UPLOAD_CENTOS_YUM} -gt 0 ]; then
ensure_version ${version} ${rc} centos
upload_yum ${version} ${rc} centos
fi
fi
echo "Success! The release candidate binaries are available here:"
if [ ${have_debian} = "yes" ]; then
echo " https://bintray.com/${BINTRAY_REPOSITORY}/debian-rc/${version}-rc${rc}"
fi
if [ ${have_ubuntu} = "yes" ]; then
echo " https://bintray.com/${BINTRAY_REPOSITORY}/ubuntu-rc/${version}-rc${rc}"
fi
if [ ${have_centos} = "yes" ]; then
echo " https://bintray.com/${BINTRAY_REPOSITORY}/centos-rc/${version}-rc${rc}"
fi
if [ ${have_python} = "yes" ]; then
echo " https://bintray.com/${BINTRAY_REPOSITORY}/python-rc/${version}-rc${rc}"
fi