-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathbuild-docker-image.sh
More file actions
executable file
·467 lines (400 loc) · 14.1 KB
/
Copy pathbuild-docker-image.sh
File metadata and controls
executable file
·467 lines (400 loc) · 14.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
#!/bin/bash
# Open LPR Docker Image Build Script
# This script builds latest Docker image for Open LPR application
# Based on GitHub workflow at .github/workflows/docker-publish.yml
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Default values
IMAGE_NAME="open-lpr"
REGISTRY="ghcr.io"
REPOSITORY_OWNER="faisalthaheem"
TAG="latest"
PLATFORMS="linux/amd64,linux/arm64"
PUSH=false
BUILD_CACHE=true
NO_CACHE=false
SBOM=false
HELP=false
VERBOSE=false
# Function to print colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Function to show help
show_help() {
cat << EOF
Open LPR Docker Image Build Script
USAGE:
$0 [OPTIONS]
OPTIONS:
-t, --tag TAG Docker image tag (default: latest)
-n, --name NAME Image name (default: open-lpr)
-r, --registry REGISTRY Container registry (default: ghcr.io)
-o, --owner OWNER Repository owner (default: faisalthaheem)
-p, --push Push image to registry after build
-c, --no-cache Disable build cache
--platforms PLATFORMS Target platforms (default: linux/amd64,linux/arm64)
--sbom Generate SBOM after build
--build-cache Use build cache (default: true)
-v, --verbose Verbose output
-h, --help Show this help message
EXAMPLES:
# Build local image with latest tag
$0
# Build and push with custom tag
$0 --tag v1.2.3 --push
# Build for specific platform only
$0 --platforms linux/amd64
# Build without cache
$0 --no-cache
# Generate SBOM after build
$0 --sbom
EOF
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-t|--tag)
TAG="$2"
shift 2
;;
-n|--name)
IMAGE_NAME="$2"
shift 2
;;
-r|--registry)
REGISTRY="$2"
shift 2
;;
-o|--owner)
REPOSITORY_OWNER="$2"
shift 2
;;
-p|--push)
PUSH=true
shift
;;
-c|--no-cache)
NO_CACHE=true
BUILD_CACHE=false
shift
;;
--platforms)
PLATFORMS="$2"
shift 2
;;
--sbom)
SBOM=true
shift
;;
--build-cache)
BUILD_CACHE=true
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
-h|--help)
HELP=true
shift
;;
*)
print_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
# Show help if requested
if [ "$HELP" = true ]; then
show_help
exit 0
fi
# Construct full image name
FULL_IMAGE_NAME="${REGISTRY}/${REPOSITORY_OWNER}/${IMAGE_NAME}:${TAG}"
CANARY_IMAGE_NAME="${IMAGE_NAME}-canary"
CANARY_FULL_IMAGE_NAME="${REGISTRY}/${REPOSITORY_OWNER}/${CANARY_IMAGE_NAME}:${TAG}"
# Validate Docker installation
if ! command -v docker &> /dev/null; then
print_error "Docker is not installed or not in PATH"
exit 1
fi
# Check if Docker Buildx is available
if ! docker buildx version &> /dev/null; then
print_error "Docker Buildx is not available. Please install Docker Buildx."
exit 1
fi
# Check if we're logged in to registry (if pushing)
if [ "$PUSH" = true ]; then
if ! docker info | grep -q "Username"; then
print_warning "Not logged in to Docker registry. Please run 'docker login' first."
fi
fi
# Main build function for LPR app
build_main_image() {
print_status "Starting Docker image build for Open LPR application..."
print_status "Image: ${FULL_IMAGE_NAME}"
print_status "Platforms: ${PLATFORMS}"
# Create buildx builder if it doesn't exist
BUILDER_NAME="openlpr-builder"
if ! docker buildx ls | grep -q "$BUILDER_NAME"; then
print_status "Creating Docker Buildx builder: $BUILDER_NAME"
docker buildx create --name "$BUILDER_NAME" --driver docker-container --use --bootstrap
else
docker buildx use "$BUILDER_NAME"
fi
# Check if building for multiple platforms locally and handle accordingly
if [ "$PUSH" = false ] && [[ "$PLATFORMS" == *","* ]]; then
print_warning "Cannot load multiple platforms locally. Building for first platform only or use --push to push to registry."
# Extract first platform for local build
FIRST_PLATFORM=$(echo "$PLATFORMS" | cut -d',' -f1)
BUILD_ARGS=(
"--platform" "$FIRST_PLATFORM"
"--tag" "$FULL_IMAGE_NAME"
"--file" "./Dockerfile"
"."
"--load"
)
print_status "Building for platform: $FIRST_PLATFORM"
else
# Prepare build arguments for single platform or push
BUILD_ARGS=(
"--platform" "$PLATFORMS"
"--tag" "$FULL_IMAGE_NAME"
"--file" "./Dockerfile"
"."
)
fi
# Add cache configuration
if [ "$BUILD_CACHE" = true ]; then
BUILD_ARGS+=("--cache-from" "type=gha")
BUILD_ARGS+=("--cache-to" "type=gha,mode=max")
print_status "Using GitHub Actions cache"
fi
if [ "$NO_CACHE" = true ]; then
BUILD_ARGS+=("--no-cache")
print_status "Build cache disabled"
fi
# Add verbose output if requested
if [ "$VERBOSE" = true ]; then
BUILD_ARGS+=("--progress" "plain")
fi
# Add push flag if requested
if [ "$PUSH" = true ]; then
BUILD_ARGS+=("--push")
print_status "Will push image to registry after build"
else
# Only add --load for single platform builds
if [[ "$PLATFORMS" != *","* ]]; then
BUILD_ARGS+=("--load")
print_status "Will load image locally"
fi
fi
# Extract metadata (similar to GitHub workflow)
print_status "Extracting build metadata..."
# Get git commit info
GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
print_status "Git commit: ${GIT_COMMIT}"
print_status "Git branch: ${GIT_BRANCH}"
print_status "Build date: ${BUILD_DATE}"
# Add labels
BUILD_ARGS+=(
"--label" "org.opencontainers.image.title=${IMAGE_NAME}"
"--label" "org.opencontainers.image.description=Open LPR - License Plate Recognition Application"
"--label" "org.opencontainers.image.url=https://github.com/${REPOSITORY_OWNER}/${IMAGE_NAME}"
"--label" "org.opencontainers.image.source=https://github.com/${REPOSITORY_OWNER}/${IMAGE_NAME}"
"--label" "org.opencontainers.image.version=${TAG}"
"--label" "org.opencontainers.image.created=${BUILD_DATE}"
"--label" "org.opencontainers.image.revision=${GIT_COMMIT}"
"--label" "org.opencontainers.image.branch=${GIT_BRANCH}"
"--label" "org.opencontainers.image.licenses=MIT"
)
# Build image
print_status "Building Docker image..."
if [ "$VERBOSE" = true ]; then
print_status "Build command: docker buildx build ${BUILD_ARGS[*]}"
fi
if docker buildx build "${BUILD_ARGS[@]}"; then
print_success "Docker image built successfully: ${FULL_IMAGE_NAME}"
else
print_error "Docker build failed"
exit 1
fi
}
# Function to build canary image
build_canary_image() {
print_status "Building canary image..."
print_status "Canary Image: ${CANARY_FULL_IMAGE_NAME}"
# Build canary image using simple docker build (no need for buildx for single platform)
CANARY_BUILD_ARGS=(
"--tag" "$CANARY_FULL_IMAGE_NAME"
"--file" "./canary/Dockerfile"
"./canary"
)
if [ "$NO_CACHE" = true ]; then
CANARY_BUILD_ARGS+=("--no-cache")
fi
if [ "$VERBOSE" = true ]; then
CANARY_BUILD_ARGS+=("--progress" "plain")
fi
# Add labels
CANARY_BUILD_ARGS+=(
"--label" "org.opencontainers.image.title=${CANARY_IMAGE_NAME}"
"--label" "org.opencontainers.image.description=Open LPR Canary Service - Monitoring and Health Checks"
"--label" "org.opencontainers.image.url=https://github.com/${REPOSITORY_OWNER}/${IMAGE_NAME}"
"--label" "org.opencontainers.image.source=https://github.com/${REPOSITORY_OWNER}/${IMAGE_NAME}"
"--label" "org.opencontainers.image.version=${TAG}"
"--label" "org.opencontainers.image.created=${BUILD_DATE}"
"--label" "org.opencontainers.image.revision=${GIT_COMMIT}"
"--label" "org.opencontainers.image.branch=${GIT_BRANCH}"
"--label" "org.opencontainers.image.licenses=MIT"
)
print_status "Building canary Docker image..."
if [ "$VERBOSE" = true ]; then
print_status "Canary build command: docker build ${CANARY_BUILD_ARGS[*]}"
fi
if docker build "${CANARY_BUILD_ARGS[@]}"; then
print_success "Canary image built successfully: ${CANARY_FULL_IMAGE_NAME}"
else
print_error "Canary build failed"
exit 1
fi
# Push canary image if pushing is enabled
if [ "$PUSH" = true ]; then
print_status "Pushing canary image to registry..."
if docker push "$CANARY_FULL_IMAGE_NAME"; then
print_success "Canary image pushed successfully: ${CANARY_FULL_IMAGE_NAME}"
else
print_error "Canary image push failed"
exit 1
fi
fi
}
# Function to generate SBOM
generate_sbom() {
if [ "$SBOM" = true ]; then
print_status "Generating SBOM..."
# Check if syft is available
if command -v syft &> /dev/null; then
syft "${FULL_IMAGE_NAME}" -o spdx-json -o "sbom-${TAG}.spdx.json"
print_success "SBOM generated: sbom-${TAG}.spdx.json"
else
print_warning "syft is not installed. Skipping SBOM generation."
print_warning "Install syft with: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin"
fi
fi
}
# Function to show image information
show_image_info() {
print_status "Image information:"
# Show image size
if docker images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}" | grep -E "${IMAGE_NAME}:${TAG}"; then
:
else
print_warning "Image size information not available (might be a remote image)"
fi
# Show image digest if available
if docker manifest inspect "${FULL_IMAGE_NAME}" &> /dev/null; then
DIGEST=$(docker manifest inspect "${FULL_IMAGE_NAME}" | jq -r '.manifests[0].digest' 2>/dev/null || echo "N/A")
print_status "Image digest: ${DIGEST}"
fi
}
# Function to run basic tests
run_tests() {
print_status "Running basic container tests..."
# Test if image exists locally
if docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "${FULL_IMAGE_NAME}"; then
print_status "Testing container startup..."
# Create a temporary container to test
CONTAINER_NAME="test-$(date +%s)"
if docker run --name "$CONTAINER_NAME" --rm -d \
-e DEBUG=False \
-e SECRET_KEY=test-key-for-build-validation \
"${FULL_IMAGE_NAME}" sleep 10 &> /dev/null; then
# Check if container is running
if docker ps --format "{{.Names}}" | grep -q "$CONTAINER_NAME"; then
print_success "Container startup test passed"
docker stop "$CONTAINER_NAME" &> /dev/null
else
print_error "Container startup test failed"
docker logs "$CONTAINER_NAME" 2>&1 | tail -10
exit 1
fi
else
print_warning "Container startup test could not be performed"
fi
else
print_warning "Image not available locally for testing (remote build)"
fi
}
# Function to tag local images with GHCR names for local development
tag_local_images() {
if [ "$PUSH" = false ]; then
print_status "Tagging local images for development use..."
# Tag main image for local use
LOCAL_MAIN_TAG="${IMAGE_NAME}:latest"
if docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "${FULL_IMAGE_NAME}"; then
if docker tag "${FULL_IMAGE_NAME}" "${LOCAL_MAIN_TAG}"; then
print_success "Tagged main image locally: ${LOCAL_MAIN_TAG}"
fi
fi
# Tag canary image for local use
LOCAL_CANARY_TAG="${CANARY_IMAGE_NAME}:latest"
if docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "${CANARY_FULL_IMAGE_NAME}"; then
if docker tag "${CANARY_FULL_IMAGE_NAME}" "${LOCAL_CANARY_TAG}"; then
print_success "Tagged canary image locally: ${LOCAL_CANARY_TAG}"
fi
fi
fi
}
# Main execution
main() {
print_status "Open LPR Docker Build Script"
print_status "============================"
# Show configuration
print_status "Configuration:"
print_status " Image Name: ${IMAGE_NAME}"
print_status " Tag: ${TAG}"
print_status " Full Name: ${FULL_IMAGE_NAME}"
print_status " Registry: ${REGISTRY}"
print_status " Owner: ${REPOSITORY_OWNER}"
print_status " Platforms: ${PLATFORMS}"
print_status " Push: ${PUSH}"
print_status " Build Cache: ${BUILD_CACHE}"
print_status " Generate SBOM: ${SBOM}"
echo ""
# Build main image
build_main_image
# Build canary image
build_canary_image
# Tag local images for development
tag_local_images
# Generate SBOM if requested
generate_sbom
# Show image information
show_image_info
# Run tests if image is local
if [ "$PUSH" = false ]; then
run_tests
fi
print_success "Build process completed successfully!"
}
# Run main function
main "$@"