Skip to main content
tutorial

Unity Build Automation Part 3: Pre-Build Hooks & Build Reliability

Angry Shark Studio
15 min
Unity Python Build Automation Pre-Build Hooks Tutorial DevOps Productivity

It’s 11:45 AM. You start a WebGL build, expecting it to finish during lunch. Perfect timing.

You return at 1:00 PM to find Unity crashed 5 minutes after you left. The build never completed. You wasted 75 minutes waiting for nothing.

Sound familiar?

In Part 1, we automated the clicking. In Part 2, we automated the deployment.

This post covers the missing piece: making builds you can actually walk away from.

Pre-build hooks prepare builds automatically. Error handling catches failures. Logging helps you debug crashes. Workflows fit your schedule.

Start your builds, close your laptop, live your life.

Get the complete code: Unity Build Automation Repository

Split-screen comparison: stressed developer at desk with multiple monitors versus relaxed developer at cafe with single laptop - illustrating manual versus automated build workflows

What You’ll Learn

By the end of this tutorial, you’ll have:

  • Pre-build hooks that run automatically before every build
  • Automatic version incrementing and environment switching
  • Build validation to catch errors before Unity starts
  • Logging and notifications for build status
  • Personal workflows (morning builds, lunch builds, overnight builds)
  • Troubleshooting strategies for failed builds

Everything runs without your intervention. You start the process. Automation handles the rest.

Understanding Pre-Build Hooks

What Are Pre-Build Hooks?

Pre-build hooks are Unity methods that run before builds start. They automate repetitive tasks you’d otherwise do manually.

Common scenarios they solve:

Problem 1: “I always forget to change the server URL for release builds” Solution: Hook switches to production servers automatically

Problem 2: “Version numbers are always wrong” Solution: Hook increments build number before each build

Problem 3: “Debug code keeps making it into production” Solution: Hook removes debug flags and validates configuration

Problem 4: “Different platforms need different settings” Solution: Hook applies platform-specific configuration

Hooks prevent mistakes. They ensure consistency. They save time.

How Hooks Work

The automation system calls Unity methods before building. You write the methods. The system executes them.

Flow diagram showing build automation workflow: script execution, pre-build hook, Unity build process, and notification

Basic hook example:

// In Unity: Assets/Scripts/Editor/BuildHooks.cs
using UnityEditor;
using UnityEngine;

public class BuildHooks
{
    public static void PrepareReleaseBuild()
    {
        Debug.Log("[BUILD HOOK] Preparing release build...");

        // Increment build number
        var version = PlayerSettings.bundleVersion;
        var parts = version.Split('.');
        if (parts.Length == 3)
        {
            var build = int.Parse(parts[2]) + 1;
            PlayerSettings.bundleVersion = $"{parts[0]}.{parts[1]}.{build}";
        }

        // Switch to production environment
        PlayerSettings.SetScriptingDefineSymbolsForGroup(
            BuildTargetGroup.Standalone,
            "RELEASE_BUILD"
        );

        Debug.Log($"[BUILD HOOK] Ready for release: v{PlayerSettings.bundleVersion}");
    }
}

This hook runs before every build. It increments the version. It sets release flags. You never forget.

Configuring Hooks

Configure hooks in your .env file:

# Always run this hook before builds
PRE_BUILD_HOOK="BuildHooks.PrepareReleaseBuild"

Or use command line to override:

# Use specific hook for this build
python BuildAutomation/build_cli.py webgl --hook "BuildHooks.PrepareWebGL"

# Skip configured hook
python BuildAutomation/build_cli.py webgl --no-hook

Hooks are optional. Use them when you need them. Skip them when you don’t.

Essential Pre-Build Hooks

Hook 1: Version Manager

Automatically increment build numbers:

public class BuildHooks
{
    public static void IncrementBuildNumber()
    {
        var currentVersion = PlayerSettings.bundleVersion;
        var versionParts = currentVersion.Split('.');

        if (versionParts.Length != 3)
        {
            Debug.LogError("[HOOK] Invalid version format. Expected X.Y.Z");
            return;
        }

        var major = versionParts[0];
        var minor = versionParts[1];
        var build = int.Parse(versionParts[2]) + 1;

        var newVersion = $"{major}.{minor}.{build}";
        PlayerSettings.bundleVersion = newVersion;

        // Also update Android version code
        PlayerSettings.Android.bundleVersionCode++;

        Debug.Log($"[HOOK] Version updated: {currentVersion} → {newVersion}");
    }
}

Every build gets a new version number. No manual updates needed.

Hook 2: Environment Switcher

Switch between development and production:

public class BuildHooks
{
    public static void SwitchToProduction()
    {
        Debug.Log("[HOOK] Switching to production environment");

        // Remove development symbols
        var symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
        symbols = symbols.Replace("DEVELOPMENT", "");
        symbols = symbols.Replace("DEBUG_MODE", "");
        symbols += ";PRODUCTION";

        PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, symbols);

        Debug.Log("[HOOK] Production environment active");
    }

    public static void SwitchToDevelopment()
    {
        Debug.Log("[HOOK] Switching to development environment");

        var symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
        symbols = symbols.Replace("PRODUCTION", "");
        symbols += ";DEVELOPMENT;DEBUG_MODE";

        PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, symbols);

        Debug.Log("[HOOK] Development environment active");
    }
}

Use scripting symbols in your code:

public class GameConfig : MonoBehaviour
{
    private void Start()
    {
#if PRODUCTION
        apiUrl = "https://api.production.com";
        enableAnalytics = true;
#else
        apiUrl = "https://api.dev.com";
        enableAnalytics = false;
#endif
    }
}

Hook 3: Platform-Specific Setup

Configure settings for different platforms:

public class BuildHooks
{
    public static void PrepareWebGL()
    {
        Debug.Log("[HOOK] Preparing WebGL build");

        // WebGL-specific settings
        PlayerSettings.WebGL.compressionFormat = WebGLCompressionFormat.Gzip;
        PlayerSettings.WebGL.dataCaching = true;

        // Optimize for web
        PlayerSettings.stripEngineCode = true;

        // Use custom template
        PlayerSettings.WebGL.template = "PROJECT:BetterTemplate";

        Debug.Log("[HOOK] WebGL configuration complete");
    }

    public static void PrepareMobile()
    {
        Debug.Log("[HOOK] Preparing mobile build");

        // Mobile optimizations
        QualitySettings.SetQualityLevel(1); // Medium quality

        // Texture compression
        EditorUserBuildSettings.androidBuildSubtarget = MobileTextureSubtarget.ETC2;

        Debug.Log("[HOOK] Mobile configuration complete");
    }
}

Use different hooks for different platforms:

# WebGL with WebGL hook
python BuildAutomation/build_cli.py webgl --hook "BuildHooks.PrepareWebGL"

# Android with mobile hook
python BuildAutomation/build_cli.py android --hook "BuildHooks.PrepareMobile"

Hook 4: Build Validator

Catch errors before Unity starts building:

using System.Collections.Generic;
using System.Linq;

public class BuildHooks
{
    public static void ValidateBeforeBuild()
    {
        Debug.Log("[HOOK] Validating project configuration");

        var errors = new List<string>();

        // Check scenes
        if (EditorBuildSettings.scenes.Length == 0)
        {
            errors.Add("No scenes in build settings");
        }

        // Check icons
        var icons = PlayerSettings.GetIconsForTargetGroup(BuildTargetGroup.Standalone);
        if (icons == null || icons.Length == 0)
        {
            errors.Add("No app icon set");
        }

        // Check company and product names
        if (string.IsNullOrEmpty(PlayerSettings.companyName))
        {
            errors.Add("Company name not set");
        }

        if (string.IsNullOrEmpty(PlayerSettings.productName))
        {
            errors.Add("Product name not set");
        }

        if (errors.Any())
        {
            var message = string.Join("\n", errors);
            Debug.LogError($"[HOOK] Validation failed:\n{message}");
            throw new System.Exception($"Pre-build validation failed:\n{message}");
        }

        Debug.Log("[HOOK] Validation passed");
    }
}

If validation fails, Unity won’t start building. You catch the error immediately instead of discovering it 20 minutes into a build.

Build Reliability

Existing Reliability Features

The automation system includes several reliability features:

Build Logging Every build creates a log file:

BuildAutomation/build_windows.log
BuildAutomation/build_webgl.log
BuildAutomation/build_android.log

Logs capture Unity output, errors, and build statistics.

HTML Build Reports After builds complete, check the HTML report:

BuildAutomation/build_report.html

Reports show build times, file sizes, and success/failure status for all platforms.

Email Notifications Configure SMTP to get notified when builds complete:

# In .env
EMAIL_ENABLED=true
EMAIL_SMTP_HOST=smtp.gmail.com
EMAIL_SMTP_PORT=587
EMAIL_SMTP_USERNAME=your-email@gmail.com
EMAIL_SMTP_PASSWORD=your-app-password
EMAIL_TO=team@company.com

You’ll receive emails with build status, duration, and file sizes.

Exit Codes The build script returns proper exit codes:

  • 0 = At least one build succeeded
  • 1 = All builds failed

Use exit codes in shell scripts or CI/CD pipelines.

Timeline visualization showing build process progression from start to completion with reliability checkpoints

Enhancing Reliability (Optional)

Want to make builds even more reliable? Add timeout handling to the Python scripts.

Create BuildAutomation/unity_builder/timeout_handler.py:

import subprocess
import time
from datetime import datetime

def build_with_timeout(command, timeout_minutes=120):
    """Build with automatic timeout"""

    start_time = time.time()
    timeout_seconds = timeout_minutes * 60

    print(f"Starting build with {timeout_minutes} minute timeout")
    print(f"Command: {' '.join(command)}")

    try:
        process = subprocess.Popen(
            command,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True
        )

        stdout, stderr = process.communicate(timeout=timeout_seconds)

        if process.returncode == 0:
            print(f"Build succeeded in {(time.time() - start_time) / 60:.1f} minutes")
            return True
        else:
            print(f"Build failed with exit code {process.returncode}")
            print(f"Error: {stderr}")
            return False

    except subprocess.TimeoutExpired:
        process.kill()
        print(f"Build timed out after {timeout_minutes} minutes")
        print("Killed Unity process")
        return False

This prevents builds from hanging forever. If Unity freezes, the script kills it after the timeout.

Personal Build Workflows

Morning Build Routine

Start your day with fresh builds:

#!/bin/bash
# morning_builds.sh

echo "Good morning! Starting builds..."

# Quick Windows build for testing
python BuildAutomation/build_cli.py windows \
  --hook "BuildHooks.SwitchToDevelopment"

# WebGL build for web testing
python BuildAutomation/build_cli.py webgl \
  --hook "BuildHooks.PrepareWebGL" \
  --upload

echo "Morning builds started. Check email for results."

Run this when you arrive. Builds complete while you check emails and plan your day.

Lunch Break Workflow

Start a build before lunch:

# Alias in .bashrc or .zshrc
alias lunch-build='python BuildAutomation/build_cli.py webgl --upload'

Use it:

lunch-build
# Go eat lunch
# Build completes and deploys while you're away

End of Day Release

Build everything overnight:

#!/bin/bash
# overnight_builds.sh

echo "Starting overnight builds for all platforms..."

platforms=("windows" "mac" "webgl" "android")

for platform in "${platforms[@]}"
do
    echo "Building $platform..."

    python BuildAutomation/build_cli.py $platform \
      --hook "BuildHooks.PrepareReleaseBuild" \
      --upload \
      --gdrive-upload

    echo "$platform build complete"
done

echo "All builds complete! Check email for summary."

Run this before you leave work. Wake up to completed builds and deployment.

Friday Release Workflow

Complete release process:

#!/bin/bash
# friday_release.sh

echo "Starting Friday release workflow..."

# 1. Increment version for all platforms
echo "Incrementing version..."

# 2. Build all platforms
python BuildAutomation/build_cli.py \
  windows mac webgl \
  --hook "BuildHooks.IncrementBuildNumber" \
  --upload \
  --gdrive-upload

# 3. Notification sent automatically via email

echo "Release builds started. Check email for status."

One command. All platforms. All deployments. All notifications.

Debugging Builds You Didn’t Watch

Reading Build Logs

Builds failed while you were away? Check the logs.

Log files contain three sections:

1. Unity Output

Refreshing native plugins compatible for Editor in 123.45 ms
Scripting defines: UNITY_2022_3_0
Platform: Standalone (Windows)

2. Build Progress

Building Player...
Building WebGL Player...
Compressing to WebGL...
Build completed successfully

3. Errors (if any)

Error: Could not find scene 'Assets/Scenes/MainMenu.unity' in build settings
Build failed with exit code 1

Read from bottom to top. Errors are at the end.

Common Build Failures

Out of Memory

Error: Failed to build player because of out of memory

Solution: Close other apps before building, or build overnight when nothing else runs.

Missing Assets

Error: Asset 'Prefabs/Player.prefab' couldn't be loaded

Solution: Add validation hook to catch missing assets before building.

Platform Module Missing

Error: Requested build target 'Android' is not installed

Solution: Install platform support in Unity Hub.

Scene Not in Build Settings

Error: Scene 'GameplayScene' is not added to build settings

Solution: Add validation hook to verify scenes exist.

Smart Error Handling (Optional Enhancement)

Add this to BuildAutomation/unity_builder/error_handler.py:

def analyze_build_error(log_file):
    """Parse log file and suggest solutions"""

    with open(log_file, 'r') as f:
        log_content = f.read()

    suggestions = []

    if "out of memory" in log_content.lower():
        suggestions.append("Close other applications and try again")

    if "missing asset" in log_content.lower():
        suggestions.append("Verify all assets are in the project")

    if "platform module" in log_content.lower():
        suggestions.append("Install platform support in Unity Hub")

    if "scene" in log_content.lower() and "not" in log_content.lower():
        suggestions.append("Check Build Settings for missing scenes")

    return suggestions

Call this after builds fail to get actionable suggestions.

Complete Example: Weekly Release Workflow

Here’s a complete workflow that combines everything:

#!/bin/bash
# weekly_release.sh

echo "=== Weekly Release Workflow ==="
echo ""

# 1. Validation
echo "[1/4] Validating project..."
python BuildAutomation/build_cli.py windows \
  --hook "BuildHooks.ValidateBeforeBuild" \
  --no-hook

if [ $? -ne 0 ]; then
    echo "Validation failed. Fix errors and try again."
    exit 1
fi

# 2. Increment version
echo ""
echo "[2/4] Incrementing build version..."
python BuildAutomation/build_cli.py windows \
  --hook "BuildHooks.IncrementBuildNumber"

# 3. Build all platforms
echo ""
echo "[3/4] Building all platforms..."
python BuildAutomation/build_cli.py \
  windows mac webgl \
  --hook "BuildHooks.PrepareReleaseBuild" \
  --upload \
  --gdrive-upload

# 4. Check results
echo ""
echo "[4/4] Build complete!"
echo "Check build_report.html for details"
echo "Check email for download links"

This script:

  1. Validates configuration before starting
  2. Increments version number
  3. Builds all platforms with release settings
  4. Deploys to FTP and Google Drive
  5. Emails team with results

Run it Friday afternoon. Everything’s done by Monday morning.

Troubleshooting Guide

Hook Not Running

Symptom: Hook doesn’t execute during build

Checks:

  1. Is the method name correct in .env?

    PRE_BUILD_HOOK="BuildHooks.MyMethod"  # Exact class.method
    
  2. Is the method static and public?

    public static void MyMethod()  // Must be static
    
  3. Try running with explicit hook flag:

    python BuildAutomation/build_cli.py windows --hook "BuildHooks.MyMethod"
    

Build Hangs

Symptom: Build runs for hours without completing

Solutions:

  • Check Unity isn’t waiting for input (dialog boxes)
  • Close Unity Editor before running builds
  • Use timeout handling (shown earlier)
  • Check build logs for where it stopped

Email Not Sending

Symptom: Builds complete but no email

Checks:

  1. Email enabled in .env?

    EMAIL_ENABLED=true
    
  2. Using Gmail app password (not regular password)?

  3. Port 587 open on your network?

  4. Check SMTP logs in terminal output

Best Practices

Hook Best Practices

  1. Keep hooks fast (< 30 seconds)
  2. Always log what hooks are doing
  3. Make hooks idempotent (safe to run multiple times)
  4. Test hooks separately before building

Workflow Best Practices

  1. Start simple - Add complexity gradually
  2. Document your hooks for future you
  3. Version control your hooks - Commit BuildHooks.cs
  4. Share hooks with your team - Everyone benefits

Reliability Best Practices

  1. Always check logs - Even for successful builds
  2. Test deployment separately - Don’t combine untested hooks with deployment
  3. Build one platform at a time when testing new hooks
  4. Use validation hooks - Catch errors before Unity starts

Next Steps

You now have:

  • Pre-build hooks for automatic preparation
  • Build reliability through logging and notifications
  • Personal workflows that fit your schedule
  • Troubleshooting strategies for failed builds

No more babysitting builds. No more manual configuration. No more checking if builds finished.

Start your builds. Close your laptop. Live your life.

Complete repository: Unity Build Automation on GitHub

Series:

Your time is valuable. Automate everything.


Need expert Unity automation help? Our certified team builds production-ready automation systems. Get in touch

Angry Shark Studio Logo

About Angry Shark Studio

Angry Shark Studio is a professional Unity AR/VR development studio specializing in mobile multiplatform applications and AI solutions. Our team includes Unity Certified Expert Programmers with extensive experience in AR/VR development.

Related Articles

More Articles

Explore more insights on Unity AR/VR development, mobile apps, and emerging technologies.

View All Articles

Need Help?

Have questions about this article or need assistance with your project?

Get in Touch