Skip to content

Commit 36a4573

Browse files
committed
chore: Add build.sh script with common developer commands.
1 parent 32bfee4 commit 36a4573

3 files changed

Lines changed: 248 additions & 9 deletions

File tree

.github/workflows/lint.yaml

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,6 @@ jobs:
3030
go-version: "1.24"
3131
- name: Checkout code
3232
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
33-
- name: >
34-
Verify go mod tidy. If you're reading this and the check has
35-
failed, run `goimports -w . && go mod tidy && golangci-lint run`
33+
- name: lint
3634
run: |
37-
go mod tidy && git diff --exit-code
38-
- name: golangci-lint
39-
uses: golangci/golangci-lint-action@55c2c1448f86e01eaae002a5a3a9624417608d84 # v6.5.2
40-
with:
41-
version: latest
42-
args: --timeout 3m
35+
./build.sh lint_ci

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
# IDEs
55
.idea/
66
.vscode/
7+
*.iml
78

89
/cloud-sql-proxy
910
/cloud-sql-proxy.exe
1011

1112
/key.json
1213
/logs/
14+
.tools

build.sh

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright 2025 Google LLC.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http=//www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# Set SCRIPT_DIR to the current directory of this file.
18+
SCRIPT_DIR=$(cd -P "$(dirname "$0")" >/dev/null 2>&1 && pwd)
19+
SCRIPT_FILE="${SCRIPT_DIR}/$(basename "$0")"
20+
21+
##
22+
## Local Development
23+
##
24+
## These functions should be used to run the local development process
25+
##
26+
27+
## clean - Cleans the build output
28+
function clean() {
29+
if [[ -d '.tools' ]] ; then
30+
rm -rf .tools
31+
fi
32+
}
33+
34+
## build - Builds the project without running tests.
35+
function build() {
36+
go build ./...
37+
}
38+
39+
## test - Runs local unit tests.
40+
function test() {
41+
go test -v -race -cover -short
42+
}
43+
44+
## e2e - Runs end-to-end integration tests.
45+
function e2e() {
46+
if [[ ! -f .envrc ]] ; then
47+
write_e2e_env .envrc
48+
fi
49+
source .envrc
50+
e2e_ci
51+
}
52+
53+
# e2e_ci - Run end-to-end integration tests in the CI system.
54+
# This assumes that the secrets in the env vars are already set.
55+
function e2e_ci() {
56+
go test -race -v ./... | tee test_results.txt
57+
}
58+
59+
## fix - Fixes code format.
60+
function fix() {
61+
# Download goimports tool
62+
goimports_version=v0.36.0
63+
mkdir -p "$SCRIPT_DIR/.tools"
64+
goimports_cmd="$SCRIPT_DIR/.tools/goimports-$goimports_version"
65+
if [[ ! -f "$goimports_cmd" ]] ; then
66+
GOBIN="$SCRIPT_DIR/.tools" go install golang.org/x/tools/cmd/goimports@$goimports_version
67+
mv "$SCRIPT_DIR/.tools/goimports" "$goimports_cmd"
68+
fi
69+
70+
# run code formatting
71+
"$goimports_cmd" -w .
72+
go mod tidy
73+
go fmt ./...
74+
}
75+
76+
## lint - runs the linters
77+
function lint() {
78+
# Download golangci-lint tool
79+
golangci_lint_version=v2.3.1
80+
mkdir -p "$SCRIPT_DIR/.tools"
81+
lint_cmd="$SCRIPT_DIR/.tools/golangci-lint-$golangci_lint_version"
82+
if [[ ! -f "$lint_cmd" ]] ; then
83+
GOBIN="$SCRIPT_DIR/.tools" go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$golangci_lint_version
84+
mv "$SCRIPT_DIR/.tools/golangci-lint" "$lint_cmd"
85+
fi
86+
87+
# run lint
88+
"$lint_cmd" run --timeout 3m
89+
90+
# Check the commit includes a go.mod that is fully
91+
# up to date.
92+
fix
93+
if [[ -d "$SCRIPT_DIR/.git" ]] ; then
94+
git diff --exit-code
95+
fi
96+
}
97+
98+
# lint_ci - runs lint in the CI build job, exiting with an error code if lint fails.
99+
function lint_ci() {
100+
lint # run lint
101+
git diff --exit-code # fail if any files changed
102+
}
103+
104+
## deps - updates project dependencies to latest
105+
function deps() {
106+
go get -u ./...
107+
go get -t -u ./...
108+
109+
# Update the image label in the dockerfiles
110+
for n in Dockerfile Dockerfile.* ; do
111+
dockerfile_from_deps "$n"
112+
done
113+
}
114+
115+
# find
116+
function dockerfile_from_deps() {
117+
# FROM gcr.io/distroless/static:nonroot@sha256:627d6c5a23ad24e6bdff827f16c7b60e0289029b0c79e9f7ccd54ae3279fb45f
118+
# curl -X GET https://gcr.io/v2/distroless/static/manifests/nonroot
119+
file=$1
120+
121+
# Get the last FROM statement from the dockerfile
122+
# those ar
123+
fromLine=$(grep "FROM" $1 | tail -n1)
124+
imageUrl="${fromLine#FROM *}"
125+
126+
# If the image URL does not contain a hash, then don't do anything.
127+
if [[ $imageUrl != *@* ]] ; then
128+
echo "Image does not contain a digest, ignoring"
129+
return
130+
fi
131+
132+
oldDigest="${imageUrl#*@}" #after the '@'
133+
imageWithoutHash="${imageUrl%%@sha256*}" #before the '@sha256'
134+
imageName="${imageWithoutHash%%:*}" #before the ':'
135+
136+
imageLabel="${imageWithoutHash#*:}" #after the ':'
137+
# If none found, use "latest" as the label
138+
if [[ "$imageLabel" == "$imageName" ]] ; then
139+
imageLabel=latest
140+
fi
141+
142+
imageRepo="${imageName%%/*}" #first part of the image name path, may be a repo hostname
143+
if [[ "$imageRepo" == *.* ]]; then
144+
imageName="${imageName#*/}" # trim repo name host from imageName
145+
manifestUrl="https://${imageRepo}/v2/${imageName}/manifests/${imageLabel}"
146+
digest=$(curl -X GET "$manifestUrl" | \
147+
jq -r '.manifests[] | select(.platform.architecture=="amd64" and .platform.os=="linux") | .digest')
148+
149+
else
150+
# registry-1.docker.io requires a token
151+
docker_io_token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/alpine:pull" | jq -r .token)
152+
manifestUrl="https://registry-1.docker.io/v2/${imageName}/manifests/${imageLabel}"
153+
digest=$(curl -s -H "Authorization: Bearer $docker_io_token" \
154+
-H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \
155+
https://registry-1.docker.io/v2/library/alpine/manifests/3 | \
156+
jq -r '.manifests[] | select(.platform.architecture=="amd64" and .platform.os=="linux") | .digest')
157+
fi
158+
159+
if [[ "$oldDigest" == "$digest" ]] ; then
160+
echo "No update to image to $file"
161+
else
162+
echo "Updating docker image to $file to $digest"
163+
set -x
164+
sed -i "" "s/$oldDigest/$digest/g" "$file"
165+
fi
166+
167+
}
168+
169+
# write_e2e_env - Loads secrets from the gcloud project and writes
170+
# them to target/e2e.env to run e2e tests.
171+
function write_e2e_env(){
172+
# All secrets used by the e2e tests in the form <env_name>=<secret_name>
173+
secret_vars=(
174+
MYSQL_CONNECTION_NAME=MYSQL_CONNECTION_NAME
175+
MYSQL_USER=MYSQL_USER
176+
MYSQL_PASS=MYSQL_PASS
177+
MYSQL_DB=MYSQL_DB
178+
MYSQL_MCP_CONNECTION_NAME=MYSQL_MCP_CONNECTION_NAME
179+
MYSQL_MCP_PASS=MYSQL_MCP_PASS
180+
POSTGRES_CONNECTION_NAME=POSTGRES_CONNECTION_NAME
181+
POSTGRES_USER=POSTGRES_USER
182+
POSTGRES_USER_IAM=POSTGRES_USER_IAM
183+
POSTGRES_PASS=POSTGRES_PASS
184+
POSTGRES_DB=POSTGRES_DB
185+
POSTGRES_CAS_CONNECTION_NAME=POSTGRES_CAS_CONNECTION_NAME
186+
POSTGRES_CAS_PASS=POSTGRES_CAS_PASS
187+
POSTGRES_CUSTOMER_CAS_CONNECTION_NAME=POSTGRES_CUSTOMER_CAS_CONNECTION_NAME
188+
POSTGRES_CUSTOMER_CAS_PASS=POSTGRES_CUSTOMER_CAS_PASS
189+
POSTGRES_CUSTOMER_CAS_DOMAIN_NAME=POSTGRES_CUSTOMER_CAS_DOMAIN_NAME
190+
POSTGRES_MCP_CONNECTION_NAME=POSTGRES_MCP_CONNECTION_NAME
191+
POSTGRES_MCP_PASS=POSTGRES_MCP_PASS
192+
SQLSERVER_CONNECTION_NAME=SQLSERVER_CONNECTION_NAME
193+
SQLSERVER_USER=SQLSERVER_USER
194+
SQLSERVER_PASS=SQLSERVER_PASS
195+
SQLSERVER_DB=SQLSERVER_DB
196+
IMPERSONATED_USER=IMPERSONATED_USER
197+
)
198+
199+
if [[ -z "$TEST_PROJECT" ]] ; then
200+
echo "Set TEST_PROJECT environment variable to the project containing"
201+
echo "the e2e test suite secrets."
202+
exit 1
203+
fi
204+
205+
echo "Getting test secrets from $TEST_PROJECT into $1"
206+
{
207+
for env_name in "${secret_vars[@]}" ; do
208+
env_var_name="${env_name%%=*}"
209+
secret_name="${env_name##*=}"
210+
set -x
211+
val=$(gcloud secrets versions access latest --project "$TEST_PROJECT" --secret="$secret_name")
212+
echo "export $env_var_name='$val'"
213+
done
214+
} > "$1"
215+
216+
}
217+
218+
## help - prints the help details
219+
##
220+
function help() {
221+
# This will print the comments beginning with ## above each function
222+
# in this file.
223+
224+
echo "build.sh <command> <arguments>"
225+
echo
226+
echo "Commands to assist with local development and CI builds."
227+
echo
228+
echo "Commands:"
229+
echo
230+
grep -e '^##' "$SCRIPT_FILE" | sed -e 's/##/ /'
231+
}
232+
233+
set -euo pipefail
234+
235+
# Check CLI Arguments
236+
if [[ "$#" -lt 1 ]] ; then
237+
help
238+
exit 1
239+
fi
240+
241+
cd "$SCRIPT_DIR"
242+
243+
"$@"
244+

0 commit comments

Comments
 (0)