gofmt

コード品質フォーマッタGoDevOps静的解析Go標準ツールコードスタイル

DevOpsツール

gofmt

概要

gofmtは、Goプログラミング言語の公式コードフォーマッタで、Goコードを標準的なスタイルに自動整形します。Go言語の標準ツールチェーンに含まれており、一貫したコードスタイルの強制により、Go開発コミュニティ全体でのコード可読性と保守性を大幅に向上させています。シンプルなコマンドライン操作、高速な処理、IDE統合、CI/CD対応により、「gofmtされていないコードは受け入れられない」というGo文化の中核を担う必須ツールです。

詳細

gofmtは、Go言語の初期バージョンから提供されている公式コードフォーマッタで、Robert Griesemer、Rob Pike、Ken Thompsonらによって設計されました。「One True Brace Style」の原則に基づき、論争の余地のない一貫したコードフォーマットを提供することで、Go開発者間のスタイル議論を排除し、コードの品質と可読性に集中できる環境を実現しています。

主要な特徴

  • 標準化されたフォーマット: Goコミュニティ全体で統一されたコードスタイル
  • 高速処理: 大規模なコードベースでも瞬時にフォーマット
  • 非破壊的操作: セマンティクスを変更せずにスタイルのみを修正
  • シンプルなAPI: 直感的なコマンドライン操作
  • IDE統合: 主要なエディタ・IDEでのリアルタイムフォーマット
  • CI/CD対応: 自動化されたコード品質チェック機能
  • 差分表示: フォーマット前後の変更内容確認
  • ディレクトリ再帰処理: プロジェクト全体の一括フォーマット
  • バックアップ機能: 元ファイル保持オプション

2025年版の特徴

  • Go 1.22+対応: 最新Go言語機能との完全互換性
  • パフォーマンス向上: 並列処理による大規模プロジェクトでの高速化
  • enhanced formatting: より洗練されたコード整形アルゴリズム
  • エラーハンドリング強化: 不正なGoコードに対する詳細診断

メリット・デメリット

メリット

  • Go開発における絶対的なコードスタイル統一
  • フォーマット議論の完全排除による開発効率向上
  • 即座にプロフェッショナルな見た目のコードを生成
  • 学習コストゼロのシンプルな操作性
  • ほぼ瞬時の処理速度による開発フロー阻害なし
  • IDE統合によるリアルタイム品質向上
  • CI/CD統合による自動品質ゲート実現
  • Go標準ツールとしての完全な信頼性
  • チーム開発での一貫性保証と可読性向上
  • リファクタリング後のコード整理自動化

デメリット

  • Go言語専用のため他言語プロジェクトでは使用不可
  • 個人の好みに関係なく強制的なスタイル適用
  • カスタマイズ不可能な固定フォーマット設定
  • 一部の複雑なコード構造での非最適な整形
  • 既存のマクロ・テンプレートコードとの干渉可能性
  • 大規模ファイルでの稀な処理遅延
  • フォーマット結果の個人的審美観との不一致可能性
  • 他言語からの移行時のスタイル適応期間
  • 自動フォーマットへの過度な依存リスク
  • 手動でのコード整形スキル低下の懸念

参考ページ

書き方の例

インストールと基本セットアップ

Go標準インストール

# Go言語インストール(gofmtも含む)
# macOS (Homebrew)
brew install go

# Ubuntu/Debian
sudo apt update
sudo apt install golang-go

# Windows (Chocolatey)
choco install golang

# 手動インストール
curl -OL https://golang.org/dl/go1.22.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin

# インストール確認
go version
gofmt -help

Go環境セットアップ

# GOPATH設定
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin:/usr/local/go/bin

# Go modules初期化
mkdir my-go-project && cd my-go-project
go mod init my-go-project

# サンプルファイル作成
cat > main.go << 'EOF'
package main

import "fmt"

func main(){
fmt.Println("Hello, World!")
}
EOF

基本的な実行方法

# 基本フォーマット(標準出力)
gofmt main.go

# ファイルを直接更新
gofmt -w main.go

# ディレクトリ全体の再帰処理
gofmt -w ./...

# 差分表示(どこが変更されるか確認)
gofmt -d main.go

# 詳細差分表示
gofmt -d -s main.go

# 複数ファイル一括処理
gofmt -w *.go

# 特定パッケージのフォーマット
gofmt -w ./cmd/...
gofmt -w ./pkg/...

# バックアップ付きフォーマット
gofmt -w -backup=".orig" main.go

# 簡略化も適用(-sオプション)
gofmt -w -s main.go

高度なオプション使用例

# 簡略化適用例(-sオプション)
# Before simplification
s := make([]int, 0)
for i := 0; i < 10; i++ {
    s = append(s, i)
}

# After gofmt -s
s := make([]int, 0)
for i := range 10 {
    s = append(s, i)
}

# タブ幅設定(デフォルト8)
gofmt -tabwidth=4 main.go

# インデント設定
gofmt -tabindent=false main.go

# エラー出力抑制
gofmt -e main.go 2>/dev/null

プロジェクト全体での使用例

# プロジェクトルートでの全体フォーマット
cd /path/to/project
gofmt -w -s .

# 特定ディレクトリ除外
find . -name "*.go" -not -path "./vendor/*" -not -path "./third_party/*" | xargs gofmt -w -s

# git管理下のGoファイルのみ
git ls-files '*.go' | xargs gofmt -w -s

# 変更されたファイルのみフォーマット
git diff --name-only --diff-filter=ACM HEAD | grep '\.go$' | xargs gofmt -w -s

Makefileとの統合

# Makefile
.PHONY: fmt check-fmt

# フォーマット適用
fmt:
	gofmt -w -s .

# フォーマットチェック(CI用)
check-fmt:
	@if [ -n "$$(gofmt -l .)" ]; then \
		echo "Go code is not formatted:"; \
		gofmt -d .; \
		exit 1; \
	fi

# ビルド前フォーマット
build: fmt
	go build ./...

# テスト前フォーマット
test: fmt
	go test ./...

# 全体品質チェック
quality: check-fmt
	go vet ./...
	go test ./...
	golint ./...

IDE統合設定

VS Code設定

// .vscode/settings.json
{
  "go.formatTool": "gofmt",
  "go.formatFlags": ["-s"],
  "editor.formatOnSave": true,
  "editor.formatOnPaste": true,
  "editor.formatOnType": true,
  "go.useLanguageServer": true,
  "[go]": {
    "editor.defaultFormatter": "golang.go",
    "editor.codeActionsOnSave": {
      "source.organizeImports": true
    },
    "editor.rulers": [100],
    "editor.insertSpaces": false,
    "editor.tabSize": 4
  },
  "go.toolsManagement.autoUpdate": true
}

// tasks.json
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Go Format",
      "type": "shell",
      "command": "gofmt",
      "args": ["-w", "-s", "${workspaceFolder}"],
      "group": "build",
      "presentation": {
        "echo": true,
        "reveal": "silent"
      }
    },
    {
      "label": "Go Format Check",
      "type": "shell",
      "command": "gofmt",
      "args": ["-l", "."],
      "group": "build"
    }
  ]
}

GoLand/IntelliJ設定

# GoLand設定
# File → Settings → Editor → Code Style → Go

# Format settings:
# Use tab character: ✓
# Tab size: 8 (default)
# Indent: 8 (default)

# Automatic formatting:
# File → Settings → Tools → Actions on Save
# Reformat code: ✓
# Optimize imports: ✓

# External Tools設定:
# File → Settings → Tools → External Tools → Add
Name: gofmt
Description: Go Format
Program: gofmt
Arguments: -w -s $FilePath$
Working Directory: $ProjectFileDir$

# File Watchers設定:
# File Type: Go
# Scope: Project Files
# Program: gofmt
# Arguments: -w -s $FilePath$
# Output paths to refresh: $FilePath$

Vim/Neovim設定

" .vimrc / init.vim
" vim-go plugin setup
Plugin 'fatih/vim-go'

" Auto format on save
let g:go_fmt_autosave = 1
let g:go_fmt_command = "gofmt"
let g:go_fmt_options = {
  \ 'gofmt': '-s',
  \ }

" Manual formatting commands
autocmd FileType go nmap <leader>f :GoFmt<CR>
autocmd FileType go nmap <leader>s :GoFmtSimplify<CR>

" Format on buffer write
autocmd BufWritePre *.go :GoFmt

" Additional settings
let g:go_highlight_functions = 1
let g:go_highlight_methods = 1
let g:go_highlight_structs = 1

CI/CD統合例

GitHub Actions

# .github/workflows/gofmt.yml
name: Go Format Check

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  gofmt:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Setup Go
      uses: actions/setup-go@v4
      with:
        go-version: '1.22'
    
    - name: Check gofmt
      run: |
        if [ -n "$(gofmt -l .)" ]; then
          echo "Go code is not formatted:"
          gofmt -d .
          exit 1
        fi
        echo "Go code is properly formatted"
    
    - name: Auto-format and commit (if main branch)
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: |
        gofmt -w -s .
        if [ -n "$(git status --porcelain)" ]; then
          git config --local user.email "[email protected]"
          git config --local user.name "GitHub Action"
          git add -A
          git commit -m "Auto-format Go code with gofmt" -a
          git push
        fi

GitLab CI/CD

# .gitlab-ci.yml
stages:
  - quality

go-format-check:
  stage: quality
  image: golang:1.22-alpine
  script:
    - apk add --no-cache git
    - |
      if [ -n "$(gofmt -l .)" ]; then
        echo "Go code is not formatted:"
        gofmt -d .
        exit 1
      fi
    - echo "Go code is properly formatted"
  only:
    - main
    - develop
    - merge_requests

go-format-fix:
  stage: quality
  image: golang:1.22-alpine
  script:
    - apk add --no-cache git
    - gofmt -w -s .
    - |
      if [ -n "$(git status --porcelain)" ]; then
        git config user.email "[email protected]"
        git config user.name "GitLab CI"
        git add -A
        git commit -m "Auto-format Go code with gofmt"
        git push origin $CI_COMMIT_REF_NAME
      fi
  only:
    - main
  when: manual

Git Hooksとの統合

pre-commit hook

#!/bin/bash
# .git/hooks/pre-commit

# Go files format check
echo "Running gofmt..."

# Get all staged Go files
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$')

if [ -z "$gofiles" ]; then
    echo "No Go files to check"
    exit 0
fi

# Check formatting
unformatted=$(gofmt -l $gofiles)
if [ -n "$unformatted" ]; then
    echo "Go files must be formatted with gofmt. Please run:"
    for file in $unformatted; do
        echo "  gofmt -w $file"
    done
    exit 1
fi

echo "All Go files are properly formatted"
exit 0

pre-push hook

#!/bin/bash
# .git/hooks/pre-push

echo "Running pre-push Go quality checks..."

# Format check
if [ -n "$(gofmt -l .)" ]; then
    echo "Go code is not formatted. Run: gofmt -w -s ."
    exit 1
fi

# Vet check
if ! go vet ./...; then
    echo "go vet failed"
    exit 1
fi

# Test check
if ! go test ./...; then
    echo "Tests failed"
    exit 1
fi

echo "All checks passed"
exit 0

実用的なコード例

フォーマット前後の比較例

// Before gofmt
package main
import"fmt"
func main(){
if true{
fmt.Println("Hello")
}
var x=[]int{1,2,3,}
}

// After gofmt
package main

import "fmt"

func main() {
	if true {
		fmt.Println("Hello")
	}
	var x = []int{1, 2, 3}
}

簡略化オプション(-s)の例

// Before gofmt -s
s := make([]int, 0, 10)
for i := 0; i < len(items); i++ {
    s = append(s, items[i])
}
var _ = fmt.Sprintf("%s", "hello")

// After gofmt -s
s := make([]int, 0, 10)
for i := range items {
    s = append(s, items[i])
}
var _ = "hello"  // fmt.Sprintf removed

トラブルシューティング

よくある問題と解決法

# ファイルが読み取り専用の場合
chmod +w *.go
gofmt -w .

# 大規模プロジェクトでの高速化
find . -name "*.go" -print0 | xargs -0 -P $(nproc) gofmt -w -s

# エラーのあるGoファイルの処理
gofmt -e main.go  # エラーを出力するが継続

# バックアップから復元
gofmt -w -backup=".bak" main.go
cp main.go.bak main.go  # 復元

# CI環境での詳細確認
gofmt -d . > format-diff.txt
cat format-diff.txt

# 空のGoファイルの処理確認
touch empty.go
echo "package main" > empty.go
gofmt -w empty.go

パフォーマンス最適化

# 並列処理で高速化
find . -name "*.go" -print0 | xargs -0 -P $(nproc) gofmt -w -s

# 特定ディレクトリのみ処理
gofmt -w -s ./cmd ./pkg ./internal

# git管理ファイルのみ処理
git ls-files '*.go' | xargs gofmt -w -s

# 変更検出付き処理
if [ -n "$(gofmt -l .)" ]; then
    gofmt -w -s .
    echo "Files formatted"
else
    echo "No formatting needed"
fi