blob: 5e12869814bc7937db185f2640caab80eb7899c4 [file] [log] [blame]
Zack Williams12783ac2018-06-12 15:13:12 -07001#!/usr/bin/env bash
2
Joey Armstrong518f3572024-02-11 07:56:25 -05003# SPDX-FileCopyrightText: 2022-2024 Open Networking Foundation (ONF) and the ONF Contributors
Zack Williamsbe542312022-06-23 21:51:32 -07004# SPDX-License-Identifier: Apache-2.0
Zack Williams12783ac2018-06-12 15:13:12 -07005
6set -eu -o pipefail
7
Zack Williams8e69efd2018-06-13 15:05:18 -07008VERSIONFILE="" # file path to file containing version number
9NEW_VERSION="" # version number found in $VERSIONFILE
Zack Williams6e070f52019-10-04 11:08:59 -070010TAG_VERSION="" # version file that might have a leading v to work around go mod funkyness
Zack Williams8e69efd2018-06-13 15:05:18 -070011
Zack Williams66500002018-09-06 15:29:05 -070012SEMVER_STRICT=${SEMVER_STRICT:-0} # require semver versions
Zack Williamsbe542312022-06-23 21:51:32 -070013DOCKERPARENT_STRICT=${DOCKERPARENT_STRICT:-1} # require semver versions on parent images in dockerfiles
Zack Williams66500002018-09-06 15:29:05 -070014
Zack Williams8e69efd2018-06-13 15:05:18 -070015releaseversion=0
16fail_validation=0
17
18# when not running under Jenkins, use current dir as workspace
19WORKSPACE=${WORKSPACE:-.}
Zack Williamsbe542312022-06-23 21:51:32 -070020BASEDIR=${BASEDIR:-}
21
22# cd to the code checkout location
23cd "$WORKSPACE/$BASEDIR"
Zack Williams12783ac2018-06-12 15:13:12 -070024
25# find the version string in the repo, read into NEW_VERSION
26# Add additional places NEW_VERSION could be found to this function
27function read_version {
28 if [ -f "VERSION" ]
29 then
30 NEW_VERSION=$(head -n1 "VERSION")
Zack Williams8e69efd2018-06-13 15:05:18 -070031 VERSIONFILE="VERSION"
Zack Williams6e070f52019-10-04 11:08:59 -070032
33 # If this is a golang project, use funky v-prefixed versions
34 if [ -f "Gopkg.toml" ] || [ -f "go.mod" ]
35 then
36 echo "go-based project found, using v-prefixed version for git tags: v${NEW_VERSION}"
37 TAG_VERSION=v${NEW_VERSION}
38 else
39 TAG_VERSION=${NEW_VERSION}
40 fi
41
Zack Williams6a9d2e62018-06-22 15:18:23 -070042 elif [ -f "package.json" ]
43 then
44 NEW_VERSION=$(python -c 'import json,sys;obj=json.load(sys.stdin); print obj["version"]' < package.json)
Zack Williams6e070f52019-10-04 11:08:59 -070045 TAG_VERSION=$NEW_VERSION
Zack Williams6a9d2e62018-06-22 15:18:23 -070046 VERSIONFILE="package.json"
Zack Williams866ef3c2019-09-27 15:41:02 -070047 elif [ -f "pom.xml" ]
48 then
49 NEW_VERSION=$(xmllint --xpath '/*[local-name()="project"]/*[local-name()="version"]/text()' pom.xml)
Zack Williams6e070f52019-10-04 11:08:59 -070050 TAG_VERSION=$NEW_VERSION
Zack Williams866ef3c2019-09-27 15:41:02 -070051 VERSIONFILE="pom.xml"
Zack Williams12783ac2018-06-12 15:13:12 -070052 else
53 echo "ERROR: No versioning file found!"
Matteo Scandolo93b531d2021-03-22 14:36:48 -070054 fail_validation=1
Zack Williams12783ac2018-06-12 15:13:12 -070055 fi
56}
57
Zack Williams8e69efd2018-06-13 15:05:18 -070058# check if the version is a released version
59function check_if_releaseversion {
60 if [[ "$NEW_VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]
61 then
62 echo "Version string '$NEW_VERSION' found in '$VERSIONFILE' is a SemVer released version!"
63 releaseversion=1
64 else
Zack Williams66500002018-09-06 15:29:05 -070065 if [ "$SEMVER_STRICT" -eq "1" ]
66 then
67 echo "Version string '$NEW_VERSION' in '$VERSIONFILE' is not a SemVer released version, SEMVER_STRICT enabled, failing!"
68 fail_validation=1
69 else
70 echo "Version string '$NEW_VERSION' in '$VERSIONFILE' is not a SemVer released version, skipping."
71 fi
Zack Williams8e69efd2018-06-13 15:05:18 -070072 fi
73}
74
Zack Williams6e070f52019-10-04 11:08:59 -070075# check if the version is already a tag in git
76function is_git_tag_duplicated {
Matteo Scandoloe0890432021-04-06 06:37:21 -070077 for existing_tag in $existing_tags
Zack Williams6e070f52019-10-04 11:08:59 -070078 do
79 if [ "$TAG_VERSION" = "$existing_tag" ]
80 then
81 echo "ERROR: Duplicate tag: $existing_tag"
Matteo Scandolo93b531d2021-03-22 14:36:48 -070082 fail_validation=2
Zack Williams6e070f52019-10-04 11:08:59 -070083 fi
Matteo Scandoloe0890432021-04-06 06:37:21 -070084 done
Matteo Scandolo93b531d2021-03-22 14:36:48 -070085}
86
87# from https://github.com/cloudflare/semver_bash/blob/master/semver.sh
88function semverParseInto() {
89 local RE='[^0-9]*\([0-9]*\)[.]\([0-9]*\)[.]\([0-9]*\)\([0-9A-Za-z-]*\)'
90 #MAJOR
91 eval $2=`echo $1 | sed -e "s#$RE#\1#"`
92 #MINOR
93 eval $3=`echo $1 | sed -e "s#$RE#\2#"`
94 #MINOR
95 eval $4=`echo $1 | sed -e "s#$RE#\3#"`
96 #SPECIAL
97 eval $5=`echo $1 | sed -e "s#$RE#\4#"`
98}
99
100# if it's a -dev version check if a previous tag has been created (to avoid going from 2.7.0-dev to 2.7.1-dev)
101function is_valid_version {
102 local MAJOR=0 MINOR=0 PATCH=0 SPECIAL=""
103 local C_MAJOR=0 C_MINOR=0 C_PATCH=0 C_SPECIAL="" # these are used in the inner loops to compare
104
105 semverParseInto $NEW_VERSION MAJOR MINOR PATCH SPECIAL
106
107 found_parent=false
108
109 # if minor == 0, check that there was a release with MAJOR-1.X.X
110 if [[ "$MINOR" == 0 ]]; then
111 new_major=$(( $MAJOR - 1 ))
112 parent_version="$new_major.x.x"
Matteo Scandolo3e89c072021-04-01 09:36:26 -0700113 for existing_tag in $existing_tags
Matteo Scandolo93b531d2021-03-22 14:36:48 -0700114 do
115 semverParseInto $existing_tag C_MAJOR C_MINOR C_PATCH C_SPECIAL
116 if [[ "$new_major" == "$C_MAJOR" ]]; then
117 found_parent=true
118 fi
Matteo Scandolo3e89c072021-04-01 09:36:26 -0700119 done
Matteo Scandolo9725cdb2021-06-03 09:03:43 -0700120 fi
Matteo Scandolo93b531d2021-03-22 14:36:48 -0700121
122 # if patch == 0, check that there was a release with MAJOR.MINOR-1.X
Matteo Scandolo9725cdb2021-06-03 09:03:43 -0700123 if [[ "$PATCH" == 0 ]]; then
Matteo Scandolo93b531d2021-03-22 14:36:48 -0700124 new_minor=$(( $MINOR - 1 ))
125 parent_version="$MAJOR.$new_minor.x"
Matteo Scandolo3e89c072021-04-01 09:36:26 -0700126 for existing_tag in $existing_tags
Matteo Scandolo93b531d2021-03-22 14:36:48 -0700127 do
128 semverParseInto $existing_tag C_MAJOR C_MINOR C_PATCH C_SPECIAL
129 if [[ "$new_minor" == "$C_MINOR" ]]; then
130 found_parent=true
131 fi
Matteo Scandolo3e89c072021-04-01 09:36:26 -0700132 done
Matteo Scandolo9725cdb2021-06-03 09:03:43 -0700133 fi
Matteo Scandolo93b531d2021-03-22 14:36:48 -0700134
135 # if patch != 0 check that there was a release with MAJOR.MINOR.PATCH-1
Matteo Scandolo9725cdb2021-06-03 09:03:43 -0700136 if [[ "$PATCH" != 0 ]]; then
Matteo Scandolo93b531d2021-03-22 14:36:48 -0700137 new_patch=$(( $PATCH - 1 ))
138 parent_version="$MAJOR.$MINOR.$new_patch"
Matteo Scandolo3e89c072021-04-01 09:36:26 -0700139 for existing_tag in $existing_tags
Matteo Scandolo93b531d2021-03-22 14:36:48 -0700140 do
Matteo Scandoloacccab42021-04-01 12:05:04 -0700141 semverParseInto $existing_tag C_MAJOR C_MINOR C_PATCH C_SPECIAL
142 if [[ "$MAJOR" == "$C_MAJOR" && "$MINOR" == "$C_MINOR" && "$new_patch" == "$C_PATCH" ]]
Matteo Scandolo93b531d2021-03-22 14:36:48 -0700143 then
144 found_parent=true
145 fi
Matteo Scandolo3e89c072021-04-01 09:36:26 -0700146 done
Matteo Scandolo93b531d2021-03-22 14:36:48 -0700147 fi
148
149 # if we are the beginning the is no parent, but that's fine
150 if [[ "$MAJOR" == 0 ]]; then
151 found_parent=true
152 fi
153
154 if [[ $found_parent == false ]]; then
155 echo "Invalid $NEW_VERSION version. Expected parent version $parent_version does not exist."
156 fail_validation=1
157 fi
Zack Williams6e070f52019-10-04 11:08:59 -0700158}
159
Zack Williams8e69efd2018-06-13 15:05:18 -0700160# check if Dockerfiles have a released version as their parent
161function dockerfile_parentcheck {
Zack Williamsbe542312022-06-23 21:51:32 -0700162 if [ "$DOCKERPARENT_STRICT" -eq "0" ];
163 then
164 echo "DOCKERPARENT_STRICT is disabled - skipping parent checks"
165 else
166 while IFS= read -r -d '' dockerfile
Zack Williams8e69efd2018-06-13 15:05:18 -0700167 do
Zack Williamsbe542312022-06-23 21:51:32 -0700168 echo "Checking dockerfile: '$dockerfile'"
Zack Williams8e69efd2018-06-13 15:05:18 -0700169
Zack Williamsbe542312022-06-23 21:51:32 -0700170 # split on newlines
171 IFS=$'\n'
172 df_parents=($(grep "^FROM" "$dockerfile"))
Zack Williams8e69efd2018-06-13 15:05:18 -0700173
Zack Williamsbe542312022-06-23 21:51:32 -0700174 # check all parents in the Dockerfile
175 for df_parent in "${df_parents[@]}"
176 do
Zack Williams8e69efd2018-06-13 15:05:18 -0700177
Zack Williamsbe542312022-06-23 21:51:32 -0700178 df_pattern="[FfRrOoMm] +(--platform=[^ ]+ +)?([^@: ]+)(:([^: ]+)|@sha[^ ]+)?"
179 if [[ "$df_parent" =~ $df_pattern ]]
180 then
Matteo Scandoloc3aaa032021-04-07 10:33:40 -0700181
Zack Williamsbe542312022-06-23 21:51:32 -0700182 p_image="${BASH_REMATCH[2]}"
183 p_sha=${BASH_REMATCH[3]}
184 p_version="${BASH_REMATCH[4]}"
185
186 echo "IMAGE: '${p_image}'"
187 echo "VERSION: '$p_version'"
188 echo "SHA: '$p_sha'"
189
190 if [[ "${p_image}" == "scratch" ]]
191 then
192 echo " OK: Using the versionless 'scratch' parent: '$df_parent'"
193 elif [[ "${p_image}:${p_version}" == "gcr.io/distroless/static:nonroot" ]]
194 then
195 echo " OK: Using static distroless image with nonroot: '${p_image}:${p_version}'"
196 elif [[ "${p_version}" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]
197 then
198 echo " OK: Parent '$p_image:$p_version' is a released SemVer version"
199 elif [[ "${p_sha}" =~ ^@sha256:[0-9a-f]{64}.*$ ]]
200 then
201 # allow sha256 hashes to be used as version specifiers
202 echo " OK: Parent '$p_image$p_sha' is using a specific sha256 hash as a version"
203 elif [[ "${p_version}" =~ ^.*([0-9]+)\.([0-9]+).*$ ]]
204 then
205 # handle non-SemVer versions that have a Major.Minor version specifier in the name
206 # 'ubuntu:16.04'
207 # 'postgres:10.3-alpine'
208 # 'openjdk:8-jre-alpine3.8'
209 echo " OK: Parent '$p_image:$p_version' is using a non-SemVer, but sufficient, version"
210 elif [[ -z "${p_version}" ]]
211 then
212 echo " ERROR: Parent '$p_image' is NOT using a specific version"
213 fail_validation=1
214 else
215 echo " ERROR: Parent '$p_image:$p_version' is NOT using a specific version"
216 fail_validation=1
217 fi
218
Zack Williams8e69efd2018-06-13 15:05:18 -0700219 else
Zack Williamsbe542312022-06-23 21:51:32 -0700220 echo " ERROR: Couldn't find a parent image in $df_parent"
Zack Williams8e69efd2018-06-13 15:05:18 -0700221 fi
222
Zack Williamsbe542312022-06-23 21:51:32 -0700223 done
Zack Williams8e69efd2018-06-13 15:05:18 -0700224
Zack Williamsbe542312022-06-23 21:51:32 -0700225 done < <( find "${WORKSPACE}" -name 'Dockerfile*' ! -path "*/vendor/*" ! -name "*dockerignore" -print0 )
226 fi
Zack Williams8e69efd2018-06-13 15:05:18 -0700227}
228
Zack Williamsbe542312022-06-23 21:51:32 -0700229# Start of actual code
Zack Williams12783ac2018-06-12 15:13:12 -0700230echo "Checking git repo with remotes:"
231git remote -v
232
233echo "Branches:"
Matteo Scandolo93b531d2021-03-22 14:36:48 -0700234branches=$(git branch -v)
235echo $branches
Zack Williams12783ac2018-06-12 15:13:12 -0700236
237echo "Existing git tags:"
Matteo Scandolo3e89c072021-04-01 09:36:26 -0700238existing_tags=$(git tag -l)
Matteo Scandolo93b531d2021-03-22 14:36:48 -0700239echo $existing_tags
Zack Williams12783ac2018-06-12 15:13:12 -0700240
241read_version
Matteo Scandolo93b531d2021-03-22 14:36:48 -0700242is_valid_version
Zack Williams8e69efd2018-06-13 15:05:18 -0700243check_if_releaseversion
Zack Williams12783ac2018-06-12 15:13:12 -0700244
Zack Williams8e69efd2018-06-13 15:05:18 -0700245# perform checks if a released version
246if [ "$releaseversion" -eq "1" ]
247then
248 is_git_tag_duplicated
249 dockerfile_parentcheck
250fi
Zack Williams12783ac2018-06-12 15:13:12 -0700251
Zack Williams8e69efd2018-06-13 15:05:18 -0700252exit $fail_validation