Go Project Structure
This skill provides a battle-tested project structure for Go CLI applications with emphasis on maintainability, CI/CD, and professional documentation.
When to Use
- Starting a new Go CLI project
- Restructuring an existing Go project
- Setting up CI/CD for Go projects
- Creating release workflows
Standard Directory Structure
project/
├── .github/
│ └── workflows/
│ ├── build.yml # Build and test on push/PR
│ └── release.yml # Release workflow for tags
├── .agent/
│ ├── rules/ # AI agent rules
│ │ └── go-style-guide.md
│ └── skills/ # Reusable AI skills
├── cmd/ # CLI commands (Cobra)
│ ├── root.go
│ ├── version.go
│ └── <command>.go
├── internal/ # Private packages (not importable)
│ └── config/
├── pkg/ # Public packages (importable)
│ └── utils/
├── <domain>/ # Domain-specific packages
│ ├── <domain>.go
│ ├── <domain>_test.go
│ └── mock_test.go
├── ui/ # TUI components (if applicable)
│ └── table.go
├── docs/ # Documentation
│ ├── README.md
│ ├── DEVELOPER.md
│ ├── CONTRIBUTING.md
│ └── FEATURE_ROADMAP.md
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── go.mod
├── go.sum
└── main.go
Key Files
1. main.go (Entry Point)
Keep it minimal - just call the command executor:
package main
import "yourproject/cmd"
func main() {
cmd.Execute()
}
2. .gitignore
# Binaries
*.exe
*.dll
*.so
*.dylib
# Build output
/build/
/dist/
# Test outputs
coverage.out
coverage.html
*.coverprofile
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS files
.DS_Store
Thumbs.db
# Go
vendor/
3. Makefile
.DEFAULT_GOAL := help
# Variables
BINARY_NAME=myapp.exe
VERSION?=$(shell git describe --tags --always --dirty)
BUILD_DIR=build
.PHONY: help
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-20s %s\n", $$1, $$2}'
.PHONY: build
build: ## Build the application
go build -o $(BINARY_NAME) .
.PHONY: build-optimized
build-optimized: ## Build with size optimization
go build -ldflags="-s -w" -o $(BINARY_NAME) .
.PHONY: build-all
build-all: ## Build for multiple architectures
@mkdir -p $(BUILD_DIR)
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o $(BUILD_DIR)/$(BINARY_NAME) .
GOOS=windows GOARCH=arm64 go build -ldflags="-s -w" -o $(BUILD_DIR)/myapp-arm64.exe .
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o $(BUILD_DIR)/myapp-linux .
GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o $(BUILD_DIR)/myapp-darwin .
.PHONY: test
test: ## Run tests
go test -v ./...
.PHONY: test-coverage
test-coverage: ## Run tests with coverage
go test -v -cover ./...
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
.PHONY: lint
lint: ## Run linter
golangci-lint run
.PHONY: fmt
fmt: ## Format code
go fmt ./...
.PHONY: clean
clean: ## Clean build artifacts
rm -f $(BINARY_NAME)
rm -rf $(BUILD_DIR)
rm -f coverage.out coverage.html
.PHONY: deps
deps: ## Download dependencies
go mod download
go mod tidy
.PHONY: check
check: fmt lint test ## Run all checks
.PHONY: dev
dev: fmt test build ## Development cycle
.PHONY: release
release: clean test build-all ## Create release
4. GitHub Actions - Build (.github/workflows/build.yml)
name: Build
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Cache Go modules
uses: actions/cache@v4
with:
path: |
~\go\pkg\mod
~\AppData\Local\go-build
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Download dependencies
run: go mod download
- name: Run tests
run: go test -v ./...
- name: Build
run: go build -ldflags="-s -w" -o myapp.exe
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: myapp-windows
path: myapp.exe
retention-days: 30
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: latest
5. GitHub Actions - Release (.github/workflows/release.yml)
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Build AMD64
run: |
$env:GOOS="windows"
$env:GOARCH="amd64"
go build -ldflags="-s -w" -o myapp-windows-amd64.exe
shell: pwsh
- name: Build ARM64
run: |
$env:GOOS="windows"
$env:GOARCH="arm64"
go build -ldflags="-s -w" -o myapp-windows-arm64.exe
shell: pwsh
- name: Create checksums
run: |
Get-FileHash *.exe -Algorithm SHA256 | ForEach-Object {
"$($_.Hash) $(Split-Path $_.Path -Leaf)" | Out-File -Append checksums.txt
}
shell: pwsh
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: |
myapp-windows-amd64.exe
myapp-windows-arm64.exe
checksums.txt
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
6. CHANGELOG.md Template
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/),
and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
### Added
- New feature description
### Changed
- Change description
### Fixed
- Bug fix description
## [1.0.0] - 2024-01-01
### Added
- Initial release
- Feature 1
- Feature 2
[Unreleased]: https://github.com/user/project/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/user/project/releases/tag/v1.0.0
7. README.md Template
# Project Name 🚀
<div align="center">


[](https://github.com/user/project/actions)
**A brief, compelling description of your project**
[Features](#features) • [Installation](#installation) • [Usage](#usage) • [Documentation](#documentation)
</div>
---
## ✨ Features
- 🎯 Feature 1 - Description
- ⚡ Feature 2 - Description
- 🛡️ Feature 3 - Description
## 📋 Prerequisites
- Go 1.21+
- Additional requirements...
## 🚀 Installation
### Quick Install
\`\`\`bash
go install github.com/user/project@latest
\`\`\`
### Build from Source
\`\`\`bash
git clone https://github.com/user/project.git
cd project
go build -o myapp
\`\`\`
## 📖 Usage
\`\`\`bash
# Basic usage
myapp command
# With options
myapp command --flag value
\`\`\`
## 🏗️ Architecture
\`\`\`
project/
├── cmd/ # CLI commands
├── pkg/ # Public packages
└── internal/ # Private packages
\`\`\`
## 📚 Documentation
- [Developer Guide](docs/DEVELOPER.md)
- [Contributing](docs/CONTRIBUTING.md)
## 🤝 Contributing
Contributions welcome! Please read [CONTRIBUTING.md](docs/CONTRIBUTING.md).
## 📝 License
MIT License - see [LICENSE](LICENSE)
Best Practices
-
Keep
main.gominimal - Only callcmd.Execute() -
Use
internal/for truly private code that shouldn't be imported -
Domain packages at root - For domain logic (e.g.,
hyperv/,user/) -
Consistent naming:
- Files:
lowercase_with_underscores.go - Test files:
*_test.go - Mock files:
mock_test.go
- Files:
-
Document exported symbols - Every exported function/type needs a comment
-
Use semantic versioning -
v1.0.0,v1.1.0,v2.0.0 -
Keep CHANGELOG updated - Document all notable changes
