1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ApplibotAdvent Calendar 2024

Day 22

Nxで非Javascriptプロジェクトのモノレポを構築する

Last updated at Posted at 2024-12-22

この記事は Applibot Advent Calendar 2024 22日目の記事です。前回の記事は コチラ です。

本記事の目的

Nx を用いて非 Javascript プロジェクトのモノレポを構築する際に、公式ドキュメントにはチュートリアルなどでまとまっておらず少し困ったので、自分がドキュメントを読みながら試行錯誤して行った手順をまとめる。

構築するもの

  • 複数の go.mod をもつ Go のモノレポに Nx を導入する
  • Go 以外の言語を増やした場合でも使える手順・ディレクトリ構成にする
  • main ブランチとの差分を検知して、変更があるモジュールのみフォーマット・リント・テスト・ビルドできるようにする
  • どのようにフォーマットやリントするかなどを柔軟に設定できる
最終ディレクトリ構成
.
├── .gitignore
├── .nx
│   ├── cache
│   │   ├── run.json
│   │   └── terminalOutputs
│   ├── installation
│   │   ├── node_modules
│   │   ├── package-lock.json
│   │   └── package.json
│   ├── nxw.js
│   └── workspace-data
├── Makefile
├── go
│   ├── .golangci.yaml
│   ├── app
│   │   └── bff
│   │       ├── Makefile
│   │       ├── cmd
│   │       │   └── api
│   │       │       └── main.go
│   │       ├── go.mod
│   │       └── project.json
│   ├── pkg
│   │   └── logger
│   │       ├── Makefile
│   │       ├── go.mod
│   │       ├── go.sum
│   │       ├── logger.go
│   │       └── project.json
│   └── tools
├── nx
├── nx.bat
└── nx.json

前提

  • デバイスは Macbook
  • Node.js がインストールされている
  • Go がインストールされている
  • Github をレポジトリに使っている(手順3)
初期ディレクトリ構成
.
└── go
    ├── .golangci.yaml
    ├── app
    │   └── bff
    │       ├── cmd
    │       │   └── api
    │       │       └── main.go
    │       └── go.mod
    ├── pkg
    │   └── logger
    │       ├── go.mod
    │       ├── go.sum
    │       └── logger.go
    └── tools

補足

なお、サードパーティのプラグインとして @nx-go/nx-go が存在するが、フォーマットに goimports を使ったり、リントに golangci-lint を使ったりすることができなさそうだったため、今回は使わないものとする。

手順

1. Nx のセットアップ

公式ドキュメントの「Install Nx in a Non-Javascript Repo」のように init コマンドをルートディレクトリで実行する。(要 Node.js

$ npx nx init
Setting Nx up installation in `.nx`. You can run Nx commands like: `./nx --help`
CREATE nx.json
CREATE .gitignore
CREATE .nx/nxw.js
CREATE nx.bat
CREATE nx

生成される nx ファイルがコマンドを実行するためのファイル、nx.json が設定ファイル、.nx/ 配下にプラグインやキャッシュなどが格納される。
nx.json に下記の修正をいれる。(JSON Schema の設定と、変更差分を検知するためのベースブランチ設定)

nx.json
 {
+  "$schema": ".nx/installation/node_modules/nx/schemas/nx-schema.json",
   "installation": {
     "version": "20.3.0"
-  }
+  },
+  "defaultBase": "main"
 }

2. 既存の Go のモジュールを Nx プロジェクト化する

go.mod があるディレクトリに、project.json を作成する。(下記は go/app/bff の例)
project.json を作成することで、Nx にプロジェクトとして認識されるようになる。

go/app/bff/project.json
{
  "name": "go/app/bff",
  "$schema": "../../../.nx/installation/node_modules/nx/schemas/project-schema.json",
  "projectType": "application",
  "sourceRoot": "go/app/bff",
  "tags": [],
  "targets": {
    "build": {
      "command": "go build -o bin/bff-api ./cmd/api",
      "options": {
        "cwd": "go/app/bff"
      }
    },
    "serve": {
      "command": "go run cmd/api/main.go",
      "options": {
        "cwd": "go/app/bff"
      }
    },
    "fmt": {
      "command": "go fmt ./...",
      "options": {
        "cwd": "go/app/bff"
      }
    },
    "lint": {
      "command": "go vet ./...",
      "options": {
        "cwd": "go/app/bff"
      }
    },
    "test": {
      "command": "go test -v ./...",
      "options": {
        "cwd": "go/app/bff"
      }
    }
  }
}

上記の targets に列挙されているものがそれぞれ Nx タスクの識別子となる。

JSON Schema を参照し、go/pkg/loggerのものも作成する。(上記例で、go/app/bffgo/pkg/logger に置き換え、projectTypelibrary にする)

タスク実行方法

例えばルートディレクトリで下記コマンドを実行することで、指定プロジェクトの test タスクを実行できる。

$ ./nx test go/app/bff

全てのプロジェクトの指定タスクを実行する場合は、下記コマンドとなる。

$ ./nx run-many -t test

main ブランチとの変更差分があるプロジェクトのみで指定タスクを実行する場合は、下記コマンドとなる。

$ ./nx affected -t test

また、エディタ拡張の Nx Console を使うと以下のように GUI で各コマンドを実行できるようになる。(下記は Jetbrains の例)
Screenshot 2024-12-22 at 10.09.01 PM.png

3. コマンドをカスタマイズする(こちらは一例)

今回は、コマンドのカスタマイズとして下記を一元的に記述して実行するために Makefile を用いる。

  • fmt は、goimportsgofumpt・go mod tidy が全て実行されるようにする
  • lint は、 golangci-lint を使用し、設定ファイルは共通のものを使う。また、govulncheck も実行する
  • Go のプライベートレポジトリ参照も考慮したコマンドを実行できるようにする

プロジェクトのルートディレクトリに下記 Makefile を作成する。
<your-github-org><your-github-repository>は適切に変えてください。

Makefile
### General
SHELL := /bin/bash
ROOT := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
BIN := $(ROOT)/bin

### GitHub
GITHUB_ORG := <your-github-org>
REPOSITORY_NAME := <your-github-repository>

### Go
GO ?= go
GO_ENV ?= CGO_ENABLED=0 GOPRIVATE=github.com/${GITHUB_ORG} GOBIN=$(BIN)
GO_MODULE ?= github.com/${GITHUB_ORG}/${REPOSITORY_NAME}

### Tools
$(shell mkdir -p $(BIN))

GOIMPORTS_VERSION := 0.28.0
$(BIN)/goimports-$(GOIMPORTS_VERSION):
	unlink $(BIN)/goimports || true
	$(GO_ENV) ${GO} install golang.org/x/tools/cmd/goimports@v$(GOIMPORTS_VERSION)
	mv $(BIN)/goimports $(BIN)/goimports-$(GOIMPORTS_VERSION)
	ln -s $(BIN)/goimports-$(GOIMPORTS_VERSION) $(BIN)/goimports

GOFUMPT_VERSION := 0.7.0
$(BIN)/gofumpt-$(GOFUMPT_VERSION):
	unlink $(BIN)/gofumpt || true
	$(GO_ENV) ${GO} install mvdan.cc/gofumpt@v$(GOFUMPT_VERSION)
	mv $(BIN)/gofumpt $(BIN)/gofumpt-$(GOFUMPT_VERSION)
	ln -s $(BIN)/gofumpt-$(GOFUMPT_VERSION) $(BIN)/gofumpt

GOLANGCI_LINT_VERSION := 1.62.2
$(BIN)/golangci-lint-$(GOLANGCI_LINT_VERSION):
	unlink $(BIN)/golangci-lint || true
	$(GO_ENV) ${GO} install github.com/golangci/golangci-lint/cmd/golangci-lint@v$(GOLANGCI_LINT_VERSION)
	mv $(BIN)/golangci-lint $(BIN)/golangci-lint-$(GOLANGCI_LINT_VERSION)
	ln -s $(BIN)/golangci-lint-$(GOLANGCI_LINT_VERSION) $(BIN)/golangci-lint

GOVULNCHECK_VERSION := 1.1.3
$(BIN)/govulncheck-$(GOVULNCHECK_VERSION):
	unlink $(BIN)/govulncheck || true
	$(GO_ENV) ${GO} install golang.org/x/vuln/cmd/govulncheck@v$(GOVULNCHECK_VERSION)
	mv $(BIN)/govulncheck $(BIN)/govulncheck-$(GOVULNCHECK_VERSION)
	ln -s $(BIN)/govulncheck-$(GOVULNCHECK_VERSION) $(BIN)/govulncheck


### Commands

.PHONY: initialize
initialize: initialize-once initialize-once-go

.PHONY: initialize-once
initialize-once:
	git config --global url.git@github.com:${GITHUB_ORG}/.insteadOf https://github.com${GITHUB_ORG}/ # NOTE: go でマルチモジュール構成を取っており、プライベートレポジトリを参照するための設定

.PHONY: initialize-once-go
initialize-once-go: $(BIN)/goimports-$(GOIMPORTS_VERSION) $(BIN)/gofumpt-$(GOFUMPT_VERSION) $(BIN)/golangci-lint-$(GOLANGCI_LINT_VERSION) $(BIN)/govulncheck-$(GOVULNCHECK_VERSION)

### Development Commands
.PHONY: fmt
fmt:
	./nx affected -t fmt

.PHONY: lint
lint:
	./nx affected -t lint

.PHONY: test
test:
	./nx affected -t test

.PHONY: graph
graph:
	./nx graph

### Go Commands

.PHONY: fmt-go
fmt-go: $(BIN)/goimports-$(GOIMPORTS_VERSION) $(BIN)/gofumpt-$(GOFUMPT_VERSION)
	$(GO_ENV) $(GO) mod tidy
	$(BIN)/goimports -local $(GO_MODULE)  -w ./
	$(BIN)/gofumpt -l -w ./

.PHONY: lint-go
lint-go: $(BIN)/golangci-lint-$(GOLANGCI_LINT_VERSION) $(BIN)/govulncheck-$(GOVULNCHECK_VERSION)
	$(BIN)/golangci-lint run ./... -v -c $(ROOT)/go/.golangci.yaml ./...
	$(BIN)/govulncheck ./...

.PHONY: test-go
test-go:
	$(GO_ENV) $(GO) test -v ./...

Makefile により、make コマンドでカスタマイズしたフォーマット・リント・テストが実行できるようになる。

上記 Makefile を include した Makefile を各プロジェクトに作成し、project.jsonのコマンドを下記のように修正する。

go/app/bff/Makefile
include ../../../Makefile
go/app/bff/project.json
diff --git a/go/app/bff/project.json b/go/app/bff/project.json
index 7a9d997..a84c147 100644
--- a/go/app/bff/project.json
+++ b/go/app/bff/project.json
@@ -18,19 +18,19 @@
       }
     },
     "fmt": {
-      "command": "go fmt ./...",
+      "command": "make fmt-go",
       "options": {
         "cwd": "go/app/bff"
       }
     },
     "lint": {
-      "command": "go vet ./...",
+      "command": "make lint-go",
       "options": {
         "cwd": "go/app/bff"
       }
     },
     "test": {
-      "command": "go test -v ./...",
+      "command": "make test-go",
       "options": {
         "cwd": "go/app/bff"
       }

go/pkg/loggerも同様のファイル作成と変更を加える。

おわりに

以上で、Makefile によりどのようにフォーマットやリントするかなどを柔軟に設定できる Nx を用いた Go のモノレポを作成することができる。

ルートディレクトリで、make fmt などと実行することで main ブランチとの差分を検知して、変更があるモジュールのみにフォーマットなどをかけることができる。

2 の手順までは他言語でも同様であり、コマンドをカスタマイズすることで柔軟にフォーマットなどをカスタマイズすることができる。

ネクストアクションとして、キャッシュの最適化に関するドキュメントを記載しておく。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?