loading...

NixでNext.jsアプリをビルドする

__pandaman64__ profile image 井山梃子歴史館 ・2 min read

この記事では,Nixを使ってNext.jsアプリをビルドします.GitHubにこの記事の内容を実行したリポジトリを用意したのでそちらもご覧ください.
Nixを使う利点は適当にググってください.

まずはプロジェクトディレクトリを作成してgitリポジトリを初期化しましょう.

$ mkdir build-next-with-nix
$ cd build-next-with-nix/
$ git init
Initialized empty Git repository in /path/to/build-next-with-nix/.git/

つぎに,ビルドに必要なツールをインストールします.このプロジェクトでは以下のツールを使います.

  • gitignore.nix: .gitignoreに応じて必要なファイルをフィルタします.
  • nix-npm-buildpackage: npm(yarn)プロジェクトのビルドに使います.

nivを使ってインストールします.さくっと入れれてハッシュも自動で計算してくれるのはすごい!

$ niv init
Initializing
  Creating nix/sources.nix
  Creating nix/sources.json
  Importing 'niv' ...
  Adding package niv
    Writing new sources file
  Done: Adding package niv
  Importing 'nixpkgs' ...
  Adding package nixpkgs
    Writing new sources file
  Done: Adding package nixpkgs
Done: Initializing
$ niv add hercules-ci/gitignore.nix
Adding package gitignore.nix
  Writing new sources file
Done: Adding package gitignore.nix
$ niv add serokell/nix-npm-buildpackage
Adding package nix-npm-buildpackage
  Writing new sources file
Done: Adding package nix-npm-buildpackage

それではNextアプリを作っていきましょう.今回はTypeScript + ESLint + Jestのテンプレートを使うことにします.
まずはshell.nixを作ってnpmのインストールされた開発環境を立ち上げます.

# shell.nix
{ sources ? import ./nix/sources.nix }:
let
  pkgs = import sources.nixpkgs {};
in
  pkgs.mkShell {
    buildInputs = [
      pkgs.nodejs
    ];
  }
$ nix-shell
[nix-shell:~/build-next-with-nix]$

テンプレートを展開しましょう.これには少し時間がかかります.

$ npx create-next-app next-app --example https://github.com/vercel/next.js/tree/master/examples/with-typescript-eslint-jest

それでは,サーバを起動してみましょう!

$ cd next-app/
$ npm run dev

> with-typescript-eslint-jest@1.0.0 dev /home/pan/build-next-with-nix/next-app
> next dev

ready - started server on http://localhost:3000
event - compiled successfully

うまくいきましたね.
つぎに,このプロジェクトをNixを使ってビルドします.
まず,next.config.jsを使ってNextの設定を変更する必要があります.

// next.config.js
module.exports = {
  distDir: 'output',
  generateBuildId: async () => {
    return process.env.NEXT_BUILD_ID || 'next-build'
  },
}

ひとつ目の設定はNextの出力ディレクトリを変更しています.これは.gitignoreに含まれないディレクトリを指定しなければいけません1
次の設定はビルドを決定的にするために必要です.NextはデフォルトでビルドIDをランダムに生成するため,固定値に上書きしてあげる必要があります.ここではNixから環境変数経由で与えることにします.

次のdefault.nixをプロジェクトルートに作成してください.

# default.nix
{ sources ? import ./nix/sources.nix }:
let
  pkgs = import sources.nixpkgs {};
  bp = pkgs.callPackage sources.nix-npm-buildpackage {};
  inherit (import sources."gitignore.nix" { inherit (pkgs) lib; }) gitignoreSource;
  src = gitignoreSource ./next-app;
in
  bp.buildNpmPackage {
    inherit src;
    npmBuild = "npm run build";
    extraEnvVars = {
      NEXT_NIX_BUILD = "true";
      NEXT_BUILD_ID = builtins.hashString "sha256" "${src}";
    };
  }

ビルドしてみましょう!

$ nix-build

成功したら,resultフォルダからサーバを起動してみましょう.

$ cd result
$ ./bin/npm run start

> with-typescript-eslint-jest@1.0.0 start /nix/store/wmw2hld9cdfxy7kdggb4qaxk7ravl8a4-with-typescript-eslint-jest-1.0.0
> next start

ready - started server on http://localhost:3000

😁

Dockerイメージを作ることもできます.Dockerfile.nixを次の内容で作成します.

# Dockerfile.nix
{ sources ? import ./nix/sources.nix }:
let
  pkgs = import sources.nixpkgs {};
  contents = import ./default.nix { inherit sources; };
in
  pkgs.dockerTools.buildImage {
    name = "build-next-with-nix";
    tag = "latest";
    inherit contents;
    config = {
      Env = [ "NODE_ENV=production" ];
      Cmd = [ "/bin/npm" "run" "start" "--scripts-prepend-node-path" ];
    };
  }

ビルドしてみましょう.

$ nix-build Dockerfile.nix

作成したDockerイメージはdocker loadで読み込めます.

$ docker load -i result 
Loaded image: build-next-with-nix:latest

実行してみましょう!

$ docker run -p 3000:3000 build-next-with-nix

> with-typescript-eslint-jest@1.0.0 start /nix/store/231hhb2sbpqvcg1y4jsmhzycfwnhnvbn-with-typescript-eslint-jest-1.0.0
> next start

ready - started server on http://localhost:3000

😎👍


  1. なぜかは分かりません. 

Discussion

pic
Editor guide