From 9a6c9f7f204c8f1a8420e497179d6b9db67aed19 Mon Sep 17 00:00:00 2001 From: Tor Colvin Date: Thu, 24 Jul 2025 11:24:49 -0400 Subject: [PATCH 1/2] Cleanup jenkins scripts --- .github/workflows/shell.yml | 58 +++++++++++++++ integration-test/docker-compose.yml | 1 - integration-test/service-test.sh | 2 +- integration-test/start_server.sh | 34 ++++----- jenkins-integration-build.sh | 108 ++++++++++++++-------------- 5 files changed, 130 insertions(+), 73 deletions(-) create mode 100644 .github/workflows/shell.yml diff --git a/.github/workflows/shell.yml b/.github/workflows/shell.yml new file mode 100644 index 0000000000..6c70bc753e --- /dev/null +++ b/.github/workflows/shell.yml @@ -0,0 +1,58 @@ +# Copyright 2025-Present Couchbase, Inc. +# +# Use of this software is governed by the Business Source License included +# in the file licenses/BSL-Couchbase.txt. As of the Change Date specified +# in that file, in accordance with the Business Source License, use of this +# software will be governed by the Apache License, Version 2.0, included in +# the file licenses/APL2.txt. + +name: shell +permissions: + contents: read + +on: + push: + # Only run when we change a shell script + paths: + - '*.sh' + branches: + - 'main' + - 'release/*' + - 'feature/*' + - 'CBG*' + - 'ci-*' + pull_request: + # Only run when we change an API spec + paths: + - '*.sh' + branches: + - 'main' + - 'release/*' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ !contains(github.ref, 'release/')}} + +jobs: + shellcheck: + name: Shellcheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + ignore_paths: | + ./build.sh + ./bootstrap.sh + ./bench.sh + ./integration-test/service-test.sh + ./integration-test/service-install-tests.sh + ./rewrite-manifest.sh + ./snap-manifest.sh + ./set-version-stamp.sh + ./service/sync_gateway_service_install.sh + ./service/sync_gateway_service_uninstall.sh + ./service/sync_gateway_service_upgrade.sh + ./test-integration-init.sh + ./test.sh diff --git a/integration-test/docker-compose.yml b/integration-test/docker-compose.yml index dc59f90205..81768d3bc4 100644 --- a/integration-test/docker-compose.yml +++ b/integration-test/docker-compose.yml @@ -32,7 +32,6 @@ services: - 18097:18097 - 19102:19102 volumes: - - "${DOCKER_CBS_ROOT_DIR:-.}/cbs:/root" - "${WORKSPACE_ROOT:-.}:/workspace" couchbase-replica1: container_name: couchbase-replica1 diff --git a/integration-test/service-test.sh b/integration-test/service-test.sh index 3320595672..97f10068b2 100755 --- a/integration-test/service-test.sh +++ b/integration-test/service-test.sh @@ -1,4 +1,4 @@ -#/bin/sh +#!/bin/bash # Copyright 2024-Present Couchbase, Inc. # diff --git a/integration-test/start_server.sh b/integration-test/start_server.sh index aba7b5a20f..699b620026 100755 --- a/integration-test/start_server.sh +++ b/integration-test/start_server.sh @@ -7,15 +7,16 @@ # software will be governed by the Apache License, Version 2.0, included in # the file licenses/APL2.txt. -set -eux -o pipefail +set -eu -o pipefail function usage() { echo "Usage: $0 [-m] [-h] containername" + exit 1 } if [ $# -gt 2 ]; then echo "Expected maximally two arguments" - exit 1 + usage fi while [[ $# -gt 0 ]]; do @@ -26,8 +27,7 @@ while [[ $# -gt 0 ]]; do shift ;; -h | --help) - echo "Usage: $0 [-m] [-h] containername" - exit 1 + usage ;; --non-dockerhub) DOCKERHUB=false @@ -41,10 +41,6 @@ while [[ $# -gt 0 ]]; do done WORKSPACE_ROOT="$(pwd)" -DOCKER_CBS_ROOT_DIR="$(pwd)" -if [ "${CBS_ROOT_DIR:-}" != "" ]; then - DOCKER_CBS_ROOT_DIR="${CBS_ROOT_DIR}" -fi set +e AMAZON_LINUX_2=$(grep 'Amazon Linux 2"' /etc/os-release) @@ -54,36 +50,42 @@ if [[ -n "${AMAZON_LINUX_2}" ]]; then else DOCKER_COMPOSE="docker compose" fi -cd -- "${BASH_SOURCE%/*}/" -${DOCKER_COMPOSE} down || true +echo "Stopping existing Couchbase Server container if it exists..." export SG_TEST_COUCHBASE_SERVER_DOCKER_NAME=couchbase -# Start CBS -docker stop ${SG_TEST_COUCHBASE_SERVER_DOCKER_NAME} || true -docker rm ${SG_TEST_COUCHBASE_SERVER_DOCKER_NAME} || true -# --volume: Makes and mounts a CBS folder for storing a CBCollect if needed +cd -- "${BASH_SOURCE%/*}/" +set +e # do not error on command failure +set -x # Output all executed shell commands +${DOCKER_COMPOSE} down +docker stop ${SG_TEST_COUCHBASE_SERVER_DOCKER_NAME} +docker rm ${SG_TEST_COUCHBASE_SERVER_DOCKER_NAME} +set +x # Stop outputting all executed shell commands +set -e # Abort on errors # use dockerhub if no registry is specified, allows for pre-release images from alternative registries if [[ ! "${COUCHBASE_DOCKER_IMAGE_NAME}" =~ ghcr.io/* && "${DOCKERHUB:-}" != "false" ]]; then COUCHBASE_DOCKER_IMAGE_NAME="couchbase/server:${COUCHBASE_DOCKER_IMAGE_NAME}" fi +echo "Creating Couchbase Server container with image: ${COUCHBASE_DOCKER_IMAGE_NAME}" if [ "${MULTI_NODE:-}" == "true" ]; then + set -x # Output all executed shell commands ${DOCKER_COMPOSE} up -d --force-recreate --renew-anon-volumes --remove-orphans else + set -x # Output all executed shell commands # single node docker run --rm -d --name ${SG_TEST_COUCHBASE_SERVER_DOCKER_NAME} --volume "${WORKSPACE_ROOT}:/workspace" -p 8091-8097:8091-8097 -p 9102:9102 -p 9123:9123 -p 11207:11207 -p 11210:11210 -p 11211:11211 -p 18091-18097:18091-18097 -p 19102:19102 "${COUCHBASE_DOCKER_IMAGE_NAME}" fi # Test to see if Couchbase Server is up # Each retry min wait 5s, max 10s. Retry 20 times with exponential backoff (delay 0), fail at 120s -curl --retry-all-errors --connect-timeout 5 --max-time 10 --retry 20 --retry-delay 0 --retry-max-time 120 'http://127.0.0.1:8091' +docker exec couchbase curl --fail --silent --retry-all-errors --connect-timeout 5 --max-time 10 --retry 20 --retry-delay 0 --retry-max-time 120 'http://127.0.0.1:8091' # Set up CBS docker exec couchbase couchbase-cli cluster-init --cluster-username Administrator --cluster-password password --cluster-ramsize 3072 --cluster-index-ramsize 3072 --cluster-fts-ramsize 256 --services data,index,query docker exec couchbase couchbase-cli setting-index --cluster couchbase://localhost --username Administrator --password password --index-threads 4 --index-log-level verbose --index-max-rollback-points 10 --index-storage-setting default --index-memory-snapshot-interval 150 --index-stable-snapshot-interval 40000 -curl -u Administrator:password -v -X POST http://127.0.0.1:8091/node/controller/rename -d 'hostname=127.0.0.1' +curl -u Administrator:password http://127.0.0.1:8091/node/controller/rename -d 'hostname=127.0.0.1' if [ "${MULTI_NODE:-}" == "true" ]; then REPLICA1_NAME=couchbase-replica1 diff --git a/jenkins-integration-build.sh b/jenkins-integration-build.sh index 377a1107c0..c21e15e9ad 100755 --- a/jenkins-integration-build.sh +++ b/jenkins-integration-build.sh @@ -10,32 +10,42 @@ DEFAULT_PACKAGE_TIMEOUT="45m" set -u +set -e # Abort on errors if [ "${1:-}" == "-m" ]; then echo "Running in automated master integration mode" # Set automated setting parameters - SG_COMMIT="master" TARGET_PACKAGE="..." TARGET_TEST="ALL" RUN_WALRUS="true" - USE_GO_MODULES="true" DETECT_RACES="false" SG_EDITION="EE" - XATTRS="true" RUN_COUNT="1" # CBS server settings COUCHBASE_SERVER_PROTOCOL="couchbase" COUCHBASE_SERVER_VERSION="enterprise-7.6.6" - SG_TEST_BUCKET_POOL_SIZE="3" - SG_TEST_BUCKET_POOL_DEBUG="true" + export SG_TEST_BUCKET_POOL_DEBUG="true" GSI="true" TLS_SKIP_VERIFY="false" SG_CBCOLLECT_ALWAYS="false" fi -set -e # Abort on errors -set -x # Output all executed shell commands +REQUIRED_VARS=( + COUCHBASE_SERVER_PROTOCOL + COUCHBASE_SERVER_VERSION + GSI + TARGET_TEST + TARGET_PACKAGE + TLS_SKIP_VERIFY + SG_EDITION +) +for var in "${REQUIRED_VARS[@]}"; do + if [ -z "${!var:-}" ]; then + echo "${var} environment variable is required to be set." + exit 1 + fi +done # Use Git SSH and define private repos git config --global --replace-all url."git@github.com:".insteadOf "https://github.com/" export GOPRIVATE=github.com/couchbaselabs/go-fleecedelta @@ -44,75 +54,63 @@ export GOPRIVATE=github.com/couchbaselabs/go-fleecedelta SG_COMMIT_HASH=$(git rev-parse HEAD) echo "Sync Gateway git commit hash: $SG_COMMIT_HASH" -# Use Go modules (3.1 and above) or bootstrap for legacy Sync Gateway versions (3.0 and below) -if [ "${USE_GO_MODULES:-}" == "false" ]; then - mkdir -p sgw_int_testing # Make the directory if it does not exist - cp bootstrap.sh sgw_int_testing/bootstrap.sh - cd sgw_int_testing - chmod +x bootstrap.sh - ./bootstrap.sh -c ${SG_COMMIT} -e ee - export GO111MODULE=off - go get -u -v github.com/tebeka/go2xunit - go get -u -v github.com/axw/gocov/gocov - go get -u -v github.com/AlekSi/gocov-xml -else - # Install tools to use after job has completed - # go2xunit will fail with 1.23 with name mismatch (try disabling parallel mode), but without any t.Parallel() - go install golang.org/dl/go1.22.8@latest - ~/go/bin/go1.22.8 download - ~/go/bin/go1.22.8 install -v github.com/tebeka/go2xunit@latest - go install -v github.com/axw/gocov/gocov@latest - go install -v github.com/AlekSi/gocov-xml@latest -fi - -if [ "${SG_TEST_X509:-}" == "true" -a "${COUCHBASE_SERVER_PROTOCOL}" != "couchbases" ]; then +echo "Downloading tool dependencies..." +# Install tools to use after job has completed +# go2xunit will fail with 1.23 with name mismatch (try disabling parallel mode), but without any t.Parallel() +set -x # Output all executed shell commands +go install golang.org/dl/go1.22.8@latest +~/go/bin/go1.22.8 download +~/go/bin/go1.22.8 install -v github.com/tebeka/go2xunit@latest +go install -v github.com/axw/gocov/gocov@latest +go install -v github.com/AlekSi/gocov-xml@latest +set +x # Stop outputting all executed shell commands + +if [ "${SG_TEST_X509:-}" == "true" ] && [ "${COUCHBASE_SERVER_PROTOCOL}" != "couchbases" ]; then echo "Setting SG_TEST_X509 requires using couchbases:// protocol, aborting integration tests" exit 1 fi # Set environment vars -GO_TEST_FLAGS="-v -p 1 -count=${RUN_COUNT:-1}" +GO_TEST_FLAGS=(-v -p 1 "-count=${RUN_COUNT:-1}") INT_LOG_FILE_NAME="verbose_int" -if [ -d "godeps" ]; then - export GOPATH=$(pwd)/godeps -fi -export PATH=$PATH:$(go env GOPATH)/bin -echo "PATH: $PATH" - if [ "${TEST_DEBUG:-}" == "true" ]; then export SG_TEST_LOG_LEVEL="debug" export SG_TEST_BUCKET_POOL_DEBUG="true" fi if [ "${TARGET_TEST}" != "ALL" ]; then - GO_TEST_FLAGS="${GO_TEST_FLAGS} -run ${TARGET_TEST}" + GO_TEST_FLAGS+=(-run "${TARGET_TEST}") fi if [ "${PACKAGE_TIMEOUT:-}" != "" ]; then - GO_TEST_FLAGS="${GO_TEST_FLAGS} -test.timeout=${PACKAGE_TIMEOUT}" + GO_TEST_FLAGS+=(-test.timeout="${PACKAGE_TIMEOUT}") else echo "Defaulting package timeout to ${DEFAULT_PACKAGE_TIMEOUT}" - GO_TEST_FLAGS="${GO_TEST_FLAGS} -test.timeout=${DEFAULT_PACKAGE_TIMEOUT}" + GO_TEST_FLAGS+=(-test.timeout="${DEFAULT_PACKAGE_TIMEOUT}") fi if [ "${DETECT_RACES:-}" == "true" ]; then - GO_TEST_FLAGS="${GO_TEST_FLAGS} -race" + GO_TEST_FLAGS=(-race) fi if [ "${FAIL_FAST:-}" == "true" ]; then - GO_TEST_FLAGS="${GO_TEST_FLAGS} -failfast" + GO_TEST_FLAGS+=(-failfast) fi if [ "${SG_TEST_PROFILE_FREQUENCY:-}" == "true" ]; then export SG_TEST_PROFILE_FREQUENCY=${SG_TEST_PROFILE_FREQUENCY} fi -if [ "${RUN_WALRUS}" == "true" ]; then +if [ "${RUN_WALRUS:-}" == "true" ]; then + set +e # Do not abort on errors, so we can run both EE and CE tests + set -x # Output all executed shell commands # EE - go test -coverprofile=coverage_walrus_ee.out -coverpkg=github.com/couchbase/sync_gateway/... -tags cb_sg_devmode,cb_sg_enterprise $GO_TEST_FLAGS github.com/couchbase/sync_gateway/${TARGET_PACKAGE} > verbose_unit_ee.out.raw 2>&1 | true + go test -coverprofile=coverage_walrus_ee.out -coverpkg=github.com/couchbase/sync_gateway/... -tags cb_sg_devmode,cb_sg_enterprise "${GO_TEST_FLAGS[@]}" "github.com/couchbase/sync_gateway/${TARGET_PACKAGE}" > verbose_unit_ee.out.raw 2>&1 # CE - go test -coverprofile=coverage_walrus_ce.out -coverpkg=github.com/couchbase/sync_gateway/... -tags cb_sg_devmode $GO_TEST_FLAGS github.com/couchbase/sync_gateway/${TARGET_PACKAGE} > verbose_unit_ce.out.raw 2>&1 | true + go test -coverprofile=coverage_walrus_ce.out -coverpkg=github.com/couchbase/sync_gateway/... -tags cb_sg_devmode "${GO_TEST_FLAGS[@]}" "github.com/couchbase/sync_gateway/${TARGET_PACKAGE}" > verbose_unit_ce.out.raw 2>&1 + set +x + set -e fi # Run CBS @@ -127,45 +125,45 @@ else fi # Set up test environment variables for CBS runs -export SG_TEST_USE_XATTRS=${XATTRS} export SG_TEST_USE_GSI=${GSI} export SG_TEST_COUCHBASE_SERVER_URL="${COUCHBASE_SERVER_PROTOCOL}://127.0.0.1" export SG_TEST_BACKING_STORE="Couchbase" -export SG_TEST_BUCKET_POOL_SIZE=${SG_TEST_BUCKET_POOL_SIZE} -export SG_TEST_BUCKET_POOL_DEBUG=${SG_TEST_BUCKET_POOL_DEBUG} export SG_TEST_TLS_SKIP_VERIFY=${TLS_SKIP_VERIFY} if [ "${SG_EDITION}" == "EE" ]; then - GO_TEST_FLAGS="${GO_TEST_FLAGS} -tags cb_sg_devmode,cb_sg_enterprise" + GO_TEST_FLAGS+=(-tags "cb_sg_devmode,cb_sg_enterprise") else - GO_TEST_FLAGS="${GO_TEST_FLAGS} -tags cb_sg_devmode" + GO_TEST_FLAGS+=(-tags cb_sg_devmode) fi -go test ${GO_TEST_FLAGS} -coverprofile=coverage_int.out -coverpkg=github.com/couchbase/sync_gateway/... github.com/couchbase/sync_gateway/${TARGET_PACKAGE} 2>&1 | stdbuf -oL tee "${INT_LOG_FILE_NAME}.out.raw" | stdbuf -oL grep -a -E '(--- (FAIL|PASS|SKIP):|github.com/couchbase/sync_gateway(/.+)?\t|TEST: |panic: )' +set -x # Output all executed shell commands +go test "${GO_TEST_FLAGS[@]}" -coverprofile=coverage_int.out -coverpkg=github.com/couchbase/sync_gateway/... "github.com/couchbase/sync_gateway/${TARGET_PACKAGE}" 2>&1 | stdbuf -oL tee "${INT_LOG_FILE_NAME}.out.raw" | stdbuf -oL grep -a -E '(--- (FAIL|PASS|SKIP):|github.com/couchbase/sync_gateway(/.+)?|TEST: |panic: )' if [ "${PIPESTATUS[0]}" -ne "0" ]; then # If test exit code is not 0 (failed) echo "Go test failed! Parsing logs to find cause..." TEST_FAILED=true fi +set +x # Stop outputting all executed shell commands + # Collect CBS logs if server error occurred if [ "${SG_CBCOLLECT_ALWAYS:-}" == "true" ] || grep -a -q "server logs for details\|Timed out after 1m0s waiting for a bucket to become available" "${INT_LOG_FILE_NAME}.out.raw"; then docker exec -t couchbase /opt/couchbase/bin/cbcollect_info /workspace/cbcollect.zip fi # Generate xunit test report that can be parsed by the JUnit Plugin -LC_CTYPE=C tr -dc [:print:][:space:] < ${INT_LOG_FILE_NAME}.out.raw > ${INT_LOG_FILE_NAME}.out # Strip non-printable characters +LC_CTYPE=C tr -dc "[:print:][:space:]" < ${INT_LOG_FILE_NAME}.out.raw > ${INT_LOG_FILE_NAME}.out # Strip non-printable characters ~/go/bin/go2xunit -input "${INT_LOG_FILE_NAME}.out" -output "${INT_LOG_FILE_NAME}.xml" -if [ "${RUN_WALRUS}" == "true" ]; then +if [ "${RUN_WALRUS:-}" == "true" ]; then # Strip non-printable characters before xml creation - LC_CTYPE=C tr -dc [:print:][:space:] < "verbose_unit_ee.out.raw" > "verbose_unit_ee.out" - LC_CTYPE=C tr -dc [:print:][:space:] < "verbose_unit_ce.out.raw" > "verbose_unit_ce.out" + LC_CTYPE=C tr -dc "[:print:][:space:]" < "verbose_unit_ee.out.raw" > "verbose_unit_ee.out" + LC_CTYPE=C tr -dc "[:print:][:space:]" < "verbose_unit_ce.out.raw" > "verbose_unit_ce.out" ~/go/bin/go2xunit -input "verbose_unit_ee.out" -output "verbose_unit_ee.xml" ~/go/bin/go2xunit -input "verbose_unit_ce.out" -output "verbose_unit_ce.xml" fi # Get coverage ~/go/bin/gocov convert "coverage_int.out" | ~/go/bin/gocov-xml > coverage_int.xml -if [ "${RUN_WALRUS}" == "true" ]; then +if [ "${RUN_WALRUS:-}" == "true" ]; then ~/go/bin/gocov convert "coverage_walrus_ee.out" | ~/go/bin/gocov-xml > "coverage_walrus_ee.xml" ~/go/bin/gocov convert "coverage_walrus_ce.out" | ~/go/bin/gocov-xml > "coverage_walrus_ce.xml" fi From 14bac703110e7786993fd573dee8228237a97017 Mon Sep 17 00:00:00 2001 From: Tor Colvin Date: Fri, 25 Jul 2025 08:34:39 -0400 Subject: [PATCH 2/2] fix according to copilot, one bug, one comment --- .github/workflows/shell.yml | 2 +- jenkins-integration-build.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/shell.yml b/.github/workflows/shell.yml index 6c70bc753e..093a6f4873 100644 --- a/.github/workflows/shell.yml +++ b/.github/workflows/shell.yml @@ -22,7 +22,7 @@ on: - 'CBG*' - 'ci-*' pull_request: - # Only run when we change an API spec + # Only run when we change a shell script paths: - '*.sh' branches: diff --git a/jenkins-integration-build.sh b/jenkins-integration-build.sh index c21e15e9ad..11fffa66fb 100755 --- a/jenkins-integration-build.sh +++ b/jenkins-integration-build.sh @@ -91,7 +91,7 @@ else fi if [ "${DETECT_RACES:-}" == "true" ]; then - GO_TEST_FLAGS=(-race) + GO_TEST_FLAGS+=(-race) fi if [ "${FAIL_FAST:-}" == "true" ]; then