Repository: pcaversaccio/snekmate (language: python)

Issue:
👷‍♂️ Implement CI Tests for User Documentation `userdoc` and Developer Documentation `devdoc`
In order to ensure no compilation failures if development frameworks use the `userdoc` and/or `devdoc` CLI options, we should implement a proper CI test for those cases (after the Vyper `0.3.8` release since).

#### Example CLI Command

```bash
vyper -f userdoc,devdoc .\src\auth\AccessControl.vy
```


Discussion / hints:
A simple Python script could look like this:

```python
import os, glob

for filename in glob.glob("**/*.vy", recursive=True):
    os.system("vyper -f userdoc,devdoc %s" % (filename))
```

We should probably use the modern [`subprocess.run`](https://docs.python.org/3/library/subprocess.html) command instead:

```python
import subprocess, glob

for filename in glob.glob("**/*.vy", recursive=True):
    result = subprocess.run(
        ["vyper", "-f", "userdoc,devdoc", filename], capture_output=True, text=True
    )
    if result.stderr != "":
        raise Exception("Error compiling: " + filename)
    print("stdout:", result.stdout)
```
Another way would be to call `ape` directly as part of the CI.

Fix patch:
diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
index 4710fc82..2287e7e1 100644
--- a/.github/workflows/checks.yml
+++ b/.github/workflows/checks.yml
@@ -13,6 +13,10 @@ jobs:
       matrix:
         os:
           - ubuntu-latest
+        architecture:
+          - x64
+        python-version:
+          - 3.11
         node_version:
           - 18
 
@@ -44,6 +48,18 @@ jobs:
       - name: Prettier and lint
         run: yarn lint:check
 
+      - name: Setup Python
+        uses: actions/setup-python@v4
+        with:
+          python-version: ${{ matrix.python-version }}
+          architecture: ${{ matrix.architecture }}
+
+      - name: Check formatting with Black
+        uses: psf/black@stable
+        with:
+          options: "--check --verbose"
+          src: "./scripts"
+
   codespell:
     runs-on: ${{ matrix.os }}
     strategy:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9c096674..da25a368 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,8 @@
 
 ### ♻️ Refactoring
 
+- **General**
+  - All 🐍 snekmate contracts are now guaranteed to compile with the Vyper CLI flags `userdoc` and `devdoc`, and, if using the [Ape framework](https://github.com/ApeWorX/ape), with `ape compile`. ([#126](https://github.com/pcaversaccio/snekmate/pull/126))
 - **Extensions**
   - [`ERC4626`](https://github.com/pcaversaccio/snekmate/blob/v0.0.2/src/extensions/ERC4626.vy): Add `implements` interface `ERC20Detailed` and `ERC4626`. ([#125](https://github.com/pcaversaccio/snekmate/pull/125))
 - **Tokens**
@@ -28,6 +30,10 @@
 
 - All 🐍 snekmate contracts now target the Vyper version [`0.3.9`](https://github.com/vyperlang/vyper/releases/tag/v0.3.9). It is strongly recommended to upgrade accordingly your local Vyper version prior to using the 🐍 snekmate contracts. **Important:** The default EVM version since Vyper version `0.3.8` is set to `shanghai` (i.e. the EVM includes the [`PUSH0`](https://eips.ethereum.org/EIPS/eip-3855) instruction). If you intend to deploy on an EVM chain with no `PUSH0` support, you must compile the 🐍 snekmate contracts with the `--evm-version paris` option; e.g. `vyper --evm-version paris utils/Math.vy`. ([#122](https://github.com/pcaversaccio/snekmate/pull/122))
 
+### 👀 Full Changelog
+
+- [`v0.0.1...v0.0.2`](https://github.com/pcaversaccio/snekmate/compare/v0.0.1...v0.0.2)
+
 ## `0.0.1` (06-03-2023)
 
 ### 💥 New Features
diff --git a/ape-config.yaml b/ape-config.yaml
new file mode 100644
index 00000000..793d4989
--- /dev/null
+++ b/ape-config.yaml
@@ -0,0 +1,7 @@
+name: snekmate
+contracts_folder: src
+default_ecosystem: ethereum
+plugins:
+  - name: vyper
+vyper:
+  evm_version: shanghai
diff --git a/package.json b/package.json
index f62f1186..b627ca72 100644
--- a/package.json
+++ b/package.json
@@ -22,8 +22,8 @@
     "url": "https://github.com/pcaversaccio/snekmate/issues"
   },
   "scripts": {
-    "prettier:check": "prettier -c **/*.{md,sol,js,json,yml}",
-    "prettier:fix": "prettier -w **/*.{md,sol,js,json,yml}",
+    "prettier:check": "prettier -c **/*.{md,sol,js,json,yml,yaml}",
+    "prettier:fix": "prettier -w **/*.{md,sol,js,json,yml,yaml}",
     "solhint:check": "npx solhint '**/*.sol'",
     "solhint:fix": "npx solhint '**/*.sol' --fix",
     "lint:check": "yarn prettier:check && yarn solhint:check && eslint --ext .js .",
diff --git a/scripts/compile.py b/scripts/compile.py
new file mode 100644
index 00000000..a3dd78ae
--- /dev/null
+++ b/scripts/compile.py
@@ -0,0 +1,9 @@
+import subprocess, glob
+
+for filename in glob.glob("**/*.vy", recursive=True):
+    result = subprocess.run(
+        ["vyper", "-f", "userdoc,devdoc", filename], capture_output=True, text=True
+    )
+    if result.stderr != "":
+        raise Exception("Error compiling: " + filename)
+    print("stdout:", result.stdout)
diff --git a/src/auth/AccessControl.vy b/src/auth/AccessControl.vy
index 0332b972..192f7a28 100644
--- a/src/auth/AccessControl.vy
+++ b/src/auth/AccessControl.vy
@@ -15,16 +15,18 @@
 
         Roles can be used to represent a set of permissions. To restrict
         access to a function call, use the `external` function `hasRole`
-        or the `internal` function `_check_role`:
+        or the `internal` function `_check_role` (to avoid any NatSpec
+        parsing error, no `@` character is added to the visibility decorator
+        `@external` in the following examples; please add them accordingly):
         ```vy
-        @external
+        external
         def foo():
             assert self.hasRole[MY_ROLE][msg.sender], "AccessControl: account is missing role"
             ...
 
         OR
 
-        @external
+        external
         def foo():
             self._check_role(MY_ROLE, msg.sender)
             ...


Test patch:
diff --git a/.github/workflows/test-contracts.yml b/.github/workflows/test-contracts.yml
index 683995bc..38ee7747 100644
--- a/.github/workflows/test-contracts.yml
+++ b/.github/workflows/test-contracts.yml
@@ -35,6 +35,17 @@ jobs:
       - name: Install Vyper
         run: pip install vyper
 
+      - name: Check userdoc and devdoc compilation
+        run: python scripts/compile.py
+
+      - name: Setup Ape
+        uses: ApeWorX/github-action@v1
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Check Ape compilation
+        run: ape compile
+
       - name: Get Yarn cache directory path
         id: yarn-cache-dir-path
         run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
@@ -62,7 +73,7 @@ jobs:
           version: nightly
 
       - name: Show the Foundry CI config
-        run: "forge config"
+        run: forge config
         env:
           FOUNDRY_PROFILE: ci
 
@@ -72,7 +83,7 @@ jobs:
           FOUNDRY_PROFILE: ci
 
       - name: Show the Foundry default config
-        run: "forge config"
+        run: forge config
         env:
           FOUNDRY_PROFILE: default