Skip to content

Commit

Permalink
Automatically generate contracts documentation
Browse files Browse the repository at this point in the history
We are creating a GH Actions workflow which automatically generates the
contracts documentation based on the functions and the NatSpec-format comments
in the Solidity files stored in the `contracts` folder in
`keep-network/tbtc-v2/solidity`. For certain workflow triggers, the generated
documentation gets published to the `api-docs.threshold.network` preview or main
GCP bucket.
Workflow triggers:

* `workflow_dispatch` - If the worklow gets triggered manually, it will just
  build the docs, but will not publish them to the GCP bucket.
* `pull_request` - If the workflow gets triggered by a PR, it will check if the
  changes in the PR modify the './.github/workflows/docs.yml' file or files in
  the './solidity/contracts' folder. If yes, the workflow will build the HTML
  documentation and publish it to the preview GCP bucket
  (`api-docs.threshold.network/solidity/<branch_name>`) and will publish a link
  to the generated file in the PR's comment.
* 'push' - If the workflow gets triggered by the push to the Solidity release
  branch (branch, which name starts with `releases/mainnet/solidity/`), the
  workflow will build the HTML documentation and publish it to the preview GCP
  bucket (`api-docs.threshold.network/solidity/<branch_name>`).
* `release` - If the workflow gets triggered by the Solidity release (release,
  which tag starts with `refs/tags/solidity/`), the workflow will build the HTML
  documentation and publish it to the main GCP bucket
  (`api-docs.threshold.network/solidity/`).

How HTML documentation gets created:
The documentation gets created based on the content of the Solidity files in
`keep-network/tbtc-v2/solidity`. We first transform it to the Markdown format,
then translate it to Asciidoc and finally, based on that, create an HTML output
file with contracts documentation, which we publish to the GCP bucket. Here is
the description of that process:
1. Before we run the Docgen tool generating Markdown files, we need to perform
   some slight changes to the input files, as some of the formatting we use in
   our `.sol` files is interpreted by Docgen not the way we would like or is not
   completely in line with the NatSpec format:
   - In many `@dev` clauses in the Solidity files we have the lists of
     requirements or other items which are constructed like this:
     ```
      /// @dev Requirements:
      /// - Item one. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
      ///   Nulla sed porttitor enim, sit amet venenatis enim. Donec tincidunt
      ///   auctor velit non eleifend. Nunc sit amet est non ligula condimentum
      ///   mattis.
      /// - Item two. Quisque purus massa, pellentesque in viverra tempus,
      ///   aliquet nec urna.
     ```
     This doesn't get recognized by Docgen as a list and is translated to
     regular text, which is displayed as one continuous line. But when the space
     characters between `///` and the text get removed from he `.sol` files, the
     lists are recognized correctly (without breaking interpretation of other
     features). That's why we decided to run `sed -i 's_///[[:blank:]]*_///_'`
     command on all Solidity files.
   - In one line of the `BitcoinTx.sol` file there's an incorrectly used `//`
     comment inside the `@dev` clause, which makes the Docgen think think that
     the clause is ending in the line with `//` (which results in wierd and
     incomplete display of the description of the `BitcoinTx` function). Due to
     that we are removing the problematic line from the file before running
     Docgen by running `sed -i ':a;N;$!ba;s_///\n//\n_///\n_g'` on the file.
2. Once the files are ready, we use the Docgen tool
   (https://github.com/OpenZeppelin/solidity-docgen) to generate Markdown
   `index.md` file with the contracts documentation. The tool is
   configured to
   - export the documentation of all contracts into one common file (default
     behavior),
   - place the generated file into `generated-docs` folder,
   - don't generate documentation for contracts in the `contracts/test` folder
     (as those are test/stub contracts which are not used on Mainnet),
   - use custom template for Markdown generation (based on the default
     https://github.com/OpenZeppelin/solidity-docgen/blob/master/src/themes/markdown/common.hbs
     template, but with removed italicisation of the `{{{natspec.dev}}}` element
     - because it wasn't working well with the lists in the `@dev` clauses).
3. Then we transform the `index.md` Markdown file into `tbtc-v2-contracts.adoc`
   Asciidoc file using tool called Kramdoc-AsciiDoc
   (https://github.com/asciidoctor/kramdown-asciidoc).
4. Next we inject two lines at the beginning of that file, to add the table of
   contens:
   ```
   :toc: left
   :toclevels: 1
   ```
5. Once that is done, we build the `tbtc-v2-contracts.html' HTML file using
   custom `thesis/asciidoctor-action` GH Acion. The action generates that file
   into `./asciidoc-out/solidity/generated-docs` folder.
6. We then use `thesis/gcp-storage-bucket-action` to push the content of that
   folder to the `api-docs.threshold.network/solidity` main GCP bucket (or
   `api-docs.threshold.network/solidity/<branch_name>` preview GCP bucket)

Alternative tools for Solidity documentation generation that were considered:
* Doxity (https://github.com/DigixGlobal/doxity) - I tried to use it, but hit a
  bunch of issues during installation - some of which I resolved, but got stuck
  on DigixGlobal/doxity#25. The project is not
  maintained anymore, latest commits are from year 2017. I figured that
  exploring the tool more and trying to find a workaround for the issue I
  observed may be a waste of time.
* Dodoc (https://github.com/primitivefinance/primitive-dodoc) - yields similar
  results as Docgen (although slightly worse - some @dev/@notice comments were
  not displayed at all), but does not have an option to output the documentation
  to one combo file - each .sol file generates a single .md file.
* Solidoc (https://github.com/binodnp/solidoc) - operates on .NET, I haven't
  explored that tool
* Remix IDE plugin (https://remix-ethdoc-plugin.readthedocs.io/en/latest/) - I
  don't use Remix IDE, also I don't think we could use this plugin in automation
* Hardhat Docgen (https://www.npmjs.com/package/hardhat-docgen) - I already had
  most of the work with my workflow done when I learned about this tool. I tried
  it on, but hit errors when running `hardhat-compile`. Couldn't find a quick
  solution, decided not to investigate further.
  • Loading branch information
michalinacienciala committed Mar 8, 2023
1 parent b7d5aa2 commit 682b018
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 2 deletions.
163 changes: 163 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
name: Solidity docs

on:
pull_request:
push:
branches:
- releases/mainnet/solidity/**
release:
types:
- "published"
workflow_dispatch:

jobs:
docs-detect-changes:
runs-on: ubuntu-latest
outputs:
path-filter: ${{ steps.filter.outputs.path-filter }}
steps:
- uses: actions/checkout@v3
if: github.event_name == 'pull_request'
- uses: dorny/paths-filter@v2
if: github.event_name == 'pull_request'
id: filter
with:
filters: |
path-filter:
- './solidity/contracts/**'
- './.github/workflows/docs.yml'
docs-html:
runs-on: ubuntu-latest
needs: docs-detect-changes
if: |
github.event_name != 'pull_request'
|| needs.docs-detect-changes.outputs.path-filter == 'true'
defaults:
run:
working-directory: ./solidity
steps:
- uses: actions/checkout@v3

# In this step we modify the format of the comments in the Solidity
# contracts files. We do that because our original formatting is not
# processed by Docgen in the way we would like.
# To nicely display lists (like the list of requirements) we need to
# remove multiple space chars after the `///` comment. We do that by
# executing `sed 's_///[[:blank:]]*_///_'` on all contracts files, which
# substitutes `///` + 0 or more spaces/tabs with just `///`.
# We also need to remove unnecessary `//` comment used in the `@dev`
# section of `BitcoinTx` documentation, which was causing problem with
# rendering of the `.md` file. We do that by executing
# `sed -i ':a;N;$!ba;s_///\n//\n_///\n_g'` on the problematic file. The
# command substitutes `///` + newline + `//` + newline with just `///` +
# newline and does this in a loop.

- name: Prepare contract files for further processing
run: |
find ./contracts \
-name "*.sol" \
-type f \
-exec sed -i 's_///[[:blank:]]*_///_' {} \;
sed -i ':a;N;$!ba;s_///\n//\n_///\n_g' ./contracts/bridge/BitcoinTx.sol
# TODO: Remove after testing
- name: Export artifacts
uses: actions/upload-artifact@master
with:
name: modified-contracts
path: ./solidity/contracts

# We need this step because the `@keep-network/tbtc` which we update in
# next steps has a dependency to `@summa-tx/[email protected]` package, which
# downloads one of its sub-dependencies via unathenticated `git://`
# protocol. That protocol is no longer supported. Thanks to this step
# `https://` is used instead of `git://`. This step also prevents the
# error during `yarn install --frozen-lockfile` step in case `git://` gets
# introduced to tbtc-v2's `yarn.lock`.
- name: Configure git to don't use unauthenticated protocol
run: git config --global url."https://".insteadOf git://

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Build Markdown docs # Outputs file to ./solidity/generated-docs/index.md
run: yarn run hardhat docgen

# TODO: Remove after testing
- name: Export artifacts
uses: actions/upload-artifact@master
with:
name: md
path: ./solidity/generated-docs

# We need Ruby to install Kramdown AsciiDoc
- name: Setup Ruby
uses: ruby/[email protected]
with:
ruby-version: '3.2'
bundler-cache: true

- name: Install Kramdown AsciiDoc
run: gem install kramdown-asciidoc

- name: Convert Markdown to Asciidoc
run: kramdoc --output=./generated-docs/tbtc-v2-contracts.adoc ./generated-docs/index.md

- name: Add Table of Contents
run: |
sed -i '1s/^/:toclevels: 1 \n/' ./generated-docs/tbtc-v2-contracts.adoc
sed -i '1s/^/:toc: left \n/' ./generated-docs/tbtc-v2-contracts.adoc
# TODO: Remove after testing
- name: Export artifacts
uses: actions/upload-artifact@master
with:
name: adoc
path: ./solidity/generated-docs

- name: Build HTML docs
id: html
uses: thesis/[email protected]
with:
files: './solidity/generated-docs/tbtc-v2-contracts.adoc'
args: '-a revdate=`date +%Y-%m-%d` --failure-level=ERROR'

# TODO: Remove after testing
- name: Export artifacts
uses: actions/upload-artifact@master
with:
name: asciidoc-out
path: ./asciidoc-out

- if: github.event_name == 'release' && startsWith(github.ref, 'refs/tags/solidity/')
name: Upload asciidocs
uses: thesis/[email protected]
with:
service-key: ${{ secrets.THRESHOLD_API_DOCS_UPLOADER_SERVICE_KEY_JSON_BASE64 }}
project: keep-prd
bucket-name: api-docs.threshold.network
bucket-path: solidity
build-folder: ${{ steps.html.outputs.asciidoctor-artifacts }}/solidity/generated-docs

- if: github.event_name == 'pull_request' || github.event_name == 'push'
name: Upload asciidocs preview
uses: thesis/[email protected]
with:
service-key: ${{ secrets.THRESHOLD_API_DOCS_UPLOADER_SERVICE_KEY_JSON_BASE64 }}
project: keep-prd
bucket-name: api-docs.threshold.network
bucket-path: solidity/${{ github.ref_name }}
build-folder: ${{ steps.html.outputs.asciidoctor-artifacts }}/solidity/generated-docs

- name: Post preview URL to PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v5
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Documentation preview uploaded to https://api-docs.threshold.network/solidity/${{ github.head_ref }}/tbtc-v2-contracts.html.'
})
33 changes: 33 additions & 0 deletions solidity/docgen-templates/common.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{{h}} {{name}}

{{#if signature}}
```solidity
{{{signature}}}
```
{{/if}}

{{{natspec.notice}}}

{{#if natspec.dev}}
{{{natspec.dev}}}
{{/if}}

{{#if natspec.params}}
{{h 2}} Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
{{#each params}}
| {{name}} | {{type}} | {{{joinLines natspec}}} |
{{/each}}
{{/if}}

{{#if natspec.returns}}
{{h 2}} Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
{{#each returns}}
| {{#if name}}{{name}}{{else}}[{{@index}}]{{/if}} | {{type}} | {{{joinLines natspec}}} |
{{/each}}
{{/if}}
6 changes: 6 additions & 0 deletions solidity/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import "hardhat-deploy"
import "@tenderly/hardhat-tenderly"
import "@typechain/hardhat"
import "hardhat-dependency-compiler"
import "solidity-docgen"

const ecdsaSolidityCompilerConfig = {
version: "0.8.17",
Expand Down Expand Up @@ -246,6 +247,11 @@ const config: HardhatUserConfig = {
typechain: {
outDir: "typechain",
},
docgen: {
outputDir: "generated-docs",
templates: "docgen-templates",
exclude: ['./test'],
},
}

export default config
1 change: 1 addition & 0 deletions solidity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"prettier-plugin-solidity": "^1.0.0-beta.19",
"solhint": "^3.3.7",
"solhint-config-keep": "github:keep-network/solhint-config-keep",
"solidity-docgen": "^0.6.0-beta.34",
"ts-node": "^10.4.0",
"typechain": "^6.1.0",
"typescript": "^4.5.4"
Expand Down
44 changes: 42 additions & 2 deletions solidity/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
resolved "https://registry.yarnpkg.com/@celo/utils/-/utils-0.1.11.tgz#c35e3b385091fc6f0c0c355b73270f4a8559ad38"
integrity sha512-i3oK1guBxH89AEBaVA1d5CHnANehL36gPIcSpPBWiYZrKTGGVvbwNmVoaDwaKFXih0N22vXQAf2Rul8w5VzC3w==
dependencies:
"@umpirsky/country-list" "git+https://github.com/umpirsky/country-list#05fda51"
"@umpirsky/country-list" "git://github.com/umpirsky/country-list#05fda51"
bigi "^1.1.0"
bignumber.js "^9.0.0"
bip32 "2.0.5"
Expand Down Expand Up @@ -7442,6 +7442,18 @@ [email protected]:
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==

handlebars@^4.7.7:
version "4.7.7"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
dependencies:
minimist "^1.2.5"
neo-async "^2.6.0"
source-map "^0.6.1"
wordwrap "^1.0.0"
optionalDependencies:
uglify-js "^3.1.4"

har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
Expand Down Expand Up @@ -9535,6 +9547,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==

neo-async@^2.6.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==

next-tick@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
Expand Down Expand Up @@ -11358,11 +11375,24 @@ solidity-ast@^0.4.15:
resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.32.tgz#ba613ca24c7c79007798033e8a0f32a71285f09e"
integrity sha512-vCx17410X+NMnpLVyg6ix4NMCHFIkvWrJb1rPBBeQYEQChX93Zgb9WB9NaIY4zpsr3Q8IvAfohw+jmuBzGf8OQ==

solidity-ast@^0.4.38:
version "0.4.45"
resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.45.tgz#37c1c17bd79123106fc69d94b4a8e9237ae8c625"
integrity sha512-N6uqfaDulVZqjpjru+KvMLjV89M3hesyr/1/t8nkjohRagFSDmDxZvb9viKV98pdwpMzs61Nt2JAApgh0fkL0g==

solidity-comments-extractor@^0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19"
integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==

solidity-docgen@^0.6.0-beta.34:
version "0.6.0-beta.34"
resolved "https://registry.yarnpkg.com/solidity-docgen/-/solidity-docgen-0.6.0-beta.34.tgz#f1766b13ea864ea71b8e727796d30a69ea90014a"
integrity sha512-igdGrkg8gT1jn+B2NwzjEtSf+7NTrSi/jz88zO7MZWgETmcWbXaxgAsQP4BQeC4YFeH0Pie1NsLP7+9qDgvFtA==
dependencies:
handlebars "^4.7.7"
solidity-ast "^0.4.38"

source-map-resolve@^0.5.0:
version "0.5.3"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
Expand Down Expand Up @@ -11415,7 +11445,7 @@ source-map@^0.5.6, source-map@^0.5.7:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=

source-map@^0.6.0:
source-map@^0.6.0, source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
Expand Down Expand Up @@ -12323,6 +12353,11 @@ typical@^5.2.0:
resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066"
integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==

uglify-js@^3.1.4:
version "3.17.4"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c"
integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==

ultron@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
Expand Down Expand Up @@ -13679,6 +13714,11 @@ word-wrap@^1.2.3, word-wrap@~1.2.3:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==

wordwrap@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==

wordwrapjs@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f"
Expand Down

0 comments on commit 682b018

Please sign in to comment.