Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warn on No Cache in CI #9116

Merged
merged 8 commits into from
Oct 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions errors/no-cache.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# No Cache Detected

#### Why This Error Occurred

A Next.js build was triggered in a continuous integration environment, but no cache was detected.

This results in slower builds and can hurt Next.js' persistent caching of client-side bundles across builds.

#### Possible Ways to Fix It

> **Note**: If this is a new project, or being built for the first time in your CI, you can ignore this error.
> However, you'll want to make sure it doesn't continue to happen and fix it if it does!

Configure Next.js' cache to be persisted across builds. Next.js stores its cache in the `.next/cache` directory.

Storing this folder across builds varies by CI provider. We've provided a list of a few common providers below.

**ZEIT Now**

Next.js caching is automatically configured for you. There's no action required on your part.

**CircleCI**

Edit your `save_cache` step in `.circleci/config.yml` to include `.next/cache`:

```yaml
steps:
- save_cache:
key: dependency-cache-{{ checksum "yarn.lock" }}
paths:
- ./node_modules
- ./.next/cache
```

If you do not have a `save_cache` key, please follow CircleCI's [documentation on setting up build caching](https://circleci.com/docs/2.0/caching/).

**Travis CI**

Add or merge the following into your `.travis.yml`:

```yaml
cache:
directories:
- $HOME/.cache/yarn
- node_modules
- .next/cache
```

**GitLab CI**

Add or merge the following into your `.gitlab-ci.yml`:

```yaml
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .next/cache/
```

**Netlify CI**

It is **not possible** to cache custom build files on Netlify. Please contact their support and request they support this behavior.

You can investigate using a 3rd party solution (e.g. [`cache-me-outside`](https://github.com/DavidWells/cache-me-outside)) to manually cache the Next.js output.

**AWS CodeBuild**

Add (or merge in) the following to your `buildspec.yml`:

```yaml
cache:
paths:
- 'node_modules/**/*' # Cache `node_modules` for faster `yarn` or `npm i`
- '.next/cache/**/*' # Cache Next.js for faster application rebuilds
```
29 changes: 25 additions & 4 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import chalk from 'chalk'
import ciEnvironment from 'ci-info'
import fs from 'fs'
import Worker from 'jest-worker'
import mkdirpOrig from 'mkdirp'
Expand Down Expand Up @@ -47,6 +48,7 @@ import getBaseWebpackConfig from './webpack-config'
import { getPageChunks } from './webpack/plugins/chunk-graph-plugin'
import { writeBuildId } from './write-build-id'

const fsAccess = promisify(fs.access)
const fsUnlink = promisify(fs.unlink)
const fsRmdir = promisify(fs.rmdir)
const fsStat = promisify(fs.stat)
Expand Down Expand Up @@ -83,15 +85,34 @@ export default async function build(dir: string, conf = null): Promise<void> {
)
}

const buildSpinner = createSpinner({
prefixText: 'Creating an optimized production build',
})

const config = loadConfig(PHASE_PRODUCTION_BUILD, dir, conf)
const { target } = config
const buildId = await generateBuildId(config.generateBuildId, nanoid)
const distDir = path.join(dir, config.distDir)

if (ciEnvironment.isCI) {
const cacheDir = path.join(distDir, 'cache')
const hasCache = await fsAccess(cacheDir)
.then(() => true)
.catch(() => false)

if (!hasCache) {
// Intentionally not piping to stderr in case people fail in CI when
// stderr is detected.
console.log(
chalk.bold.yellow(`Warning: `) +
chalk.bold(
`No build cache found. Please configure build caching for faster rebuilds. Read more: https://err.sh/next.js/no-cache`
)
)
console.log('')
}
}

const buildSpinner = createSpinner({
prefixText: 'Creating an optimized production build',
})

const telemetry = new Telemetry({ distDir })

const publicDir = path.join(dir, 'public')
Expand Down
33 changes: 33 additions & 0 deletions test/integration/build-warnings/test/index.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-env jest */
/* global jasmine */
import { join } from 'path'
import { remove } from 'fs-extra'
import { nextBuild, File, waitFor } from 'next-test-utils'

jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 1
Expand Down Expand Up @@ -42,4 +43,36 @@ describe('Build warnings', () => {

nextConfig.restore()
})

it('should not warn about missing cache in non-CI', async () => {
await remove(join(appDir, '.next'))

const { stdout } = await nextBuild(appDir, undefined, {
stdout: true,
env: {
CI: '',
CIRCLECI: '',
TRAVIS: '',
SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: ''
}
})
expect(stdout).not.toContain('no-cache')
})

it('should warn about missing cache in CI', async () => {
await remove(join(appDir, '.next'))

let { stdout } = await nextBuild(appDir, undefined, {
stdout: true,
env: { CI: '1' }
})
expect(stdout).toContain('no-cache')

// Do not warn after cache is present
;({ stdout } = await nextBuild(appDir, undefined, {
stdout: true,
env: { CI: '1' }
}))
expect(stdout).not.toContain('no-cache')
})
})