Add docs and CI/CD workflows

This commit is contained in:
thePR0M3TH3AN
2025-06-19 11:58:39 -04:00
parent f3b27d1148
commit abe95a7edf
9 changed files with 156 additions and 40 deletions

3
.flake8 Normal file
View File

@@ -0,0 +1,3 @@
[flake8]
max-line-length = 120
ignore = E501,E741,E401,E402

23
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: CI
on:
push:
branches: [ main ]
pull_request:
jobs:
lint-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest InquirerPy rich
- name: Lint
run: flake8 voxvera tests
- name: Test
run: pytest

44
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
name: Release
on:
push:
tags:
- 'v*.*.*'
jobs:
build-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install build tools
run: |
python -m pip install --upgrade pip
pip install build pyinstaller
- name: Build wheel
run: python -m build --wheel --sdist
- name: Build binary
run: pyinstaller --onefile -n voxvera voxvera/cli.py
- name: Build Docker image
run: docker build -t ghcr.io/${{ github.repository_owner }}/voxvera:${{ github.ref_name }} .
- name: Login to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push Docker image
run: docker push ghcr.io/${{ github.repository_owner }}/voxvera:${{ github.ref_name }}
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
- name: Upload release assets
uses: softprops/action-gh-release@v1
with:
files: |
dist/*.whl
dist/*.tar.gz
dist/voxvera

View File

@@ -2,6 +2,17 @@
VoxVera provides scripts and templates for producing printable flyers with QR codes. These flyers link to content hosted through Tor and can also include a Nostr page. The project automates building the HTML, generating the QR codes, and copying all assets into a directory under `host/` so they can be served statically.
## TL;DR
```bash
git clone https://github.com/PR0M3TH3AN/VoxVera.git
cd VoxVera
./install.sh # use install.ps1 on Windows
voxvera quickstart
```
See [docs/usage.md](docs/usage.md) for detailed usage instructions.
## Quick Install
Run the installer to set up all dependencies and the `voxvera` CLI in one step.
@@ -103,45 +114,7 @@ The script updates the chosen config file, regenerates QR codes, obfuscates `ind
Additional documentation is available in the `src/` directory; see [src/README.md](src/README.md) for more details on the obfuscation scripts and additional usage notes.
## Step-by-Step
1. Edit `src/index-master.html` or `src/nostr-master.html` if you need custom content.
2. Run `voxvera init` and follow the prompts, or use `voxvera init --from-pdf path/to/form.pdf`.
3. Host the generated `host/<subdomain>` directory.
The `index.html` file fetches `config.json`, so the flyer must be served via a
local or remote web server rather than opened directly from disk. For a quick
test you can run `python3 -m http.server` inside the folder and then visit the
provided address.
Additional documentation, including step-by-step instructions and hosting guides, lives under the [docs](docs/) directory.
## Batch Import
Place configuration files in an `imports/` directory at the project root. Run
```bash
voxvera import
```
Each JSON file is copied to `src/config.json` and processed with
`voxvera build`. Existing folders under `host/` with the
same subdomain are removed before new files are written.
## Hosting with OnionShare
The folder under `host/<subdomain>` contains everything needed to serve the
flyer. Use the CLI to publish it over Tor:
The script now resolves the configuration and host paths internally, so it can
be invoked from any directory:
```bash
voxvera serve
```
The script launches `onionshare-cli` in persistent website mode, waits for the
generated onion URL, patches `config.json`, regenerates the QR codes and
obfuscated HTML, and then copies the updated files back into the `host`
directory. The onion address is printed when ready. Keep OnionShare running to
continue hosting.
`index.html` fetches `config.json` dynamically, so the flyer should be viewed
through a local or remote web server. For quick testing, run
`python3 -m http.server` in the folder and open the provided address instead of
loading the file directly.
This project is licensed under the [MIT License](./LICENSE).

17
docs/troubleshooting.md Normal file
View File

@@ -0,0 +1,17 @@
# Troubleshooting
This page collects common issues encountered when hosting or accessing flyers.
## Tor connectivity
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
## Firewall rules
- If `voxvera serve` fails to start OnionShare, verify that outbound connections on ports 9001 and 80 are permitted.
- Corporate or university firewalls can block the hidden service ports required by Tor.
## SELinux
- On SELinux-enabled distributions you may see `permission denied` errors when OnionShare writes to the `host` directory.
- Run `sudo chcon -Rt svirt_sandbox_file_t host` or disable SELinux enforcement for the folder.
If problems persist, consult the OnionShare and Tor documentation for more advanced configuration tips.

24
docs/usage.md Normal file
View File

@@ -0,0 +1,24 @@
# Detailed Usage
This guide covers common CLI workflows. See `docs/docker.md` for Docker instructions and `docs/templates.md` for available flyer templates.
## Step-by-Step
1. Edit `src/index-master.html` or `src/nostr-master.html` if you need custom content.
2. Run `voxvera init` and follow the prompts, or use `voxvera init --from-pdf path/to/form.pdf`.
3. Host the generated `host/<subdomain>` directory. The `index.html` file fetches `config.json`, so the flyer must be served via a local or remote web server rather than opened directly from disk. For a quick test you can run `python3 -m http.server` inside the folder and then visit the provided address.
## Batch Import
Place configuration files in an `imports/` directory at the project root and run:
```bash
voxvera import
```
Each JSON file is copied to `src/config.json` and processed with `voxvera build`. Existing folders under `host/` with the same subdomain are removed before new files are written.
## Hosting with OnionShare
Use the CLI to publish the flyer over Tor:
```bash
voxvera serve
```
The script launches `onionshare-cli` in persistent website mode, waits for the generated onion URL, patches `config.json`, regenerates the QR codes and obfuscated HTML, and then copies the updated files back into the `host` directory. Keep OnionShare running to continue hosting.
`index.html` fetches `config.json` dynamically, so the flyer should be viewed through a local or remote web server. For quick testing, run `python3 -m http.server` in the folder and open the provided address instead of loading the file directly.

18
pyproject.toml Normal file
View File

@@ -0,0 +1,18 @@
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "voxvera"
version = "0.1.0"
description = "VoxVera CLI utilities"
readme = "README.md"
authors = [{name = "VoxVera"}]
requires-python = ">=3.9"
dependencies = [
"InquirerPy",
"rich",
]
[project.scripts]
voxvera = "voxvera.cli:main"

13
tests/test_cli.py Normal file
View File

@@ -0,0 +1,13 @@
import os, sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from voxvera.cli import main
import pytest
def test_help(capsys):
with pytest.raises(SystemExit):
main(["-h"])
captured = capsys.readouterr()
assert "usage:" in captured.out

View File

@@ -164,7 +164,8 @@ def serve(config_path: str):
logfile = os.path.join(dir_path, 'onionshare.log')
proc = subprocess.Popen(['onionshare-cli', '--website', '--public', '--persistent', f'{dir_path}/.onionshare-session', dir_path], stdout=open(logfile, 'w'), stderr=subprocess.STDOUT)
try:
import time, re as _re
import time
import re as _re
onion_url = None
while onion_url is None:
time.sleep(1)