This is an automated email from the ASF dual-hosted git repository.
francischuang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite-avatica-go.git
The following commit(s) were added to refs/heads/master by this push:
new 3fd5360 [CALCITE-3032] Improve release script
3fd5360 is described below
commit 3fd53603b57f36a4f5208f449dec71c236722a00
Author: Francis Chuang <[email protected]>
AuthorDate: Fri Apr 26 15:43:22 2019 +1000
[CALCITE-3032] Improve release script
---
Dockerfile | 23 --
docker-compose.yml | 83 +++++-
docker.sh | 643 +++++++++++++++++++++++++++++++++++++++++++++
entrypoint.sh | 24 --
gen-protobuf.bat | 37 ---
gen-protobuf.sh | 42 ---
make-release-artifacts.sh | 187 -------------
site/develop/avatica-go.md | 99 +++++--
8 files changed, 796 insertions(+), 342 deletions(-)
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index d1645d7..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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.
-
-FROM golang:1.12-alpine as base
-
-ENV DEV ""
-
-RUN apk --no-cache --no-progress add build-base git
-RUN go get github.com/unchartedsoftware/witch
-WORKDIR /source
-ENTRYPOINT ["sh", "./entrypoint.sh"]
diff --git a/docker-compose.yml b/docker-compose.yml
index bb3889b..91a89f1 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -15,17 +15,84 @@
version: '3'
services:
- calcite-avatica-go:
- build: .
+ phoenix:
+ image: boostport/hbase-phoenix-all-in-one:2.0-5.0
+
+ hsqldb:
+ image: apache/calcite-avatica-hypersql:1.13.0
+ command: -u jdbc:hsqldb:mem:public
+
+ test:
+ image: golang:1.12-alpine
+ command: sh -c "apk --no-cache --no-progress add build-base git; export
AVATICA_FLAVOR=HSQLDB && go test -v ./...; export AVATICA_FLAVOR=PHOENIX && go
test -v ./..."
+ working_dir: /source
environment:
PHOENIX_HOST: http://phoenix:8765
HSQLDB_HOST: http://hsqldb:8765
- DEV: ${DEV}
volumes:
- .:/source
- $GOPATH/pkg/mod/cache:/go/pkg/mod/cache
- phoenix:
- image: boostport/hbase-phoenix-all-in-one:2.0-5.0
- hsqldb:
- image: apache/calcite-avatica-hypersql:1.13.0
- command: -u jdbc:hsqldb:mem:public
\ No newline at end of file
+ depends_on:
+ - phoenix
+ - hsqldb
+
+ dev:
+ image: golang:1.12-alpine
+ command: sh -c "apk --no-cache --no-progress add build-base git; go get
github.com/unchartedsoftware/witch; cd /source; witch --cmd=\"export
AVATICA_FLAVOR=HSQLDB && go test -v ./...; export AVATICA_FLAVOR=PHOENIX && go
test -v ./...\" --watch=\"*.mod,**/*.go\""
+ environment:
+ PHOENIX_HOST: http://phoenix:8765
+ HSQLDB_HOST: http://hsqldb:8765
+ volumes:
+ - .:/source
+ - $GOPATH/pkg/mod/cache:/go/pkg/mod/cache
+ depends_on:
+ - phoenix
+ - hsqldb
+
+ dry-run:
+ image: alpine
+ working_dir: /source
+ command: sh -c "apk --no-cache --no-progress add bash; ./docker.sh dry-run"
+ volumes:
+ - .:/source
+ - $GOPATH/pkg/mod/cache:/go/pkg/mod/cache
+
+ release:
+ image: alpine
+ working_dir: /source
+ command: sh -c "apk --no-cache --no-progress add bash; ./docker.sh release"
+ volumes:
+ - .:/source
+ - $GOPATH/pkg/mod/cache:/go/pkg/mod/cache
+
+ clean:
+ image: alpine
+ working_dir: /source
+ command: sh -c "apk --no-cache --no-progress add bash; ./docker.sh clean"
+ volumes:
+ - .:/source
+
+ publish-release-for-voting:
+ image: alpine
+ working_dir: /source
+ command: sh -c "apk --no-cache --no-progress add bash; ./docker.sh
publish-release-for-voting"
+ volumes:
+ - .:/source
+
+ promote-release:
+ image: alpine
+ working_dir: /source
+ command: sh -c "apk --no-cache --no-progress add bash; ./docker.sh
promote-release"
+ volumes:
+ - .:/source
+
+ compile-protobuf:
+ image: golang:1.12-alpine
+ working_dir: /source
+ command: sh -c "apk --no-cache --no-progress add bash; ./docker.sh
compile-protobuf"
+ environment:
+ AVATICA_VERSION: 1.13.0
+ PROTOBUF_VERSION: 3.7.1
+ volumes:
+ - .:/source
+ - $GOPATH/pkg/mod/cache:/go/pkg/mod/cache
\ No newline at end of file
diff --git a/docker.sh b/docker.sh
new file mode 100755
index 0000000..996ba55
--- /dev/null
+++ b/docker.sh
@@ -0,0 +1,643 @@
+#!/bin/bash
+
+# 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
+
+GITBOX_URL=https://gitbox.apache.org/repos/asf/calcite-avatica-go.git
+PRODUCT=apache-calcite-avatica-go
+
+function terminate() {
+ printf "\n\nUser terminated build. Exiting...\n"
+ exit 1
+}
+
+trap terminate SIGINT
+
+init_release(){
+ apk --no-cache add git gnupg tar
+}
+
+init_upload(){
+ apk --no-cache add git subversion
+}
+
+KEYS=()
+
+GPG_COMMAND="gpg"
+
+get_gpg_keys (){
+ GPG_KEYS=$($GPG_COMMAND --list-keys --with-colons --keyid-format LONG)
+
+ KEY_NUM=1
+
+ KEY_DETAILS=""
+
+ while read -r line; do
+
+ IFS=':' read -ra PART <<< "$line"
+
+ if [ ${PART[0]} == "pub" ]; then
+
+ if [ -n "$KEY_DETAILS" ]; then
+ KEYS[$KEY_NUM]=$KEY_DETAILS
+ KEY_DETAILS=""
+ ((KEY_NUM++))
+
+ fi
+
+ KEY_DETAILS=${PART[4]}
+ fi
+
+ if [ ${PART[0]} == "uid" ]; then
+ KEY_DETAILS="$KEY_DETAILS - ${PART[9]}"
+ fi
+
+ done <<< "$GPG_KEYS"
+
+ if [ -n "$KEY_DETAILS" ]; then
+ KEYS[$KEY_NUM]=$KEY_DETAILS
+ fi
+}
+
+mount_gpg_keys(){
+ mkdir -p /.gnupg
+
+ if [[ -z "$(ls -A /.gnupg)" ]]; then
+ echo "Please mount the contents of your .gnupg folder into /.gnupg.
Exiting..."
+ exit 1
+ fi
+
+ mkdir -p /root/.gnupg
+
+ cp -r /.gnupg/ /root/
+
+ chmod -R 700 /root/.gnupg/
+
+ rm -rf /root/.gnupg/*.lock
+}
+
+SELECTED_GPG_KEY=""
+
+select_gpg_key(){
+
+ get_gpg_keys
+
+ export GPG_TTY=/dev/console
+
+ touch /root/.gnupg/gpg-agent.conf
+ echo 'default-cache-ttl 10000' >> /root/.gnupg/gpg-agent.conf
+ echo 'max-cache-ttl 10000' >> /root/.gnupg/gpg-agent.conf
+
+ echo "Starting GPG agent..."
+ gpg-agent --daemon
+
+ while $INVALID_KEY_SELECTED; do
+
+ if [[ "${#KEYS[@]}" -le 0 ]]; then
+ echo "You do not have any GPG keys available. Exiting..."
+ exit 1
+ fi
+
+ echo "You have the following GPG keys:"
+
+ for i in "${!KEYS[@]}"; do
+ echo "$i) ${KEYS[$i]}"
+ done
+
+ read -p "Select your GPG key for signing: " KEY_INDEX
+
+ SELECTED_GPG_KEY=$(sed 's/ -.*//' <<< ${KEYS[$KEY_INDEX]})
+
+ if [[ -z $SELECTED_GPG_KEY ]]; then
+ echo "Selected key is invalid, please try again."
+ continue
+ fi
+
+ echo "Authenticating your GPG key..."
+
+ echo "test" | $GPG_COMMAND --local-user $SELECTED_GPG_KEY --output
/dev/null --sign -
+
+ if [[ $? != 0 ]]; then
+ echo "Invalid GPG passphrase or GPG error. Please try again."
+ continue
+ fi
+
+ echo "You have selected the following GPG key to sign the release:"
+ echo "${KEYS[$KEY_INDEX]}"
+
+ INVALID_CONFIRMATION=true
+
+ while $INVALID_CONFIRMATION; do
+ read -p "Is this correct? (y/n) " CONFIRM
+
+ if [[ ($CONFIRM == "Y") || ($CONFIRM == "y") ]]; then
+ INVALID_KEY_SELECTED=false
+ INVALID_CONFIRMATION=false
+ elif [[ ($CONFIRM == "N") || ($CONFIRM == "n") ]]; then
+ INVALID_CONFIRMATION=false
+ fi
+ done
+ done
+}
+
+check_release_guidelines(){
+
+ # Exclude files without the Apache license header
+ for i in $(git ls-files); do
+ case "$i" in
+ # The following are excluded from the license header check
+
+ # License files
+ (LICENSE|NOTICE);;
+
+ # Generated files
+
(message/common.pb.go|message/requests.pb.go|message/responses.pb.go|Gopkg.lock|Gopkg.toml|go.mod|go.sum);;
+
+ # Binaries
+ (test-fixtures/calcite.png);;
+
+ (*) grep -q "Licensed to the Apache Software Foundation" $i || echo "$i
has no header";;
+ esac
+ done
+
+ # Check copyright year in NOTICE
+ if ! grep -Fq "Copyright 2012-$(date +%Y)" NOTICE; then
+ echo "Ending copyright year in NOTICE is not $(date +%Y)"
+ exit 1
+ fi
+}
+
+check_if_tag_exists(){
+ # Get new tags from remote
+ git fetch --tags $GITBOX_URL
+
+ for tag in "$@"
+ do
+ # Check to see if a tag with a v in front of it has been released
+ if git show-ref --tags | egrep -q "refs/tags/v$tag$"; then
+ echo "A release with version $1 was already released. Check that
the version number entered is correct."
+ exit 1
+ fi
+
+ # Check to see if a tag without a v in front of it has been released
(3.0.0 and below)
+ if git show-ref --tags | egrep -q "refs/tags/$tag$"; then
+ echo "A release with version $1 was already released. Check that
the version number entered is correct."
+ exit 1
+ fi
+ done
+}
+
+check_local_remote_are_even(){
+ REMOTE_COMMIT=$(git ls-remote $GITBOX_URL | head -1 | sed "s/HEAD//")
+ LOCAL_COMMIT=$(git rev-parse master)
+
+ if [[ $REMOTE_COMMIT != $LOCAL_COMMIT ]]; then
+ echo "Master in Apache repository is not even with local master"
+ exit 1
+ fi
+}
+
+
+check_import_paths(){
+ TAG_MAJOR_VERSION=$(echo $1 | sed -e 's/\..*//')
+
+ # Check that go.mod's module path contains the right version
+ if ! grep -Fq "module
github.com/apache/calcite-avatica-go/$TAG_MAJOR_VERSION" go.mod; then
+ echo "Module declaration in go.mod does not contain the correct
version. Expected: $TAG_MAJOR_VERSION"
+ exit 1
+ fi
+
+ # Make sure import paths contain the correct version
+ BAD_IMPORT_PATHS=false
+
+ for i in $(git ls-files); do
+
+ if [[ "$i" == "docker.sh" || "$i" =~ \.md$ ]]; then
+ continue
+ fi
+
+ lines=$(grep -F '"github.com/apache/calcite-avatica-go' $i)
+
+ if ! [[ -z "$lines" ]]; then
+ while read -r line; do
+ if ! grep -q
"github.com/apache/calcite-avatica-go/$TAG_MAJOR_VERSION" <<< "$line" ; then
+ BAD_IMPORT_PATHS=true
+ echo "Import for github.com/apache/calcite-avatica-go in
$i does not have the correct version ($TAG_MAJOR_VERSION) in its path"
+ fi
+ done <<< "$lines"
+ fi
+ done
+
+ if "$BAD_IMPORT_PATHS" == true; then
+ exit 1
+ fi
+}
+
+RELEASE_VERSION=""
+RC_NUMBER=""
+ASF_USERNAME=""
+ASF_PASSWORD=""
+
+set_git_credentials(){
+ echo https://$ASF_USERNAME:[email protected] >>
/root/.git-credentials
+ git config --global credential.helper 'store --file=/root/.git-credentials'
+}
+
+get_asf_credentials(){
+ while $ASF_CREDS_NOT_CONFIRMED; do
+ read -p "Enter your ASF username: " ASF_USERNAME
+ read -s -p "Enter your ASF password: " ASF_PASSWORD
+ printf "\n"
+ echo "Your ASF Username is:" $ASF_USERNAME
+
+ INVALID_CONFIRMATION=true
+
+ while $INVALID_CONFIRMATION; do
+ read -p "Is this correct? (y/n) " CONFIRM
+
+ if [[ ($CONFIRM == "Y") || ($CONFIRM == "y") ]]; then
+ ASF_CREDS_NOT_CONFIRMED=false
+ INVALID_CONFIRMATION=false
+ elif [[ ($CONFIRM == "N") || ($CONFIRM == "n") ]]; then
+ INVALID_CONFIRMATION=false
+ fi
+ done
+ done
+}
+
+clean_release_directory(){
+ rm -rf dist
+}
+
+make_release_artifacts(){
+
+ CURRENT_BRANCH=$(git branch | grep \* | cut -d ' ' -f2)
+
+ if [ $CURRENT_BRANCH != "master" ]; then
+ echo "You are currently on the $CURRENT_BRANCH branch. A release must
be made from the master branch. Exiting..."
+ exit 1
+ fi
+
+ check_local_remote_are_even
+
+ check_release_guidelines
+
+ while $NOT_CONFIRMED; do
+ read -p "Enter the version number to be released (example: 4.0.0, do
not include the rc number): " RELEASE_VERSION
+ read -p "Enter the release candidate number (example: if you are
releasing rc0, enter 0): " RC_NUMBER
+ echo "Build configured as follows:"
+ echo "Release: $RELEASE_VERSION-rc$RC_NUMBER"
+
+ INVALID_CONFIRMATION=true
+
+ while $INVALID_CONFIRMATION; do
+ read -p "Is this correct? (y/n) " CONFIRM
+
+ if [[ ($CONFIRM == "Y") || ($CONFIRM == "y") ]]; then
+ NOT_CONFIRMED=false
+ INVALID_CONFIRMATION=false
+ elif [[ ($CONFIRM == "N") || ($CONFIRM == "n") ]]; then
+ INVALID_CONFIRMATION=false
+ fi
+ done
+ done
+
+ select_gpg_key
+
+ check_if_tag_exists $RELEASE_VERSION $RELEASE_VERSION-rc$RC_NUMBER
+
+ TAG="v$RELEASE_VERSION-rc$RC_NUMBER"
+
+ check_import_paths $TAG
+
+ clean_release_directory
+
+ TAR_FILE=$PRODUCT-$RELEASE_VERSION-src.tar.gz
+ RELEASE_DIR=dist/$PRODUCT-$RELEASE_VERSION-rc$RC_NUMBER
+
+ #Make release dir
+ mkdir -p $RELEASE_DIR
+
+ # Make tar
+ tar -zcf $RELEASE_DIR/$TAR_FILE --transform
"s/^/$PRODUCT-$RELEASE_VERSION-src\//g" $(git ls-files)
+
+ cd $RELEASE_DIR
+
+ # Calculate SHA512
+ gpg --print-md SHA512 $TAR_FILE > $TAR_FILE.sha512
+
+ # Sign
+ gpg -u $SELECTED_GPG_KEY --armor --output $TAR_FILE.asc --detach-sig
$TAR_FILE
+
+ echo "Release artifacts created!"
+}
+
+make_release_artifacts_and_push_tag(){
+ make_release_artifacts
+
+ get_asf_credentials
+
+ # Create the tag
+ git tag $TAG
+
+ # Push the tag
+ set_git_credentials
+
+ git push $GITBOX_URL $TAG
+
+ echo "Release $RELEASE_VERSION-rc$RC_NUMBER has been tagged and pushed!"
+}
+
+publish_release_for_voting(){
+
+ LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
+
+ if [[ ! $LATEST_TAG =~ .+-rc[[:digit:]]+$ ]]; then
+ echo "The latest tag ($LATEST_TAG) is not a RC release and should not
be published for voting."
+ exit 1
+ fi
+
+ TAG_WITHOUT_V=$(echo $LATEST_TAG | sed -e 's/v//')
+ TAG_WITHOUT_RC=$(echo $TAG_WITHOUT_V | sed -e 's/-rc[0-9][0-9]*//')
+ SOURCE_RELEASE=$PRODUCT-$TAG_WITHOUT_RC-src.tar.gz
+ GPG_SIGNATURE=$PRODUCT-$TAG_WITHOUT_RC-src.tar.gz.asc
+ SHA512=$PRODUCT-$TAG_WITHOUT_RC-src.tar.gz.sha512
+ COMMIT=$(git rev-list -n 1 $LATEST_TAG)
+
+ # Check to see a release is built
+ MISSING_FILES=false
+
+ if [ ! -f "dist/$PRODUCT-$TAG_WITHOUT_V/$SOURCE_RELEASE" ]; then
+ echo "Did not find source release ($SOURCE_RELEASE) in dist folder."
+ MISSING_FILES=true
+ fi
+
+ if [ ! -f "dist/$PRODUCT-$TAG_WITHOUT_V/$GPG_SIGNATURE" ]; then
+ echo "Did not find GPG signature ($GPG_SIGNATURE) in dist folder."
+ MISSING_FILES=true
+ fi
+
+ if [ ! -f "dist/$PRODUCT-$TAG_WITHOUT_V/$SHA512" ]; then
+ echo "Did not find SHA512($SHA512) in dist folder."
+ MISSING_FILES=true
+ fi
+
+ if $MISSING_FILES == true; then
+ exit 1
+ fi
+
+ while $NOT_CONFIRMED; do
+ echo "Publish configured as follows:"
+ echo "Release: $TAG_WITHOUT_V"
+
+ INVALID_CONFIRMATION=true
+
+ while $INVALID_CONFIRMATION; do
+ read -p "Is this correct? (y/n) " CONFIRM
+
+ if [[ ($CONFIRM == "Y") || ($CONFIRM == "y") ]]; then
+ NOT_CONFIRMED=false
+ INVALID_CONFIRMATION=false
+ elif [[ ($CONFIRM == "N") || ($CONFIRM == "n") ]]; then
+ INVALID_CONFIRMATION=false
+ fi
+ done
+ done
+
+ HASH_CONTENTS=$(cat "dist/$PRODUCT-$TAG_WITHOUT_V/$SHA512" | tr -d '\n')
+ HASH=${HASH_CONTENTS#"$SOURCE_RELEASE: "}
+
+ get_asf_credentials
+
+ svn checkout https://dist.apache.org/repos/dist/dev/calcite/ /tmp/calcite
--depth empty
+ cp -R dist/$PRODUCT-$TAG_WITHOUT_V /tmp/calcite/
+
+ cd /tmp/calcite
+ svn add $PRODUCT-$TAG_WITHOUT_V
+ chmod -x $PRODUCT-$TAG_WITHOUT_V/*
+
+ svn commit -m "$PRODUCT-$TAG_WITHOUT_V" --force-log --username
$ASF_USERNAME --password $ASF_PASSWORD
+
+ [[ $LATEST_TAG =~ -rc([[:digit:]]+)$ ]]
+ RC_NUMBER=${BASH_REMATCH[1]}
+
+ read -p "Please enter your first name for the voting email: " FIRST_NAME
+
+ echo "The release $PRODUCT-$TAG_WITHOUT_V has been uploaded to the
development repository."
+ printf "\n"
+ printf "\n"
+ echo "Email the following message to [email protected]. Please check
the message before sending."
+ printf "\n"
+ echo "To: [email protected]"
+ echo "Subject: [VOTE] Release $PRODUCT-$TAG_WITHOUT_V (release candidate
$RC_NUMBER)"
+ echo "Message:
+Hi all,
+
+I have created a release for Apache Calcite Avatica Go $TAG_WITHOUT_V, release
candidate $RC_NUMBER.
+
+Thanks to everyone who has contributed to this release. The release notes are
available here:
+https://github.com/apache/calcite-avatica-go/blob/$COMMIT/site/_docs/go_history.md
+
+The commit to be voted on:
+https://gitbox.apache.org/repos/asf?p=calcite-avatica-go.git;a=commit;h=$COMMIT
+
+The hash is $COMMIT
+
+The artifacts to be voted on are located here:
+https://dist.apache.org/repos/dist/dev/calcite/$PRODUCT-$TAG_WITHOUT_V/
+
+The hashes of the artifacts are as follows:
+src.tar.gz $HASH
+
+Release artifacts are signed with the following key:
+https://people.apache.org/keys/committer/$ASF_USERNAME.asc
+
+Instructions for running the test suite is located here:
+https://github.com/apache/calcite-avatica-go/blob/$COMMIT/site/develop/avatica-go.md#testing
+
+Please vote on releasing this package as Apache Calcite Avatica Go
$TAG_WITHOUT_RC.
+
+To run the tests without a Go environment, install docker and docker-compose.
Then, in the root of the release's directory, run:
+docker-compose run test
+
+When the test suite completes, run \"docker-compose down\" to remove and
shutdown all the containers.
+
+The vote is open for the next 72 hours and passes if a majority of
+at least three +1 PMC votes are cast.
+
+[ ] +1 Release this package as Apache Calcite Avatica Go $TAG_WITHOUT_RC
+[ ] 0 I don't feel strongly about it, but I'm okay with the release
+[ ] -1 Do not release this package because...
+
+
+Here is my vote:
+
++1 (binding)
+
+$FIRST_NAME
+"
+}
+
+promote_release(){
+
+ RELEASE_REPO=https://dist.apache.org/repos/dist/release/calcite/
+ DEV_REPO=https://dist.apache.org/repos/dist/dev/calcite/
+
+ LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
+
+ if [[ ! $LATEST_TAG =~ .+-rc[[:digit:]]+$ ]]; then
+ echo "The latest tag ($LATEST_TAG) is not a RC release and should not
be re-released."
+ exit 1
+ fi
+
+ TAG_WITHOUT_V=$(echo $LATEST_TAG | sed -e 's/v//')
+ TAG_WITHOUT_RC=$(echo $TAG_WITHOUT_V | sed -e 's/-rc[0-9][0-9]*//')
+
+ if ! svn ls $DEV_REPO/$PRODUCT-$TAG_WITHOUT_V; then
+ echo "The release $PRODUCT-$TAG_WITHOUT_V was not found in the dev
repository. Was it uploaded for voting?"
+ exit 1
+ fi
+
+ get_asf_credentials
+
+ set_git_credentials
+
+ git tag v$TAG_WITHOUT_RC $LATEST_TAG
+
+ git push $GITBOX_URL v$TAG_WITHOUT_RC
+
+ svn checkout $RELEASE_REPO /tmp/release
+ rm -rf /tmp/release/$PRODUCT-$TAG_WITHOUT_RC
+ mkdir -p /tmp/release/$PRODUCT-$TAG_WITHOUT_RC
+
+ svn checkout $DEV_REPO/$PRODUCT-$TAG_WITHOUT_V /tmp/
+ cp -rp /tmp/rc/* /tmp/release/$PRODUCT-$TAG_WITHOUT_RC
+
+ cd /tmp/release
+
+ svn add $PRODUCT-$TAG_WITHOUT_RC
+
+ # If there is more than 1 release, delete all of them, except for the
newest one
+ # To do this, we do the following:
+ # 1. Get the list of releases with verbose information from svn
+ # 2. Sort by the first field (revision number) in descending order
+ # 3. Select apache-calcite-avatica-go releases
+ # 4. Exclude the release we're trying to promote, in case it was from a
failed promotion.
+ # 5. Trim all whitespace down to 1 empty space.
+ # 6. Select field 7, which is each release's folder
+ CURRENT_RELEASES=$(svn ls -v $RELEASE_REPO | sort -k1 -r | grep
apache-calcite-avatica-go | grep -v apache-calcite-avatica-go-$TAG_WITHOUT_RC |
tr -s ' ' | cut -d ' ' -f 7)
+
+ RELEASE_COUNT=0
+ while read -r RELEASE; do
+ if [[ $RELEASE_COUNT -ne 0 ]]; then
+ svn rm $RELEASE
+ echo "Removing release $RELEASE"
+ fi
+
+ RELEASE_COUNT=$((RELEASE_COUNT+1))
+ done <<< "$CURRENT_RELEASES"
+
+ svn commit -m "Release $PRODUCT-$TAG_WITHOUT_V" --force-log --username
$ASF_USERNAME --password $ASF_PASSWORD
+
+ echo "Release $PRODUCT-$LATEST_TAG successfully promoted to
$PRODUCT-$TAG_WITHOUT_RC"
+}
+
+compile_protobuf(){
+
+ if [ -z "$AVATICA_VERSION" ]; then
+ echo "The AVATICA_VERSION environment variable must be set to a valid
avatica version"
+ fi
+
+ if [ -z "$PROTOBUF_VERSION" ]; then
+ echo "The PROTOBUF_VERSION environment variable must be set to a valid
protobuf version"
+ fi
+
+ apk --no-cache add ca-certificates git wget
+
+ # Install glibc
+ wget -q -O /etc/apk/keys/sgerrand.rsa.pub
https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
+ wget -O /tmp/glibc.apk
https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.29-r0/glibc-2.29-r0.apk
+ apk add /tmp/glibc.apk
+
+
+ # Install go protobuf compiler
+ go install github.com/golang/protobuf/protoc-gen-go
+
+ # Install protoc
+ mkdir -p /tmp/protoc
+ wget -q -O /tmp/protoc/protoc.zip
https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-linux-x86_64.zip
+ cd /tmp/protoc
+ unzip protoc.zip
+ mv /tmp/protoc/include /usr/local/include
+ mv /tmp/protoc/bin/protoc /usr/local/bin/protoc
+
+ # Clone a temp copy of avatica and check out the selected version
+ mkdir /tmp/avatica
+ cd /tmp/avatica
+ git init
+ git remote add origin https://github.com/apache/calcite-avatica.git
+ git config core.sparsecheckout true
+ echo "core/src/main/protobuf/*" >> .git/info/sparse-checkout
+
+ git fetch --depth=1 origin rel/avatica-$AVATICA_VERSION
+ git checkout FETCH_HEAD
+
+ # Compile the protobuf
+ protoc --proto_path=/tmp/avatica/core/src/main/protobuf
--go_out=import_path=message:/source/message/
/tmp/avatica/core/src/main/protobuf/*.proto
+
+ echo "Protobuf compiled successfully"
+}
+
+case $1 in
+ dry-run)
+ init_release
+ mount_gpg_keys
+ make_release_artifacts
+ ;;
+
+ release)
+ init_release
+ mount_gpg_keys
+ make_release_artifacts_and_push_tag
+ ;;
+
+ clean)
+ clean_release_directory
+ echo "Release directory ./dist removed"
+ ;;
+
+ publish-release-for-voting)
+ init_upload
+ publish_release_for_voting
+ ;;
+
+ promote-release)
+ init_upload
+ promote_release
+ ;;
+
+ compile-protobuf)
+ compile_protobuf
+ ;;
+
+ *)
+ echo $"Usage: $0
{dry-run|release|clean|publish-release-for-voting|promote-release}"
+ ;;
+
+esac
+
+# End
\ No newline at end of file
diff --git a/entrypoint.sh b/entrypoint.sh
deleted file mode 100755
index 0d061fd..0000000
--- a/entrypoint.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-
-# 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.
-
-TEST_COMMAND="export AVATICA_FLAVOR=HSQLDB && go test -v ./...; export
AVATICA_FLAVOR=PHOENIX && go test -v ./..."
-
-if [[ -z "${DEV}" ]]; then
- sh -c "$TEST_COMMAND"
-else
- witch --cmd="$TEST_COMMAND" --watch="*.mod,**/*.go"
-fi
\ No newline at end of file
diff --git a/gen-protobuf.bat b/gen-protobuf.bat
deleted file mode 100644
index 8a9eb8c..0000000
--- a/gen-protobuf.bat
+++ /dev/null
@@ -1,37 +0,0 @@
-@ECHO OFF
-rem Licensed to the Apache Software Foundation (ASF) under one or more
-rem contributor license agreements. See the NOTICE file distributed with
-rem this work for additional information regarding copyright ownership.
-rem The ASF licenses this file to you under the Apache License, Version 2.0
-rem (the "License"); you may not use this file except in compliance with
-rem the License. You may obtain a copy of the License at
-
-rem http://www.apache.org/licenses/LICENSE-2.0
-
-rem Unless required by applicable law or agreed to in writing, software
-rem distributed under the License is distributed on an "AS IS" BASIS,
-rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-rem See the License for the specific language governing permissions and
-rem limitations under the License.
-
-SET AVATICA_VER=rel/avatica-1.13.0
-
-IF EXIST message\ rmdir /Q /S message
-IF EXIST avatica-tmp\ rmdir /Q /S avatica-tmp
-
-git init avatica-tmp
-cd avatica-tmp
-git remote add origin https://github.com/apache/calcite-avatica/
-git config core.sparsecheckout true
-echo core/src/main/protobuf/* >> .git/info/sparse-checkout
-git fetch --depth=1 origin %AVATICA_VER%
-git checkout FETCH_HEAD
-
-cd ..
-mkdir message
-protoc --proto_path=avatica-tmp/core/src/main/protobuf/
--go_out=import_path=message:message avatica-tmp/core/src/main/protobuf/*.proto
-
-rmdir /Q /S avatica-tmp
-
-echo.
-echo Protobufs generated!
diff --git a/gen-protobuf.sh b/gen-protobuf.sh
deleted file mode 100644
index d59a333..0000000
--- a/gen-protobuf.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env bash
-
-# 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
-
-rm -rf message avatica-tmp
-
-export AVATICA_VER="rel/avatica-1.13.0"
-
-mkdir -p avatica-tmp
-pushd avatica-tmp &> /dev/null
-
-git init
-git remote add origin https://github.com/apache/calcite-avatica.git
-git config core.sparsecheckout true
-echo "core/src/main/protobuf/*" >> .git/info/sparse-checkout
-
-git fetch --depth=1 origin $AVATICA_VER
-git checkout FETCH_HEAD
-
-popd &> /dev/null
-
-mkdir -p message
-
-protoc --proto_path=avatica-tmp/core/src/main/protobuf
--go_out=import_path=message:message avatica-tmp/core/src/main/protobuf/*.proto
-
-rm -rf avatica-tmp
-
-echo -e "\nProtobufs generated!"
diff --git a/make-release-artifacts.sh b/make-release-artifacts.sh
deleted file mode 100755
index 6051880..0000000
--- a/make-release-artifacts.sh
+++ /dev/null
@@ -1,187 +0,0 @@
-#!/bin/bash
-
-# 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.
-
-# Clean dist directory
-rm -rf dist
-mkdir -p dist
-
-# Get new tags from remote
-git fetch --tags
-
-# Prompt for tag to release (defaults to latest tag)
-echo -n "Enter tag to release (default: latest tag): "
-read tag
-
-if [[ -z $tag ]]; then
- tag=$(git describe --tags `git rev-list --tags --max-count=1`)
- echo "No tag provided. Using the latest tag: $tag"
-fi
-
-# Exclude files without the Apache license header
-for i in $(git ls-files); do
- case "$i" in
- # The following are excluded from the license header check
-
- # License files
- (LICENSE|NOTICE);;
-
- # Generated files
-
(message/common.pb.go|message/requests.pb.go|message/responses.pb.go|Gopkg.lock|Gopkg.toml|go.mod|go.sum);;
-
- # Binaries
- (test-fixtures/calcite.png);;
-
- (*) grep -q "Licensed to the Apache Software Foundation" $i || echo "$i has
no header";;
- esac
-done
-
-# Check copyright year in NOTICE
-if ! grep -Fq "Copyright 2012-$(date +%Y)" NOTICE; then
- echo "Ending copyright year in NOTICE is not $(date +%Y)"
- exit 1
-fi
-
-tagMajorVersion=$(echo $tag | sed -e 's/\..*//')
-
-# Check that go.mod's module path contains the right version
-if ! grep -Fq "module github.com/apache/calcite-avatica-go/$tagMajorVersion"
go.mod; then
- echo "module declaration in go.mod does not contain the correct version.
Expected: $tagMajorVersion"
- exit 1
-fi
-
-# Make sure import paths contain the correct version
-badImportPaths=false
-
-for i in $(git ls-files); do
-
- if [[ "$i" == "make-release-artifacts.sh" || "$i" =~ \.md$ ]]; then
- continue
- fi
-
- lines=$(grep -F '"github.com/apache/calcite-avatica-go' $i)
-
- if ! [[ -z "$lines" ]]; then
- while read -r line; do
- if ! grep -q
"github.com/apache/calcite-avatica-go/$tagMajorVersion" <<< "$line" ; then
- badImportPaths=true
- echo "import for github.com/apache/calcite-avatica-go in $i
does not have the correct version ($tagMajorVersion) in its path"
- fi
- done <<< "$lines"
- fi
-done
-
-if "$badImportPaths" == true; then
- exit 1
-fi
-
-# Check that Avatica versions in both gen-protobuf.bat and gen-protobuf.sh
match
-EXPECTED_AVATICA_VERSION=$(grep -oP '^export AVATICA_VER="\K[^"]+'
gen-protobuf.sh)
-
-if ! grep -Fq "SET AVATICA_VER=$EXPECTED_AVATICA_VERSION" gen-protobuf.bat;
then
- echo "AVATICA_VER in gen-protobuf.sh and gen-protobuf.bat does not match.
Expected: $EXPECTED_AVATICA_VERSION"
- exit 1
-fi
-
-tagWithoutV=$(echo $tag | sed -e 's/v//')
-tagWithoutRC=$(echo $tagWithoutV | sed -e 's/-rc[0-9][0-9]*//')
-product=apache-calcite-avatica-go
-tarFile=$product-$tagWithoutRC-src.tar.gz
-releaseDir=dist/$product-$tagWithoutV
-
-#Make release dir
-mkdir -p $releaseDir
-
-# Checkout tag
-if ! git checkout $tag; then
- echo "Could not check out tag $tag. Does it exist?"
- exit 1
-fi
-
-# Make tar
-tar -zcf $releaseDir/$tarFile --transform "s/^/$product-$tagWithoutRC-src\//g"
$(git ls-files)
-
-cd $releaseDir
-
-# Calculate SHA512
-gpg --print-md SHA512 $tarFile > $tarFile.sha512
-
-# Select GPG key for signing
-KEYS=()
-
-GPG_COMMAND="gpg2"
-
-get_gpg_keys (){
- GPG_KEYS=$($GPG_COMMAND --list-keys --with-colons --keyid-format LONG)
-
- KEY_NUM=1
-
- KEY_DETAILS=""
-
- while read -r line; do
-
- IFS=':' read -ra PART <<< "$line"
-
- if [ ${PART[0]} == "pub" ]; then
-
- if [ -n "$KEY_DETAILS" ]; then
- KEYS[$KEY_NUM]=$KEY_DETAILS
- KEY_DETAILS=""
- ((KEY_NUM++))
-
- fi
-
- KEY_DETAILS=${PART[4]}
- fi
-
- if [ ${PART[0]} == "uid" ]; then
- KEY_DETAILS="$KEY_DETAILS - ${PART[9]}"
- fi
-
- done <<< "$GPG_KEYS"
-
- if [ -n "$KEY_DETAILS" ]; then
- KEYS[$KEY_NUM]=$KEY_DETAILS
- fi
-}
-
-get_gpg_keys
-
-if [ "${#KEYS[@]}" -le 0 ]; then
- echo "You do not have any GPG keys available. Exiting..."
- exit 1
-fi
-
-echo "You have the following GPG keys:"
-
-for i in "${!KEYS[@]}"; do
- echo "$i) ${KEYS[$i]}"
-done
-
-read -p "Select your GPG key for signing: " KEY_INDEX
-
-GPG_KEY=$(sed 's/ -.*//' <<< ${KEYS[$KEY_INDEX]})
-
-if [ -z $GPG_KEY ]; then
- echo "Selected key is invalid. Exiting..."
- exit 1
-fi
-
-# Sign
-gpg -u $GPG_KEY --armor --output $tarFile.asc --detach-sig $tarFile
-
-echo "Release created!"
-# End
\ No newline at end of file
diff --git a/site/develop/avatica-go.md b/site/develop/avatica-go.md
index fd6faca..77a1923 100644
--- a/site/develop/avatica-go.md
+++ b/site/develop/avatica-go.md
@@ -32,12 +32,10 @@ To file issues, please use the [Calcite
JIRA](https://issues.apache.org/jira/pro
as the component.
## Updating protobuf definitions
-
-To update the procotol buffer definitions, update `AVATICA_VER` in
`gen-protobuf.bat` and `gen-protobuf.sh` to match
-the version you want to generate protobufs for and then run the appropriate
script for your platform.
+1. Install [docker](https://docs.docker.com/install/) and
[docker-compose](https://docs.docker.com/compose/install/).
+2. From the root of the repository, run `docker-compose run compile-protobuf`
## Live reload during development
-
It is possible to reload the code in real-time during development. This
executes the test suite every time a `.go` or
`.mod` file is updated. The test suite takes a while to run, so the tests will
not complete instantly, but live-reloading
during development allows us to not have to manually execute the test suite on
save.
@@ -45,16 +43,19 @@ during development allows us to not have to manually
execute the test suite on s
### Set up
1. Install [docker](https://docs.docker.com/install/) and
[docker-compose](https://docs.docker.com/compose/install/).
-2. From the root of the repository, run `DEV=true docker-compose up --build`.
+2. From the root of the repository, run `docker-compose run dev`.
-## Testing
+3. After terminating the container, stop all the containers and remove them
using: `docker-compose down`
+## Testing
The test suite takes around 4 minutes to run if you run both the Avatica
HSQLDB and Apache Phoenix tests.
### Easy way
1. Install [docker](https://docs.docker.com/install/) and
[docker-compose](https://docs.docker.com/compose/install/).
-2. From the root of the repository, run `docker-compose up --build
--abort-on-container-exit`.
+2. From the root of the repository, run `docker-compose run test`.
+
+3. After the tests complete, stop all the containers and remove them using:
`docker-compose down`
### Manual set up
1. Install [Go](https://golang.org/doc/install).
@@ -80,21 +81,50 @@ PHOENIX_HOST: http://phoenix:8765
## Releasing
### Preparing for release
-1. If you have not set up a GPG signing key, set one up by following these
[instructions](https://www.apache.org/dev/openpgp.html#generate-key).
+1. You will need to have [docker](https://docs.docker.com/install/) and
[Docker Compose](https://docs.docker.com/compose/install/) installed.
-2. If this release is a new major version (we are releasing 4.0.0 vs the
current version 3.0.0), update the version in the
+2. If you have not set up a GPG signing key, set one up by following these
[instructions](https://www.apache.org/dev/openpgp.html#generate-key).
+
+3. If this release is a new major version (we are releasing 4.0.0 vs the
current version 3.0.0), update the version in the
import path in `go.mod`. The import paths in the various sample code snippets
should also be updated.
-3. Since we need to support Go modules, tags must be prefixed with a `v`. For
example, tag as `v3.1.0` rather than `3.1.0`.
+4. Since we need to support Go modules, tags must be prefixed with a `v`. For
example, tag as `v3.1.0` rather than `3.1.0`.
+
+5. Check that `NOTICE` has the current copyright year.
+
+### Perform a dry-run
+* The script expects you to mount your `~/.gnupg` directory into the `/.gnupg`
directory in the container. Once mounted into the container,
+the script will make a copy of the contents and move it to a different
location, so that it will not modify the contents of your original
+`~/.gnupg` directory during the build.
+
+{% highlight bash %}
+# On Linux:
+docker-compose run -v ~/.gnupg:/.gnupg dry-run
+
+# On Windows
+docker-compose run -v /c/Users/username/AppData/Roaming/gnupg:/.gnupg dry-run
+{% endhighlight %}
### Build the release
-1. Tag the release. For example, if releasing `vX.Y.Z` and this is `rc0`,
execute: `git tag vX.Y.Z-rcN` on the master branch.
+{% highlight bash %}
+# On Linux:
+docker-compose run -v ~/.gnupg:/.gnupg release
+
+# On Windows
+docker-compose run -v /c/Users/username/AppData/Roaming/gnupg:/.gnupg release
+{% endhighlight %}
-2. Push the tag to git: `git push origin vX.Y.Z-rcN`
+If the build fails, perform a clean:
+1. Remove the git tag locally and remotely:
+{% highlight bash %}
+git tag -d vX.Y.Z-rcA
+git push origin :refs/tags/vX.Y.Z-rcA
+{% endhighlight %}
-2. From the root of the repository, run `./make-release-artifacts.sh`. You
will be asked to select the tag to build release
-artifacts for. The latest tag is automatically selected if no tag is selected.
The release artifacts will be placed in a
-folder named for the release within the `dist/` folder.
+2. Clean the local repository
+{% highlight bash %}
+docker-compose run clean
+{% endhighlight %}
### Check the release before uploading
The name of the release folder must be in the following format:
`apache-calcite-avatica-go-X.Y.Z-rcN`. The version must
@@ -110,9 +140,10 @@ The tar.gz must be named
`apache-calcite-avatica-go-X.Y.Z-src.tar.gz`.
There must be a GPG signature for the tar.gz named:
`apache-calcite-avatica-go-X.Y.Z-src.tar.gz.asc`
-There must be a SHA256 hash for the tar.gz named:
`apache-calcite-avatica-go-X.Y.Z-src.tar.gz.sha256`
+There must be a SHA256 hash for the tar.gz named:
`apache-calcite-avatica-go-X.Y.Z-src.tar.gz.sha512`
### Uploading release artifacts to dev for voting
+#### Manually
`svn` must be installed in order to upload release artifacts.
1. Check out the Calcite dev release subdirectory: `svn co
"https://dist.apache.org/repos/dist/dev/calcite/" calcite-dev`.
@@ -125,6 +156,16 @@ correct release in the command.
4. Commit to upload the artifacts: `svn commit -m
"apache-calcite-avatica-go-X.Y.Z-rcN" --username yourapacheusername --force-log`
Note the use of `--force-log` to suppress the svn warning, because the commit
message is the same as the name of the directory.
+#### Using docker
+This assumes that a release was built and the artifacts are in the `dist/`
folder.
+
+{% highlight bash %}
+docker-compose run publish-release-for-voting
+{% endhighlight %}
+
+The script will also generate a vote email to send to the dev list. You can
use this email, but make sure to check that
+all the details are correct.
+
### Send an email to the Dev list for voting:
Send out the email for voting:
@@ -136,14 +177,13 @@ Hi all,
I have created a build for Apache Calcite Avatica Go X.Y.Z, release candidate
N.
-Thanks to everyone who has contributed to this release.
-<Further details about release.> You can read the release notes here:
+Thanks to everyone who has contributed to this release. The release notes are
available here:
https://github.com/apache/calcite-avatica-go/blob/XXXX/site/_docs/go_history.md
The commit to be voted upon:
-https://gitbox.apache.org/repos/asf/calcite-avatica-go/commit/NNNNNN
+https://gitbox.apache.org/repos/asf?p=calcite-avatica-go.git;a=commit;h=NNNNNN
-Its hash is XXXX.
+The hash is XXXX.
The artifacts to be voted on are located here:
https://dist.apache.org/repos/dist/dev/calcite/apache-calcite-avatica-go-X.Y.Z-rcN/
@@ -154,8 +194,16 @@ src.tar.gz.sha256 XXXX
Release artifacts are signed with the following key:
https://people.apache.org/keys/committer/francischuang.asc
+Instructions for running the test suite is located here:
+https://github.com/apache/calcite-avatica-go/blob/$COMMIT/site/develop/avatica-go.md#testing
+
Please vote on releasing this package as Apache Calcite Avatica Go X.Y.Z.
+To run the tests without a Go environment, install docker and docker-compose.
Then, in the root of the release's directory, run:
+docker-compose run test
+
+When the test suite completes, run \"docker-compose down\" to remove and
shutdown all the containers.
+
The vote is open for the next 72 hours and passes if a majority of
at least three +1 PMC votes are cast.
@@ -198,6 +246,7 @@ Francis
{% endhighlight %}
### Promoting a release after voting
+#### Manually
`svn` must be installed in order to upload release artifacts.
NOTE: Only official releases that has passed a vote may be uploaded to the
release directory.
@@ -219,7 +268,15 @@ git tag vX.Y.Z X.Y.Z-rcN
git push origin vX.Y.Z
{% endhighlight %}
-6. After 24 hours, announce the release by sending an announcement to the [dev
list](https://mail-archives.apache.org/mod_mbox/calcite-dev/)
+#### Using docker
+This assumes that a rc release was tagged and pushed to the git repository.
+
+{% highlight bash %}
+docker-compose run promote-release
+{% endhighlight %}
+
+### Announce the release
+After 24 hours, announce the release by sending an announcement to the [dev
list](https://mail-archives.apache.org/mod_mbox/calcite-dev/)
and
[[email protected]](https://mail-archives.apache.org/mod_mbox/www-announce/).
An example of the announcement could look like: