1#!/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[CMakeLists.txt]=simpleReplace
53FILES[Thrift.podspec]=simpleReplace
54FILES[appveyor.yml]=simpleReplace
55FILES[bower.json]=jsonReplace
56FILES[configure.ac]=configureReplace
57FILES[contrib/thrift.spec]=simpleReplace
58FILES[doc/specs/idl.md]=simpleReplace
59FILES[lib/as3/gradle.properties]=simpleReplace
60FILES[lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs]=simpleReplace
61FILES[lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj]=simpleReplace
62FILES[lib/csharp/src/Properties/AssemblyInfo.cs]=simpleReplace
63FILES[lib/csharp/src/Thrift.csproj]=simpleReplace
64FILES[lib/csharp/test/Multiplex/Client/MultiplexClient.csproj]=simpleReplace
65FILES[lib/csharp/test/Multiplex/Client/Properties/AssemblyInfo.cs]=simpleReplace
66FILES[lib/csharp/test/Multiplex/Server/MultiplexServer.csproj]=simpleReplace
67FILES[lib/csharp/test/Multiplex/Server/Properties/AssemblyInfo.cs]=simpleReplace
68FILES[lib/csharp/test/ThriftMVCTest/Properties/AssemblyInfo.cs]=simpleReplace
69FILES[lib/d/src/thrift/base.d]=simpleReplace
70FILES[lib/dart/pubspec.yaml]=pubspecReplace
71FILES[lib/delphi/src/Thrift.pas]=simpleReplace
72FILES[lib/erl/src/thrift.app.src]=simpleReplace
73FILES[lib/haxe/haxelib.json]=simpleReplace
74FILES[lib/hs/thrift.cabal]=simpleReplace
75FILES[lib/java/gradle.properties]=simpleReplace
76FILES[lib/js/package.json]=jsonReplace
77FILES[lib/js/src/thrift.js]=simpleReplace
78FILES[lib/lua/Thrift.lua]=simpleReplace
79FILES[lib/netcore/Thrift/Properties/AssemblyInfo.cs]=simpleReplace
80FILES[lib/netcore/Thrift/Transports/Client/THttpClientTransport.cs]=simpleReplace
81FILES[lib/netstd/Thrift/Properties/AssemblyInfo.cs]=simpleReplace
82FILES[lib/netstd/Thrift/Transports/Client/THttpClientTransport.cs]=simpleReplace
83FILES[lib/ocaml/_oasis]=simpleReplace
84FILES[lib/perl/lib/Thrift.pm]=simpleReplace
85FILES[lib/py/setup.py]=simpleReplace
86FILES[lib/rb/thrift.gemspec]=simpleReplace
87FILES[lib/rs/Cargo.toml]=simpleReplace
88FILES[lib/st/package.xml]=simpleReplace
89FILES[lib/swift/Sources/Thrift.swift]=simpleReplace
90FILES[lib/swift/Tests/ThriftTests/ThriftTests.swift]=simpleReplace
91FILES[lib/ts/package.json]=jsonReplace
92FILES[package.json]=jsonReplace
93FILES[sonar-project.properties]=simpleReplace
94FILES[test/csharp/Properties/AssemblyInfo.cs]=simpleReplace
95FILES[test/csharp/ThriftTest.csproj]=simpleReplace
96FILES[test/dart/test_client/pubspec.yaml]=pubspecReplace
97FILES[test/erl/src/thrift_test.app.src]=simpleReplace
98FILES[tutorial/csharp/CsharpClient/Properties/AssemblyInfo.cs]=simpleReplace
99FILES[tutorial/csharp/CsharpServer/Properties/AssemblyInfo.cs]=simpleReplace
100FILES[tutorial/dart/client/pubspec.yaml]=pubspecReplace
101FILES[tutorial/dart/console_client/pubspec.yaml]=pubspecReplace
102FILES[tutorial/dart/server/pubspec.yaml]=pubspecReplace
103FILES[tutorial/delphi/DelphiClient/DelphiClient.dproj]=simpleReplace
104FILES[tutorial/delphi/DelphiServer/DelphiServer.dproj]=simpleReplace
105FILES[tutorial/hs/ThriftTutorial.cabal]=simpleReplace
106FILES[tutorial/ocaml/_oasis]=simpleReplace
107
108if [ ! -f "CHANGES.md" ]; then
109    >&2 echo "error: run veralign.sh while in the thrift root directory"
110    exit 1
111fi
112
113if [ $# -ne 2 ]; then
114    >&2 echo "usage: veralign.sh <oldVersion> <newVersion>"
115    exit 1
116fi
117
118jq --version 1>/dev/null 2>/dev/null
119if [ $? -ne 0 ]; then
120    >&2 echo "error: the 'jq' package is not installed"
121    exit 1
122fi
123
124#
125# validateVersion: check that a version matches the major.minor.patch
126#   format which is the lowest common denominator supported by all
127#   project systems.
128# \param $1 the version
129# \returns 0 if the version is compliant
130#
131function validateVersion
132{
133    local result
134    local valid
135    valid=$(echo "$1" | sed '/^[[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+$/!{q22}')
136    result=$?
137    if [ $result -eq 22 ]; then
138        >&2 echo "error: version '$1' does not conform to the required major.minor.patch format"
139        return ${result}
140    fi
141}
142
143OLDVERSION=$1
144NEWVERSION=$2
145validateVersion "${OLDVERSION}" || exit $?
146validateVersion "${NEWVERSION}" || exit $?
147
148#
149# escapeVersion: escape the version for use as a sed search
150# \param $1 the version to escape
151# \output the escaped string
152# \returns 0
153# \example VERSEARCH=$(escapeVersion "[1.0.0]"); echo $VERSEARCH; => "\[1\.0\.0\]"
154#
155function escapeVersion
156{
157    echo "$(echo $1 | sed 's/\./\\./g' | sed 's/\[/\\\[/g' | sed 's/\]/\\\]/g')"
158}
159
160# Set up verbose hilighting if running interactive
161if [ "$(tput colors)" -ne 0 ]; then
162    reverse=$(tput rev)
163    red=$(tput setaf 1)
164    green=$(tput setaf 2)
165    yellow=$(tput setaf 3)
166    normal=$(tput sgr0)
167fi
168
169declare -A MANUAL
170
171#
172# manual: note that update of said file is manual
173# \param $1 filename to do replacements on
174# \returns 0
175#
176function manual
177{
178    MANUAL["$1"]=""
179    return 0
180}
181
182#
183# configureReplace: replace the AC_INIT field in configure.ac
184# \param $1 filename to do replacements on
185# \returns 0 on success
186#
187
188function configureReplace
189{
190    replace "$1" "[thrift], [${OLDVERSION}]" "[thrift], [${NEWVERSION}]"
191}
192
193#
194# jsonReplace: replace a specific version field in a JSON file
195#   must be a top level "version" field in the json structure
196# \param $1 filename to do replacements on
197# \returns 0 on success
198#
199
200function jsonReplace
201{
202    local result
203    local output
204    if [ ! -z "$DRYRUN" ]; then
205        output=$(jq -e ".version" "$1")
206    else
207        output=$(jq -e ".version = \"${NEWVERSION}\"" "$1" > tmp.$$.json && mv tmp.$$.json "$1")
208    fi
209    result=$?
210    if [ $? -ne 0 ]; then
211        printf "%-60s | %5d | ${red}ERROR${normal}: version tag not found" "$1" "$count"
212        echo
213        return 1
214    elif [ ! -z "$DRYRUN" ]; then
215        output=${output%\"}
216        output=${output#\"}
217        printf "%-60s | %5d | MATCHES:   version: \"${reverse}${green}${output}${normal}\"" "$1" 1
218        echo
219        return 0
220    fi
221    printf "%-60s | %5d | ${green}OK${normal}" "$1" 1
222    echo
223    return 0
224}
225
226#
227# pubspecReplace: replace a specific version field in a YAML file
228#   must be a top level "version" field in the yaml structure
229#   did not find a package that preserves comments so this is
230#   somewhat brain-dead, but it gets the job done
231# \param $1 filename to do replacements on
232# \returns 0 on success
233#
234
235function pubspecReplace
236{
237    replace "$1" "version: ${OLDVERSION}" "version: ${NEWVERSION}"
238}
239
240#
241# replace: replace occurrences of one string with another
242#     the file specified must contain the old string at least once
243#     in order to be successful.
244# \param $1 filename to do replacements on
245# \param $2 the "old" string to be replaced
246# \param $3 the "new" striing to replace it with
247# \returns 0 on success
248#
249function replace
250{
251    local result
252    local output
253    local oldString="$2"
254    local newString="$3"
255    local oldRegex=$(escapeVersion "${oldString}")
256    local count=$(grep -Ec "${oldRegex}" "$1")
257    local verbose
258    if [ $count -eq 0 ]; then
259        printf "%-60s | %5d | ${red}NOT FOUND${normal}: ${oldString}" "$1" 0
260        echo
261        return 1
262    elif [ ! -z "$DRYRUN" ]; then
263        printf "%-60s | %5d | MATCHES:" "$1" "$count"
264        echo
265        while read -r line; do
266            echo " > $(echo "$line" | sed "s/${oldRegex}/${reverse}${green}${oldString}${normal}/g")"
267        done < <(grep -E "${oldRegex}" "$1")
268        return 0
269    fi
270    output=$(sed -i "s/${oldRegex}/${newString}/g" "$1")
271    result=$?
272    if [ $result -ne 0 ]; then
273        printf "%-60s | %5d | ${red}ERROR${normal}: %s" "$1" "$count" "$output"
274        echo
275        return 1
276    fi
277    printf "%-60s | %5d | ${green}OK${normal}" "$1" "$count"
278    echo
279    return 0
280}
281
282#
283# simpleReplace: replace occurrences of ${OLDVERSION} with ${NEWVERSION}
284#     the file specified must contain OLDVERSION at least once
285#     in order to be successful.
286# \param $1 filename to do replacements on
287# \param $2 the "old" string to be replaced
288# \param $3 the "new" striing to replace it with
289# \returns 0 on success
290#
291function simpleReplace
292{
293    replace "$1" "${OLDVERSION}" "${NEWVERSION}"
294}
295
296echo ""
297echo "Apache Thrift Version Alignment Tool"
298echo "------------------------------------"
299echo ""
300echo "Previous Version: ${OLDVERSION}"
301echo "     New Version: ${NEWVERSION}"
302echo ""
303echo "-------------------------------------------------------------+-------+----------------------"
304echo "Filename                                                     | Count | Status               "
305echo "-------------------------------------------------------------+-------+----------------------"
306
307for file in $(echo "${!FILES[@]}" | sort); do
308    ${FILES[$file]} $file || exit $?
309done
310
311echo
312echo "Files that must be modified manually:"
313echo
314for manu in $(echo "${!MANUAL[@]}" | sort); do
315    echo " > ${yellow}${manu}${normal}"
316done
317
318exit 0
319