Skip to content

Instantly share code, notes, and snippets.

@cumulus13
Created January 17, 2026 12:33
Show Gist options
  • Select an option

  • Save cumulus13/b99c0adda42f83bbd578acd0fccf24bb to your computer and use it in GitHub Desktop.

Select an option

Save cumulus13/b99c0adda42f83bbd578acd0fccf24bb to your computer and use it in GitHub Desktop.
Robust Go project build script with notifications
:: File: GoBuild.bat
:: Author: Hadi Cahyadi <cumulus13@gmail.com>
:: Date: 2026-01-17
:: Description: Robust Go project build script with notifications
:: License: MIT
:: Usage:
:: gobuild main.go -o app.exe
:: gobuild mydir\ -o app.exe
:: gobuild -o app.exe main.go
:: gobuild --output=app.exe --tags=prod --ldflags="-s -w"
:: gobuild -h
@echo off
setlocal enabledelayedexpansion
:: Jump to main immediately to skip function definitions
GOTO :main
:: ============================================================================
:: Helper Functions
:: ============================================================================
:show_help
echo GoBuild - Robust Go Build Script v1.0.0
echo.
echo Usage:
echo gobuild [source] [options]
echo.
echo Arguments:
echo source Source file or directory (default: current directory)
echo.
echo Options:
echo -o, --output FILE Output binary name (required)
echo -t, --tags TAGS Build tags (comma-separated)
echo -l, --ldflags FLAGS Linker flags
echo --os OS Target OS (windows, linux, darwin)
echo --arch ARCH Target architecture (amd64, 386, arm64)
echo -c, --clean Clean build cache before building
echo -r, --run-tests Run tests before building
echo -v, --verbose Verbose output
echo -z, --compress Compress binary with UPX after build
echo -h, --help Show this help message
echo.
echo Examples:
echo gobuild main.go -o app.exe
echo gobuild mydir\ -o app.exe
echo gobuild -o app.exe --tags=prod --ldflags="-s -w"
echo gobuild --os=linux --arch=amd64 -o myapp
echo gobuild -c -r -o app.exe
GOTO :EOF
:get_timestamp
for /f "tokens=* delims=" %%a in ('date /t') do set current_date=%%a
for /f "tokens=* delims=" %%b in ('time /t') do set current_time=%%b
GOTO :EOF
:log_info
if %VERBOSE%==1 echo [INFO] %~1
GOTO :EOF
:log_error
echo [ERROR] %~1
GOTO :EOF
:send_notification
set notif_type=%~1
set notif_title=%~2
set notif_message=%~3
set notif_priority=%~4
call :get_timestamp
if %NTFY_ENABLED%==1 (
if exist "%NTFY_PATH%" (
"%NTFY_PATH%" pub -t "Go: %notif_type% %current_date% %current_time%" -m "[%current_date% %current_time%] %notif_message%" -i "%NTFY_ICON%" %NTFY_URL% >nul 2>&1
)
)
if %GROWL_ENABLED%==1 (
where sendgrowl >nul 2>&1
if !errorlevel!==0 (
sendgrowl GoBuild %notif_type% "%notif_title%" "%notif_message%" -p %notif_priority% >nul 2>&1
)
)
GOTO :EOF
:check_go_installation
where go >nul 2>&1
if errorlevel 1 (
call :log_error "Go is not installed or not in PATH"
call :send_notification "FAILED" "Build Failed" "❌ Go is not installed or not in PATH" 2
exit /b 1
)
GOTO :EOF
:clean_build_cache
call :log_info "Cleaning build cache..."
go clean -cache -modcache -testcache 2>nul
if errorlevel 1 (
call :log_error "Failed to clean build cache"
exit /b 1
)
call :log_info "Build cache cleaned successfully"
GOTO :EOF
:run_tests
call :log_info "Running tests..."
if "%INPUT_SOURCE%"=="" (
go test ./... -v
) else (
go test "%INPUT_SOURCE%" -v
)
if errorlevel 1 (
call :log_error "Tests failed"
call :send_notification "FAILED" "Test Failed" "❌ Tests failed before building" 2
exit /b 1
)
call :log_info "All tests passed"
GOTO :EOF
:compress_binary
if not exist "%OUTPUT_FILE%" (
call :log_error "Output file not found for compression"
exit /b 1
)
where upx >nul 2>&1
if errorlevel 1 (
call :log_error "UPX is not installed or not in PATH, skipping compression"
exit /b 0
)
call :log_info "Compressing binary with UPX..."
upx --best --lzma "%OUTPUT_FILE%" >nul 2>&1
if errorlevel 1 (
call :log_error "Compression failed, but build was successful"
exit /b 0
)
call :log_info "Binary compressed successfully"
GOTO :EOF
:: ============================================================================
:: Argument Parsing
:: ============================================================================
:parse_args
:parse_loop
if "%~1"=="" GOTO :parse_done
set "arg=%~1"
:: Help
if /i "%arg%"=="-h" (
call :show_help
exit /b 0
)
if /i "%arg%"=="--help" (
call :show_help
exit /b 0
)
:: Output (-o requires next argument)
if /i "%arg%"=="-o" (
if "%~2"=="" (
call :log_error "Option -o requires an argument"
exit /b 1
)
set "OUTPUT_FILE=%~2"
shift
shift
GOTO :parse_loop
)
:: Output (--output=value)
if /i "%arg:~0,9%"=="--output=" (
set "OUTPUT_FILE=%arg:~9%"
shift
GOTO :parse_loop
)
:: Output (--output requires next argument)
if /i "%arg%"=="--output" (
if "%~2"=="" (
call :log_error "Option --output requires an argument"
exit /b 1
)
set "OUTPUT_FILE=%~2"
shift
shift
GOTO :parse_loop
)
:: Build tags (-t requires next argument)
if /i "%arg%"=="-t" (
if "%~2"=="" (
call :log_error "Option -t requires an argument"
exit /b 1
)
set "BUILD_TAGS=%~2"
shift
shift
GOTO :parse_loop
)
:: Build tags (--tags=value)
if /i "%arg:~0,7%"=="--tags=" (
set "BUILD_TAGS=%arg:~7%"
shift
GOTO :parse_loop
)
:: Build tags (--tags requires next argument)
if /i "%arg%"=="--tags" (
if "%~2"=="" (
call :log_error "Option --tags requires an argument"
exit /b 1
)
set "BUILD_TAGS=%~2"
shift
shift
GOTO :parse_loop
)
:: Linker flags (-l requires next argument)
if /i "%arg%"=="-l" (
if "%~2"=="" (
call :log_error "Option -l requires an argument"
exit /b 1
)
set "LD_FLAGS=%~2"
shift
shift
GOTO :parse_loop
)
:: Linker flags (--ldflags=value)
if /i "%arg:~0,10%"=="--ldflags=" (
set "LD_FLAGS=%arg:~10%"
shift
GOTO :parse_loop
)
:: Linker flags (--ldflags requires next argument)
if /i "%arg%"=="--ldflags" (
if "%~2"=="" (
call :log_error "Option --ldflags requires an argument"
exit /b 1
)
set "LD_FLAGS=%~2"
shift
shift
GOTO :parse_loop
)
:: Target OS (--os=value)
if /i "%arg:~0,5%"=="--os=" (
set "GO_OS=%arg:~5%"
shift
GOTO :parse_loop
)
:: Target OS (--os requires next argument)
if /i "%arg%"=="--os" (
if "%~2"=="" (
call :log_error "Option --os requires an argument"
exit /b 1
)
set "GO_OS=%~2"
shift
shift
GOTO :parse_loop
)
:: Target Architecture (--arch=value)
if /i "%arg:~0,7%"=="--arch=" (
set "GO_ARCH=%arg:~7%"
shift
GOTO :parse_loop
)
:: Target Architecture (--arch requires next argument)
if /i "%arg%"=="--arch" (
if "%~2"=="" (
call :log_error "Option --arch requires an argument"
exit /b 1
)
set "GO_ARCH=%~2"
shift
shift
GOTO :parse_loop
)
:: Verbose flag
if /i "%arg%"=="-v" (
set VERBOSE=1
shift
GOTO :parse_loop
)
if /i "%arg%"=="--verbose" (
set VERBOSE=1
shift
GOTO :parse_loop
)
:: Clean flag
if /i "%arg%"=="-c" (
set CLEAN_BUILD=1
shift
GOTO :parse_loop
)
if /i "%arg%"=="--clean" (
set CLEAN_BUILD=1
shift
GOTO :parse_loop
)
:: Run tests flag
if /i "%arg%"=="-r" (
set RUN_TESTS=1
shift
GOTO :parse_loop
)
if /i "%arg%"=="--run-tests" (
set RUN_TESTS=1
shift
GOTO :parse_loop
)
:: Compress flag
if /i "%arg%"=="-z" (
set COMPRESS=1
shift
GOTO :parse_loop
)
if /i "%arg%"=="--compress" (
set COMPRESS=1
shift
GOTO :parse_loop
)
:: If starts with -, it's unknown option
if "%arg:~0,1%"=="-" (
call :log_error "Unknown option: %arg%"
echo.
call :show_help
exit /b 1
)
:: Otherwise, treat as input source
if "%INPUT_SOURCE%"=="" (
set "INPUT_SOURCE=%arg%"
shift
GOTO :parse_loop
)
:: If we already have input source, this is extra argument
call :log_error "Extra argument: %arg%"
echo.
call :show_help
exit /b 1
:parse_done
GOTO :EOF
:: ============================================================================
:: Main Build Process
:: ============================================================================
:main
:: Configuration
set SCRIPT_VERSION=1.0.0
set CURRENT_DIR=%CD%
set BUILD_TAGS=
set LD_FLAGS=
set OUTPUT_FILE=
set INPUT_SOURCE=
set GO_OS=
set GO_ARCH=
set VERBOSE=0
set CLEAN_BUILD=0
set RUN_TESTS=0
set COMPRESS=0
:: Notification settings
set NTFY_ENABLED=1
set GROWL_ENABLED=1
set NTFY_PATH=c:\TOOLS\ntfy_2.11.0_windows_amd64\ntfy.exe
set NTFY_URL=http://222.222.222.5:89/androcall
set NTFY_ICON=https://go.dev/blog/go-brand/Go-Logo/PNG/Go-Logo_Blue.png
:: Parse arguments
call :parse_args %*
if errorlevel 1 exit /b 1
:: Validate required arguments
if "%OUTPUT_FILE%"=="" (
call :log_error "Output file is required. Use -o or --output to specify."
echo.
call :show_help
exit /b 1
)
:: Check Go installation
call :check_go_installation
if errorlevel 1 exit /b 1
:: Set default input source if not provided
if "%INPUT_SOURCE%"=="" (
set "INPUT_SOURCE=."
call :log_info "No source specified, using current directory"
)
:: Validate input source
if not exist "%INPUT_SOURCE%" (
call :log_error "Source not found: %INPUT_SOURCE%"
exit /b 1
)
:: Get project name from current directory
for %%A in ("%CD%") do set "PROJECT_NAME=%%~nxA"
echo.
echo ============================================================
echo GoBuild - Building Go Project
echo ============================================================
echo Project: %PROJECT_NAME%
echo Source: %INPUT_SOURCE%
echo Output: %OUTPUT_FILE%
if not "%BUILD_TAGS%"=="" echo Tags: %BUILD_TAGS%
if not "%LD_FLAGS%"=="" echo Flags: %LD_FLAGS%
if not "%GO_OS%"=="" echo Target OS: %GO_OS%
if not "%GO_ARCH%"=="" echo Target Arch: %GO_ARCH%
echo ============================================================
echo.
:: Clean build cache if requested
if %CLEAN_BUILD%==1 (
call :clean_build_cache
if errorlevel 1 exit /b 1
)
:: Run tests if requested
if %RUN_TESTS%==1 (
call :run_tests
if errorlevel 1 exit /b 1
)
:: Build command construction
set "BUILD_CMD=go build"
:: Add build tags
if not "%BUILD_TAGS%"=="" (
set "BUILD_CMD=!BUILD_CMD! -tags=%BUILD_TAGS%"
)
:: Add linker flags
if not "%LD_FLAGS%"=="" (
set "BUILD_CMD=!BUILD_CMD! -ldflags=%LD_FLAGS%"
)
:: Add verbose flag
if %VERBOSE%==1 (
set "BUILD_CMD=!BUILD_CMD! -v"
)
:: Add output file
set "BUILD_CMD=!BUILD_CMD! -o %OUTPUT_FILE%"
:: Add source
set "BUILD_CMD=!BUILD_CMD! %INPUT_SOURCE%"
:: Set cross-compilation environment
if not "%GO_OS%"=="" (
set GOOS=%GO_OS%
)
if not "%GO_ARCH%"=="" (
set GOARCH=%GO_ARCH%
)
:: Execute build
call :log_info "Building..."
call :log_info "Command: %BUILD_CMD%"
%BUILD_CMD%
if errorlevel 1 (
call :log_error "Build failed!"
call :send_notification "FAILED" "Build Failed" "❌ Build failed for %PROJECT_NAME%" 2
cd /d "%CURRENT_DIR%"
exit /b 1
)
:: Compress if requested
if %COMPRESS%==1 (
call :compress_binary
)
:: Get file size
if exist "%OUTPUT_FILE%" (
for %%A in ("%OUTPUT_FILE%") do set FILE_SIZE=%%~zA
set /a FILE_SIZE_KB=!FILE_SIZE! / 1024
call :log_info "Output size: !FILE_SIZE_KB! KB"
)
echo.
echo ============================================================
echo Build Successful!
echo ============================================================
echo Output: %OUTPUT_FILE%
if defined FILE_SIZE_KB echo Size: !FILE_SIZE_KB! KB
echo ============================================================
echo.
call :send_notification "SUCCESS" "Build Success" "✅ Build successful: %OUTPUT_FILE%" 0
cd /d "%CURRENT_DIR%"
exit /b 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment