Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents
2 changes: 1 addition & 1 deletion .github/workflows/agents.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:
concurrency:
group: agents-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.number }}
queue: max
uses: cinchapi/cinchapi-agent-skills/.github/workflows/router.yml@31f89b30ce3f75f4b381156bf6ee1ce9752de45a
uses: cinchapi/cinchapi-agent-skills/.github/workflows/router.yml@cfd86bdd6113b67c3cd4b281142b29b896a07a0c
permissions:
contents: read
id-token: write
Expand Down
22 changes: 15 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,20 @@ def getVersion = { ->
}
return stdout.toString().trim()
}

// The version that a build version publishes as, also from the "version.sh"
// script, which drops only the build number and keeps the branch's name whole.
def getPublishedVersion = { buildVersion ->
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'bash', 'version.sh', '--published', buildVersion
standardOutput = stdout
}
return stdout.toString().trim()
}
version = getVersion()

// Drop the build component from version number and use that for
// publishing
ext.mavenVersion = version.split('\\.')
ext.mavenVersion[3] = ext.mavenVersion[3].replaceAll("[0-9]+-", "-")
ext.mavenVersion[3] = ext.mavenVersion[3].replaceAll("[0-9]+", "").trim()
ext.mavenVersion = ext.mavenVersion.join(".").replace(".-", "-").replaceAll('\\.$', "")
ext.mavenVersion = getPublishedVersion(version)

sourceCompatibility = '1.8'
targetCompatibility = '1.8'
Expand Down Expand Up @@ -160,7 +166,9 @@ publishing {
name = 'cloudsmith'
def releasesRepoUrl = "https://api-g.cloudsmith.io/maven/cinchapi/platform/"
def snapshotsRepoUrl = "https://api-g.cloudsmith.io/maven/cinchapi/platform-snapshots/"
url = mavenVersion.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
// Only a master build publishes a plain X.Y.Z, so every other version,
// such as a release candidate or a feature build, is a snapshot.
url = (mavenVersion ==~ /\d+\.\d+\.\d+/) ? releasesRepoUrl : snapshotsRepoUrl
credentials {
username = System.getenv('CLOUDSMITH_API_USER')
password = System.getenv('CLOUDSMITH_API_KEY')
Expand Down
320 changes: 240 additions & 80 deletions version.sh
Original file line number Diff line number Diff line change
@@ -1,101 +1,261 @@
# Cinchapi Inc. CONFIDENTIAL
# Copyright (c) 2016 Cinchapi Inc. All Rights Reserved.
#!/usr/bin/env bash
# Copied to a repo's version.sh by .agents/setup.sh on every skills pull.
#
# All information contained herein is, and remains the property of Cinchapi.
# The intellectual and technical concepts contained herein are proprietary to
# Cinchapi and may be covered by U.S. and Foreign Patents, patents in process,
# and are protected by trade secret or copyright law. Dissemination of this
# information or reproduction of this material is strictly forbidden unless
# prior written permission is obtained from Cinchapi. Access to the source code
# contained herein is hereby forbidden to anyone except current Cinchapi
# employees, managers or contractors who have executed Confidentiality and
# Non-disclosure agreements explicitly covering such access.
# Print the build version of the repo that holds this script, convert a build
# version to the version it publishes as, or set the repo's semantic version.
#
# The copyright notice above does not evidence any actual or intended
# publication or disclosure of this source code, which includes information
# that is confidential and/or proprietary, and is a trade secret, of Cinchapi.
# Usage:
# version.sh [--semantic | --calendar]
# version.sh --published BUILD_VERSION
# version.sh [--semantic] X.Y.Z
# version.sh --help
#
# ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, OR PUBLIC
# DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT THE EXPRESS WRITTEN
# CONSENT OF COMPANY IS STRICTLY PROHIBITED, AND IN VIOLATION OF APPLICABLE
# LAWS AND INTERNATIONAL TREATIES. THE RECEIPT OR POSSESSION OF THIS SOURCE
# CODE AND/OR RELATED INFORMATION DOES NOT CONVEY OR IMPLY ANY RIGHTS TO
# REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, OR TO MANUFACTURE, USE, OR
# SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART.
# A repo with a .version file uses semantic versions, and any other repo uses
# calendar versions. --semantic or --calendar chooses the scheme instead.
#
# A build version is a base, a build number, and a suffix for the branch. A
# semantic base is the X.Y.Z in .version. Its build number is the epoch second
# when CONTAINER_BUILD is set, as it is in CI, and otherwise the next count in
# the repo's .counter. A calendar base is the day as YYYY.MM.DD, and its build
# number is the epoch second. The suffix depends on the branch:
#
# master no suffix 1.2.0.7
# release/* -rc 1.2.0.7-rc
# develop -SNAPSHOT 1.2.0.7-SNAPSHOT
# any other its whole name 1.2.0.7-FEATURE-GH-60-FIX
#
# Any other branch's name is uppercased, with each run of other characters as
# one dash. It is cut only as far as keeps the build version within the 128
# characters of a Docker tag. A detached checkout is named HEAD, and a build
# outside Git is named BUILD. The branch is the first of CIRCLE_BRANCH,
# GITHUB_HEAD_REF, GITHUB_REF_NAME, BRANCH_NAME, CI_COMMIT_BRANCH, and
# GIT_BRANCH that is set and not empty, or else Git's.
#
# --published prints the version that a build publishes as: the build version
# without its build number, except that a -rc build carries the number as
# -rc<n>, so each release candidate is a distinct version. A version with no
# build number prints unchanged. It reads and writes no file, so it runs from
# anywhere, as tools/version too.
#
# X.Y.Z sets the version of a semantic repo. It first runs the repo's
# .on-version-set.sh script, if there is one, with bash from the repo's root,
# with the new and the old version as its arguments, so the repo can rewrite the
# other files that name its version. The script must be a bash script. The old
# version is empty when the repo had none. Then it writes .version and removes
# .counter. When the script fails, the set stops with the script's status:
# .version and .counter stay as they were, and the script's own edits stay in
# place.
#
# A build or a set works on the directory that holds the script, so it runs only
# as a repo's version.sh. Bad usage and bad input exit 2 before anything
# changes, and a failed .on-version-set.sh script exits with its own status.
# Needs bash 3.2, git, and stock macOS or Linux tools.
set -euo pipefail

#################################################
### Script to get or set the current version ###
#################################################
# Docker caps a tag at 128 characters, and builds tag images with their version.
readonly MAX_VERSION_LENGTH=128
readonly RE_SEMANTIC='^[0-9]+\.[0-9]+\.[0-9]+$'
readonly RE_BUILD='^([0-9]+\.[0-9]+\.[0-9]+)\.([0-9]+)(-[^[:space:]]+)?$'
readonly RE_PUBLISHED='^[0-9]+\.[0-9]+\.[0-9]+(-[^[:space:]]+)?$'
readonly RE_COUNT='^[0-9]{1,15}$'

#Ensure that this script operates from the directory in which it resides
cd "$(dirname "$0")"
# Prints message $1 and exits 2.
die() {
printf 'version.sh: %s\n' "$1" >&2
exit 2
}

# Bootstrap Git's ability to give the branch, if necessary
git rev-parse --abbrev-ref HEAD > /dev/null 2>&1
if [ $? -ne 0 ]; then
git commit --allow-empty -m "Bootstrapping version.sh"
fi
# Prints the usage from this script's header.
usage() { awk 'NR > 3 && !/^#/ { exit } NR > 3 { sub(/^# ?/, ""); print }' "$0"; }

BASE_VERSION_FILE=".version"
JENKINS_HOME="/opt/jenkins"
if [ -w $JENKINS_HOME ]; then
COUNTER_FILE="$JENKINS_HOME/.counter"
else
COUNTER_FILE=".counter"
fi
# Prints the version that build version $1 publishes as. A version with no build
# number prints unchanged, and input that is no version exits 2.
published_version() {
if [[ $1 =~ $RE_BUILD ]]; then
if [ "${BASH_REMATCH[3]:-}" = -rc ]; then
printf '%s-rc%s\n' "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
else
printf '%s%s\n' "${BASH_REMATCH[1]}" "${BASH_REMATCH[3]:-}"
fi
elif [[ $1 =~ $RE_PUBLISHED ]]; then
printf '%s\n' "$1"
else
die "'$1' is not a version"
fi
}

# Prints the X.Y.Z in .version, or exits 2 when .version is missing or holds
# something else.
semantic_base() {
local base
if [ -f .version ]; then
base=$(tr -d '[:space:]' <.version)
else
die 'a semantic version needs a .version file'
fi
if [[ $base =~ $RE_SEMANTIC ]]; then
printf '%s\n' "$base"
else
die ".version must hold X.Y.Z, not '$base'"
fi
}

if [ -z "$1" ] ; then
VERSION=`cat $BASE_VERSION_FILE`
BRANCH=`git rev-parse --abbrev-ref HEAD`
COMMIT=`git rev-parse HEAD | cut -c1-10`
# Prints the next count in .counter and stores it there. A missing counter, or
# one that holds no count, starts again at 1.
next_count() {
local n=""
if [ -f .counter ]; then
n=$(tr -d '[:space:]' <.counter)
fi
if [[ $n =~ $RE_COUNT ]]; then
n=$((10#$n + 1))
else
n=1
fi
printf '%s\n' "$n" >.counter
printf '%s\n' "$n"
}

# Get counter value
if [ ! -f $COUNTER_FILE ] ; then
echo 0 > $COUNTER_FILE
# Prints the branch this build is for: the CI's, else Git's, with HEAD for a
# detached checkout, and nothing outside Git.
branch_name() {
local ci=${CIRCLE_BRANCH:-${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-}}}
ci=${ci:-${BRANCH_NAME:-${CI_COMMIT_BRANCH:-${GIT_BRANCH:-}}}}
if [ -n "$ci" ]; then
printf '%s\n' "$ci"
elif git rev-parse --git-dir >/dev/null 2>&1; then
git symbolic-ref --quiet --short HEAD || printf '%s\n' HEAD
else
printf '\n'
fi
if [ -z ${CONTAINER_BUILD+x} ]; then
COUNTER=`cat $COUNTER_FILE`
((COUNTER++))
}

# Prints the suffix that branch $1 gives a build version whose part before the
# suffix is $2 characters long.
suffix_for() {
local name room
case "$1" in
master) printf '\n' ;;
release/*) printf '%s\n' -rc ;;
develop) printf '%s\n' -SNAPSHOT ;;
*)
name=$(printf '%s\n' "$1" | LC_ALL=C tr '[:lower:]' '[:upper:]' |
LC_ALL=C sed -E 's/[^A-Z0-9]+/-/g; s/^-+//; s/-+$//')
name=${name:-BUILD}
room=$((MAX_VERSION_LENGTH - $2 - 1))
name=$(printf '%s\n' "${name:0:$room}" | sed -E 's/-+$//')
printf -- '-%s\n' "$name"
;;
esac
}

# Prints the next build version of the repo in the current directory under
# scheme $1, semantic or calendar.
build_version() {
local base number stamp suffix
if [ "$1" = semantic ]; then
base=$(semantic_base)
if [ -n "${CONTAINER_BUILD+x}" ]; then
number=$(date +%s)
else
number=$(next_count)
fi
else
# If the build is run in a container, the COUNTER_FILE will be wiped
# away on each build, so we'll use the current unix timestamp for the
# counter.
COUNTER=`date +%s`
# One reading of the clock keeps the day and the second from straddling
# midnight.
stamp=$(date +%Y.%m.%d.%s)
base=${stamp%.*}
number=${stamp##*.}
fi
suffix=$(suffix_for "$(branch_name)" $((${#base} + 1 + ${#number})))
printf '%s.%s%s\n' "$base" "$number" "$suffix"
}

# Sets the repo in the current directory to semantic version $1, after the
# repo's .on-version-set.sh script rewrites the other files that name it. Writes
# .version, removes .counter, and prints a confirmation. When the script fails,
# exits with its status and leaves .version and .counter as they were.
set_version() {
local old=""
if [ -f .version ]; then
old=$(tr -d '[:space:]' <.version)
fi
echo $COUNTER > $COUNTER_FILE
VERSION=$VERSION.$COUNTER
case $BRANCH in
develop )
EXTRA="-SNAPSHOT"
if [ -f .on-version-set.sh ]; then
bash ./.on-version-set.sh "$1" "$old"
fi
printf '%s\n' "$1" >.version
rm -f .counter
printf 'The version has been set to %s\n' "$1"
}

scheme=""
publish=""
published=""
new=""
help=""
while [ $# -gt 0 ]; do
case "$1" in
--semantic|--calendar)
if [ -n "$scheme" ]; then
die 'give one of --semantic and --calendar'
else
scheme=${1#--}
shift
fi
;;
feature* )
IFS='/'
PARTS=( $BRANCH )
EXTRA="-${PARTS[1]}"
EXTRA=`echo $EXTRA | tr '[:lower:]' '[:upper:]'`
--published)
if [ $# -lt 2 ]; then
die '--published needs a build version'
else
publish=1
published=$2
shift 2
fi
;;
release* )
EXTRA=""
-h|--help)
help=1
shift
;;
* )
# At this point we do not need to refer
# to any commit hash since we'll have a
# tag that represents the overall release
EXTRA=""
-*)
die "unknown option $1"
;;
*)
if [ -n "$new" ]; then
die 'give one version to set'
else
new=$1
shift
fi
;;
esac
echo $VERSION$EXTRA
else
NEW_VERSION=$1
if [[ $NEW_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] ; then
echo $NEW_VERSION > $BASE_VERSION_FILE
rm $COUNTER_FILE 2>/dev/null
sed -i '' -E "s/[0-9]+\.[0-9]+\.[0-9]+/$NEW_VERSION/g" README.md
done

echo "The version has been set to $NEW_VERSION"
if [ -n "$help" ]; then
usage
elif [ -n "$publish" ]; then
if [ -n "$scheme$new" ]; then
die '--published takes nothing else'
else
published_version "$published"
fi
elif [ "$(basename "$0")" != version.sh ]; then
die "run the repo's version.sh, which works on the directory that holds it"
else
cd "$(dirname "$0")"
if [ -z "$scheme" ]; then
if [ -f .version ]; then
scheme=semantic
else
scheme=calendar
fi
fi
if [ -z "$new" ]; then
build_version "$scheme"
elif ! [[ $new =~ $RE_SEMANTIC ]]; then
die "set a version as X.Y.Z, not '$new'"
elif [ "$scheme" != semantic ]; then
die 'only a semantic version can be set; --semantic X.Y.Z starts one'
else
echo "Please specify a valid version <major>.<minor>.<patch>"
exit 1
set_version "$new"
fi
fi
exit 0