﻿# nbdev1 Migration


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## v3.3: Directive syntax updates

All directive spellings are now equivalent: `#| default_exp: core`,
`#| default_exp:core`, and `#| default_exp core` mean the same thing
(previously each directive worked with only one of the delimiters, and
the wrong one failed silently). A value of `true` is the same as no
value, so `#| hide: true` and `#| hide` are one directive. Existing
notebooks need no changes. Directives can also be stored in cell
metadata, as a dict under an `nbdev` key:
`{"nbdev": {"hide": "true", "eval": "false"}}` behaves the same as
writing `#| hide` and `#| eval: false` in the cell. Every metadata value
must be a str, matching the comment form’s raw-text values: JSON
`true`/`false` raise an error rather than silently differing from
`"true"`/`"false"`. Notebook-level directives such as `default_exp` can
go in the notebook metadata the same way. If a directive appears both in
metadata and as a comment, the comment wins.

There are breaking changes for extension authors only.
`nbdev.process.extract_directives` is gone: read directives from a
cell’s `directives` property, and use `cell.remove_directives()` to
strip them from the source. The `cell.directives_` dict has also changed
shape: keys never include a trailing colon, and each value is the
directive’s raw text rather than a list of whitespace-split tokens (`''`
for a bare directive), so `directives_['eval:'] == ['false']` becomes
`directives_['eval'] == 'false'`. Processor methods are unchanged: they
still receive whitespace-split positional args.

`nbdev-clean` (and therefore the git and Jupyter save hooks) now also
repairs structural problems by default, such as stray `outputs` attrs on
markdown cells, so a previously-invalid notebook shows a one-time diff
on its next clean (`--repair false` disables this). It also gains opt-in
migration flags which the hooks never trigger: `--dirs` respells comment
directives canonically, `--to_meta`/`--to_comments` move named
directives between comments and cell metadata, and `--nb_meta` moves
`default_exp` into notebook metadata.

`nbdev-install-hooks` now also registers a git diff driver, so
`git diff` shows notebook changes cell by cell as small source diffs
(see `nbdev.diff`). The merge and diff drivers install independently
(`--merge false`/`--diff false`), and `--globally` defines them
machine-wide in `~/.gitconfig` and your global git attributes file
instead of per repo. Both drivers are now named `jupyternotebook`, the
name nbdime also uses, so a committed `.gitattributes` activates
whichever notebook driver each collaborator has enabled; repos installed
under the old `nbdev-merge`/`nbdev-diff` names keep working, and
re-running `nbdev-install-hooks` moves them to the new name (the stale
attribute lines are harmless, as the appended new ones win). The new
`nbdev-diff` command renders the same output between any two refs
without touching your git config.

## 🛑**Jan 2026 Major Version Update – Breaking Change**🛑

**nbdev3 is here!** As many of you have been requesting, configuration
has moved from `settings.ini` to `pyproject.toml`, following modern
Python packaging standards ([PEP
621](https://peps.python.org/pep-0621/)). Your project metadata now
lives in the standard `[project]` section, while nbdev-specific settings
go in `[tool.nbdev]`.

**Migrating from nbdev2:** Run `nbdev-migrate-config` in your project
root to automatically convert your `settings.ini` to `pyproject.toml`
and update your GitHub Actions workflows to use nbdev3-compatible
versions. Your existing notebooks and code don’t need any changes.

**Status of this document:** The rest of this doc describes migration
from the *very* old nbdev1 to nbdev2, and is not likely to be relevant
to most folks who haven’t been frozen in tundra for the last few years.

## Nbdev2 - Initial setup

nbdev v2 is a new from-scratch rewrite of nbdev that’s not backwards
compatible. This page describes the changes you need to make to upgrade
your nbdev v1 repo to work with the new version. The steps shown here
should work on macOS or Linux (including Windows WSL)

The biggest change is that nbdev2 uses [Quarto](https://quarto.org/) to
generate your website, whereas nbdev1 used nbconvert and jekyll. You can
use all of Quarto’s features directly in nbdev, so checkout the Quarto
website to see all the amazing functionality it supports.

If you’ve pinned nbdev in `requirements.txt` or `pyproject.toml` (e.g
`nbdev<2`) remove the version pin. (*If you don’t know what this means,
then you don’t have it, so you can ignore this step*).

Install the latest version of nbdev by typing:

    pip install -U nbdev

or:

    conda install -c fastai nbdev

You may need to restart your terminal for the new commands to be visible
to your shell.

## Upgrade directives

nbdev has slightly changed how “directive comments” like `export` and
`default_exp` work, in order to align with how Quarto does things. Now,
instead of just adding a `#` to the start to indicate a directive (e.g
`#export`), you now need to use `#|` (e.g `#| export`). You can also
optionally add a space (e.g `#| export`).

To automatically upgrade your directives to the new format, run in the
root of your repo:

    nbdev-migrate

You should now test that you can export your module by running:

    nbdev-export

Note that `nbdev-export` replaces `nbdev_build_lib`. Run
`nbdev-export -h` to see the options you can pass to it (normally you
won’t need to pass any). To see a list of all the commands available in
nbdev2, run `nbdev-help`.

## Add and remove files

First set a variable with the name of your library, by running the
following (replacing “yourlib” with the name of your library’s
subdirectory)

    export LIBNAME=yourlib

Now run the following:

``` bash
git rm Makefile
git add $LIBNAME/_modidx.py
rm -rf docs
rm -f .gitconfig 
rm -f .git/hooks/post-merge

rm -f setup.py
curl -O https://raw.githubusercontent.com/fastai/nbdev3-template/master/styles.css
curl -O https://raw.githubusercontent.com/fastai/nbdev3-template/master/setup.py

cat >>.gitignore <<EOF
_docs/
_proc/
EOF
```

As you see above, we’ve remove the Makefile – that’s because all the
things done by `make` before are now handled by `nbdev` commands
directly.

<div>

> **Note**
>
> All documentation related files should be included in your `nbs_path`,
> and all paths should be relative to it. If you have set the `nbs_path`
> in your `pyproject.toml`, then copy your `styles.css` file inside of
> your `nbs_path` folder.

</div>

If you use GitHub Actions for continuous integration (CI) you can update
this to use nbdev too as follows:

``` bash
rm -f .github/workflows/main.yml
curl -O https://raw.githubusercontent.com/fastai/nbdev3-template/master/.github/workflows/test.yaml
curl -O https://raw.githubusercontent.com/fastai/nbdev3-template/master/.github/workflows/deploy.yaml
mv deploy.yaml test.yaml .github/workflows/
```

## Update directive names

A number of directives have changed names. We’ll use `perl` to fix them.
Run these lines in the root of your repo:

``` bash
find . -name '*.ipynb' -exec perl -pi -e 's/#\|\s*hide_input/#| echo: false/' {} +
find . -name '*.ipynb' -exec perl -pi -e 's/#\|\s*hide_output/#| output: false/' {} +
find . -name '*.ipynb' -exec perl -pi -e 's/#\|\s*skip/#| eval: false/' {} +
find . -name '*.ipynb' -exec perl -pi -e 's/from nbdev.export import notebook2script/from nbdev import nbdev_export/' {} +
find . -name '*.ipynb' -exec perl -pi -e 's/notebook2script/nbdev_export/' {} +
```

These change the following directives to use functionality built into
Quarto:

- `hide_input` –\> `echo: false`
- `hide_output` –\> `output: false`
- `skip` –\> `eval: false`

They also update the new location and name of the
[`nbdev_export`](https://nbdev.fast.ai/api/doclinks.html#nbdev_export)
python function.

If you have any notebooks that you’ve asked nbdev1 to skip (using
`all_slow`), you’ll need to add a [raw
cell](https://quarto.org/docs/tools/jupyter-lab.html#yaml-front-matter)
to the top of your notebook containing YAML frontmatter. The frontmatter
needs to include `skip_showdoc: true` to avoid running cells when
rendering docs, and `skip_exec: true` to skip this notebook when running
tests. E.g to do both, you would add a raw cell (or update your existing
frontmatter raw cell) to contain:

    ---
    skip_showdoc: true
    skip_exec: true
    ---

Or you can also add these flags in a markdown cell,

``` md
# title
> description

- skip_showdoc: true
- skip_exec: true
```

## Edit Workflow Permissions

Make sure your workflow permissions are set to “Read and write
permissions”, which you can find in Settings → Actions → General →
Workflow permissions:

<figure>
<img
src="https://user-images.githubusercontent.com/1483922/191905974-b9621c01-67ad-4ec3-bfe9-8beec9dbf8fb.png"
alt="GitHub Pages settings" />
<figcaption aria-hidden="true">GitHub Pages settings</figcaption>
</figure>

<div>

> **Important**
>
> Failure to set the correct permissions may result in an error message
> like this:
>
>      fatal: unable to access 'https://github.com/user/repo.git/': The requested URL returned error: 403
>       Error: Action failed with "The process '/usr/bin/git' failed with exit code 128"

</div>

## Edit GitHub Pages Permissions

At this point you will want to commit the files with the changes you
made to GitHub. Wait for GitHub Actions to run and pass. A new branch in
your repo will automatically be created called `gh-pages`. You want to
enable GitHub Pages to work off this branch by configuring your Pages to
settings to look like this:

Access this screen under Settings → Pages

- Select “Deploy from a branch” in the drop down list for Source.
- Specify `gh-pages` as the branch
- Specify the `/root` as the location
- Click save

<figure>
<img
src="https://user-images.githubusercontent.com/1483922/191906074-ec59420a-56e3-4801-b2b1-84acbd9a2725.png"
alt="Actions workflow permissions" />
<figcaption aria-hidden="true">Actions workflow permissions</figcaption>
</figure>

## Final steps

You should now edit `pyproject.toml`, and change `doc_path` from `docs`
to `_docs` in `[tool.nbdev]`, since that’s where nbdev2 will build your
website.

If you use a custom domain for your website, you should move your
`CNAME` file into the directory containing your notebooks.

Before pushing to GitHub, check that your website looks OK locally by
running:

    nbdev-preview

Now prepare to commit to GitHub:

    nbdev-prepare

You can now commit to GitHub as usual. Finally, update Github Pages by
clicking on the Settings tab in your repo, then click Pages on the left
side bar. Set “Source” to gh-pages branch and the /root folder.
