# yaml-language-server: $schema=https://json.schemastore.org/github-action.json # permissions: # - contents: read # Required for checking out repository --- name: Go Build description: 'Builds the Go project.' author: 'Ismo Vuorinen' branding: icon: package color: blue inputs: go-version: description: 'Go version to use.' required: false destination: description: 'Build destination directory.' required: false default: './bin' max-retries: description: 'Maximum number of retry attempts for go mod download operations' required: false default: '3' token: description: 'GitHub token for authentication' required: false default: '' outputs: build_status: description: 'Build completion status (success/failure)' value: ${{ steps.build.outputs.status }} test_status: description: 'Test execution status (success/failure/skipped)' value: ${{ steps.test.outputs.status }} go_version: description: 'Version of Go used' value: ${{ steps.detect-go-version.outputs.detected-version }} binary_path: description: 'Path to built binaries' value: ${{ inputs.destination }} coverage_path: description: 'Path to coverage report' value: 'coverage.out' runs: using: composite steps: - name: Checkout Repository uses: actions/checkout@71cf2267d89c5cb81562390fa70a37fa40b1305e # v6-beta with: token: ${{ inputs.token || github.token }} - name: Detect Go Version id: detect-go-version shell: sh env: DEFAULT_VERSION: "${{ inputs.go-version || '1.24' }}" run: | set -eu # Function to validate version format validate_version() { version=$1 case "$version" in [0-9]*\.[0-9]* | [0-9]*\.[0-9]*\.[0-9]*) return 0 ;; *) return 1 ;; esac } # Function to clean version string clean_version() { printf '%s' "$1" | sed 's/^[vV]//' | tr -d ' \n\r' } detected_version="" # Parse .tool-versions file if [ -f .tool-versions ]; then echo "Checking .tool-versions for golang..." >&2 version=$(awk '/^golang[[:space:]]/ {gsub(/#.*/, ""); print $2; exit}' .tool-versions 2>/dev/null || echo "") if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found Go version in .tool-versions: $version" >&2 detected_version="$version" fi fi fi # Parse Dockerfile if [ -z "$detected_version" ] && [ -f Dockerfile ]; then echo "Checking Dockerfile for golang..." >&2 version=$(grep -iF "FROM" Dockerfile | grep -F "golang:" | head -1 | \ sed -n -E "s/.*golang:([0-9]+(\.[0-9]+)*)(-[^:]*)?.*/\1/p" || echo "") if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found Go version in Dockerfile: $version" >&2 detected_version="$version" fi fi fi # Parse devcontainer.json if [ -z "$detected_version" ] && [ -f .devcontainer/devcontainer.json ]; then echo "Checking devcontainer.json for golang..." >&2 if command -v jq >/dev/null 2>&1; then version=$(jq -r '.image // empty' .devcontainer/devcontainer.json 2>/dev/null | sed -n -E "s/.*golang:([0-9]+(\.[0-9]+)*)(-[^:]*)?. */\1/p" || echo "") if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found Go version in devcontainer: $version" >&2 detected_version="$version" fi fi fi fi # Parse .go-version file if [ -z "$detected_version" ] && [ -f .go-version ]; then echo "Checking .go-version..." >&2 version=$(tr -d '\r' < .go-version | head -1) if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found Go version in .go-version: $version" >&2 detected_version="$version" fi fi fi # Parse go.mod if [ -z "$detected_version" ] && [ -f go.mod ]; then echo "Checking go.mod..." >&2 version=$(grep -E '^go[[:space:]]+[0-9]' go.mod | awk '{print $2}' | head -1 || echo "") if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found Go version in go.mod: $version" >&2 detected_version="$version" fi fi fi # Use default version if nothing detected if [ -z "$detected_version" ]; then detected_version="$DEFAULT_VERSION" echo "Using default Go version: $detected_version" >&2 fi # Set output printf 'detected-version=%s\n' "$detected_version" >> "$GITHUB_OUTPUT" echo "Final detected Go version: $detected_version" >&2 - name: Setup Go uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version: ${{ steps.detect-go-version.outputs.detected-version }} cache: true - name: Download Dependencies uses: step-security/retry@e1d59ce1f574b32f0915e3a8df055cfe9f99be5d # v3 with: timeout_minutes: 10 max_attempts: ${{ inputs.max-retries }} command: | echo "Downloading Go dependencies..." go mod download go mod verify - name: Build Go Project id: build shell: sh env: DESTINATION: ${{ inputs.destination }} run: | set -eu echo "Building Go project..." # Create destination directory mkdir -p "$DESTINATION" build_success=true # Check if there are any main packages if find . -name "*.go" -exec grep -l "package main" {} \; | head -1 | grep -q .; then # Build all main packages find . -name "*.go" -exec grep -l "package main" {} \; | xargs dirname | sort -u | while IFS= read -r main_dir; do echo "Building package in $main_dir..." output_name=$(basename -- "$main_dir") if ! go build -ldflags="-s -w" -o "$DESTINATION/$output_name" "$main_dir"; then build_success=false fi done else echo "No main packages found, building library..." if ! go build ./...; then build_success=false fi fi if [ "$build_success" = true ]; then echo "status=success" >> "$GITHUB_OUTPUT" echo "Build completed successfully" else echo "status=failure" >> "$GITHUB_OUTPUT" echo "Build failed" exit 1 fi - name: Run Tests id: test shell: sh run: | set -eu echo "Running Go tests..." if find . -name "*_test.go" | grep -q .; then # Check if race detector is supported on this platform # The race detector is only supported on specific GOOS/GOARCH combinations: # linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64, # freebsd/amd64, netbsd/amd64 RACE_FLAG="" GOOS=$(go env GOOS) GOARCH=$(go env GOARCH) case "${GOOS}/${GOARCH}" in linux/amd64|linux/arm64|darwin/amd64|darwin/arm64|windows/amd64|freebsd/amd64|netbsd/amd64) RACE_FLAG="-race" echo "Race detector enabled for ${GOOS}/${GOARCH}" ;; *) echo "Race detector not supported on ${GOOS}/${GOARCH}, skipping -race flag" ;; esac if go test -v ./... $RACE_FLAG -coverprofile=coverage.out; then echo "status=success" >> "$GITHUB_OUTPUT" echo "Tests completed successfully" else echo "status=failure" >> "$GITHUB_OUTPUT" echo "Tests failed" exit 1 fi else echo "No test files found, skipping test execution." echo "status=skipped" >> "$GITHUB_OUTPUT" fi - name: Upload Build Artifacts if: always() uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: go-build-artifacts path: | ${{ inputs.destination }}/* coverage.out if-no-files-found: ignore