## File: README.md

# :cloud: Air - Live reload for Go apps

[](https://github.com/air-verse/air/releases)
[](https://github.com/air-verse/air/blob/master/go.mod)
[](https://github.com/air-verse/air/releases)
[](https://hub.docker.com/r/cosmtrek/air)
[](https://github.com/air-verse/air/actions?query=workflow%3AGo+branch%3Amaster)
[](https://www.codacy.com/gh/air-verse/air/dashboard?utm_source=github.com&utm_medium=referral&utm_content=air-verse/air&utm_campaign=Badge_Grade)
[](https://codecov.io/gh/air-verse/air)

English | [简体中文](README-zh_cn.md) | [繁體中文](README-zh_tw.md) | [日本語](README-ja.md)

Air is a live-reloading command line utility for developing Go applications. Run `air` in your project root directory, leave it alone, and focus on your code. Note that this tool has nothing to do with hot-deploy for production.



## Contents

- [Air - Live reload for Go apps](#cloud-air---live-reload-for-go-apps)
  - [Contents](#contents)
  - [Installation](#installation)
    - [Via `go install` (recommended)](#via-go-install-recommended)
    - [Via `go get -tool` (project install)](#via-go-get--tool-project-install)
    - [Via install.sh](#via-installsh)
    - [Via goblin.run](#via-goblinrun)
    - [Via Homebrew](#via-homebrew)
    - [Via Scoop](#via-scoop)
    - [Via mise](#via-mise)
    - [Via Docker/Podman](#via-dockerpodman)
  - [Quick start](#quick-start)
  - [Features](#features)
  - [Usage](#usage)
    - [Runtime arguments](#runtime-arguments)
    - [Debug](#debug)
  - [Configuration](#configuration)
    - [Overwrite specific configuration from arguments](#overwrite-specific-configuration-from-arguments)
    - [Startup banner](#startup-banner)
    - [Entrypoint](#entrypoint)
    - [Environment files](#environment-files)
    - [Platform-specific build overrides](#platform-specific-build-overrides)
    - [Watch rules: run a command instead of rebuilding](#watch-rules-run-a-command-instead-of-rebuilding)
    - [Proxy: reload the browser automatically](#proxy-reload-the-browser-automatically)
  - [Docker](#docker)
    - [Using the official image](#using-the-official-image)
    - [Shell function](#shell-function)
    - [Docker Compose](#docker-compose)
    - [Without the air image](#without-the-air-image)
  - [Q&A](#qa)
    - [command not found: air](#command-not-found-air)
    - [Error under WSL when the bin path contains a single quote](#error-under-wsl-when-the-bin-path-contains-a-single-quote)
    - [How to do hot compile only and not run anything?](#how-to-do-hot-compile-only-and-not-run-anything)
    - [How to reload the browser automatically on static file changes?](#how-to-reload-the-browser-automatically-on-static-file-changes)
  - [Development](#development)
    - [Release](#release)
  - [Motivation](#motivation)
  - [Star History](#star-history)
  - [Sponsor](#sponsor)
  - [License](#license)

## Installation

### Via `go install` (recommended)

With go 1.25 or higher:

```shell
go install github.com/air-verse/air@latest
```

Make sure your Go bin directory is in your `PATH`:

```shell
export PATH="$PATH:$(go env GOPATH)/bin"
```

### Via `go get -tool` (project install)

With go 1.25 or higher:

```shell
go get -tool github.com/air-verse/air@latest

# then use it like so:
go tool air -v
```

### Via install.sh

```shell
# binary will be $(go env GOPATH)/bin/air
curl -sSfL https://raw.githubusercontent.com/air-verse/air/master/install.sh | sh -s -- -b $(go env GOPATH)/bin

# or install it into ./bin/
curl -sSfL https://raw.githubusercontent.com/air-verse/air/master/install.sh | sh -s

air -v
```

### Via goblin.run

See [goblin.run](https://goblin.run).

```shell
# binary will be /usr/local/bin/air
curl -sSfL https://goblin.run/github.com/air-verse/air | sh

# to put to a custom path
curl -sSfL https://goblin.run/github.com/air-verse/air | PREFIX=/tmp sh
```

### Via Homebrew

```shell
brew install go-air
```

### Via Scoop

```shell
scoop install air
```

### Via mise

```shell
mise use -g air
```

### Via Docker/Podman

Pull the [cosmtrek/air](https://hub.docker.com/r/cosmtrek/air) image and see [Docker](#docker) for usage.

## Quick start

```shell
# enter your project
cd /path/to/your_project

# first tries `.air.toml` in the current directory; if not found, uses defaults
air
```

To generate a config file you can edit, run `air init` once and then `air` from then on:

```shell
# writes .air.toml with the default settings
air init

# picks up .air.toml automatically
air
```

To use a specific config file explicitly, pass `-c`:

```shell
air -c .air.toml
```

For every available option refer to the [air_example.toml](air_example.toml) file.

## Features

- [x] Colorful log output
- [x] Customize build or any command
- [x] Support excluding subdirectories
- [x] Allow watching new directories after Air started
- [x] Better building process
- [x] Configurable `.env` file loading

## Usage

### Runtime arguments

You can pass arguments for running the built binary by adding them after the air command.

```shell
# Will run ./tmp/main bench
air bench

# Will run ./tmp/main server --port 8080
air server --port 8080
```

You can separate the arguments passed for the air command and the built binary with the `--` argument.

```shell
# Will run ./tmp/main -h
air -- -h

# Will run air with custom config and pass -h argument to the built binary
air -c .air.toml -- -h
```

### Debug

`air -d` prints all logs.

## Configuration

### Overwrite specific configuration from arguments

Air config fields are supported as command-line arguments. You can view the available arguments by running:

```shell
air -h
# or
air --help
```

If you want to configure the build command and run command, you can use the following command without a config file:

```shell
air --build.cmd "go build -o bin/api cmd/run.go" --build.entrypoint "./bin/api"
```

Use a comma to separate items for arguments that take a list as input:

```shell
air --build.cmd "go build -o bin/api cmd/run.go" --build.entrypoint "./bin/api" --build.exclude_dir "templates,build"
```

List arguments can also be repeated, and the values are appended in the order they appear. This is handy when the command line is generated by a script or Makefile:

```shell
# equivalent to --env_files ".env,.env.local,.env.secret"
air --env_files ".env,.env.local" --env_files ".env.secret"
```

### Startup banner

Use `misc.startup_banner` to control what Air prints at startup.

```toml
[misc]
# Not set (default): show built-in ASCII banner with version.

# Set to empty string: print nothing.
startup_banner = ""

# Set to custom text: print this text instead of the built-in banner.
# startup_banner = "API watcher"
```

### Entrypoint

Use `build.entrypoint` to point at the binary generated by `build.cmd` and describe how it should be executed. The value can be either a string (just the executable) or an array of strings. When using an array, the first element is the executable (resolved relative to `root` unless it lacks a path separator, in which case `$PATH` is consulted) and every subsequent element is treated as a default argument. Values from `build.args_bin` and the command line are appended after the inline arguments. The legacy `build.bin` field is deprecated and will be removed in a future release, so prefer the entrypoint form going forward.

```toml
[build]
entrypoint = ["./tmp/main"]
args_bin = ["server", ":8080"]

# Inline the default arguments directly after the binary.
entrypoint = ["./tmp/main", "server", ":8080"]

# Use PATH-resolved tools like dlv by omitting path separators.
entrypoint = [
  "dlv", "exec", "--accept-multiclient", "--log", "--headless", "--continue",
  "--listen=:8999", "--api-version", "2", "./tmp/main",
]
```

### Environment files

Air can automatically load environment variables from `.env` files before both building and running when `env_files` is configured.

```toml
# Loads .env.development and then .env files.
# Values in the lattermost file overwrite any preceding ones.
# Does not overwrite variables that were present before running air.
env_files = [".env.development", ".env"]
```

### Platform-specific build overrides

You can override build settings per OS with `[build.windows]`, `[build.darwin]`, and `[build.linux]`. These blocks override the base `[build]` values when running on the matching platform. Only the following fields are supported in platform blocks: `pre_cmd`, `cmd`, `post_cmd`, `bin`, `entrypoint`, `full_bin`, `args_bin`.

```toml
[build]
cmd = "go build -o ./tmp/main ."
bin = "./tmp/main"

[build.windows]
cmd = "go build -o ./tmp/main.exe ."
bin = "tmp\\main.exe"
entrypoint = ["tmp\\main.exe"]
```

Running `air init` adds a platform block for the current OS when its defaults differ from the base configuration.

### Watch rules: run a command instead of rebuilding

Sometimes a change should run a command rather than rebuild your app — frontend assets served from disk, `templ`/`sqlc`/`go generate` pipelines, and so on. Declare a `[[build.rules]]` block for each of them:

```toml
[build]
cmd = "go build -o ./tmp/main ."
# the main build ignores the frontend...
exclude_dir = ["web"]

# ...but this rule watches it and rebuilds the assets on change
[[build.rules]]
name = "assets"
include_dir = ["web"]
include_ext = ["js", "ts", "css"]
cmd = "npm run build"

[[build.rules]]
name = "templ"
include_ext = ["templ"]
cmd = "templ generate"
```

A file matched by a rule runs the rule's `cmd` and never triggers a rebuild, even if it would also match the main build's watch settings. Rule directories are watched even when listed in `exclude_dir`. If a rule's command generates files the main build watches (for example, `templ generate` writing `.go` files), the rebuild follows naturally.

Each rule supports `include_dir`, `include_ext`, `include_file`, `exclude_regex`, and a `delay` (debounce in milliseconds, default 1000). At least one of the `include_*` matchers is required. Rules run their commands to completion; changes arriving meanwhile queue a follow-up run.

### Proxy: reload the browser automatically

Air can put a small proxy in front of your web app that refreshes the browser after every successful rebuild, so you no longer have to hit reload yourself.

```toml
[proxy]
enabled = true
# the port you open in the browser
proxy_port = 8090
# the port your app listens on
app_port = 8080
```

Start `air` as usual and open `http://localhost:8090` instead of your app's own port. Requests are forwarded to `app_port`, and Air injects a tiny script before the `` tag of every HTML response; when a rebuild finishes, the script reloads the page.

Two things are required for this to work:

- your HTML must contain a `` tag — there is nowhere to inject the script otherwise, and the page is served unchanged;
- the files you edit must be watched, so static assets need to be covered by `include_dir`, `include_ext`, or `include_file`.

If your app is slow to boot (database connections, config loading) and you see "unable to reach app" errors, give it more time:

```toml
[proxy]
# how long to keep retrying the app after a rebuild, in milliseconds (default 5000)
app_start_timeout = 10000
```

## Docker

### Using the official image

Pull this Docker image: [cosmtrek/air](https://hub.docker.com/r/cosmtrek/air).

```shell
docker/podman run -it --rm \
    -w "" \
    -e "air_wd=" \
    -v $(pwd): \
    -p :<APP SERVER PORT> \
    cosmtrek/air \
    -c <CONF>
```

`` is your project path in the container, e.g. `/go/example`. If you want to enter the container, add `--entrypoint=bash`.

One of my projects runs in Docker:

```shell
docker run -it --rm \
  -w "/go/src/github.com/cosmtrek/hub" \
  -v $(pwd):/go/src/github.com/cosmtrek/hub \
  -p 9090:9090 \
  cosmtrek/air
```

### Shell function

If you want to use air continuously like a normal app, create a function in your `${SHELL}rc` (Bash, Zsh, etc…):

```shell
air() {
  podman/docker run -it --rm \
    -w "$PWD" -v "$PWD":"$PWD" \
    -p "$AIR_PORT":"$AIR_PORT" \
    docker.io/cosmtrek/air "$@"
}
```

`$PWD` is replaced with the current directory, `$AIR_PORT` is the port to publish, and `$@` accepts arguments of the application itself, for example `-c`:

```shell
cd /go/src/github.com/cosmtrek/hub
AIR_PORT=8080 air -c "config.toml"
```

### Docker Compose

```yaml
services:
  my-project-with-air:
    image: cosmtrek/air
    # working_dir value has to be the same of mapped volume
    working_dir: /project-package
    ports:
      - <any>:<any>
    environment:
      - ENV_A=${ENV_A}
      - ENV_B=${ENV_B}
      - ENV_C=${ENV_C}
    volumes:
      - ./project-relative-path/:/project-package/
```

### Without the air image

`Dockerfile`

```Dockerfile
# Choose whatever you want, version >= 1.25
FROM golang:1.25-alpine

WORKDIR /app

RUN go install github.com/air-verse/air@latest

COPY go.mod go.sum ./
RUN go mod download

CMD ["air", "-c", ".air.toml"]
```

`docker-compose.yaml`

```yaml
version: "3.8"
services:
  web:
    build:
      context: .
      # Correct the path to your Dockerfile
      dockerfile: Dockerfile
    ports:
      - 8080:3000
    # Important to bind/mount your codebase dir to /app dir for live reload
    volumes:
      - ./:/app
```

## Q&A

### command not found: air

Also reported as "No such file or directory". Make sure the Go bin directory is on your `PATH`:

```shell
export GOPATH=$HOME/xxxxx
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
export PATH=$PATH:$(go env GOPATH)/bin #Confirm this line in your .profile and make sure to source the .profile if you add it!!!
```

### Error under WSL when the bin path contains a single quote

Use `\` to escape the `'` in the bin. Related issue: [#305](https://github.com/air-verse/air/issues/305)

### How to do hot compile only and not run anything?

See [#365](https://github.com/air-verse/air/issues/365).

```toml
[build]
  cmd = "/usr/bin/true"
```

### How to reload the browser automatically on static file changes?

Enable the proxy — see [Proxy: reload the browser automatically](#proxy-reload-the-browser-automatically). Make sure your static files are covered by `include_dir`, `include_ext`, or `include_file`, otherwise editing them never triggers a reload. Refer to issue [#512](https://github.com/air-verse/air/issues/512) for additional details.

## Development

Please note that it requires Go 1.25+ (see `go.mod`).

```shell
# Fork this project

# Clone it
mkdir -p $GOPATH/src/github.com/cosmtrek
cd $GOPATH/src/github.com/cosmtrek
git clone git@github.com:<YOUR USERNAME>/air.git

# Install dependencies
cd air
make ci

# Explore it and happy hacking!
make install
```

Pull requests are welcome.

### Release

```shell
# Checkout to master
git checkout master

# Add the version that needs to be released
git tag v1.xx.x

# Push to remote
git push origin v1.xx.x

# The CI will process and release a new version. Wait about 5 min, and you can fetch the latest version
```

## Motivation

When I started developing websites in Go and using the [gin](https://github.com/gin-gonic/gin) framework, it was a pity that gin lacked a live-reloading function. So I searched around and tried [fresh](https://github.com/pilu/fresh); it seemed not much flexible, so I intended to rewrite it better. Finally, Air's born.

In addition, great thanks to [pilu](https://github.com/pilu), no fresh, no air :)

## Star History

<a href="https://www.star-history.com/?type=date&repos=air-verse%2Fair">
 
   
   
   
 
</a>

## Sponsor

[](https://www.buymeacoffee.com/cosmtrek)

Give huge thanks to lots of supporters. I've always been remembering your kindness.

## License

[GNU General Public License v3.0](LICENSE)

---

## File: README-ja.md

# :cloud: Air - Go アプリケーションのためのライブリロード

[](https://github.com/air-verse/air/releases)
[](https://github.com/air-verse/air/blob/master/go.mod)
[](https://github.com/air-verse/air/releases)
[](https://hub.docker.com/r/cosmtrek/air)
[](https://github.com/air-verse/air/actions?query=workflow%3AGo+branch%3Amaster)
[](https://www.codacy.com/gh/air-verse/air/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=air-verse/air&amp;utm_campaign=Badge_Grade)
[](https://codecov.io/gh/air-verse/air)

[English](README.md) | [简体中文](README-zh_cn.md) | [繁體中文](README-zh_tw.md) | 日本語

Air は Go アプリケーション開発用のライブリロードコマンドラインユーティリティです。プロジェクトのルートディレクトリで `air` を実行し、放置し、コードに集中してください。なお、このツールは本番環境へのホットデプロイとは無関係です。



## 目次

- [Air - Go アプリケーションのためのライブリロード](#cloud-air---go-アプリケーションのためのライブリロード)
  - [目次](#目次)
  - [インストール](#インストール)
    - [`go install` を使う場合（推奨）](#go-install-を使う場合推奨)
    - [`go get -tool` を使う場合（プロジェクトへのインストール）](#go-get--tool-を使う場合プロジェクトへのインストール)
    - [`install.sh` を使う場合](#installsh-を使う場合)
    - [goblin.run を使う場合](#goblinrun-を使う場合)
    - [Homebrew を使う場合](#homebrew-を使う場合)
    - [Scoop を使う場合](#scoop-を使う場合)
    - [mise を使う場合](#mise-を使う場合)
    - [Docker/Podman を使う場合](#dockerpodman-を使う場合)
  - [クイックスタート](#クイックスタート)
  - [特徴](#特徴)
  - [使い方](#使い方)
    - [実行時引数](#実行時引数)
    - [デバッグ](#デバッグ)
  - [設定](#設定)
    - [引数から指定された設定を上書き](#引数から指定された設定を上書き)
    - [起動バナー](#起動バナー)
    - [Entrypoint](#entrypoint)
    - [環境変数ファイル](#環境変数ファイル)
    - [プラットフォームごとのビルド設定の上書き](#プラットフォームごとのビルド設定の上書き)
    - [監視ルール: リビルドの代わりにコマンドを実行する](#監視ルール-リビルドの代わりにコマンドを実行する)
    - [プロキシ: ブラウザを自動的にリロードする](#プロキシ-ブラウザを自動的にリロードする)
  - [Docker](#docker)
    - [公式イメージを使う](#公式イメージを使う)
    - [シェル関数](#シェル関数)
    - [Docker Compose](#docker-compose)
    - [air イメージを使わない場合](#air-イメージを使わない場合)
  - [Q&A](#qa)
    - ["command not found: air" または "No such file or directory"](#command-not-found-air-または-no-such-file-or-directory)
    - [bin のパスに ' が含まれる場合の WSL でのエラー](#bin-のパスに--が含まれる場合の-wsl-でのエラー)
    - [ホットコンパイルのみを行い、何も実行しない方法は？](#ホットコンパイルのみを行い何も実行しない方法は)
    - [静的ファイルの変更時にブラウザを自動的にリロードする方法](#静的ファイルの変更時にブラウザを自動的にリロードする方法)
  - [開発](#開発)
    - [リリース](#リリース)
  - [動機](#動機)
  - [スターヒストリー](#スターヒストリー)
  - [スポンサー](#スポンサー)
  - [ライセンス](#ライセンス)

## インストール

### `go install` を使う場合（推奨）

go 1.25 以上が必要です。

```shell
go install github.com/air-verse/air@latest
```

Go の bin ディレクトリが `PATH` に含まれていることを確認してください。

```shell
export PATH="$PATH:$(go env GOPATH)/bin"
```

### `go get -tool` を使う場合（プロジェクトへのインストール）

go 1.25 以上が必要です。

```shell
go get -tool github.com/air-verse/air@latest

# 使い方は以下の通りです:
go tool air -v
```

### `install.sh` を使う場合

```shell
# バイナリは $(go env GOPATH)/bin/air にインストールされます
curl -sSfL https://raw.githubusercontent.com/air-verse/air/master/install.sh | sh -s -- -b $(go env GOPATH)/bin

# または./bin/にインストールすることもできます
curl -sSfL https://raw.githubusercontent.com/air-verse/air/master/install.sh | sh -s

air -v
```

### goblin.run を使う場合

[goblin.run](https://goblin.run) を参照してください。

```shell
# バイナリは /usr/local/bin/air にインストールされます
curl -sSfL https://goblin.run/github.com/air-verse/air | sh

# 任意のパスに配置することもできます
curl -sSfL https://goblin.run/github.com/air-verse/air | PREFIX=/tmp sh
```

### Homebrew を使う場合

```shell
brew install go-air
```

### Scoop を使う場合

```shell
scoop install air
```

### mise を使う場合

```shell
mise use -g air
```

### Docker/Podman を使う場合

[cosmtrek/air](https://hub.docker.com/r/cosmtrek/air) イメージを pull してください。使い方は [Docker](#docker) を参照してください。

## クイックスタート

```shell
# プロジェクトに移動します
cd /path/to/your_project

# カレントディレクトリの `.air.toml` を優先し、見つからない場合はデフォルト値を使います
air
```

編集可能な設定ファイルが必要な場合は、最初に一度 `air init` を実行し、それ以降は `air` を実行するだけです。

```shell
# デフォルト設定で .air.toml を生成します
air init

# .air.toml が自動的に読み込まれます
air
```

特定の設定ファイルを明示的に使う場合は `-c` を指定します。

```shell
air -c .air.toml
```

利用可能な設定項目については [air_example.toml](air_example.toml) を参照してください。

## 特徴

- [x] カラフルなログ出力
- [x] ビルドやその他のコマンドをカスタマイズ
- [x] サブディレクトリを除外することをサポート
- [x] Air 起動後は新しいディレクトリを監視します
- [x] より良いビルドプロセス
- [x] 設定可能な `.env` ファイルの読み込み

## 使い方

### 実行時引数

air コマンドの後に引数を追加することで、ビルドしたバイナリを実行するための引数を渡すことができます。

```shell
# ./tmp/main benchを実行します
air bench

# ./tmp/main server --port 8080を実行します
air server --port 8080
```

air コマンドに渡す引数とビルドしたバイナリに渡す引数は `--` で区切ることができます。

```shell
# ./tmp/main -hを実行します
air -- -h

# カスタム設定で air を実行し、ビルドされたバイナリに -h 引数を渡す
air -c .air.toml -- -h
```

### デバッグ

`air -d`は全てのログを出力します。

## 設定

### 引数から指定された設定を上書き

air の設定フィールドはコマンドライン引数としてサポートされています。利用可能な引数は以下のコマンドで確認できます。

```shell
air -h
# または
air --help
```

もしビルドコマンドと起動コマンドを設定したい場合は、設定ファイルを使わずに以下のようにコマンドを使うことができます。

```shell
air --build.cmd "go build -o bin/api cmd/run.go" --build.entrypoint "./bin/api"
```

入力値としてリストを取る引数には、アイテムを区切るためにコンマを使用します。

```shell
air --build.cmd "go build -o bin/api cmd/run.go" --build.entrypoint "./bin/api" --build.exclude_dir "templates,build"
```

リストを取る引数は繰り返し指定することもでき、値は現れた順に追加されます。コマンドラインをスクリプトや Makefile で生成する場合に便利です。

```shell
# --env_files ".env,.env.local,.env.secret" と同じです
air --env_files ".env,.env.local" --env_files ".env.secret"
```

### 起動バナー

Air が起動時に出力する内容は `misc.startup_banner` で制御できます。

```toml
[misc]
# 未設定（デフォルト）: 組み込みの ASCII バナーとバージョンを表示します。

# 空文字列を設定: 何も出力しません。
startup_banner = ""

# 任意のテキストを設定: 組み込みバナーの代わりにこのテキストを出力します。
# startup_banner = "API watcher"
```

### Entrypoint

`build.entrypoint` では、`build.cmd` が生成したバイナリと、その実行方法を指定します。値は文字列（実行ファイルのみ）でも文字列の配列でも構いません。配列の場合、最初の要素が実行ファイルで（`root` からの相対パスとして解決されます。パス区切り文字を含まない場合は `$PATH` から探されます）、それ以降の要素はすべてデフォルト引数として扱われます。`build.args_bin` とコマンドラインで渡された引数は、これらの引数の後に追加されます。従来の `build.bin` フィールドは非推奨で、今後のリリースで削除される予定です。entrypoint の書き方を使ってください。

```toml
[build]
entrypoint = ["./tmp/main"]
args_bin = ["server", ":8080"]

# デフォルト引数をバイナリの直後にインラインで書くこともできます。
entrypoint = ["./tmp/main", "server", ":8080"]

# パス区切り文字を省くと、dlv のように PATH から解決されます。
entrypoint = [
  "dlv", "exec", "--accept-multiclient", "--log", "--headless", "--continue",
  "--listen=:8999", "--api-version", "2", "./tmp/main",
]
```

### 環境変数ファイル

`env_files` を設定すると、Air はビルド前と実行前に `.env` ファイルから環境変数を自動的に読み込みます。

```toml
# .env.development、続いて .env を読み込みます。
# 後ろのファイルの値が前の値を上書きします。
# air の実行前から存在していた変数は上書きしません。
env_files = [".env.development", ".env"]
```

### プラットフォームごとのビルド設定の上書き

`[build.windows]`、`[build.darwin]`、`[build.linux]` を使うと、OS ごとにビルド設定を上書きできます。これらのブロックは、対応するプラットフォームで実行したときに `[build]` の値を上書きします。プラットフォームブロックでサポートされるのは次のフィールドのみです: `pre_cmd`、`cmd`、`post_cmd`、`bin`、`entrypoint`、`full_bin`、`args_bin`。

```toml
[build]
cmd = "go build -o ./tmp/main ."
bin = "./tmp/main"

[build.windows]
cmd = "go build -o ./tmp/main.exe ."
bin = "tmp\\main.exe"
entrypoint = ["tmp\\main.exe"]
```

現在の OS のデフォルト値が基本設定と異なる場合、`air init` はその OS 向けのプラットフォームブロックを追加します。

### 監視ルール: リビルドの代わりにコマンドを実行する

ファイルの変更に対して、アプリのリビルドではなくコマンドを実行したい場合があります。ディスクから配信するフロントエンドのアセットや、`templ`/`sqlc`/`go generate` のようなパイプラインなどです。そうしたものにはそれぞれ `[[build.rules]]` ブロックを定義します。

```toml
[build]
cmd = "go build -o ./tmp/main ."
# メインのビルドはフロントエンドを無視しますが……
exclude_dir = ["web"]

# ……このルールがそれを監視し、変更時にアセットをリビルドします
[[build.rules]]
name = "assets"
include_dir = ["web"]
include_ext = ["js", "ts", "css"]
cmd = "npm run build"

[[build.rules]]
name = "templ"
include_ext = ["templ"]
cmd = "templ generate"
```

ルールに一致したファイルはそのルールの `cmd` を実行するだけで、メインのビルドの監視条件にも一致する場合であってもリビルドは発生しません。ルールのディレクトリは `exclude_dir` に含まれていても監視されます。ルールのコマンドがメインのビルドの監視対象となるファイルを生成した場合（たとえば `templ generate` が `.go` ファイルを書き出す場合）、リビルドは自然に続いて実行されます。

各ルールは `include_dir`、`include_ext`、`include_file`、`exclude_regex`、そして `delay`（デバウンス、ミリ秒単位、デフォルト 1000）をサポートします。`include_*` のいずれか 1 つは必須です。ルールはコマンドの完了まで待ちます。実行中に届いた変更はキューに入り、完了後にもう一度実行されます。

### プロキシ: ブラウザを自動的にリロードする

Air は Web アプリの前段に小さなプロキシを置くことができます。ビルドが成功するたびにブラウザを更新するので、自分でリロードする必要がなくなります。

```toml
[proxy]
enabled = true
# ブラウザで開くポート
proxy_port = 8090
# アプリが listen しているポート
app_port = 8080
```

いつも通り `air` を起動し、アプリ自身のポートではなく `http://localhost:8090` をブラウザで開きます。リクエストは `app_port` に転送され、Air はすべての HTML レスポンスの `` タグの前に小さなスクリプトを注入します。ビルドが完了すると、そのスクリプトがページをリロードします。

これが動作するには 2 つの条件があります。

- HTML に `` タグが含まれていること。含まれていないとスクリプトを注入する場所がなく、ページはそのまま配信されます。
- 編集するファイルが監視対象であること。静的ファイルは `include_dir`、`include_ext`、`include_file` のいずれかでカバーする必要があります。

アプリの起動が遅く（データベース接続や設定の読み込みなど）、"unable to reach app" というエラーが出る場合は、待ち時間を延ばしてください。

```toml
[proxy]
# ビルド後にアプリへの接続をリトライする時間（ミリ秒、デフォルト 5000）
app_start_timeout = 10000
```

## Docker

### 公式イメージを使う

この Docker イメージを pull してください: [cosmtrek/air](https://hub.docker.com/r/cosmtrek/air)。

```shell
docker/podman run -it --rm \
    -w "" \
    -e "air_wd=" \
    -v $(pwd): \
    -p :<APP SERVER PORT> \
    cosmtrek/air \
    -c <CONF>
```

`` はコンテナ内のプロジェクトのパスです（例: `/go/example`）。コンテナに入りたい場合は `--entrypoint=bash` を追加してください。

私のプロジェクトのひとつは Docker で動作しています。

```shell
docker run -it --rm \
  -w "/go/src/github.com/cosmtrek/hub" \
  -v $(pwd):/go/src/github.com/cosmtrek/hub \
  -p 9090:9090 \
  cosmtrek/air
```

### シェル関数

通常のアプリケーションのように air を継続的に使いたい場合は、`${SHELL}rc`（Bash、Zsh など）に関数を作成できます。

```shell
air() {
  podman/docker run -it --rm \
    -w "$PWD" -v "$PWD":"$PWD" \
    -p "$AIR_PORT":"$AIR_PORT" \
    docker.io/cosmtrek/air "$@"
}
```

`$PWD` は現在のディレクトリに置き換えられ、`$AIR_PORT` は公開するポートを指定し、`$@` は `-c` のようなアプリケーション自体の引数を受け取るためのものです。

```shell
cd /go/src/github.com/cosmtrek/hub
AIR_PORT=8080 air -c "config.toml"
```

### Docker Compose

```yaml
services:
  my-project-with-air:
    image: cosmtrek/air
    # working_dir の値はマップされたボリュームの値と同じでなければなりません
    working_dir: /project-package
    ports:
      - <any>:<any>
    environment:
      - ENV_A=${ENV_A}
      - ENV_B=${ENV_B}
      - ENV_C=${ENV_C}
    volumes:
      - ./project-relative-path/:/project-package/
```

### air イメージを使わない場合

`Dockerfile`

```Dockerfile
# 1.25以上の任意のバージョンを選択してください
FROM golang:1.25-alpine

WORKDIR /app

RUN go install github.com/air-verse/air@latest

COPY go.mod go.sum ./
RUN go mod download

CMD ["air", "-c", ".air.toml"]
```

`docker-compose.yaml`

```yaml
version: "3.8"
services:
  web:
    build:
      context: .
      # Dockerfile へのパスを正してください
      dockerfile: Dockerfile
    ports:
      - 8080:3000
    # ライブリロードのために、コードベースディレクトリを /app ディレクトリにバインド/マウントすることが重要です
    volumes:
      - ./:/app
```

## Q&A

### "command not found: air" または "No such file or directory"

Go の bin ディレクトリが `PATH` に含まれていることを確認してください。

```shell
export GOPATH=$HOME/xxxxx
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
export PATH=$PATH:$(go env GOPATH)/bin #この設定を .profile で確認し、追加した場合は .profile を source するのを忘れないでください!!!
```

### bin のパスに ' が含まれる場合の WSL でのエラー

bin の `'` をエスケープするには `\` を使用したほうが良いです。関連する issue: [#305](https://github.com/air-verse/air/issues/305)

### ホットコンパイルのみを行い、何も実行しない方法は？

[#365](https://github.com/air-verse/air/issues/365) を参照してください。

```toml
[build]
  cmd = "/usr/bin/true"
```

### 静的ファイルの変更時にブラウザを自動的にリロードする方法

プロキシを有効にしてください。詳しくは[プロキシ: ブラウザを自動的にリロードする](#プロキシ-ブラウザを自動的にリロードする)を参照してください。静的ファイルが `include_dir`、`include_ext`、`include_file` のいずれかでカバーされていることを確認してください。カバーされていないと、変更してもリロードされません。詳細は issue [#512](https://github.com/air-verse/air/issues/512) を参照してください。

## 開発

必要な Go のバージョンは 1.25+ です（`go.mod` を参照）。

```shell
# プロジェクトをフォークしてください

# クローンしてください
mkdir -p $GOPATH/src/github.com/cosmtrek
cd $GOPATH/src/github.com/cosmtrek
git clone git@github.com:<YOUR USERNAME>/air.git

# 依存関係をインストールしてください
cd air
make ci

# コードを探検してコーディングを楽しんでください！
make install
```

プルリクエストを歓迎します。

### リリース

```shell
# master にチェックアウトします
git checkout master

# リリースに必要なバージョンタグを付与します
git tag v1.xx.x

# リモートにプッシュします
git push origin v1.xx.x

# CI が実行され、新しいバージョンがリリースされます。約5分待つと最新バージョンを取得できます
```

## 動機

Go でウェブサイトを開発し始め、[gin](https://github.com/gin-gonic/gin) を使っていた時、gin にはライブリロード機能がないのが残念でした。そこで探し回って [fresh](https://github.com/pilu/fresh) を試してみましたが、あまり柔軟ではないようでした。なので、もっと良いものを書くことにしました。そうして、Air が誕生しました。

加えて、[pilu](https://github.com/pilu) に感謝します。fresh がなければ、Air もありませんでした。:)

## スターヒストリー

<a href="https://www.star-history.com/?type=date&repos=air-verse%2Fair">
 
   
   
   
 
</a>

## スポンサー

[](https://www.buymeacoffee.com/cosmtrek)

多くの支援者に心から感謝します。皆さんの親切をいつも覚えています。

## ライセンス

[GNU General Public License v3.0](LICENSE)

---

## File: README-zh_cn.md

# :cloud: Air - Go 应用的实时重载工具

[](https://github.com/air-verse/air/releases)
[](https://github.com/air-verse/air/blob/master/go.mod)
[](https://github.com/air-verse/air/releases)
[](https://hub.docker.com/r/cosmtrek/air)
[](https://github.com/air-verse/air/actions?query=workflow%3AGo+branch%3Amaster)
[](https://www.codacy.com/gh/air-verse/air/dashboard?utm_source=github.com&utm_medium=referral&utm_content=air-verse/air&utm_campaign=Badge_Grade)
[](https://codecov.io/gh/air-verse/air)

[English](README.md) | 简体中文 | [繁體中文](README-zh_tw.md) | [日本語](README-ja.md)

Air 是为 Go 应用开发设计的实时重载命令行工具。只需在你的项目根目录下输入 `air`，然后把它放在一边，专注于你的代码即可。注意：该工具与生产环境的热部署无关。



## 目录

- [Air - Go 应用的实时重载工具](#cloud-air---go-应用的实时重载工具)
  - [目录](#目录)
  - [安装](#安装)
    - [使用 `go install`（推荐）](#使用-go-install推荐)
    - [使用 `go get -tool`（安装到项目中）](#使用-go-get--tool安装到项目中)
    - [使用 install.sh](#使用-installsh)
    - [使用 goblin.run](#使用-goblinrun)
    - [使用 Homebrew](#使用-homebrew)
    - [使用 Scoop](#使用-scoop)
    - [使用 mise](#使用-mise)
    - [使用 Docker/Podman](#使用-dockerpodman)
  - [快速开始](#快速开始)
  - [特色](#特色)
  - [使用方法](#使用方法)
    - [运行时参数](#运行时参数)
    - [调试](#调试)
  - [配置](#配置)
    - [使用参数覆盖指定配置](#使用参数覆盖指定配置)
    - [启动横幅](#启动横幅)
    - [Entrypoint](#entrypoint)
    - [环境变量文件](#环境变量文件)
    - [按平台覆盖构建配置](#按平台覆盖构建配置)
    - [监听规则：执行命令而非重新构建](#监听规则执行命令而非重新构建)
    - [代理：自动刷新浏览器](#代理自动刷新浏览器)
  - [Docker](#docker)
    - [使用官方镜像](#使用官方镜像)
    - [Shell 函数](#shell-函数)
    - [Docker Compose](#docker-compose)
    - [不使用 air 镜像](#不使用-air-镜像)
  - [Q&A](#qa)
    - [遇到 "command not found: air" 怎么办](#遇到-command-not-found-air-怎么办)
    - [在 WSL 下 bin 路径包含单引号时报错](#在-wsl-下-bin-路径包含单引号时报错)
    - [如何只进行热编译而不运行？](#如何只进行热编译而不运行)
    - [如何在静态文件更改时自动刷新浏览器？](#如何在静态文件更改时自动刷新浏览器)
  - [开发](#开发)
    - [发布新版本](#发布新版本)
  - [开发动机](#开发动机)
  - [Star 历史](#star-历史)
  - [赞助](#赞助)
  - [许可证](#许可证)

## 安装

### 使用 `go install`（推荐）

使用 go 1.25 或更高版本：

```shell
go install github.com/air-verse/air@latest
```

请确保你的 Go bin 目录已加入 `PATH`：

```shell
export PATH="$PATH:$(go env GOPATH)/bin"
```

### 使用 `go get -tool`（安装到项目中）

使用 go 1.25 或更高版本：

```shell
go get -tool github.com/air-verse/air@latest

# 然后像这样使用：
go tool air -v
```

### 使用 install.sh

```shell
# 二进制文件会安装到 $(go env GOPATH)/bin/air
curl -sSfL https://raw.githubusercontent.com/air-verse/air/master/install.sh | sh -s -- -b $(go env GOPATH)/bin

# 或者把它安装在 ./bin/ 路径下
curl -sSfL https://raw.githubusercontent.com/air-verse/air/master/install.sh | sh -s

air -v
```

### 使用 goblin.run

参见 [goblin.run](https://goblin.run)。

```shell
# 二进制文件将会安装到 /usr/local/bin/air
curl -sSfL https://goblin.run/github.com/air-verse/air | sh

# 自定义路径安装
curl -sSfL https://goblin.run/github.com/air-verse/air | PREFIX=/tmp sh
```

### 使用 Homebrew

```shell
brew install go-air
```

### 使用 Scoop

```shell
scoop install air
```

### 使用 mise

```shell
mise use -g air
```

### 使用 Docker/Podman

拉取 [cosmtrek/air](https://hub.docker.com/r/cosmtrek/air) 镜像，用法见 [Docker](#docker)。

## 快速开始

```shell
# 进入你的项目目录
cd /path/to/your_project

# 先尝试读取当前目录中的 `.air.toml`；如果不存在，则使用默认配置
air
```

如果想生成一份可以自行修改的配置文件，先执行一次 `air init`，之后直接执行 `air` 即可：

```shell
# 生成带有默认配置的 .air.toml
air init

# 会自动读取 .air.toml
air
```

如果要显式指定配置文件，可以使用 `-c`：

```shell
air -c .air.toml
```

全部可用配置项请参考 [air_example.toml](air_example.toml) 文件。

## 特色

- [x] 彩色的日志输出
- [x] 自定义构建或必要的命令
- [x] 支持排除子目录
- [x] 在 Air 启动之后，允许监听新创建的目录
- [x] 更棒的构建过程
- [x] 可配置的 `.env` 文件加载

## 使用方法

### 运行时参数

你可以把参数添加在 air 命令之后，传递给构建出来的二进制文件。

```shell
# 会执行 ./tmp/main bench
air bench

# 会执行 ./tmp/main server --port 8080
air server --port 8080
```

你可以使用 `--` 参数来分隔传递给 air 命令和传递给二进制文件的参数。

```shell
# 会运行 ./tmp/main -h
air -- -h

# 会使用个性化配置来运行 air，并把 -h 传给构建出的二进制文件
air -c .air.toml -- -h
```

### 调试

`air -d` 命令能打印所有日志。

## 配置

### 使用参数覆盖指定配置

air 的配置字段都支持以命令行参数的形式传入。可以通过下面的命令查看全部可用参数：

```shell
air -h
# 或者
air --help
```

如果你只是想配置构建命令和运行命令，可以直接使用以下命令，而无需配置文件：

```shell
air --build.cmd "go build -o bin/api cmd/run.go" --build.entrypoint "./bin/api"
```

对于以列表形式输入的参数，使用逗号来分隔各项：

```shell
air --build.cmd "go build -o bin/api cmd/run.go" --build.entrypoint "./bin/api" --build.exclude_dir "templates,build"
```

列表类参数也可以重复传入，取值会按出现顺序依次追加。当命令行由脚本或 Makefile 生成时，这种写法很方便：

```shell
# 等价于 --env_files ".env,.env.local,.env.secret"
air --env_files ".env,.env.local" --env_files ".env.secret"
```

### 启动横幅

使用 `misc.startup_banner` 控制 Air 启动时打印的内容。

```toml
[misc]
# 不设置（默认）：显示内置的 ASCII 横幅和版本号。

# 设置为空字符串：什么都不打印。
startup_banner = ""

# 设置为自定义文本：打印这段文本，替代内置横幅。
# startup_banner = "API watcher"
```

### Entrypoint

使用 `build.entrypoint` 指定 `build.cmd` 构建出的二进制文件，以及它的执行方式。该值可以是字符串（只指定可执行文件），也可以是字符串数组。使用数组时，第一个元素是可执行文件（相对于 `root` 解析；如果其中不含路径分隔符，则从 `$PATH` 中查找），其余元素都会作为默认参数。`build.args_bin` 和命令行传入的参数会追加在这些内联参数之后。旧的 `build.bin` 字段已弃用，将在未来的版本中移除，请优先使用 entrypoint 的写法。

```toml
[build]
entrypoint = ["./tmp/main"]
args_bin = ["server", ":8080"]

# 也可以把默认参数直接内联写在二进制文件后面。
entrypoint = ["./tmp/main", "server", ":8080"]

# 不带路径分隔符时会从 PATH 中查找，例如 dlv。
entrypoint = [
  "dlv", "exec", "--accept-multiclient", "--log", "--headless", "--continue",
  "--listen=:8999", "--api-version", "2", "./tmp/main",
]
```

### 环境变量文件

配置 `env_files` 后，Air 会在构建和运行之前自动从 `.env` 文件中加载环境变量。

```toml
# 依次加载 .env.development 和 .env。
# 靠后文件中的值会覆盖前面的值。
# 不会覆盖运行 air 之前就已存在的变量。
env_files = [".env.development", ".env"]
```

### 按平台覆盖构建配置

你可以用 `[build.windows]`、`[build.darwin]` 和 `[build.linux]` 按操作系统覆盖构建配置。在对应平台上运行时，这些配置块会覆盖 `[build]` 中的值。平台配置块中只支持以下字段：`pre_cmd`、`cmd`、`post_cmd`、`bin`、`entrypoint`、`full_bin`、`args_bin`。

```toml
[build]
cmd = "go build -o ./tmp/main ."
bin = "./tmp/main"

[build.windows]
cmd = "go build -o ./tmp/main.exe ."
bin = "tmp\\main.exe"
entrypoint = ["tmp\\main.exe"]
```

当当前系统的默认配置与基础配置不同时，`air init` 会自动为当前系统生成对应的平台配置块。

### 监听规则：执行命令而非重新构建

有时候文件变更需要的是执行某个命令，而不是重新构建应用——比如直接从磁盘提供的前端资源，或者 `templ`/`sqlc`/`go generate` 这类流程。可以为它们各自声明一个 `[[build.rules]]` 配置块：

```toml
[build]
cmd = "go build -o ./tmp/main ."
# 主构建流程忽略前端目录……
exclude_dir = ["web"]

# ……但这条规则会监听它，并在变更时重新构建前端资源
[[build.rules]]
name = "assets"
include_dir = ["web"]
include_ext = ["js", "ts", "css"]
cmd = "npm run build"

[[build.rules]]
name = "templ"
include_ext = ["templ"]
cmd = "templ generate"
```

被规则匹配到的文件只会执行该规则的 `cmd`，绝不会触发重新构建，即使它同时也符合主构建的监听条件。规则涉及的目录即使写在 `exclude_dir` 里也依然会被监听。如果规则的命令生成了主构建正在监听的文件（例如 `templ generate` 生成 `.go` 文件），重新构建自然会随之触发。

每条规则支持 `include_dir`、`include_ext`、`include_file`、`exclude_regex` 以及 `delay`（防抖时间，单位毫秒，默认 1000）。`include_*` 匹配条件至少要填一项。规则会等待命令执行完毕；执行期间到达的变更会排队，结束后再跑一次。

### 代理：自动刷新浏览器

Air 可以在你的 Web 应用前面挂一个小型代理，每次构建成功后自动刷新浏览器，这样就不用再手动按刷新了。

```toml
[proxy]
enabled = true
# 你在浏览器中访问的端口
proxy_port = 8090
# 你的应用实际监听的端口
app_port = 8080
```

照常启动 `air`，然后在浏览器中访问 `http://localhost:8090`，而不是应用自己的端口。请求会被转发到 `app_port`，同时 Air 会在每个 HTML 响应的 `` 标签前注入一小段脚本；构建完成后，这段脚本就会刷新页面。

要让它生效，有两个前提：

- HTML 中必须有 `` 标签——否则没有地方注入脚本，页面会原样返回；
- 你修改的文件必须处于监听范围内，所以静态资源需要被 `include_dir`、`include_ext` 或 `include_file` 覆盖到。

如果你的应用启动较慢（连接数据库、加载配置等），并出现 "unable to reach app" 错误，可以把等待时间调长：

```toml
[proxy]
# 构建完成后重试连接应用的时长，单位毫秒（默认 5000）
app_start_timeout = 10000
```

## Docker

### 使用官方镜像

请拉取这个 Docker 镜像：[cosmtrek/air](https://hub.docker.com/r/cosmtrek/air)。

```shell
docker/podman run -it --rm \
    -w "" \
    -e "air_wd=" \
    -v $(pwd): \
    -p :<APP SERVER PORT> \
    cosmtrek/air \
    -c <CONF>
```

`` 是容器中的项目路径，例如 `/go/example`。如果你想进入容器，请添加 `--entrypoint=bash`。

我的一个项目运行在 Docker 中：

```shell
docker run -it --rm \
  -w "/go/src/github.com/cosmtrek/hub" \
  -v $(pwd):/go/src/github.com/cosmtrek/hub \
  -p 9090:9090 \
  cosmtrek/air
```

### Shell 函数

如果你想像使用普通应用程序那样持续使用 air，可以在你的 `${SHELL}rc`（Bash、Zsh 等）中创建一个函数：

```shell
air() {
  podman/docker run -it --rm \
    -w "$PWD" -v "$PWD":"$PWD" \
    -p "$AIR_PORT":"$AIR_PORT" \
    docker.io/cosmtrek/air "$@"
}
```

其中 `$PWD` 会被替换为当前目录，`$AIR_PORT` 是要发布的端口，而 `$@` 用于接收应用程序本身的参数，例如 `-c`：

```shell
cd /go/src/github.com/cosmtrek/hub
AIR_PORT=8080 air -c "config.toml"
```

### Docker Compose

```yaml
services:
  my-project-with-air:
    image: cosmtrek/air
    # working_dir 的值必须和挂载的卷保持一致
    working_dir: /project-package
    ports:
      - <any>:<any>
    environment:
      - ENV_A=${ENV_A}
      - ENV_B=${ENV_B}
      - ENV_C=${ENV_C}
    volumes:
      - ./project-relative-path/:/project-package/
```

### 不使用 air 镜像

`Dockerfile`

```Dockerfile
# 选择你想要的版本，>= 1.25
FROM golang:1.25-alpine

WORKDIR /app

RUN go install github.com/air-verse/air@latest

COPY go.mod go.sum ./
RUN go mod download

CMD ["air", "-c", ".air.toml"]
```

`docker-compose.yaml`

```yaml
version: "3.8"
services:
  web:
    build:
      context: .
      # 修改为你的 Dockerfile 路径
      dockerfile: Dockerfile
    ports:
      - 8080:3000
    # 为了实时重载，将代码目录绑定到 /app 目录是很重要的
    volumes:
      - ./:/app
```

## Q&A

### 遇到 "command not found: air" 怎么办

有时也表现为 "No such file or directory"。请确认 Go bin 目录已加入 `PATH`：

```shell
export GOPATH=$HOME/xxxxx
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
export PATH=$PATH:$(go env GOPATH)/bin <---- 请确认这行在您的配置信息中！！！
```

### 在 WSL 下 bin 路径包含单引号时报错

应该使用 `\` 来转义 bin 中的 `'`。相关 issue：[#305](https://github.com/air-verse/air/issues/305)

### 如何只进行热编译而不运行？

参见 [#365](https://github.com/air-verse/air/issues/365)。

```toml
[build]
  cmd = "/usr/bin/true"
```

### 如何在静态文件更改时自动刷新浏览器？

开启代理即可，详见[代理：自动刷新浏览器](#代理自动刷新浏览器)。请确认你的静态文件被 `include_dir`、`include_ext` 或 `include_file` 覆盖到，否则修改它们不会触发刷新。更多细节请参考 [#512](https://github.com/air-verse/air/issues/512)。

## 开发

请注意：当前需要 Go 1.25+（见 `go.mod`）。

```shell
# 1. 首先复刻（fork）这个项目

# 2. 其次克隆（clone）它
mkdir -p $GOPATH/src/github.com/cosmtrek
cd $GOPATH/src/github.com/cosmtrek
git clone git@github.com:<YOUR USERNAME>/air.git

# 3. 安装依赖
cd air
make ci

# 4. 这样就可以快乐地探索和玩耍啦！
make install
```

顺便说一句：欢迎 PR~

### 发布新版本

```shell
# 1. checkout 到 master 分支
git checkout master

# 2. 添加需要发布的版本号
git tag v1.xx.x

# 3. 推送到远程
git push origin v1.xx.x

# CI 将处理并发布新版本。等待大约 5 分钟，你就可以获取最新版本了
```

## 开发动机

当我用 Go 和 [gin](https://github.com/gin-gonic/gin) 框架开发网站时，gin 缺乏实时重载的功能是令人遗憾的。我曾经尝试过 [fresh](https://github.com/pilu/fresh)，但是它用起来不太灵活，所以我试着用更好的方式来重写它。Air 就这样诞生了。

此外，非常感谢 [pilu](https://github.com/pilu)。没有 fresh 就不会有 air :)

## Star 历史

<a href="https://www.star-history.com/?type=date&repos=air-verse%2Fair">
 
   
   
   
 
</a>

## 赞助

[](https://www.buymeacoffee.com/cosmtrek)

非常感谢众多支持者。我一直铭记你们的善意。

## 许可证

[GNU General Public License v3.0](LICENSE)

---

## File: README-zh_tw.md

# :cloud: Air - Go 應用的即時重新載入工具

[](https://github.com/air-verse/air/releases)
[](https://github.com/air-verse/air/blob/master/go.mod)
[](https://github.com/air-verse/air/releases)
[](https://hub.docker.com/r/cosmtrek/air)
[](https://github.com/air-verse/air/actions?query=workflow%3AGo+branch%3Amaster)
[](https://www.codacy.com/gh/air-verse/air/dashboard?utm_source=github.com&utm_medium=referral&utm_content=air-verse/air&utm_campaign=Badge_Grade)
[](https://codecov.io/gh/air-verse/air)

[English](README.md) | [简体中文](README-zh_cn.md) | 繁體中文 | [日本語](README-ja.md)

Air 是一個用於開發 Go 應用的自動重新編譯執行命令列工具。在你的專案根目錄下運行 `air`，將它執行於背景中，並專注於你的程式碼。注意：此工具與生產環境的熱部署無關。



## 目錄

- [Air - Go 應用的即時重新載入工具](#cloud-air---go-應用的即時重新載入工具)
  - [目錄](#目錄)
  - [安裝](#安裝)
    - [使用 `go install`（推薦）](#使用-go-install推薦)
    - [使用 `go get -tool`（安裝到專案中）](#使用-go-get--tool安裝到專案中)
    - [透過 install.sh](#透過-installsh)
    - [透過 goblin.run](#透過-goblinrun)
    - [透過 Homebrew](#透過-homebrew)
    - [透過 Scoop](#透過-scoop)
    - [使用 mise](#使用-mise)
    - [透過 Docker/Podman](#透過-dockerpodman)
  - [快速開始](#快速開始)
  - [功能列表](#功能列表)
  - [使用方式](#使用方式)
    - [運行時參數](#運行時參數)
    - [除錯](#除錯)
  - [配置](#配置)
    - [用參數覆寫指定的配置](#用參數覆寫指定的配置)
    - [啟動橫幅](#啟動橫幅)
    - [Entrypoint](#entrypoint)
    - [環境變數檔案](#環境變數檔案)
    - [依平台覆寫建置配置](#依平台覆寫建置配置)
    - [監視規則：執行命令而非重新建置](#監視規則執行命令而非重新建置)
    - [代理：自動重新載入瀏覽器](#代理自動重新載入瀏覽器)
  - [Docker](#docker)
    - [使用官方映像檔](#使用官方映像檔)
    - [Shell 函數](#shell-函數)
    - [Docker Compose](#docker-compose)
    - [不使用 air 映像檔](#不使用-air-映像檔)
  - [Q&A](#qa)
    - [出現「找不到命令：air」或「找不到檔案或目錄」](#出現找不到命令air或找不到檔案或目錄)
    - [當 bin 路徑中包含單引號時，在 WSL 下的錯誤](#當-bin-路徑中包含單引號時在-wsl-下的錯誤)
    - [如何只進行熱編譯而不執行任何東西？](#如何只進行熱編譯而不執行任何東西)
    - [如何在靜態檔案變更時自動重新載入瀏覽器？](#如何在靜態檔案變更時自動重新載入瀏覽器)
  - [開發](#開發)
    - [發佈版本](#發佈版本)
  - [開發動機](#開發動機)
  - [星星歷史](#星星歷史)
  - [贊助專案](#贊助專案)
  - [授權](#授權)

## 安裝

### 使用 `go install`（推薦）

需要使用 go 1.25 或更高版本：

```shell
go install github.com/air-verse/air@latest
```

請確認你的 Go bin 目錄已加入 `PATH`：

```shell
export PATH="$PATH:$(go env GOPATH)/bin"
```

### 使用 `go get -tool`（安裝到專案中）

需要使用 go 1.25 或更高版本：

```shell
go get -tool github.com/air-verse/air@latest

# 然後這樣使用：
go tool air -v
```

### 透過 install.sh

```shell
# 執行檔會安裝到 $(go env GOPATH)/bin/air
curl -sSfL https://raw.githubusercontent.com/air-verse/air/master/install.sh | sh -s -- -b $(go env GOPATH)/bin

# 或者把它安裝到 ./bin/ 路徑下
curl -sSfL https://raw.githubusercontent.com/air-verse/air/master/install.sh | sh -s

air -v
```

### 透過 goblin.run

請參考 [goblin.run](https://goblin.run)。

```shell
# 執行檔會安裝到 /usr/local/bin/air
curl -sSfL https://goblin.run/github.com/air-verse/air | sh

# 安裝到自訂路徑
curl -sSfL https://goblin.run/github.com/air-verse/air | PREFIX=/tmp sh
```

### 透過 Homebrew

```shell
brew install go-air
```

### 透過 Scoop

```shell
scoop install air
```

### 使用 mise

```shell
mise use -g air
```

### 透過 Docker/Podman

請讀取 [cosmtrek/air](https://hub.docker.com/r/cosmtrek/air) 映像檔，用法請見 [Docker](#docker)。

## 快速開始

```shell
# 進入你的專案目錄
cd /path/to/your_project

# 先嘗試讀取目前目錄中的 `.air.toml`；如果不存在，則使用預設配置
air
```

如果想產生一份可以自行修改的配置檔，先執行一次 `air init`，之後直接執行 `air` 即可：

```shell
# 產生帶有預設配置的 .air.toml
air init

# 會自動讀取 .air.toml
air
```

如果要明確指定配置檔，可以使用 `-c`：

```shell
air -c .air.toml
```

全部可用的配置項目請參閱 [air_example.toml](air_example.toml) 檔案。

## 功能列表

- [x] 彩色的日誌輸出
- [x] 自訂建置或任何命令
- [x] 支援排除子目錄
- [x] 允許在 Air 開始後監視新目錄
- [x] 更佳的建置過程
- [x] 可設定的 `.env` 檔案載入

## 使用方式

### 運行時參數

你可以在 air 命令後添加參數，這些參數會傳遞給建構出來的執行檔。

```shell
# 會執行 ./tmp/main bench
air bench

# 會執行 ./tmp/main server --port 8080
air server --port 8080
```

你可以使用 `--` 參數來分隔傳遞給 air 命令和傳遞給執行檔的參數。

```shell
# 會執行 ./tmp/main -h
air -- -h

# 會以自訂配置執行 air，並把 -h 傳給建構出的執行檔
air -c .air.toml -- -h
```

### 除錯

`air -d` 會印出所有日誌。

## 配置

### 用參數覆寫指定的配置

air 的配置欄位都支援以命令列參數的形式傳入。可以用下列命令查看全部可用參數：

```shell
air -h
# 或者
air --help
```

如果你想設定建置命令和執行命令，可以在不需要配置檔案的情況下如下使用命令：

```shell
air --build.cmd "go build -o bin/api cmd/run.go" --build.entrypoint "./bin/api"
```

對於需要輸入列表的參數，可以使用逗號將項目分隔：

```shell
air --build.cmd "go build -o bin/api cmd/run.go" --build.entrypoint "./bin/api" --build.exclude_dir "templates,build"
```

列表類參數也可以重複傳入，取值會依照出現順序依序附加。當命令列是由腳本或 Makefile 產生時，這種寫法很方便：

```shell
# 等同於 --env_files ".env,.env.local,.env.secret"
air --env_files ".env,.env.local" --env_files ".env.secret"
```

### 啟動橫幅

使用 `misc.startup_banner` 控制 Air 啟動時印出的內容。

```toml
[misc]
# 不設定（預設）：顯示內建的 ASCII 橫幅與版本號。

# 設為空字串：什麼都不印出。
startup_banner = ""

# 設為自訂文字：印出這段文字，取代內建橫幅。
# startup_banner = "API watcher"
```

### Entrypoint

使用 `build.entrypoint` 指定 `build.cmd` 建構出的執行檔，以及它的執行方式。這個值可以是字串（只指定執行檔），也可以是字串陣列。使用陣列時，第一個元素是執行檔（相對於 `root` 解析；若其中不含路徑分隔符號，則從 `$PATH` 中尋找），其餘元素都會被視為預設參數。`build.args_bin` 與命令列傳入的參數會附加在這些內聯參數之後。舊的 `build.bin` 欄位已被棄用，未來版本會移除，請優先使用 entrypoint 的寫法。

```toml
[build]
entrypoint = ["./tmp/main"]
args_bin = ["server", ":8080"]

# 也可以把預設參數直接內聯寫在執行檔後面。
entrypoint = ["./tmp/main", "server", ":8080"]

# 不帶路徑分隔符號時會從 PATH 中尋找，例如 dlv。
entrypoint = [
  "dlv", "exec", "--accept-multiclient", "--log", "--headless", "--continue",
  "--listen=:8999", "--api-version", "2", "./tmp/main",
]
```

### 環境變數檔案

設定 `env_files` 之後，Air 會在建置與執行之前，自動從 `.env` 檔案載入環境變數。

```toml
# 依序載入 .env.development 與 .env。
# 越後面的檔案中的值會覆寫前面的值。
# 不會覆寫執行 air 之前就已存在的變數。
env_files = [".env.development", ".env"]
```

### 依平台覆寫建置配置

你可以用 `[build.windows]`、`[build.darwin]` 和 `[build.linux]` 依作業系統覆寫建置配置。在對應平台上執行時，這些區塊會覆寫 `[build]` 中的值。平台區塊中只支援下列欄位：`pre_cmd`、`cmd`、`post_cmd`、`bin`、`entrypoint`、`full_bin`、`args_bin`。

```toml
[build]
cmd = "go build -o ./tmp/main ."
bin = "./tmp/main"

[build.windows]
cmd = "go build -o ./tmp/main.exe ."
bin = "tmp\\main.exe"
entrypoint = ["tmp\\main.exe"]
```

當目前系統的預設配置與基礎配置不同時，`air init` 會自動為目前系統產生對應的平台區塊。

### 監視規則：執行命令而非重新建置

有時候檔案變更需要的是執行某個命令，而不是重新建置應用程式——例如直接從磁碟提供的前端資源，或是 `templ`/`sqlc`/`go generate` 這類流程。可以為它們各自宣告一個 `[[build.rules]]` 區塊：

```toml
[build]
cmd = "go build -o ./tmp/main ."
# 主要建置流程忽略前端目錄……
exclude_dir = ["web"]

# ……但這條規則會監視它，並在變更時重新建置前端資源
[[build.rules]]
name = "assets"
include_dir = ["web"]
include_ext = ["js", "ts", "css"]
cmd = "npm run build"

[[build.rules]]
name = "templ"
include_ext = ["templ"]
cmd = "templ generate"
```

被規則比對到的檔案只會執行該規則的 `cmd`，絕不會觸發重新建置，即使它同時也符合主要建置的監視條件。規則涉及的目錄即使寫在 `exclude_dir` 裡也依然會被監視。如果規則的命令產生了主要建置正在監視的檔案（例如 `templ generate` 產生 `.go` 檔案），重新建置自然會隨之觸發。

每條規則支援 `include_dir`、`include_ext`、`include_file`、`exclude_regex` 以及 `delay`（防抖動時間，單位毫秒，預設 1000）。`include_*` 比對條件至少要填一項。規則會等待命令執行完畢；執行期間到達的變更會排入佇列，結束後再跑一次。

### 代理：自動重新載入瀏覽器

Air 可以在你的 Web 應用前面掛一個小型代理，每次建置成功後自動重新載入瀏覽器，這樣就不必再手動按重新整理了。

```toml
[proxy]
enabled = true
# 你在瀏覽器中開啟的連接埠
proxy_port = 8090
# 你的應用實際監聽的連接埠
app_port = 8080
```

照常啟動 `air`，然後在瀏覽器中開啟 `http://localhost:8090`，而不是應用自己的連接埠。請求會被轉發到 `app_port`，同時 Air 會在每個 HTML 回應的 `` 標籤前注入一小段腳本；建置完成後，這段腳本就會重新載入頁面。

要讓它生效，有兩個前提：

- HTML 中必須有 `` 標籤——否則沒有地方可以注入腳本，頁面會原樣回傳；
- 你修改的檔案必須在監視範圍內，所以靜態資源需要被 `include_dir`、`include_ext` 或 `include_file` 涵蓋。

如果你的應用啟動較慢（連接資料庫、載入配置等），並出現 "unable to reach app" 錯誤，可以把等待時間調長：

```toml
[proxy]
# 建置完成後重試連接應用的時間長度，單位毫秒（預設 5000）
app_start_timeout = 10000
```

## Docker

### 使用官方映像檔

請讀取 Docker 映像檔：[cosmtrek/air](https://hub.docker.com/r/cosmtrek/air)。

```shell
docker/podman run -it --rm \
    -w "" \
    -e "air_wd=" \
    -v $(pwd): \
    -p :<APP SERVER PORT> \
    cosmtrek/air \
    -c <CONF>
```

`` 是你的容器中的專案路徑，例如 `/go/example`。如果你想要進入容器，請加上 `--entrypoint=bash`。

我其中一個專案是在 Docker 中運行：

```shell
docker run -it --rm \
  -w "/go/src/github.com/cosmtrek/hub" \
  -v $(pwd):/go/src/github.com/cosmtrek/hub \
  -p 9090:9090 \
  cosmtrek/air
```

### Shell 函數

如果你想像常規應用程式一樣持續使用 air，你可以在你的 `${SHELL}rc`（Bash、Zsh 等）中建立一個函數：

```shell
air() {
  podman/docker run -it --rm \
    -w "$PWD" -v "$PWD":"$PWD" \
    -p "$AIR_PORT":"$AIR_PORT" \
    docker.io/cosmtrek/air "$@"
}
```

其中 `$PWD` 會被替換為當前目錄，`$AIR_PORT` 是要發佈的連接埠，而 `$@` 用來接受應用程式本身的參數，例如 `-c`：

```shell
cd /go/src/github.com/cosmtrek/hub
AIR_PORT=8080 air -c "config.toml"
```

### Docker Compose

```yaml
services:
  my-project-with-air:
    image: cosmtrek/air
    # working_dir 的值必須與掛載的 volume 相同
    working_dir: /project-package
    ports:
      - <any>:<any>
    environment:
      - ENV_A=${ENV_A}
      - ENV_B=${ENV_B}
      - ENV_C=${ENV_C}
    volumes:
      - ./project-relative-path/:/project-package/
```

### 不使用 air 映像檔

`Dockerfile`

```Dockerfile
# 選擇你想要的版本，>= 1.25
FROM golang:1.25-alpine

WORKDIR /app

RUN go install github.com/air-verse/air@latest

COPY go.mod go.sum ./
RUN go mod download

CMD ["air", "-c", ".air.toml"]
```

`docker-compose.yaml`

```yaml
version: "3.8"
services:
  web:
    build:
      context: .
      # 修改為你的 Dockerfile 路徑
      dockerfile: Dockerfile
    ports:
      - 8080:3000
    # 為了即時重新載入，將程式碼目錄掛載到 /app 目錄很重要
    volumes:
      - ./:/app
```

## Q&A

### 出現「找不到命令：air」或「找不到檔案或目錄」

請確認 Go bin 目錄已加入 `PATH`：

```shell
export GOPATH=$HOME/xxxxx
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
export PATH=$PATH:$(go env GOPATH)/bin <---- Confirm this line in you profile!!!
```

### 當 bin 路徑中包含單引號時，在 WSL 下的錯誤

應該使用 `\` 來跳脫 bin 中的 `'`。相關議題：[#305](https://github.com/air-verse/air/issues/305)

### 如何只進行熱編譯而不執行任何東西？

請參考 [#365](https://github.com/air-verse/air/issues/365)。

```toml
[build]
  cmd = "/usr/bin/true"
```

### 如何在靜態檔案變更時自動重新載入瀏覽器？

啟用代理即可，詳見[代理：自動重新載入瀏覽器](#代理自動重新載入瀏覽器)。請確認你的靜態檔案有被 `include_dir`、`include_ext` 或 `include_file` 涵蓋，否則修改它們不會觸發重新載入。更多細節請參考 [#512](https://github.com/air-verse/air/issues/512)。

## 開發

請注意：目前需要 Go 1.25+（請參考 `go.mod`）。

```shell
# Fork this project

# Clone it
mkdir -p $GOPATH/src/github.com/cosmtrek
cd $GOPATH/src/github.com/cosmtrek
git clone git@github.com:<YOUR USERNAME>/air.git

# Install dependencies
cd air
make ci

# Explore it and happy hacking!
make install
```

歡迎提出 Pull Request

### 發佈版本

```shell
# Checkout to master
git checkout master

# Add the version that needs to be released
git tag v1.xx.x

# Push to remote
git push origin v1.xx.x

# The CI will process and release a new version. Wait about 5 min, and you can fetch the latest version
```

## 開發動機

當我開始用 Go 開發網站並使用 [gin](https://github.com/gin-gonic/gin) 框架時，感到可惜的是 gin 缺乏自動重新編譯執行的方式。因此，我四處搜尋並嘗試使用 [fresh](https://github.com/pilu/fresh)，但它似乎不夠彈性，所以我打算重新寫得更好。最後，Air 就這麼誕生了。

另外，非常感謝 [pilu](https://github.com/pilu)，如果沒有 fresh，就不會有 air :)

## 星星歷史

<a href="https://www.star-history.com/?type=date&repos=air-verse%2Fair">
 
   
   
   
 
</a>

## 贊助專案

[](https://www.buymeacoffee.com/cosmtrek)

非常感謝大量的支持者。我一直記得你們的善意。

## 授權

[GNU General Public License v3.0](LICENSE)