Please purchase the course to watch this video.

Full Course
Distributing applications and code in Go involves utilizing three primary methods tailored to different needs: publishing code as packages for others to import, sharing open-source applications for users to install, and deploying binaries for end-users who may not wish to engage with the Go toolchain. A public GitHub repository serves as the central hub for sharing both packages and applications, ensuring accessibility and facilitating installation via commands like go get
and go install
. Additionally, distributing binaries across different operating systems and architectures can be achieved through the go build
command with specific environment variables to target desired platforms. Understanding licensing—such as choosing between permissive (MIT) and restrictive (GPL) licenses—is crucial for protecting both creators and users when sharing open-source code. With these foundational practices, developers can effectively share their work with the broader community.
Now that we've managed to build quite a few applications throughout this module, in this lesson, we're going to talk about how we can actually distribute our applications to other users, as well as distributing our code.
When it comes to Go, there are three main ways to distribute your code, depending on your use case:
- Package Distribution - Code that other developers import into their projects
- Open Source Applications - CLI tools users can install via
go install
- Binary Distribution - Executable files for users without Go installed
Method 1: Package Distribution
Example: UI Package
Earlier in this module, we built a UI package for CLI elements. I've published this to a GitHub repository at dreamsofcode-io/termui
.
Key Requirements for Package Distribution
To distribute a package for others to use:
- Public GitHub Repository - Code must be publicly accessible
- Matching Module Name - Module name must match GitHub URL
- Proper Go Module Structure - Valid
go.mod
file
go.mod Example:
module github.com/dreamsofcode-io/termui
go 1.21
Important: The module name (github.com/dreamsofcode-io/termui
) must exactly match the GitHub repository URL.
Using a Third-Party Package
Here's how to use our published UI package in another project:
Project Structure:
my-app/
├── main.go
└── go.mod
Step 1: Install the Package
go get github.com/dreamsofcode-io/termui
This adds the package to your go.mod
:
module my-app
go 1.21
require github.com/dreamsofcode-io/termui v0.1.0
Step 2: Import and Use
package main
import (
"time"
"github.com/dreamsofcode-io/termui/spinner"
)
func main() {
// Create and configure spinner
s := spinner.New()
s.SetFrames(spinner.FrameDots)
// Start spinner
s.Start()
// Simulate work
time.Sleep(5 * time.Second)
// Stop spinner
s.Stop()
println("Work completed!")
}
Benefits of Package Distribution
- Easy Installation - Simple
go get
command - Version Management - Go modules handle versioning automatically
- Dependency Resolution - Go resolves transitive dependencies
- Documentation - Automatic documentation via pkg.go.dev
Method 2: Open Source Application Distribution
Example: Password Checker CLI
Let's distribute our pwned
application (password leak checker) as an open source CLI tool.
Project Structure:
pwned/
├── main.go
├── go.mod
├── README.md
└── LICENSE
Step 1: Choose an Open Source License
Critical: Always include a license when distributing open source code.
Common License Options:
License | Description | Best For |
---|---|---|
MIT | Very permissive - allows commercial use, modification, distribution | Most flexible sharing |
GPL v3 | Copyleft - derivatives must also be open source | Ensuring code stays open |
Apache 2.0 | Permissive with patent protection | Corporate-friendly projects |
BSD 3-Clause | Permissive with attribution requirement | Academic/research projects |
Quick Guide:
- Want maximum freedom? → MIT
- Want to keep derivatives open? → GPL v3
Step 2: Prepare the Repository
main.go (CLI application):
package main
import (
"bufio"
"crypto/sha1"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: pwned <password>")
os.Exit(1)
}
password := os.Args[1]
if isPasswordPwned(password) {
fmt.Println("⚠️ Password found in breach database!")
os.Exit(1)
} else {
fmt.Println("✅ Password is safe")
}
}
func isPasswordPwned(password string) bool {
// Hash the password
hash := sha1.Sum([]byte(password))
hashStr := fmt.Sprintf("%X", hash)
// Use k-anonymity: send only first 5 characters
prefix := hashStr[:5]
suffix := hashStr[5:]
// Query HaveIBeenPwned API
url := fmt.Sprintf("https://api.pwnedpasswords.com/range/%s", prefix)
resp, err := http.Get(url)
if err != nil {
fmt.Printf("Error checking password: %v\n", err)
return false
}
defer resp.Body.Close()
// Scan response for our hash suffix
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := scanner.Text()
parts := strings.Split(line, ":")
if len(parts) == 2 && parts[0] == suffix {
count, _ := strconv.Atoi(parts[1])
if count > 0 {
return true
}
}
}
return false
}
go.mod:
module github.com/dreamsofcode-io/pwned
go 1.21
Step 3: Publish to GitHub
# Initialize git repository
git init
git add .
git commit -m "Initial commit"
# Add remote repository
git remote add origin [email protected]:dreamsofcode-io/pwned.git
# Push to GitHub
git push -u origin main
Step 4: Install the Application
Now anyone can install your CLI tool:
# Install latest version
go install github.com/dreamsofcode-io/pwned@latest
# Install specific version/tag
go install github.com/dreamsofcode-io/[email protected]
# Install from specific commit
go install github.com/dreamsofcode-io/pwned@abc1234
Where Applications Are Installed
# Check installation location
which pwned
# Output: /Users/username/go/bin/pwned
# The Go binary directory
echo $GOPATH/bin
# or
go env GOPATH
Installing Other Popular Go Tools
# Install Hugo static site generator
go install github.com/gohugoio/hugo@latest
# Install popular development tools
go install github.com/air-verse/air@latest # Live reload
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest # Linter
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest # Protobuf
Local Installation
You can also install from local directories:
# Install from current directory
go install .
# Install from specific local path
go install ./path/to/my/app
Method 3: Binary Distribution
The Cross-Compilation Problem
Go builds are platform-specific. A binary built on macOS won't run on Linux:
# Build on macOS
go build -o pwned
# Copy to Linux machine
scp pwned user@linux-server:~/
# Try to run on Linux
./pwned
# Error: exec format error
Understanding GOOS and GOARCH
Go uses two environment variables for cross-compilation:
GOOS
- Target operating system (linux
,windows
,darwin
, etc.)GOARCH
- Target architecture (amd64
,arm64
,386
, etc.)
Check Your Current Platform:
go env GOOS GOARCH
# Output: darwin arm64 (on Apple Silicon Mac)
# or: linux amd64 (on Intel Linux)
List All Supported Platforms:
go tool dist list
Output includes:
android/386
android/amd64
android/arm
android/arm64
darwin/amd64
darwin/arm64
freebsd/386
freebsd/amd64
linux/386
linux/amd64
linux/arm
linux/arm64
windows/386
windows/amd64
windows/arm64
...
Cross-Compilation Examples
Build for Linux (from any platform):
GOOS=linux GOARCH=amd64 go build -o pwned_linux_amd64
Build for Windows:
GOOS=windows GOARCH=amd64 go build -o pwned_windows_amd64.exe
Build for Apple Silicon Mac:
GOOS=darwin GOARCH=arm64 go build -o pwned_darwin_arm64
Build for Intel Mac:
GOOS=darwin GOARCH=amd64 go build -o pwned_darwin_amd64
Professional Binary Naming Convention
Use descriptive names that include the platform:
# Format: {app}_{os}_{arch}[.ext]
pwned_linux_amd64
pwned_darwin_arm64
pwned_windows_amd64.exe
pwned_freebsd_amd64
Complete Cross-Compilation Script
build.sh:
#!/bin/bash
APP_NAME="pwned"
VERSION="v1.0.0"
# Create build directory
mkdir -p builds
# Build for multiple platforms
platforms=(
"linux/amd64"
"linux/arm64"
"darwin/amd64"
"darwin/arm64"
"windows/amd64"
"freebsd/amd64"
)
for platform in "${platforms[@]}"
do
platform_split=(${platform//\// })
GOOS=${platform_split[0]}
GOARCH=${platform_split[1]}
output_name="${APP_NAME}_${GOOS}_${GOARCH}"
# Add .exe for Windows
if [ $GOOS = "windows" ]; then
output_name+='.exe'
fi
echo "Building for $GOOS/$GOARCH..."
env GOOS=$GOOS GOARCH=$GOARCH go build -o "builds/${output_name}"
if [ $? -ne 0 ]; then
echo 'An error has occurred! Aborting the script execution...'
exit 1
fi
done
echo "Build complete! Binaries are in ./builds/"
Make the script executable and run:
chmod +x build.sh
./build.sh
Advanced Build Options
Reduce Binary Size:
# Strip debug information and symbol table
go build -ldflags="-s -w" -o pwned_linux_amd64
# Even more aggressive compression (requires UPX)
upx --brute pwned_linux_amd64
Embed Version Information:
# Set version at build time
go build -ldflags="-X main.version=1.0.0" -o pwned_linux_amd64
// In your main.go
package main
var version = "dev" // Will be overridden at build time
func main() {
if len(os.Args) > 1 && os.Args[1] == "--version" {
fmt.Printf("pwned version %s\n", version)
return
}
// ... rest of application
}
Distribution Strategies
1. GitHub Releases
Create releases with binaries for multiple platforms:
# Create and push a tag
git tag v1.0.0
git push origin v1.0.0
Then upload binaries to the GitHub release page. Users can download the appropriate binary for their platform.
Example: Hugo Releases
2. Package Managers
Homebrew (macOS/Linux):
# Formula for Homebrew
class Pwned < Formula
desc "Check if password appears in data breaches"
homepage "https://github.com/dreamsofcode-io/pwned"
url "https://github.com/dreamsofcode-io/pwned/archive/v1.0.0.tar.gz"
sha256 "..."
depends_on "go" => :build
def install
system "go", "build", "-o", bin/"pwned"
end
end
Arch Linux (AUR):
# PKGBUILD for Arch User Repository
pkgname=pwned
pkgver=1.0.0
# ... package definition
3. Container Images
Dockerfile:
# Multi-stage build for minimal image
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o pwned
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/pwned .
CMD ["./pwned"]
Build and publish:
docker build -t dreamsofcode/pwned .
docker push dreamsofcode/pwned
4. Direct Download
Host binaries on your website:
<!-- Download page -->
<h2>Download pwned</h2>
<ul>
<li><a href="/downloads/pwned_linux_amd64">Linux (64-bit)</a></li>
<li><a href="/downloads/pwned_darwin_amd64">macOS (Intel)</a></li>
<li><a href="/downloads/pwned_darwin_arm64">macOS (Apple Silicon)</a></li>
<li><a href="/downloads/pwned_windows_amd64.exe">Windows (64-bit)</a></li>
</ul>
Best Practices for Distribution
1. Versioning
Use semantic versioning (semver):
v1.0.0
- Major releasev1.1.0
- Minor release (new features)v1.1.1
- Patch release (bug fixes)
2. Documentation
Include comprehensive documentation:
- README.md - Installation and usage instructions
- CHANGELOG.md - Version history and changes
- LICENSE - Legal terms for usage
- CONTRIBUTING.md - Guidelines for contributors
3. Automated Builds
Use GitHub Actions for automated building and releasing:
.github/workflows/release.yml:
name: Release
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.21
- name: Build binaries
run: |
# Build for multiple platforms
GOOS=linux GOARCH=amd64 go build -o pwned_linux_amd64
GOOS=darwin GOARCH=amd64 go build -o pwned_darwin_amd64
GOOS=windows GOARCH=amd64 go build -o pwned_windows_amd64.exe
- name: Create Release
uses: softprops/action-gh-release@v1
with:
files: |
pwned_linux_amd64
pwned_darwin_amd64
pwned_windows_amd64.exe
4. Testing
Test your binaries on target platforms:
# Test on different systems
docker run --rm -v $(pwd):/app ubuntu:latest /app/pwned_linux_amd64 --help
5. Security Considerations
- Sign your binaries - Use code signing certificates
- Provide checksums - Allow users to verify integrity
- Use HTTPS - Secure download links
- Scan for vulnerabilities - Regular security audits
Generate checksums:
# Generate SHA256 checksums
sha256sum pwned_* > checksums.txt
Comparing Distribution Methods
Method | Pros | Cons | Best For |
---|---|---|---|
Package | Easy to use, version management | Requires Go knowledge | Developer libraries |
go install | Simple installation, automatic updates | Requires Go toolchain | Open source CLI tools |
Binary | No dependencies, works anywhere | Manual updates, larger downloads | End users, enterprises |
Real-World Examples
Popular Go Applications and Their Distribution
- Docker - Binary releases + package managers
- Kubernetes - Binary releases + package managers + container images
- Hugo - go install + binary releases + package managers
- Terraform - Binary releases + package managers
- Prometheus - Binary releases + container images + package managers
Security and Legal Considerations
Code Signing
For production applications, consider code signing:
# macOS code signing
codesign -s "Developer ID Application: Your Name" pwned_darwin_amd64
# Windows code signing (requires certificate)
signtool sign /f certificate.p12 /p password pwned_windows_amd64.exe
License Compliance
Ensure all dependencies are compatible with your chosen license:
# Check dependency licenses
go-licenses check ./...
Summary
We've covered three main approaches to distributing Go applications:
Key Concepts Learned:
- Package Distribution - Sharing reusable code via GitHub modules
- Open Source Applications - Installing CLI tools with
go install
- Binary Distribution - Cross-compilation for multiple platforms
- Distribution Strategies - GitHub releases, package managers, containers
- Best Practices - Versioning, documentation, automation, security
Benefits of Go Distribution:
- Cross-Platform - Single codebase, multiple platforms
- No Runtime Dependencies - Static binaries work anywhere
- Easy Installation - Simple
go install
for developers - Flexible Distribution - Multiple distribution channels available
Next Steps:
In the next lesson, we'll build a binary builder tool that automates the cross-compilation process for all supported platforms, along with homework to add manifest file configuration.
This will give you a professional toolchain for distributing your Go applications to users worldwide! 🚀
Additional Resources
- Go Modules Documentation - Complete guide to module creation and versioning
- GitHub Releases - How to create and manage releases
- Cross-Compilation Guide - Detailed cross-compilation techniques
- Package Managers - Submitting to Homebrew, AUR, and other repositories
- Code Signing - Platform-specific signing requirements
Remember: The distribution method you choose depends on your audience - developers vs end users, open source vs commercial, and the level of technical expertise expected from your users.
No homework tasks for this lesson.