Skip to content

Commit

Permalink
Rework the asset generation setup
Browse files Browse the repository at this point in the history
Switching to sphinx-theme-builder simplifies the Python packaging
process, by integrating the NodeJS build step within the Python wheel
generation step. This creates much simpler semantics around installation
by avoiding the need for a separate NodeJS build step outside of the
Python installation process.

Grunt enables a simpler format for describing builds, making it much
easier to reason about and set up. This is especially relevant since
the builds are now executed with NodeJS 16 and most of the Gulp plugins
in the previous setup were not ready for Node 16.
  • Loading branch information
pradyunsg committed Nov 9, 2021
1 parent 0c4b34a commit e62e08d
Show file tree
Hide file tree
Showing 13 changed files with 6,233 additions and 5,820 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ jobs:
- uses: actions/setup-python@v2

- name: Install dependencies
run: |
pip install nox
npm install
run: pip install nox

- name: Generate documentation
run: nox -s docs
Expand Down
6 changes: 1 addition & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,8 @@ jobs:
- uses: actions/setup-python@v2

- name: Install dependencies
run: |
pip install build
npm install
run: pip install build

- name: Generate release distributions
run: ./node_modules/.bin/gulp build
- name: Generate final distribution
run: python -m build

Expand Down
67 changes: 67 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const sass = require("sass");

const asset_path = "src/furo/assets/";

const theme_static_path = "src/furo/theme/furo/static/";
const theme_css = theme_static_path + "styles/furo.css";
const extensions_css = theme_static_path + "styles/furo-extensions.css";
const theme_js = theme_static_path + "scripts/furo.js";

module.exports = function (grunt) {
grunt.initConfig({
sass: {
options: {
implementation: sass,
sourceMap: true,
},
dist: {
files: {
[theme_css]: asset_path + "styles/furo.sass",
[extensions_css]: asset_path + "styles/furo-extensions.sass",
},
},
},
autoprefixer: {
dist: {
files: {
[theme_css]: [theme_css],
},
},
},
browserify: {
dist: {
files: {
[theme_js]: asset_path + "scripts/**.js",
},
options: {
browserifyOptions: {
plugin: ["tinyify"],
debug: true,
},
},
},
},
exorcise: {
bundle: {
options: {},
files: {
[theme_js + ".map"]: theme_js,
},
},
},
concurrent: {
all: [
["browserify", "exorcise"],
["sass", "autoprefixer"],
],
},
});

grunt.loadNpmTasks("grunt-autoprefixer");
grunt.loadNpmTasks("grunt-browserify");
grunt.loadNpmTasks("grunt-concurrent");
grunt.loadNpmTasks("grunt-exorcise");
grunt.loadNpmTasks("grunt-sass");

grunt.registerTask("default", ["concurrent:all"]);
};
10 changes: 0 additions & 10 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,3 @@
"</a>!"
)
}

# Generate JS/CSS assets before running Sphinx on Read the Docs
if os.environ.get("READTHEDOCS") == "True":
subprocess.run(
[
"npx",
"gulp",
"build",
]
)
12 changes: 4 additions & 8 deletions docs/contributing/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,18 @@ The repository layout is pretty standard for a Python project, with a few quirks
- `furo/` -- main Sphinx theme folder
- `static/` -- contains compiles CSS and JS code.
- everything else here -- the underlying HTML templates.
- `gulpfile.js` -- for [Gulp](https://gulpjs.com/).
- `Gruntfile.js` -- for [Grunt](https://gruntjs.com/).
- `noxfile.py` -- for [nox](https://nox.readthedocs.io/).
- `package.json` -- for [NPM](https://npmjs.com/).
- `pyproject.toml` -- for Python Packaging.

## Theme build process

Furo's build process uses Gulp. Running `gulp build` in the repository root will compile the theme's CSS and JS assets (`src/furo/assets/`) into the correct final files (inside `src/furo/theme/furo/static`).
Furo's build process uses {pypi}`sphinx-theme-builder` and Grunt. Running `grunt` in the repository root will compile the theme's CSS and JS assets (`src/furo/assets/`) into the correct final files (inside `src/furo/theme/furo/static`).

When building the distributions for upload, `gulp build` is run once and the `src/furo/assets/` directory is excluded for the final distribution. Thus, _both_ the source distribution and wheel distribution do not contain the original source code for Furo and only contain the compiled SCSS and JS files.
When generating a wheel (eg: for upload, or for installing from a source distribution), `sphinx-theme-builder` will use {pypi}`nodeenv` to create an isolated NodeJS installation (using the system NodeJS version if it matches the requirements).

```{note}
It is not ideal that the version-controlled source tree is not installable using pip directly. There is a need for a `gulp build` command to be run between the clone and installation.
Things are set up this way due to the lack of a "build" step support in Flit. There is [an open issue for enhancement with a proposal awaiting feedback](https://github.com/takluyver/flit/issues/119#issuecomment-687779285).
```
Thus, the source distribution does not contain the compiled JS artifacts and wheel distribution does. Both contain the original SASS/JS source code for Furo. Using the SASS/JS source code of Furo is consider "unstable" under the stability policy.

## How stuff works

Expand Down
15 changes: 4 additions & 11 deletions docs/contributing/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This page describes the tooling used during development of this project. It also
This project uses the [GitHub Flow] for collaboration. The codebase contains Python code, [Jinja2]-based HTML pages, [Sass] stylesheets and Javascript code.

- [nox] is used for automating development tasks.
- [Gulp]-based build pipeline is used to process the Sass and Javascript files.
- [Grunt]-based build pipeline is used to process the Sass and Javascript files.
- [sphinx-autobuild] is used to provide live-reloading pages when working on the theme.
- [pre-commit] is used for running the linters.

Expand Down Expand Up @@ -83,23 +83,16 @@ Generate the documentation for Furo into the `build/docs` folder. This (mostly)

There are times when you might want to install the in-development version of Furo (mostly for testing that a fix actually does fix things).

Furo cannot be installed directly using pip with the Git repository directly. This is because the Git repository does not contained the compiled CSS/JS. The distributions on PyPI have the compiled assets (because they're platform agnostic and plain text).
This can be done by directly telling pip to install from Furo from GitHub. You likely want to install from a zip archive, to avoid cloning the entire Git history:

```sh
# Clone the repository
git clone https://github.com/pradyunsg/furo.git
cd furo
# Build the static assets
npm install
./node_modules/.bin/gulp build
# Install with pip
pip install .
pip install https://github.com/pradyunsg/furo/archive/refs/heads/main.zip
```

[github flow]: https://guides.github.com/introduction/flow/
[nox]: https://nox.readthedocs.io/en/stable/
[jinja2]: https://jinja.palletsprojects.com
[sass]: https://sass-lang.com
[gulp]: https://gulpjs.com
[grunt]: https://gruntjs.com/
[sphinx-autobuild]: https://github.com/executablebooks/sphinx-autobuild
[pre-commit]: https://pre-commit.com/
50 changes: 0 additions & 50 deletions gulpfile.js

This file was deleted.

118 changes: 39 additions & 79 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,82 +14,6 @@
#
# Helpers
#
def _install_this_project_with_flit(session, *, extras=None, editable=False):
session.install("flit")
args = []
if extras:
args.append("--extras")
args.append(",".join(extras))
if editable:
args.append("--pth-file" if os.name == "nt" else "--symlink")

session.run("flit", "install", "--deps=production", *args, silent=True)


#
# Development Sessions
#
@nox.session(name="docs-live", reuse_venv=True)
def docs_live(session):
if session.posargs:
docs_dir = session.posargs[0]
additional_dependencies = session.posargs[1:]
else:
docs_dir = "docs/"
additional_dependencies = ()

build_command = "npx gulp build"
_install_this_project_with_flit(session, extras=["doc"], editable=True)
session.install("sphinx-autobuild", *additional_dependencies)

with tempfile.TemporaryDirectory() as destination:
session.run(
"sphinx-autobuild",
# for sphinx-autobuild
"--port=0",
"--watch=src/",
f"--pre-build={build_command}",
r"--re-ignore=src/.*/theme/furo/static/.*\.(css|js)", # ignore the generated files
"--open-browser",
# for sphinx
"-b=dirhtml",
"-a",
docs_dir,
destination,
)


@nox.session(reuse_venv=True)
def docs(session):
# Generate relevant files prior to installation
session.run("npx", "gulp", "build", external=True)

_install_this_project_with_flit(session, extras=["doc"], editable=False)

# Generate documentation into `build/docs`
session.run("sphinx-build", "-b", "dirhtml", "-v", "docs/", "build/docs")


@nox.session(reuse_venv=True)
def lint(session):
session.install("pre-commit")

args = list(session.posargs)
args.append("--all-files")
if "CI" in os.environ:
args.append("--show-diff-on-failure")

session.run("pre-commit", "run", *args)


@nox.session
def test(session):
_install_this_project_with_flit(session, extras=["test"])

args = session.posargs or ["-n", "auto", "--cov", PACKAGE_NAME]
session.run("pytest", *args)


def _determine_versions(current_version, date):
"""Returns (version_in_release, version_after_release)"""
dev_num = int(current_version.rsplit(".dev", 1)[-1])
Expand Down Expand Up @@ -138,6 +62,38 @@ def get_release_versions(version_file):
return _determine_versions(current_version, date=datetime.date.today())


#
# Development Sessions
#
@nox.session(reuse_venv=True)
def docs(session):
# Generate relevant files prior to installation
session.install(".[doc]")

# Generate documentation into `build/docs`
session.run("sphinx-build", "-b", "dirhtml", "-v", "docs/", "build/docs")


@nox.session(reuse_venv=True)
def lint(session):
session.install("pre-commit")

args = list(session.posargs)
args.append("--all-files")
if "CI" in os.environ:
args.append("--show-diff-on-failure")

session.run("pre-commit", "run", *args)


@nox.session
def test(session):
session.install("-e", ".[test]")

args = session.posargs or ["-n", "auto", "--cov", PACKAGE_NAME]
session.run("pytest", *args)


@nox.session
def release(session):
version_file = f"src/{PACKAGE_NAME}/__init__.py"
Expand All @@ -147,7 +103,12 @@ def release(session):

release_version, next_version = get_release_versions(version_file)

session.install("flit", "twine", "release-helper", "keyring")
session.install(
"keyring",
"release-helper",
"sphinx-theme-builder[cli]",
"twine",
)

# Sanity Checks
session.run("release-helper", "version-check-validity", release_version)
Expand All @@ -168,8 +129,7 @@ def release(session):
)

# Build the package
session.run("npx", "gulp", "build", external=True)
session.run("flit", "build")
session.run("stb", "package")
session.run("twine", "check", *glob.glob("dist/*"))

# Tag the commit
Expand Down
Loading

0 comments on commit e62e08d

Please sign in to comment.