Hacking

Short instructions that aim to help potential contributors.

Getting Started

Tool versions

The versions of Node.js and Ruby used to develop the project are pinned in the mise.toml file at the root of the repository. The GitHub Actions workflows read the same file, so you get the same versions as the CI.

We recommend using mise to install and activate these versions. From the root of the repository, run:

$ mise install

If you manage your tools differently, make sure to use the versions declared in mise.toml.

Development workflow

  • Set up the Asciidoctor reveal.js plugin in development mode

  • Modify the converter, written in plain Ruby, in lib/asciidoctor_revealjs/converter.rb

There is no build step: each AsciiDoc node is converted by a matching convert_<node> method.

  • Render slides with your changes using:

    $ bundle exec asciidoctor-revealjs <source.adoc>

Each convert_<node> method runs in the context of the node being converted (self is the node), so you can call node methods and inspect them directly. The Asciidoctor API documentation describes what is available on each node.

Manual Tests

In order to help troubleshoot issues and test syntax improvements, test/converter-testkit/fixtures/standalone/ holds minimalist, real AsciiDoc presentations. You can convert them and load the result in a browser to check if asciidoctor-revealjs behaves as expected; see the "Custom tests" section further down.

$ bundle exec rake examples:convert

The converted .html files are written next to their .adoc source in test/converter-testkit/fixtures/standalone/, ready to open in a browser.

Asciidoctor API’s gotchas

Attribute inheritance

The attr and attr? methods inherit by default. That means if they don’t find the attribute defined on the Node, they look on the document.

You only want to enable inheritance if you intend to allow an attribute of the same name to be controlled globally. That might be good for configuring transitions. For instance:

= My Slides
:transition-speed: fast

== First Slide

However, there may be attributes that you don’t want to inherit. If that’s the case, you generally use the form:

attr('name', nil, false)

The second parameter value is the default attribute value, which is nil by default.

Merge / Review policy

Any non-trivial change should be integrated in main via a pull-request. This gives the community a chance to participate and helps write better code because it encourages people to review their own patches.

Pull requests should come from personal forks in order not to clutter the upstream repository.

Wait time

Once a pull request is submitted, let it sit for 24-48 hours for small changes. If you get positive feedback you can merge before the sitting time frame. If you don’t get feedback, just merge after the sitting time frame.

Larger changes should sit longer at around a week. Positive feedback or no feedback should be handled like for small changes.

Breaking changes should sit until a maintainer has reviewed and approved them.

Remember that this is a slower-moving project, since people are not designing slides every day.

Work-in-progress pull-requests

Letting the maintainers know that you are working on a feature or a fix is useful. Early communication often saves time-consuming mistakes or avoids duplicated effort. We encourage contributors to communicate with us early.

Branches on forks of this project are not very visible to maintainers as much as pull requests (PR). For this reason we recommend to open a draft PR to let everyone know that you are working on a specific topic. Now, instead of prepending “WIP”, we recommend using GitHub “draft pull request” feature instead.

“needs review” label

You can apply that label to a pull request that is complete and ready for review.

Makes triaging easier.

Node package

Test locally

The converter is written natively in JavaScript (in js/src), so there is no build step. In order to test the Node package, create a tarball of the project.

$ npm pack

That last command will produce a file named asciidoctor-reveal.js-<version>.tgz in the working directory.

Then, create a test project adjacent to the clone of the asciidoctor-reveal.js repository:

$ mkdir test-project
$ cd test-project

Now, install the dependencies from the tarball:

$ npm i --save ../asciidoctor-reveal.js/asciidoctor-reveal.js-<version>.tgz
The relative portion of the last command is where you are installing the local asciidoctor-reveal.js version from.

Then proceed as documented in the README.adoc.

RubyGem package

Test a local asciidoctor-revealjs version

The converter is written in plain Ruby (in lib/asciidoctor_revealjs/converter.rb), so there is no build step.

In a clean directory besides the asciidoctor-reveal.js repository, create the following Gemfile file:

source 'https://rubygems.org'
gem 'asciidoctor-revealjs', :path => '../asciidoctor-reveal.js'

Then run:

$ bundle --path=.bundle/gems --binstubs=.bundle/.bin

Update dependencies

Update one dependency at a time by bumping its version manually, then install it and run the tests.

Avoid bundle update and npm update: they may upgrade many (transitive) dependencies at once, which makes regressions much harder to track down.

For a Ruby dependency, bump its version in asciidoctor-revealjs.gemspec (or in the Gemfile), then:

bundle install
bundle exec rubocop
bundle exec rake test
bundle exec rake examples:convert

For a JavaScript dependency, bump its version in package.json, then:

npm install
npm test
npm run examples

Release process

Prepare the release

  1. Make sure that the changelog is up-to-date: all the entries for the upcoming release should be listed under the ## main (unreleased) section of CHANGELOG.md

    You can get the list of all contributors using git (don’t forget to replace %prev-version-tag% with the previous tag name):

    $ git log %prev-version-tag%.. --format="%aN" --reverse | perl -e 'my %dedupe; while (<STDIN>) { print unless $dedupe{$_}++}' | sort
  2. Make sure that the highlight plugin code embed in lib/asciidoctor_revealjs/highlightjs.rb is up-to-date with the version of reveal.js

  3. Make sure the changelog is committed and pushed to main.

Release

The release is fully automated and triggered from GitHub Actions.

Both registries use trusted publishing (OIDC), so no API key or token needs to be configured: publishing to rubygems.org relies on RubyGems trusted publishing and publishing to npmjs.com relies on npm trusted publishers. See the .github/workflows/release.yml file for details.

To trigger a release:

  1. Go to the Release workflow on GitHub Actions.

  2. Click Run workflow, keep the main branch selected, and enter the version to release (e.g., 5.3.0 or 5.3.0-beta.1).

  3. Click Run workflow.

The workflow bumps the version in package.json and lib/asciidoctor_revealjs/version.rb, rolls the main (unreleased) changelog section into a dated %version% (%date%) section, and runs the tests. Only if the tests pass does it commit, tag (v%version%) and push to main, then publish to rubygems.org and npmjs.com and build the single executable binaries (one per platform) attached to the GitHub release.

The main branch must allow the github-actions[bot] to push the release commits and tag. If branch protection rules reject the push, the release will fail at the Commit, tag and push step.

Once the release workflow has been completed:

In addition, you can submit pull requests to downstream project:

  • submit a pull request downstream to update Asciidoctor reveal.js version in the Asciidoctor Docker Container

  • submit a pull request downstream to update AsciidoctorJ reveal.js version

    • modify gradle.properties and asciidoctorj-revealjs/gradle.properties. If the reveal.js version has been updated,
      also amend WhenBackendIsRevealJs.java in both tests and itests folders and README.adoc in: github.com/asciidoctor/asciidoctorj-reveal.js

Prepare next version

This step is optional, manual, and run locally — the next version isn’t always a minor bump (it could be a patch or a breaking major release), so it’s not computed automatically.

Pick the next -dev version (e.g., after releasing 5.3.0, that’s usually 5.4.0-dev, but use 5.3.1-dev or 6.0.0-dev if that fits better), then:

$ node tasks/version.js next <version>
$ git commit -a -m "Begin development on next release <version>"
$ git push origin main

This sets <version> in package.json and lib/asciidoctor_revealjs/version.rb, and its major.minor in docs/antora.yml.

Maintenance branch

You can (not must) create a maintenance branch from a tag, only when needed (e.g., to backport a fix to a previous line). Name it {major}.{minor}.x, without a v prefix (e.g., 5.2.x): the release workflow only accepts main or a branch matching this pattern, and derives the npm dist-tag from it (latest-5-2 for 5.2.x).

To release from a maintenance branch, select it instead of main when you run the release workflow.

New major version

In case of a new major version, you must follow the steps described for a Maintenance branch.

Ruby and asciidoc-converter-testkit tests

Running tests

We recommend tests to be run with a fresh installation of all dependencies in a local folder that won’t affect your Ruby install (a .bundle/ in this directory):

$ bundle --path=.bundle/gems --binstubs=.bundle/.bin

The fixture corpus runs through asciidoc-converter-testkit (the asciidoc-converter-testkit-cli npm package), so also install the JavaScript dependencies:

$ npm install

Then you can execute the full test suite (unit tests and the fixture corpus) with:

$ bundle exec rake test

bundle exec rake test:unit runs just the unit tests, skipping the fixture corpus — useful on JRuby/TruffleRuby, where the corpus’s hundreds of sequential bundle exec spawns turn a couple of minutes into ten or more.

Fixture corpus

rake testkit:test runs two invocations of asciidoc-converter-testkit against the Ruby converter, since a converter flag it needs differs between them (see tasks/testkit.rake for the detail):

  • the bundled 37 generic AsciiDoc-construct families (test/converter-testkit/expected/generic/<family>/<case>.html), converted embedded;

  • the project’s own standalone family (test/converter-testkit/expected/standalone/<case>.html), converted standalone and narrowed to the relevant fragment via each case’s <name>.config.json select sidecar.

Everything under test/converter-testkit/ belongs to the asciidoc-converter-testkit corpus: fixtures/ holds the input side (matching asciidoc-converter-testkit’s own --fixtures flag); expected/ holds the expected output — deliberately not also named "fixtures", to keep the two apart at a glance.

rake testkit:update regenerates the expected files from the current converter’s output — review the diff before committing.

Custom tests

Presentations in test/converter-testkit/fixtures/standalone/ double as a browsable gallery of real presentations and as regression tests. A file with no matching expected file under test/converter-testkit/expected/standalone/ is simply not (yet) covered by a regression test — that’s fine, it’s still a valid example.

The release notes showcase under release-notes/ (see below) is a separate, unrelated directory: it is not part of this test corpus at all.

Netlify Integration

On every commit or PR, the Netlify service will convert the release notes (release-notes/) into slides and host the resulting pages on its platform where it will be visible by anyone. It hosts the converted HTML files, reveal.js framework and static content like images and CSS. This integration will allow us to preview PRs and demo features to users (source and converted result). See the release_notes:publish rake task in tasks/publish.rake and the netlify.toml configuration file.

Sensitive Data Accidentally Pushed Out

Only content that is copied into the public/ directory will be published on the Netlify site. If, by accident, something sensitive is copied over there, delete it, rewrite the git history to remove the sensitive information and force push the branch. Reach out to our Netlify integration contact to make sure that deployed branches were rebuilt and no longer contain the sensitive information.

Integration Contact

Main Contact: @ggrossetie

Also, in order to stay with a free plan, only the following people have control over our Netlify integration: @ggrossetie, @mojavelinux and @graphitefriction.