1x#!/usr/bin/env bash
2#
3# Licensed to the Apache Software Foundation (ASF) under one
4# or more contributor license agreements.  See the NOTICE file
5# distributed with this work for additional information
6# regarding copyright ownership.  The ASF licenses this file
7# to you under the Apache License, Version 2.0 (the
8# "License"); you may not use this file except in compliance
9# with the License.  You may obtain a copy of the License at
10#
11#   http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing,
14# software distributed under the License is distributed on an
15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16# KIND, either express or implied.  See the License for the
17# specific language governing permissions and limitations
18# under the License.
19#
20
21#
22# The veralign script sets the appropriate versions in all of
23# the package configuration files for all of the supported
24# languages.  It is used to prepare a release or move master
25# forward to the next anticipated version.
26#
27# USAGE
28# -----------------------------------------------------------
29# usage: veralign.sh <oldVersion> <newVersion>
30#
31# EXAMPLE
32# -----------------------------------------------------------
33# $ ./veralign.sh 0.12.0 1.0.0
34# $ ./veralign.sh 1.0.0 1.1.0
35#
36# IMPORTANT USAGE NOTE
37# -----------------------------------------------------------
38# Define the environment variable DRYRUN to have the script
39# print out all matches to the oldVersion hilighted so that
40# you can verify it will change the right things.
41#
42
43declare -A FILES
44
45# These files require a manual touch:
46FILES[CHANGES.md]=manual
47FILES[debian/changelog]=manual
48FILES[doap.rdf]=manual
49
50# These files can be updated automatically:
51FILES[ApacheThrift.nuspec]=simpleReplace
52FILES[appveyor.yml]=simpleReplace
53FILES[bower.json]=jsonReplace
54FILES[CMakeLists.txt]=simpleReplace
55FILES[compiler/cpp/src/thrift/version.h]=simpleReplace
56FILES[configure.ac]=configureReplace
57FILES[contrib/Rebus/Properties/AssemblyInfo.cs]=simpleReplace
58FILES[contrib/thrift.spec]=simpleReplace
59FILES[contrib/zeromq/csharp/AssemblyInfo.cs]=simpleReplace
60FILES[contrib/thrift-maven-plugin/pom.xml]=pomReplace
61FILES[doc/specs/idl.md]=simpleReplace
62FILES[lib/d/src/thrift/base.d]=simpleReplace
63FILES[lib/dart/pubspec.yaml]=pubspecReplace
64FILES[lib/delphi/src/Thrift.pas]=simpleReplace
65FILES[lib/erl/src/thrift.app.src]=simpleReplace
66FILES[lib/haxe/haxelib.json]=simpleReplace
67FILES[lib/java/gradle.properties]=simpleReplace
68FILES[lib/js/package-lock.json]=jsonReplace
69FILES[lib/js/package.json]=jsonReplace
70FILES[lib/js/src/thrift.js]=simpleReplace
71FILES[lib/lua/Thrift.lua]=simpleReplace
72FILES[lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj]=simpleReplace
73FILES[lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj]=simpleReplace
74FILES[lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj]=simpleReplace
75FILES[lib/netstd/Thrift/Properties/AssemblyInfo.cs]=simpleReplace
76FILES[lib/netstd/Thrift/Thrift.csproj]=simpleReplace
77FILES[lib/ocaml/_oasis]=simpleReplace
78FILES[lib/perl/lib/Thrift.pm]=simpleReplace
79FILES[lib/py/setup.py]=simpleReplace
80FILES[lib/rb/thrift.gemspec]=simpleReplace
81FILES[lib/rs/Cargo.toml]=simpleReplace
82FILES[lib/st/package.xml]=simpleReplace
83FILES[lib/swift/Sources/Thrift.swift]=simpleReplace
84FILES[lib/swift/Tests/ThriftTests/ThriftTests.swift]=simpleReplace
85FILES[lib/ts/package-lock.json]=jsonReplace
86FILES[lib/ts/package.json]=jsonReplace
87FILES[package-lock.json]=jsonReplace
88FILES[package.json]=jsonReplace
89FILES[sonar-project.properties]=simpleReplace
90FILES[test/dart/test_client/pubspec.yaml]=pubspecReplace
91FILES[test/erl/src/thrift_test.app.src]=simpleReplace
92FILES[test/netstd/Client/Client.csproj]=simpleReplace
93FILES[test/netstd/Server/Server.csproj]=simpleReplace
94FILES[Thrift.podspec]=simpleReplace
95FILES[tutorial/dart/client/pubspec.yaml]=pubspecReplace
96FILES[tutorial/dart/console_client/pubspec.yaml]=pubspecReplace
97FILES[tutorial/dart/server/pubspec.yaml]=pubspecReplace
98FILES[tutorial/delphi/DelphiClient/DelphiClient.dproj]=simpleReplace
99FILES[tutorial/delphi/DelphiServer/DelphiServer.dproj]=simpleReplace
100FILES[tutorial/netstd/Client/Client.csproj]=simpleReplace
101FILES[tutorial/netstd/Interfaces/Interfaces.csproj]=simpleReplace
102FILES[tutorial/netstd/Server/Server.csproj]=simpleReplace
103FILES[tutorial/ocaml/_oasis]=simpleReplace
104
105
106
107if [ ! -f "CHANGES.md" ]; then
108    >&2 echo "error: run veralign.sh while in the thrift root directory"
109    exit 1
110fi
111
112if [ $# -ne 2 ]; then
113    >&2 echo "usage: veralign.sh <oldVersion> <newVersion>"
114    exit 1
115fi
116
117jq --version 1>/dev/null 2>/dev/null
118if [ $? -ne 0 ]; then
119    >&2 echo "error: the 'jq' package is not installed"
120    exit 1
121fi
122
123#
124# validateVersion: check that a version matches the major.minor.patch
125#   format which is the lowest common denominator supported by all
126#   project systems.
127# \param $1 the version
128# \returns 0 if the version is compliant
129#
130function validateVersion
131{
132    local result
133    local valid
134    valid=$(echo "$1" | sed '/^[[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+$/!{q22}')
135    result=$?
136    if [ $result -eq 22 ]; then
137        >&2 echo "error: version '$1' does not conform to the required major.minor.patch format"
138        return ${result}
139    fi
140}
141
142OLDVERSION=$1
143NEWVERSION=$2
144validateVersion "${OLDVERSION}" || exit $?
145validateVersion "${NEWVERSION}" || exit $?
146
147#
148# escapeVersion: escape the version for use as a sed search
149# \param $1 the version to escape
150# \output the escaped string
151# \returns 0
152# \example VERSEARCH=$(escapeVersion "[1.0.0]"); echo $VERSEARCH; => "\[1\.0\.0\]"
153#
154function escapeVersion
155{
156    echo "$(echo "$1" | sed 's/\./\\./g' | sed 's/\[/\\\[/g' | sed 's/\]/\\\]/g')"
157}
158
159# Set up verbose hilighting if running interactive
160if [ "$(tput colors)" -ne 0 ]; then
161    reverse=$(tput rev)
162    red=$(tput setaf 1)
163    green=$(tput setaf 2)
164    yellow=$(tput setaf 3)
165    normal=$(tput sgr0)
166fi
167
168declare -A MANUAL
169
170#
171# manual: note that update of said file is manual
172# \param $1 filename to do replacements on
173# \returns 0
174#
175function manual
176{
177    MANUAL["$1"]=""
178    return 0
179}
180
181#
182# configureReplace: replace the AC_INIT field in configure.ac
183# \param $1 filename to do replacements on
184# \returns 0 on success
185#
186
187function configureReplace
188{
189    replace "$1" "[thrift], [${OLDVERSION}]" "[thrift], [${NEWVERSION}]"
190}
191
192#
193# jsonReplace: replace a specific version field in a JSON file
194#   must be a top level "version" field in the json structure
195# \param $1 filename to do replacements on
196# \returns 0 on success
197#
198
199function jsonReplace
200{
201    local result
202    local output
203    if [ ! -z "$DRYRUN" ]; then
204        output=$(jq -e ".version" "$1")
205    else
206        output=$(jq -e ".version = \"${NEWVERSION}\"" "$1" > tmp.$$.json && mv tmp.$$.json "$1")
207    fi
208    result=$?
209    if [ $? -ne 0 ]; then
210        printf "%-60s | %5d | ${red}ERROR${normal}: version tag not found" "$1" "$count"
211        echo
212        return 1
213    elif [ ! -z "$DRYRUN" ]; then
214        output=${output%\"}
215        output=${output#\"}
216        printf "%-60s | %5d | MATCHES:   version: \"${reverse}${green}${output}${normal}\"" "$1" 1
217        echo
218        return 0
219    fi
220    printf "%-60s | %5d | ${green}OK${normal}" "$1" 1
221    echo
222    return 0
223}
224
225#
226# pubspecReplace: replace a specific version field in a YAML file
227#   must be a top level "version" field in the yaml structure
228#   did not find a package that preserves comments so this is
229#   somewhat brain-dead, but it gets the job done
230# \param $1 filename to do replacements on
231# \returns 0 on success
232#
233
234function pubspecReplace
235{
236    replace "$1" "version: ${OLDVERSION}" "version: ${NEWVERSION}"
237}
238
239#
240# pomReplace: replace a specific version field in a maven pom file
241#   must be a top level "version" field in the xml structure
242# \param $1 filename to do replacements on
243# \returns 0 on success
244#
245
246function pomReplace
247{
248    replace "$1" "^  <version>${OLDVERSION}<\/version>" "  <version>${NEWVERSION}<\/version>"
249}
250
251#
252# replace: replace occurrences of one string with another
253#     the file specified must contain the old string at least once
254#     in order to be successful.
255# \param $1 filename to do replacements on
256# \param $2 the "old" string to be replaced
257# \param $3 the "new" striing to replace it with
258# \returns 0 on success
259#
260function replace
261{
262    local result
263    local output
264    local oldString="$2"
265    local newString="$3"
266    local oldRegex=$(escapeVersion "${oldString}")
267    local count=$(grep -Ec "${oldRegex}" "$1")
268    local verbose
269    if [ $count -eq 0 ]; then
270        printf "%-60s | %5d | ${red}NOT FOUND${normal}: ${oldString}" "$1" 0
271        echo
272        return 1
273    elif [ ! -z "$DRYRUN" ]; then
274        printf "%-60s | %5d | MATCHES:" "$1" "$count"
275        echo
276        while read -r line; do
277            echo " > $(echo "$line" | sed "s/${oldRegex}/${reverse}${green}${oldString}${normal}/g")"
278        done < <(grep -E "${oldRegex}" "$1")
279        return 0
280    fi
281    output=$(sed -i "s/${oldRegex}/${newString}/g" "$1")
282    result=$?
283    if [ $result -ne 0 ]; then
284        printf "%-60s | %5d | ${red}ERROR${normal}: %s" "$1" "$count" "$output"
285        echo
286        return 1
287    fi
288    printf "%-60s | %5d | ${green}OK${normal}" "$1" "$count"
289    echo
290    return 0
291}
292
293#
294# simpleReplace: replace occurrences of ${OLDVERSION} with ${NEWVERSION}
295#     the file specified must contain OLDVERSION at least once
296#     in order to be successful.
297# \param $1 filename to do replacements on
298# \param $2 the "old" string to be replaced
299# \param $3 the "new" striing to replace it with
300# \returns 0 on success
301#
302function simpleReplace
303{
304    replace "$1" "${OLDVERSION}" "${NEWVERSION}"
305}
306
307echo ""
308echo "Apache Thrift Version Alignment Tool"
309echo "------------------------------------"
310echo ""
311echo "Previous Version: ${OLDVERSION}"
312echo "     New Version: ${NEWVERSION}"
313echo ""
314echo "-------------------------------------------------------------+-------+----------------------"
315echo "Filename                                                     | Count | Status               "
316echo "-------------------------------------------------------------+-------+----------------------"
317
318for file in $(echo "${!FILES[@]}" | sort); do
319    ${FILES[$file]} $file || exit $?
320done
321
322echo
323echo "Files that must be modified manually:"
324echo
325for manu in $(echo "${!MANUAL[@]}" | sort); do
326    echo " > ${yellow}${manu}${normal}"
327done
328
329exit 0
330