README
ΒΆ

Golang Calculator
An interactive CLI calculatur that makes 1999 jealous with a release, update and distribution model from the modern century with enterprise-grade CI/CD pipelines and sophisticated auto-update system. This is a demonstration of production-ready software engineering practices including semantic versioning, channel-based release management, testing, and automated deployment workflows.
Table of Contents
- Quick Start
- Installation
- Development Workflow
- Release Channel System
- Understanding Channel Isolation (Important!)
- Auto-Update Architecture
- CI/CD Pipeline
- Code Architecture
- Testing Strategy
- Troubleshooting
- Contributing
Quick Start
Get the calculator running in under 60 seconds:
# Clone the repository
git clone https://github.com/jondkelley/cicd_golang_calculator.git
cd cicd_golang_calculator
# Build and run alpha version
export VERSION=0.0.1-alpha
make build
./calc
# Build and run beta version
export VERSION=0.0.1-beta
make build
/calc
# Build and run stable
export VERSION=0.0.1
make build
./calc
# Example usage
> 2 + 3
= 5
> sqrt(16)
= 4
> 10 / 3
= 3.333333
The calculator supports these operations: addition (+), subtraction (-), multiplication (*), division (/), modulus (%), exponentiation (^), and square root (sqrt()).
Installation
Linux Installation
Download the latest release and make it executable:
# Download the latest stable release
curl -L https://github.com/jondkelley/cicd_golang_calculator/releases/latest/download/calc-linux-amd64 -o calc
# Make executable
chmod 755 calc
# Optional: Move to system PATH
sudo mv calc /usr/local/bin/
macOS Installation
macOS requires additional steps due to code signing restrictions:
# Download the appropriate binary for your Mac
# For Intel Macs:
curl -L https://github.com/jondkelley/cicd_golang_calculator/releases/latest/download/calc-darwin-amd64 -o calc
# For Apple Silicon Macs:
curl -L https://github.com/jondkelley/cicd_golang_calculator/releases/latest/download/calc-darwin-arm64 -o calc
# Make executable
chmod 755 calc
# Remove quarantine attribute (required for unsigned binaries)
xattr -d com.apple.quarantine calc
# Optional: Move to system PATH
sudo mv calc /usr/local/bin/
Note: The quarantine removal step is necessary because we don't currently have an Apple Developer Certificate. Future releases will include proper code signing to eliminate this requirement.
Windows Installation
# Download the Windows executable
curl -L https://github.com/jondkelley/cicd_golang_calculator/releases/latest/download/calc-windows-amd64.exe -o calc.exe
# Run directly
calc.exe
Windows Defender might flag the executable as potentially unsafe. This happens with unsigned binaries. Click "More info" then "Run anyway" if prompted.
Development Workflow
Common Build Operations
The project uses a comprehensive Makefile for all build operations. Here are the essential commands:
# Build for current platform
make build
# Run comprehensive tests
make test
# Format all Go code
make fmt
# Run static analysis
make vet
# Build for all platforms
make build-all
# Complete CI pipeline locally
make ci
For detailed information about all available targets, see Makefile.md which contains comprehensive documentation of the build options.
Code Quality Standards
This project maintains production-grade code quality through automated checks:
- Go Format: All code must pass
gofmtformatting - Go Lint: Zero linting violations allowed via
golint - Go Vet: Static analysis must pass without warnings
- Test Coverage: Comprehensive unit tests required for all logic
Pre-commit Hooks
Set up pre-commit hooks to catch issues before pushing:
# Create pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
echo "Running pre-commit checks..."
# Format check
if ! make fmt; then
echo "XXX Code formatting failed"
exit 1
fi
# Vet check
if ! make vet; then
echo "XXX Go vet failed"
exit 1
fi
# Run tests
if ! make test; then
echo "XXX Tests failed"
exit 1
fi
echo "[OK] All pre-commit checks passed"
EOF
# Make executable
chmod +x .git/hooks/pre-commit
Pull Request Requirements: All PRs must pass format checks, linting, and the complete test suite before merging. The CI pipeline enforces these requirements automatically.
Release Channel System
The calculator implements a sophisticated three-tier release channel system providing users with different stability guarantees.
Channel Types
| Channel | Stability | Use Case |
|---|---|---|
| Stable | Production-ready | End users, production environments |
| Beta | Feature-complete, testing phase | Early adopters, staging environments |
| Alpha | Bleeding edge, experimental | Developers, testing new features |
Environment Variables
Control your update channel and behavior using environment variables:
# Stay on stable channel (default behavior)
./calc
# Enable alpha releases
CALC_ALLOW_ALPHA=1 ./calc
# Enable beta releases
CALC_ALLOW_BETA=1 ./calc
# Enable automatic updates without prompts
CALC_UNPROMPTED_ENABLE=1 ./calc
# Combine flags for automatic (seamless) alpha updates, this also works with any other version (beta, stable) as well.
CALC_ALLOW_ALPHA=1 CALC_UNPROMPTED_ENABLE=1 ./calc
Update Behavior Controls:
CALC_ALLOW_ALPHA=1: Enables updates to alpha releases for alpha usersCALC_ALLOW_BETA=1: Enables updates to beta releases for beta usersCALC_UNPROMPTED_ENABLE=1: Automatically installs available updates without user confirmation
Channel Isolation Logic
The system enforces strict channel isolation to prevent unexpected version changes:
graph TD
A[User Current Version] --> B{Parse Version Channel}
B --> C[Stable User]
B --> D[Beta User]
B --> E[Alpha User]
C --> F{CALC_ALLOW_ALPHA?}
C --> G{CALC_ALLOW_BETA?}
C --> H[Only Stable Updates]
D --> I{CALC_ALLOW_BETA?}
D --> J[No Updates Without Flag]
I --> K[Only Beta Updates]
E --> L{CALC_ALLOW_ALPHA?}
E --> M[No Updates Without Flag]
L --> N[Only Alpha Updates]
H --> O[Update to Latest Stable]
K --> P[Update to Latest Beta]
N --> Q[Update to Latest Alpha]
Understanding Channel Isolation (Important!)
π¨ CRITICAL CONCEPT: The calculator uses strict channel isolation to prevent unexpected upgrades and downgrades. This means you cannot switch between channels through updates - you can only update within your current channel.
Why You Need Environment Variables
The environment variables (CALC_ALLOW_ALPHA and CALC_ALLOW_BETA) are required for pre-release users to receive updates. This is a safety feature to prevent accidental upgrades to unstable versions.
Scenario Examples:
β Alpha User Getting Alpha Updates:
# Current version: v0.0.1-alpha
# Available: v0.0.2-alpha
CALC_ALLOW_ALPHA=1 ./calc
# Result: "new version v0.0.2-alpha (ALPHA RELEASE) available"
β Alpha User Trying to Get Beta Updates:
# Current version: v0.0.2-alpha
# Available: v0.0.100-beta
CALC_ALLOW_BETA=1 ./calc
# Result: "everything is up to date!" (beta updates blocked)
β Beta User Getting Beta Updates:
# Current version: v0.0.1-beta
# Available: v0.0.100-beta
CALC_ALLOW_BETA=1 ./calc
# Result: "new version v0.0.100-beta (BETA RELEASE) available"
β Beta User Without Environment Variable:
# Current version: v0.0.1-beta
# Available: v0.0.100-beta
./calc # No CALC_ALLOW_BETA=1
# Result: "everything is up to date!" (no updates without flag)
Channel Switching Limitations
You CANNOT switch channels through the auto-updater. The system enforces these rules:
| Current Channel | Can Update To | Cannot Update To |
|---|---|---|
Alpha (v1.0.0-alpha) |
β Newer alpha only | β Beta, Stable |
Beta (v1.0.0-beta) |
β Newer beta only | β Alpha, Stable |
Stable (v1.0.0) |
β Newer stable only | β Alpha, Beta |
Common Channel Scenarios
Scenario 1: "I'm on alpha but want to try beta"
# β This will NOT work:
CALC_ALLOW_BETA=1 ./calc # Still shows "up to date"
# β
Manual channel switch required:
wget https://github.com/jondkelley/cicd_golang_calculator/releases/download/v0.0.100-beta/calc-linux-amd64
chmod +x calc-linux-amd64 && mv calc-linux-amd64 calc
Scenario 2: "I'm on beta but want the latest stable"
# β This will NOT work:
./calc # Still shows "up to date" (can't cross to stable)
# β
Manual installation required:
wget https://github.com/jondkelley/cicd_golang_calculator/releases/latest/download/calc-linux-amd64
chmod +x calc-linux-amd64 && mv calc-linux-amd64 calc
Scenario 3: "I forgot to set the environment variable"
# Current: v0.0.1-alpha, Available: v0.0.2-alpha
./calc # Shows "everything is up to date!" β
# Fix: Add the environment variable
CALC_ALLOW_ALPHA=1 ./calc # Shows update available β
Why This Design?
This strict channel isolation prevents:
- Accidental downgrades (e.g., stable β beta)
- Unexpected instability (e.g., auto-upgrade from stable to alpha)
- Version confusion (mixing different pre-release types)
- Dependency conflicts (different channels may have different requirements)
Bottom Line: If you want to switch channels, you must manually download and install a release from your desired channel. The auto-updater only works within your current channel for safety.
Auto-Update Architecture
Update Flow Diagram
sequenceDiagram
participant U as User
participant C as Calculator
participant M as Version Manifest
participant G as GitHub Releases
U->>C: Start Application
C->>M: Fetch version.json
M->>C: Return available releases
C->>C: Filter by channel + permissions
C->>C: Compare with current version
alt New Version Available
C->>U: Prompt for update (Y/N)
U->>C: Confirm update
C->>C: Create backup of current binary
C->>G: Download new binary
G->>C: Stream binary data
C->>C: Validate executable format
C->>C: Test binary with --version
C->>C: Replace current binary atomically
C->>U: Update complete, restart required
else No Updates
C->>U: Continue with current version
end
Version Comparison Logic
The semantic version parser handles complex comparison scenarios:
// Example version comparisons
v1.2.3 > v1.2.2 // Patch increment
v1.3.0 > v1.2.9 // Minor increment
v2.0.0 > v1.9.9 // Major increment
v1.0.0 > v1.0.0-beta // Stable > pre-release
v1.0.0-beta > v1.0.0-alpha // Beta > alpha
Critical Edge Case Handling:
- Pre-release versions stay within their channel
- Invalid version strings get skipped gracefully
- Network failures don't crash the application
- Malformed manifest data triggers warnings but allows continued operation
Security & Validation
The update system implements multiple validation layers:
- HTTP Response Validation: Status codes, content types, response sizes
- Executable Format Validation: Magic byte verification (ELF, Mach-O, PE)
- Functional Testing: Execute
--versionon downloaded binary - Atomic Replacement: Temp file validation before overwriting current binary
- Backup Creation: Automatic backup before any update attempt
Future Security Enhancements: The architecture supports cryptographic signature verification. Future releases can include SHA-256 checksums and GPG signatures for enhanced security.
CI/CD Pipeline
GitHub Actions Workflows
The project uses two primary workflows for comprehensive automation:
Release Workflow (release.yml)
Triggers on git tag pushes and handles:
- Multi-platform binary compilation (Linux, Windows, macOS Intel/ARM)
- GitHub release creation with binary attachments
- Version extraction from git tags
- Build artifact verification
Quality Checks Workflow (checks.yml)
Runs on every push and PR:
- Go formatting verification
- Linting with golint
- Complete test suite execution
- Prevents merge of non-compliant code
Release Process
Create releases using git tags with semantic versioning:
# Stable release
git tag v1.2.3
git push origin v1.2.3
(or just make tag TAG=v1.2.3)
# Beta release
git tag v1.3.0-beta
git push origin v1.3.0-beta
(or just make tag TAG=v1.3.0-beta)
# Alpha release
git tag v1.4.0-alpha
git push origin v1.4.0-alpha
(or just make tag TAG=v1.4.0-alpha)
The CI pipeline automatically:
- Builds binaries for all platforms
- Creates GitHub release with assets
- Updates the global version manifest
- Handles concurrent release conflicts with exponential backoff
Version Manifest Management
The version.json file serves as the central registry for all releases:
{
"releases": [
{
"version": "v1.0.1",
"urls": {
"linux": "https://github.com/.../calc-linux-amd64",
"windows": "https://github.com/.../calc-windows-amd64.exe",
"darwin": "https://github.com/.../calc-darwin-amd64",
"darwin-arm64": "https://github.com/.../calc-darwin-arm64"
},
"isAlpha": false,
"isBeta": false,
"releaseDate": "2025-06-01T03:53:44Z"
}
]
}
Conflict Resolution: The system rebuilds the entire manifest from GitHub API data using Python scripts with retry logic. This prevents race conditions during concurrent releases.
Code Architecture
Directory Structure
βββ cmd/calculator/ # Main application entry point
β βββ main.go # CLI interface and expression parsing
β βββ main_test.go # Integration tests
βββ internal/calculator/ # Core calculation engine
β βββ calculator.go # Mathematical operations
β βββ calculator_test.go # Comprehensive unit tests
βββ internal/updater/ # Auto-update system
β βββ types.go # Data structures
β βββ manifest.go # Version manifest handling
β βββ download.go # Binary download logic
β βββ validation.go # Security validation
β βββ utils.go # File operations
β βββ updater.go # Update orchestration
βββ .github/workflows/ # CI/CD automation
βββ Makefile # Build automation
Package Dependencies
The project maintains minimal external dependencies:
- Standard Library Only: Core functionality uses only Go standard library
- GitHub API: Release manifest fetching via HTTP client
- Cross-Platform Support: Runtime detection for platform-specific downloads
Core Components
Calculator Engine (internal/calculator):
- Comprehensive error handling for edge cases
- Floating-point precision management
- Support for basic and advanced operations
Update System (internal/updater):
- Semantic version parsing and comparison
- Channel-based release filtering
- Binary download and validation
CLI Interface (cmd/calculator):
- Interactive REPL with signal handling
- Expression parsing with regex validation
- Version information display
- Update check integration
Testing Strategy
The project implements comprehensive testing across multiple layers:
Unit Tests
- Mathematical operation validation
- Edge case handling (division by zero, negative square roots)
- Floating-point precision verification
- Channel isolation verification
- Version comparison logic
Integration Tests:
- Expression parsing with real calculator instances
- Update flow simulation with mock HTTP responses
- Cross-platform binary validation
Run the complete test suite:
# Run all tests with verbose output
make test
# Run tests with coverage analysis
make test-coverage
# Run benchmarks
make bench
Troubleshooting
Common Issues
Update Check Failures:
# Check network connectivity
curl -I https://raw.githubusercontent.com/jondkelley/cicd_golang_calculator/main/version.json
# Verify environment variables
echo $CALC_ALLOW_ALPHA
echo $CALC_ALLOW_BETA
macOS Security Warnings:
# Remove quarantine after download
xattr -d com.apple.quarantine calc-darwin-amd64
# Verify removal
xattr -l calc-darwin-amd64
Build Issues:
# Clean and rebuild
make clean
make deps
make build
# Check Go version
go version # Requires Go 1.21+
Contributing
Follow these guidelines:
- Fork and Clone: Create your own fork of the repository
- Branch Strategy: Create feature branches from
main - Code Quality: Ensure all pre-commit checks pass
- Testing: Add tests for new functionality
- Documentation: Update relevant documentation
Development Setup:
git clone https://github.com/your-fork/cicd_golang_calculator.git
cd cicd_golang_calculator
make deps
make ci # Verify everything works
Directories
ΒΆ
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
calculator
command
main.go
|
main.go |
|
internal
|
|
|
calculator
Package calculator provides basic arithmetic operations with proper error handling.
|
Package calculator provides basic arithmetic operations with proper error handling. |
|
updater
Package updater handles automatic updates for the calculator application.
|
Package updater handles automatic updates for the calculator application. |