github.com/openshift/source-to-image@v1.4.1-0.20240516041539-bf52fc02204e/hack/common.sh (about) 1 #!/bin/bash 2 3 # This script provides common script functions for the hacks 4 # Requires S2I_ROOT to be set 5 6 set -o errexit 7 set -o nounset 8 set -o pipefail 9 10 # The root of the build/dist directory 11 S2I_ROOT=$( 12 unset CDPATH 13 sti_root=$(dirname "${BASH_SOURCE}")/.. 14 cd "${sti_root}" 15 pwd 16 ) 17 18 S2I_OUTPUT_SUBPATH="${S2I_OUTPUT_SUBPATH:-_output/local}" 19 S2I_OUTPUT="${S2I_ROOT}/${S2I_OUTPUT_SUBPATH}" 20 S2I_OUTPUT_BINPATH="${S2I_OUTPUT}/bin" 21 S2I_OUTPUT_PKGDIR="${S2I_OUTPUT}/pkgdir" 22 S2I_LOCAL_BINPATH="${S2I_OUTPUT}/go/bin" 23 S2I_LOCAL_RELEASEPATH="${S2I_OUTPUT}/releases" 24 RELEASE_LDFLAGS=${RELEASE_LDFLAGS:-""} 25 26 27 readonly S2I_GO_PACKAGE=github.com/openshift/source-to-image 28 readonly S2I_GOPATH="${S2I_OUTPUT}/go" 29 30 readonly S2I_CROSS_COMPILE_PLATFORMS=( 31 darwin/amd64 32 darwin/arm64 33 linux/386 34 linux/amd64 35 linux/arm64 36 linux/ppc64le 37 linux/s390x 38 windows/amd64 39 windows/arm64 40 ) 41 readonly S2I_CROSS_COMPILE_TARGETS=( 42 cmd/s2i 43 ) 44 readonly S2I_CROSS_COMPILE_BINARIES=("${S2I_CROSS_COMPILE_TARGETS[@]##*/}") 45 46 readonly S2I_ALL_TARGETS=( 47 "${S2I_CROSS_COMPILE_TARGETS[@]}" 48 ) 49 50 readonly S2I_BINARY_SYMLINKS=( 51 sti 52 ) 53 readonly S2I_BINARY_RELEASE_WINDOWS=( 54 sti.exe 55 s2i.exe 56 ) 57 58 # s2i::build::binaries_from_targets take a list of build targets and return the 59 # full go package to be built 60 s2i::build::binaries_from_targets() { 61 local target 62 for target; do 63 echo "${S2I_GO_PACKAGE}/${target}" 64 done 65 } 66 67 # Asks golang what it thinks the host platform is. The go tool chain does some 68 # slightly different things when the target platform matches the host platform. 69 s2i::build::host_platform() { 70 echo "$(go env GOHOSTOS)/$(go env GOHOSTARCH)" 71 } 72 73 74 # Build binaries targets specified 75 # 76 # Input: 77 # $@ - targets and go flags. If no targets are set then all binaries targets 78 # are built. 79 # S2I_BUILD_PLATFORMS - Incoming variable of targets to build for. If unset 80 # then just the host architecture is built. 81 s2i::build::build_binaries() { 82 # Create a sub-shell so that we don't pollute the outer environment 83 ( 84 # Check for `go` binary and set ${GOPATH}. 85 s2i::build::setup_env 86 87 # Fetch the version. 88 local version_ldflags 89 version_ldflags=$(s2i::build::ldflags) 90 91 s2i::build::export_targets "$@" 92 93 local platform 94 for platform in "${platforms[@]}"; do 95 s2i::build::set_platform_envs "${platform}" 96 echo "++ Building go targets for ${platform}:" "${targets[@]}" 97 CGO_ENABLED=0 go install "${goflags[@]:+${goflags[@]}}" \ 98 -pkgdir "${S2I_OUTPUT_PKGDIR}" \ 99 -ldflags "${version_ldflags} ${RELEASE_LDFLAGS}" \ 100 -mod vendor \ 101 "${binaries[@]}" 102 s2i::build::unset_platform_envs "${platform}" 103 done 104 ) 105 } 106 107 # Generates the set of target packages, binaries, and platforms to build for. 108 # Accepts binaries via $@, and platforms via S2I_BUILD_PLATFORMS, or defaults to 109 # the current platform. 110 s2i::build::export_targets() { 111 # Use eval to preserve embedded quoted strings. 112 local goflags 113 eval "goflags=(${S2I_GOFLAGS:-})" 114 115 targets=() 116 local arg 117 for arg; do 118 if [[ "${arg}" == -* ]]; then 119 # Assume arguments starting with a dash are flags to pass to go. 120 goflags+=("${arg}") 121 else 122 targets+=("${arg}") 123 fi 124 done 125 126 if [[ ${#targets[@]} -eq 0 ]]; then 127 targets=("${S2I_ALL_TARGETS[@]}") 128 fi 129 130 binaries=($(s2i::build::binaries_from_targets "${targets[@]}")) 131 132 platforms=("${S2I_BUILD_PLATFORMS[@]:+${S2I_BUILD_PLATFORMS[@]}}") 133 if [[ ${#platforms[@]} -eq 0 ]]; then 134 platforms=("$(s2i::build::host_platform)") 135 fi 136 } 137 138 139 # Takes the platform name ($1) and sets the appropriate golang env variables 140 # for that platform. 141 s2i::build::set_platform_envs() { 142 [[ -n ${1-} ]] || { 143 echo "!!! Internal error. No platform set in s2i::build::set_platform_envs" 144 exit 1 145 } 146 147 export GOOS=${platform%/*} 148 export GOARCH=${platform##*/} 149 } 150 151 # Takes the platform name ($1) and resets the appropriate golang env variables 152 # for that platform. 153 s2i::build::unset_platform_envs() { 154 unset GOOS 155 unset GOARCH 156 } 157 158 159 # Create the GOPATH tree under $S2I_ROOT 160 s2i::build::create_gopath_tree() { 161 local go_pkg_dir="${S2I_GOPATH}/src/${S2I_GO_PACKAGE}" 162 local go_pkg_basedir=$(dirname "${go_pkg_dir}") 163 164 mkdir -p "${go_pkg_basedir}" 165 rm -f "${go_pkg_dir}" 166 167 # TODO: This symlink should be relative. 168 if [[ "$OSTYPE" == "cygwin" ]]; then 169 S2I_ROOT_cyg=$(cygpath -w ${S2I_ROOT}) 170 go_pkg_dir_cyg=$(cygpath -w ${go_pkg_dir}) 171 cmd /c "mklink ${go_pkg_dir_cyg} ${S2I_ROOT_cyg}" &>/dev/null 172 else 173 ln -s "${S2I_ROOT}" "${go_pkg_dir}" 174 fi 175 } 176 177 178 # s2i::build::setup_env will check that the `go` commands is available in 179 # ${PATH}. If not running on Travis, it will also check that the Go version is 180 # good enough for the Kubernetes build. 181 # 182 # Input Vars: 183 # S2I_EXTRA_GOPATH - If set, this is included in created GOPATH 184 # 185 # Output Vars: 186 # export GOPATH - A modified GOPATH to our created tree along with extra 187 # stuff. 188 # export GOBIN - This is actively unset if already set as we want binaries 189 # placed in a predictable place. 190 s2i::build::setup_env() { 191 s2i::build::create_gopath_tree 192 193 if [[ -z "$(which go)" ]]; then 194 cat <<EOF 195 196 Can't find 'go' in PATH, please fix and retry. 197 See http://golang.org/doc/install for installation instructions. 198 199 EOF 200 exit 2 201 fi 202 203 # Enabling support for Go Modules. 204 export GO111MODULE=on 205 206 # For any tools that expect this to be set (it is default in golang 1.6), 207 # force vendor experiment. 208 export GO15VENDOREXPERIMENT=1 209 210 GOPATH=${S2I_GOPATH} 211 212 # Append S2I_EXTRA_GOPATH to the GOPATH if it is defined. 213 if [[ -n ${S2I_EXTRA_GOPATH:-} ]]; then 214 GOPATH="${GOPATH}:${S2I_EXTRA_GOPATH}" 215 fi 216 217 if [[ "$OSTYPE" == "cygwin" ]]; then 218 GOPATH=$(cygpath -w -p $GOPATH) 219 fi 220 221 export GOPATH 222 223 # Unset GOBIN in case it already exists in the current session. 224 unset GOBIN 225 } 226 227 # This will take binaries from $GOPATH/bin and copy them to the appropriate 228 # place in ${S2I_OUTPUT_BINDIR} 229 # 230 # If S2I_RELEASE_ARCHIVE is set to a directory, it will have tar archives of 231 # each S2I_RELEASE_PLATFORMS created 232 # 233 # Ideally this wouldn't be necessary and we could just set GOBIN to 234 # S2I_OUTPUT_BINDIR but that won't work in the face of cross compilation. 'go 235 # install' will place binaries that match the host platform directly in $GOBIN 236 # while placing cross compiled binaries into `platform_arch` subdirs. This 237 # complicates pretty much everything else we do around packaging and such. 238 s2i::build::place_bins() { 239 ( 240 local host_platform 241 host_platform=$(s2i::build::host_platform) 242 243 echo "++ Placing binaries" 244 245 if [[ "${S2I_RELEASE_ARCHIVE-}" != "" ]]; then 246 s2i::build::get_version_vars 247 mkdir -p "${S2I_LOCAL_RELEASEPATH}" 248 fi 249 250 s2i::build::export_targets "$@" 251 252 for platform in "${platforms[@]}"; do 253 # The substitution on platform_src below will replace all slashes with 254 # underscores. It'll transform darwin/amd64 -> darwin_amd64. 255 local platform_src="/${platform//\//_}" 256 if [[ $platform == $host_platform ]]; then 257 platform_src="" 258 fi 259 260 # Skip this directory if the platform has no binaries. 261 local full_binpath_src="${S2I_GOPATH}/bin${platform_src}" 262 if [[ ! -d "${full_binpath_src}" ]]; then 263 continue 264 fi 265 266 mkdir -p "${S2I_OUTPUT_BINPATH}/${platform}" 267 268 # Create an array of binaries to release. Append .exe variants if the platform is windows. 269 local -a binaries=() 270 for binary in "${targets[@]}"; do 271 binary=$(basename $binary) 272 if [[ $platform =~ ^windows ]]; then 273 binaries+=("${binary}.exe") 274 else 275 binaries+=("${binary}") 276 fi 277 done 278 279 # Move the specified release binaries to the shared S2I_OUTPUT_BINPATH. 280 for binary in "${binaries[@]}"; do 281 mv "${full_binpath_src}/${binary}" "${S2I_OUTPUT_BINPATH}/${platform}/" 282 done 283 284 # If no release archive was requested, we're done. 285 if [[ "${S2I_RELEASE_ARCHIVE-}" == "" ]]; then 286 continue 287 fi 288 289 # Create a temporary bin directory containing only the binaries marked for release. 290 local release_binpath=$(mktemp -d sti.release.${S2I_RELEASE_ARCHIVE}.XXX) 291 for binary in "${binaries[@]}"; do 292 cp "${S2I_OUTPUT_BINPATH}/${platform}/${binary}" "${release_binpath}/" 293 done 294 295 # Create binary copies where specified. 296 local suffix="" 297 if [[ $platform =~ ^windows ]]; then 298 suffix=".exe" 299 fi 300 for linkname in "${S2I_BINARY_SYMLINKS[@]}"; do 301 local src="${release_binpath}/s2i${suffix}" 302 if [[ -f "${src}" ]]; then 303 ln -s "s2i${suffix}" "${release_binpath}/${linkname}${suffix}" 304 fi 305 done 306 307 # Create the release archive. 308 local platform_segment="${platform//\//-}" 309 if [[ $platform =~ ^windows ]]; then 310 local archive_name="${S2I_RELEASE_ARCHIVE}-${S2I_GIT_VERSION}-${S2I_GIT_COMMIT}-${platform_segment}.zip" 311 echo "++ Creating archive ${archive_name}" 312 for file in "${S2I_BINARY_RELEASE_WINDOWS[@]}"; do 313 zip "${S2I_LOCAL_RELEASEPATH}/${archive_name}" -qj "${release_binpath}/${file}" 314 done 315 pushd "${S2I_LOCAL_RELEASEPATH}" > /dev/null 316 echo "++ Generating sha512sum for ${archive_name}" 317 sha512sum "${archive_name}" >> "SHA512-SUMS.txt" 318 popd > /dev/null 319 else 320 local archive_name="${S2I_RELEASE_ARCHIVE}-${S2I_GIT_VERSION}-${S2I_GIT_COMMIT}-${platform_segment}.tar.gz" 321 echo "++ Creating archive ${archive_name}" 322 tar -czf "${S2I_LOCAL_RELEASEPATH}/${archive_name}" -C "${release_binpath}" . 323 pushd "${S2I_LOCAL_RELEASEPATH}" > /dev/null 324 echo "++ Generating sha512sum for ${archive_name}" 325 sha512sum "${archive_name}" >> "SHA512-SUMS.txt" 326 popd > /dev/null 327 fi 328 rm -rf "${release_binpath}" 329 done 330 if [[ -d "${S2I_LOCAL_RELEASEPATH}" ]]; then 331 pushd "${S2I_LOCAL_RELEASEPATH}" > /dev/null 332 echo "++ Verifying sha512sums for archives" 333 if ! sha512sum -c "SHA512-SUMS.txt"; then 334 echo "!! Unable to verify sha512sum for archives" 335 exit 1 336 else 337 echo "++ Verified sha512sums for archives" 338 fi 339 popd > /dev/null 340 fi 341 ) 342 343 } 344 345 # s2i::build::make_binary_symlinks makes symlinks for the sti 346 # binary in _output/local/go/bin 347 s2i::build::make_binary_symlinks() { 348 platform=$(s2i::build::host_platform) 349 if [[ -f "${S2I_OUTPUT_BINPATH}/${platform}/s2i" ]]; then 350 for linkname in "${S2I_BINARY_SYMLINKS[@]}"; do 351 if [[ $platform =~ ^windows ]]; then 352 cp "${S2I_OUTPUT_BINPATH}/${platform}/s2i.exe" "${S2I_OUTPUT_BINPATH}/${platform}/${linkname}.exe" 353 else 354 ln -sf s2i "${S2I_OUTPUT_BINPATH}/${platform}/${linkname}" 355 fi 356 done 357 fi 358 } 359 360 # s2i::build::detect_local_release_tars verifies there is only one primary and one 361 # image binaries release tar in S2I_LOCAL_RELEASEPATH for the given platform specified by 362 # argument 1, exiting if more than one of either is found. 363 # 364 # If the tars are discovered, their full paths are exported to the following env vars: 365 # 366 # S2I_PRIMARY_RELEASE_TAR 367 s2i::build::detect_local_release_tars() { 368 local platform="$1" 369 370 if [[ ! -d "${S2I_LOCAL_RELEASEPATH}" ]]; then 371 echo "There are no release artifacts in ${S2I_LOCAL_RELEASEPATH}" 372 exit 2 373 fi 374 if [[ ! -f "${S2I_LOCAL_RELEASEPATH}/.commit" ]]; then 375 echo "There is no release .commit identifier ${S2I_LOCAL_RELEASEPATH}" 376 exit 2 377 fi 378 local primary=$(find ${S2I_LOCAL_RELEASEPATH} -maxdepth 1 -type f -name source-to-image-*-${platform}*) 379 if [[ $(echo "${primary}" | wc -l) -ne 1 ]]; then 380 echo "There should be exactly one ${platform} primary tar in $S2I_LOCAL_RELEASEPATH" 381 exit 2 382 fi 383 384 export S2I_PRIMARY_RELEASE_TAR="${primary}" 385 export S2I_RELEASE_COMMIT="$(cat ${S2I_LOCAL_RELEASEPATH}/.commit)" 386 } 387 388 389 # s2i::build::get_version_vars loads the standard version variables as 390 # ENV vars 391 s2i::build::get_version_vars() { 392 if [[ -n ${S2I_VERSION_FILE-} ]]; then 393 source "${S2I_VERSION_FILE}" 394 return 395 fi 396 s2i::build::sti_version_vars 397 } 398 399 # s2i::build::sti_version_vars looks up the current Git vars 400 s2i::build::sti_version_vars() { 401 local git=(git --work-tree "${S2I_ROOT}") 402 403 if [[ -n ${S2I_GIT_COMMIT-} ]] || S2I_GIT_COMMIT=$("${git[@]}" rev-parse --short "HEAD^{commit}" 2>/dev/null); then 404 if [[ -z ${S2I_GIT_TREE_STATE-} ]]; then 405 # Check if the tree is dirty. default to dirty 406 if git_status=$("${git[@]}" status --porcelain 2>/dev/null) && [[ -z ${git_status} ]]; then 407 S2I_GIT_TREE_STATE="clean" 408 else 409 S2I_GIT_TREE_STATE="dirty" 410 fi 411 fi 412 413 # Use git describe to find the version based on annotated tags. 414 if [[ -n ${S2I_GIT_VERSION-} ]] || S2I_GIT_VERSION=$("${git[@]}" describe --tags "${S2I_GIT_COMMIT}^{commit}" 2>/dev/null); then 415 if [[ "${S2I_GIT_TREE_STATE}" == "dirty" ]]; then 416 # git describe --dirty only considers changes to existing files, but 417 # that is problematic since new untracked .go files affect the build, 418 # so use our idea of "dirty" from git status instead. 419 S2I_GIT_VERSION+="-dirty" 420 fi 421 422 # Try to match the "git describe" output to a regex to try to extract 423 # the "major" and "minor" versions and whether this is the exact tagged 424 # version or whether the tree is between two tagged versions. 425 if [[ "${S2I_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)([.-].*)?$ ]]; then 426 S2I_GIT_MAJOR=${BASH_REMATCH[1]} 427 S2I_GIT_MINOR=${BASH_REMATCH[2]} 428 if [[ -n "${BASH_REMATCH[3]}" ]]; then 429 S2I_GIT_MINOR+="+" 430 fi 431 fi 432 fi 433 fi 434 } 435 436 # Saves the environment flags to $1 437 s2i::build::save_version_vars() { 438 local version_file=${1-} 439 [[ -n ${version_file} ]] || { 440 echo "!!! Internal error. No file specified in s2i::build::save_version_vars" 441 return 1 442 } 443 444 cat <<EOF >"${version_file}" 445 S2I_GIT_COMMIT='${S2I_GIT_COMMIT-}' 446 S2I_GIT_TREE_STATE='${S2I_GIT_TREE_STATE-}' 447 S2I_GIT_VERSION='${S2I_GIT_VERSION-}' 448 S2I_GIT_MAJOR='${S2I_GIT_MAJOR-}' 449 S2I_GIT_MINOR='${S2I_GIT_MINOR-}' 450 EOF 451 } 452 453 # golang 1.5 wants `-X key=val`, but golang 1.4- REQUIRES `-X key val` 454 s2i::build::ldflag() { 455 local key=${1} 456 local val=${2} 457 458 GO_VERSION=($(go version)) 459 if [[ -n $(echo "${GO_VERSION[2]}" | grep -E 'go1.4') ]]; then 460 echo "-X ${S2I_GO_PACKAGE}/pkg/version.${key} ${val}" 461 else 462 echo "-X ${S2I_GO_PACKAGE}/pkg/version.${key}=${val}" 463 fi 464 } 465 466 # s2i::build::ldflags calculates the -ldflags argument for building STI 467 s2i::build::ldflags() { 468 ( 469 # Run this in a subshell to prevent settings/variables from leaking. 470 set -o errexit 471 set -o nounset 472 set -o pipefail 473 474 cd "${S2I_ROOT}" 475 476 s2i::build::get_version_vars 477 478 declare -a ldflags=() 479 ldflags+=($(s2i::build::ldflag "majorFromGit" "${S2I_GIT_MAJOR}")) 480 ldflags+=($(s2i::build::ldflag "minorFromGit" "${S2I_GIT_MINOR}")) 481 ldflags+=($(s2i::build::ldflag "versionFromGit" "${S2I_GIT_VERSION}")) 482 ldflags+=($(s2i::build::ldflag "commitFromGit" "${S2I_GIT_COMMIT}")) 483 # The -ldflags parameter takes a single string, so join the output. 484 echo "${ldflags[*]-}" 485 ) 486 }