mirror of
https://github.com/PR0M3TH3AN/VoxVera.git
synced 2026-10-27 17:50:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da987d028f | ||
|
|
031d84b8b2 | ||
|
|
024680fbce | ||
|
|
858f58d19c | ||
|
|
06c8565b94 | ||
|
|
a4bb4dce23 | ||
|
|
66db78c950 | ||
|
|
f0caf2323e | ||
|
|
c5573075b6 | ||
|
|
6313db3f05 | ||
|
|
3ae321fcbd | ||
|
|
a017bf876f | ||
|
|
193345e616 | ||
|
|
3165effa8a | ||
|
|
6efd049851 | ||
|
|
0fdba633f4 | ||
|
|
b2b1e75ec6 | ||
|
|
4df0829c80 |
No files matched your search
@@ -10,7 +10,10 @@ jobs:
|
||||
build:
|
||||
name: Build for ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
include:
|
||||
@@ -38,6 +41,12 @@ jobs:
|
||||
pip install -r requirements.txt
|
||||
pip install pyinstaller
|
||||
|
||||
- name: Download Tor Binaries
|
||||
shell: bash
|
||||
run: |
|
||||
chmod +x scripts/download_tor.sh
|
||||
./scripts/download_tor.sh
|
||||
|
||||
- name: Build with PyInstaller
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -51,8 +60,55 @@ jobs:
|
||||
--name "${{ matrix.artifact_name }}" \
|
||||
voxvera/cli.py
|
||||
|
||||
- name: Build .deb (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
chmod +x packaging/build_deb.sh
|
||||
./packaging/build_deb.sh dist/${{ matrix.artifact_name }}
|
||||
|
||||
- name: Build AppImage (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
mkdir -p voxvera/resources/bin
|
||||
cp dist/${{ matrix.artifact_name }} voxvera/resources/bin/voxvera-linux-x86_64
|
||||
chmod +x packaging/build_appimage.sh
|
||||
./packaging/build_appimage.sh
|
||||
|
||||
- name: Build Flatpak (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y flatpak flatpak-builder
|
||||
chmod +x packaging/build_flatpak.sh
|
||||
./packaging/build_flatpak.sh || echo "Flatpak build failed"
|
||||
|
||||
- name: Build .dmg (macOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
chmod +x packaging/build_dmg.sh
|
||||
./packaging/build_dmg.sh dist/${{ matrix.artifact_name }}
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.asset_name }}
|
||||
path: dist/${{ matrix.artifact_name }}
|
||||
path: |
|
||||
dist/${{ matrix.artifact_name }}
|
||||
voxvera/resources/bin/*.deb
|
||||
voxvera/resources/bin/*.dmg
|
||||
voxvera/resources/bin/*.AppImage
|
||||
voxvera/resources/bin/*.flatpak
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
files: |
|
||||
dist/${{ matrix.artifact_name }}
|
||||
voxvera/resources/bin/*.deb
|
||||
voxvera/resources/bin/*.dmg
|
||||
voxvera/resources/bin/*.AppImage
|
||||
voxvera/resources/bin/*.flatpak
|
||||
fail_on_unmatched_files: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,62 +0,0 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
|
||||
jobs:
|
||||
build-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
- 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 \
|
||||
--add-data "voxvera/locales:voxvera/locales" \
|
||||
--add-data "voxvera/src:voxvera/src" \
|
||||
--add-data "voxvera/templates:voxvera/templates" \
|
||||
--add-data "voxvera/resources:voxvera/resources" \
|
||||
--add-data "requirements.txt:." \
|
||||
--name voxvera-linux \
|
||||
voxvera/cli.py
|
||||
- name: Create AppImage
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y wget
|
||||
bash packaging/build_appimage.sh
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
OWNER_LC=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
|
||||
docker build -t ghcr.io/${OWNER_LC}/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: |
|
||||
OWNER_LC=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
|
||||
docker push ghcr.io/${OWNER_LC}/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-linux
|
||||
dist/VoxVera.AppImage
|
||||
+8
-2
@@ -46,6 +46,12 @@ voxvera/src/qrcode-tear-offs.png
|
||||
# PyInstaller
|
||||
*.spec
|
||||
dist_bin/
|
||||
voxvera/resources/bin/voxvera-*
|
||||
site/download/voxvera-portable.zip
|
||||
voxvera/resources/bin/*
|
||||
!voxvera/resources/bin/.gitkeep
|
||||
site/download/*.zip
|
||||
site/download/*.exe
|
||||
site/download/*.deb
|
||||
site/download/*.dmg
|
||||
site/download/*.AppImage
|
||||
site/download/*.flatpak
|
||||
site/download/voxvera-linux
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
# VoxVera Release Process
|
||||
|
||||
**⚠️ ATTENTION AI AGENTS (CRITICAL WORKFLOW):**
|
||||
Do **NOT** automatically package, tag, or push a new release after completing a set of changes. Releases should **ONLY** be triggered when the user explicitly requests one (e.g., "Package a release now" or "Let's release this").
|
||||
Instead of releasing immediately, you must append a summary of your completed work to the `UPCOMING_RELEASE.md` file. This allows development to span multiple context sessions and ensures that new releases contain a substantial amount of value. When a release is finally requested, use the contents of `UPCOMING_RELEASE.md` to format the GitHub Release notes, and then clear the document for the next cycle.
|
||||
|
||||
This document outlines the mandatory steps to perform before every release to ensure code quality, cross-language integrity, and professional distribution assets.
|
||||
|
||||
## 1. Pre-Flight Checks (Quality Assurance)
|
||||
|
||||
Before any release, the codebase must pass all internal quality checks **without taking any shortcuts**. All tests must be rigorous, use real data where appropriate, and passing them should be a true mark of quality.
|
||||
|
||||
### Run Tests
|
||||
Ensure all core CLI, E2E, and Integrity tests pass:
|
||||
```bash
|
||||
pytest tests/
|
||||
```
|
||||
*Note: The `test_integrity.py` check is critical as it verifies that all 14+ language locales have synchronized keys and all binary bundles are 100% self-contained.*
|
||||
|
||||
### Run Linting
|
||||
Ensure the code adheres to style guidelines (no trailing whitespace, proper spacing, etc.):
|
||||
```bash
|
||||
# Using flake8 (excluding vendored dependencies)
|
||||
flake8 voxvera/ --exclude=voxvera/vendor/
|
||||
```
|
||||
|
||||
## 2. Update & Generate Localized Assets
|
||||
|
||||
Before releasing, you must ensure that **all documentation accurately reflects the new features and functions** introduced in the release.
|
||||
|
||||
All documentation and site templates must be synchronized with the translation system. This ensures that any changes to documentation are reflected across all 14+ supported languages.
|
||||
|
||||
### Synchronize Documentation
|
||||
If you have modified any documentation templates in `docs/templates/` (which you should, to cover new features), you **must** use the translation system to regenerate the localized documentation before release:
|
||||
```bash
|
||||
# Regenerate docs/ folders for all languages from templates and locales
|
||||
python3 -m voxvera.cli build-docs
|
||||
```
|
||||
|
||||
### Synchronize Site Assets
|
||||
The main `site/` directory (including QRs, HTML, and portable Zips) must also be refreshed to include the latest binary names and locale strings:
|
||||
```bash
|
||||
# Refresh site/ directory
|
||||
python3 -m voxvera.cli build-site
|
||||
```
|
||||
|
||||
## 3. Build Distribution Binaries
|
||||
|
||||
Standardized binaries and AppImages must be generated for the current platform.
|
||||
|
||||
### Standard Binary
|
||||
Generates an architecture-specific binary (e.g., `voxvera-linux-x86_64`) in `voxvera/resources/bin/`.
|
||||
```bash
|
||||
./scripts/build_binaries.sh
|
||||
```
|
||||
|
||||
### AppImage (Linux)
|
||||
Wraps the standard binary into a self-contained AppImage.
|
||||
```bash
|
||||
./packaging/build_appimage.sh
|
||||
```
|
||||
|
||||
### Flatpak (Linux)
|
||||
Generates a standalone `.flatpak` bundle. Requires `flatpak-builder`.
|
||||
```bash
|
||||
./packaging/build_flatpak.sh
|
||||
```
|
||||
|
||||
### Debian Package (Linux)
|
||||
Generates a `.deb` package for Debian/Ubuntu systems.
|
||||
```bash
|
||||
./packaging/build_deb.sh
|
||||
```
|
||||
|
||||
### DMG (macOS)
|
||||
Generates a `.dmg` installer for macOS (must be run on macOS).
|
||||
```bash
|
||||
./packaging/build_dmg.sh
|
||||
```
|
||||
|
||||
## 4. Release Execution
|
||||
|
||||
1. **Version Bump:** Update `__version__` in `voxvera/__init__.py` and `version` in `pyproject.toml`.
|
||||
2. **Commit & Tag:**
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "chore: release vX.Y.Z"
|
||||
git tag -a vX.Y.Z -m "VoxVera vX.Y.Z"
|
||||
git push origin main --tags
|
||||
```
|
||||
## 5. Automated Releases (GitHub Actions)
|
||||
|
||||
The repository is configured with a **Build Standalone Binaries** workflow that automatically runs when a new tag (`v*`) is pushed. This workflow:
|
||||
- Builds native executables for **Linux**, **Windows (.exe)**, and **macOS**.
|
||||
- Automatically attaches these binaries to the corresponding GitHub Release.
|
||||
|
||||
This ensures that Windows users receive a professional `voxvera-windows.exe` rather than having to rely on `.bat` or `.sh` scripts.
|
||||
Whitespace-only changes.
+28
-20
@@ -4,31 +4,39 @@ This guide covers common CLI workflows. See `docs/docker.md` for Docker instruct
|
||||
|
||||
## Prerequisites
|
||||
|
||||
VoxVera is designed to be highly portable and requires minimal system dependencies.
|
||||
VoxVera is designed to be highly portable. You can run it directly from the source code on any platform with Python installed.
|
||||
|
||||
### 1. Standalone Binaries (Recommended)
|
||||
You can download standalone, zero-dependency binaries for your operating system:
|
||||
- **Linux:** [voxvera-linux](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-linux)
|
||||
- **Windows:** [voxvera-windows.exe](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-windows.exe)
|
||||
- **macOS:** [voxvera-macos](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-macos)
|
||||
|
||||
These binaries include everything needed to run VoxVera (except `onionshare-cli`).
|
||||
|
||||
### 2. One-Line Installer
|
||||
Alternatively, install via our automated script:
|
||||
### 1. Automated Install (Recommended)
|
||||
The easiest way to install VoxVera is using our cross-platform installer script:
|
||||
|
||||
**Linux / macOS:**
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.sh | bash
|
||||
```
|
||||
|
||||
### 3. Manual Python Install
|
||||
If you prefer to run from source:
|
||||
|
||||
```bash
|
||||
pipx install 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'
|
||||
sudo apt install tor onionshare-cli # Debian/Ubuntu
|
||||
**Windows (PowerShell):**
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
### 2. Manual Source Install
|
||||
If you have downloaded the source code ZIP:
|
||||
|
||||
1. **Extract** the ZIP file.
|
||||
2. **Open a terminal** in the extracted folder.
|
||||
3. **Run the setup script**:
|
||||
- **Linux/macOS:** `./setup.sh`
|
||||
- **Windows:** `.\voxvera-install.sh` (or run `python -m pip install .`)
|
||||
|
||||
## Uninstallation
|
||||
|
||||
If you wish to remove VoxVera from your system, you can use the provided uninstall scripts from the source directory. This will safely remove the CLI tool and any downloaded dependencies.
|
||||
|
||||
- **Linux / macOS:** Run `./uninstall.sh`
|
||||
- **Windows (PowerShell):** Run `.\uninstall.ps1`
|
||||
|
||||
*Note: Uninstallation will not delete your generated flyers or your `.onion` keys stored in the `~/voxvera-exports` or local `host/` folders.*
|
||||
|
||||
## Step-by-Step
|
||||
|
||||
1. **Initialize:** Run `voxvera init` and follow the prompts. You will be asked to select your language first.
|
||||
@@ -79,10 +87,10 @@ Features:
|
||||
|
||||
To ensure VoxVera remains accessible even if central repositories are censored, every flyer acts as a mirror for the tool.
|
||||
|
||||
When you host a flyer, the **"Download"** button on the landing page provides a `voxvera-portable.zip` containing:
|
||||
When you host a flyer, the **"Download Source Code"** button on the landing page provides a `voxvera-source.zip` containing:
|
||||
- The full source code and all supported languages.
|
||||
- All Python dependencies (pre-vendored).
|
||||
- Cross-platform Tor binaries.
|
||||
- Installation and setup scripts for all platforms.
|
||||
- Complete documentation and templates.
|
||||
|
||||
This allows anyone who scans your flyer to become a new distributor of the VoxVera tool.
|
||||
|
||||
|
||||
+28
-20
@@ -4,31 +4,39 @@ This guide covers common CLI workflows. See `docs/docker.md` for Docker instruct
|
||||
|
||||
## Prerequisites
|
||||
|
||||
VoxVera is designed to be highly portable and requires minimal system dependencies.
|
||||
VoxVera is designed to be highly portable. You can run it directly from the source code on any platform with Python installed.
|
||||
|
||||
### 1. Standalone Binaries (Recommended)
|
||||
You can download standalone, zero-dependency binaries for your operating system:
|
||||
- **Linux:** [voxvera-linux](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-linux)
|
||||
- **Windows:** [voxvera-windows.exe](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-windows.exe)
|
||||
- **macOS:** [voxvera-macos](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-macos)
|
||||
|
||||
These binaries include everything needed to run VoxVera (except `onionshare-cli`).
|
||||
|
||||
### 2. One-Line Installer
|
||||
Alternatively, install via our automated script:
|
||||
### 1. Automated Install (Recommended)
|
||||
The easiest way to install VoxVera is using our cross-platform installer script:
|
||||
|
||||
**Linux / macOS:**
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.sh | bash
|
||||
```
|
||||
|
||||
### 3. Manual Python Install
|
||||
If you prefer to run from source:
|
||||
|
||||
```bash
|
||||
pipx install 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'
|
||||
sudo apt install tor onionshare-cli # Debian/Ubuntu
|
||||
**Windows (PowerShell):**
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
### 2. Manual Source Install
|
||||
If you have downloaded the source code ZIP:
|
||||
|
||||
1. **Extract** the ZIP file.
|
||||
2. **Open a terminal** in the extracted folder.
|
||||
3. **Run the setup script**:
|
||||
- **Linux/macOS:** `./setup.sh`
|
||||
- **Windows:** `.\voxvera-install.sh` (or run `python -m pip install .`)
|
||||
|
||||
## Uninstallation
|
||||
|
||||
If you wish to remove VoxVera from your system, you can use the provided uninstall scripts from the source directory. This will safely remove the CLI tool and any downloaded dependencies.
|
||||
|
||||
- **Linux / macOS:** Run `./uninstall.sh`
|
||||
- **Windows (PowerShell):** Run `.\uninstall.ps1`
|
||||
|
||||
*Note: Uninstallation will not delete your generated flyers or your `.onion` keys stored in the `~/voxvera-exports` or local `host/` folders.*
|
||||
|
||||
## Step-by-Step
|
||||
|
||||
1. **Initialize:** Run `voxvera init` and follow the prompts. You will be asked to select your language first.
|
||||
@@ -79,10 +87,10 @@ Features:
|
||||
|
||||
To ensure VoxVera remains accessible even if central repositories are censored, every flyer acts as a mirror for the tool.
|
||||
|
||||
When you host a flyer, the **"הורד כלי וקוד מקור"** button on the landing page provides a `voxvera-portable.zip` containing:
|
||||
When you host a flyer, the **"הורד כלי וקוד מקור"** button on the landing page provides a `voxvera-source.zip` containing:
|
||||
- The full source code and all supported languages.
|
||||
- All Python dependencies (pre-vendored).
|
||||
- Cross-platform Tor binaries.
|
||||
- Installation and setup scripts for all platforms.
|
||||
- Complete documentation and templates.
|
||||
|
||||
This allows anyone who scans your flyer to become a new distributor of the VoxVera tool.
|
||||
|
||||
|
||||
Vendored
+28
-20
@@ -4,31 +4,39 @@ This guide covers common CLI workflows. See `docs/docker.md` for Docker instruct
|
||||
|
||||
## Prerequisites
|
||||
|
||||
VoxVera is designed to be highly portable and requires minimal system dependencies.
|
||||
VoxVera is designed to be highly portable. You can run it directly from the source code on any platform with Python installed.
|
||||
|
||||
### 1. Standalone Binaries (Recommended)
|
||||
You can download standalone, zero-dependency binaries for your operating system:
|
||||
- **Linux:** [voxvera-linux](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-linux)
|
||||
- **Windows:** [voxvera-windows.exe](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-windows.exe)
|
||||
- **macOS:** [voxvera-macos](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-macos)
|
||||
|
||||
These binaries include everything needed to run VoxVera (except `onionshare-cli`).
|
||||
|
||||
### 2. One-Line Installer
|
||||
Alternatively, install via our automated script:
|
||||
### 1. Automated Install (Recommended)
|
||||
The easiest way to install VoxVera is using our cross-platform installer script:
|
||||
|
||||
**Linux / macOS:**
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.sh | bash
|
||||
```
|
||||
|
||||
### 3. Manual Python Install
|
||||
If you prefer to run from source:
|
||||
|
||||
```bash
|
||||
pipx install 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'
|
||||
sudo apt install tor onionshare-cli # Debian/Ubuntu
|
||||
**Windows (PowerShell):**
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
### 2. Manual Source Install
|
||||
If you have downloaded the source code ZIP:
|
||||
|
||||
1. **Extract** the ZIP file.
|
||||
2. **Open a terminal** in the extracted folder.
|
||||
3. **Run the setup script**:
|
||||
- **Linux/macOS:** `./setup.sh`
|
||||
- **Windows:** `.\voxvera-install.sh` (or run `python -m pip install .`)
|
||||
|
||||
## Uninstallation
|
||||
|
||||
If you wish to remove VoxVera from your system, you can use the provided uninstall scripts from the source directory. This will safely remove the CLI tool and any downloaded dependencies.
|
||||
|
||||
- **Linux / macOS:** Run `./uninstall.sh`
|
||||
- **Windows (PowerShell):** Run `.\uninstall.ps1`
|
||||
|
||||
*Note: Uninstallation will not delete your generated flyers or your `.onion` keys stored in the `~/voxvera-exports` or local `host/` folders.*
|
||||
|
||||
## Step-by-Step
|
||||
|
||||
1. **Initialize:** Run `voxvera init` and follow the prompts. You will be asked to select your language first.
|
||||
@@ -79,10 +87,10 @@ Features:
|
||||
|
||||
To ensure VoxVera remains accessible even if central repositories are censored, every flyer acts as a mirror for the tool.
|
||||
|
||||
When you host a flyer, the **"{{t('web.download_button')}}"** button on the landing page provides a `voxvera-portable.zip` containing:
|
||||
When you host a flyer, the **"{{t('web.download_button')}}"** button on the landing page provides a `voxvera-source.zip` containing:
|
||||
- The full source code and all supported languages.
|
||||
- All Python dependencies (pre-vendored).
|
||||
- Cross-platform Tor binaries.
|
||||
- Installation and setup scripts for all platforms.
|
||||
- Complete documentation and templates.
|
||||
|
||||
This allows anyone who scans your flyer to become a new distributor of the VoxVera tool.
|
||||
|
||||
|
||||
+30
-13
@@ -1,21 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
if [ ! -f dist/voxvera-linux ]; then
|
||||
echo "Run PyInstaller first" >&2
|
||||
# Ensure we are in project root
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
ARCH=$(uname -m)
|
||||
BINARY="voxvera/resources/bin/voxvera-linux-$ARCH"
|
||||
|
||||
if [ ! -f "$BINARY" ]; then
|
||||
echo "Binary not found: $BINARY. Run scripts/build_binaries.sh first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
APPDIR=dist/AppDir
|
||||
APPDIR=build/AppDir
|
||||
mkdir -p "$APPDIR/usr/bin"
|
||||
cp dist/voxvera-linux "$APPDIR/usr/bin/voxvera"
|
||||
cp "$BINARY" "$APPDIR/usr/bin/voxvera"
|
||||
chmod +x "$APPDIR/usr/bin/voxvera"
|
||||
mkdir -p "$APPDIR/usr/lib/voxvera/resources"
|
||||
cp -r voxvera/resources/tor "$APPDIR/usr/lib/voxvera/resources/"
|
||||
# also bundle Tor for the Electron GUI
|
||||
mkdir -p gui/electron/voxvera/resources
|
||||
cp -r voxvera/resources/tor gui/electron/voxvera/resources/
|
||||
|
||||
# AppRun symlink (Crucial for AppImage to launch)
|
||||
ln -sf usr/bin/voxvera "$APPDIR/AppRun"
|
||||
chmod +x "$APPDIR/AppRun"
|
||||
|
||||
# Desktop file
|
||||
cat > "$APPDIR/voxvera.desktop" <<EOD
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
@@ -23,13 +29,24 @@ Name=VoxVera
|
||||
Exec=voxvera
|
||||
Icon=voxvera
|
||||
Categories=Utility;
|
||||
Terminal=true
|
||||
EOD
|
||||
|
||||
touch "$APPDIR/voxvera.png"
|
||||
# Use a generic icon since we don't have one
|
||||
# (AppImageTool requires an icon file to exist)
|
||||
# We will copy a qrcode as a placeholder if nothing else exists
|
||||
cp site/qrcode-content.png "$APPDIR/voxvera.png"
|
||||
|
||||
wget -q https://github.com/AppImage/AppImageKit/releases/latest/download/appimagetool-x86_64.AppImage -O appimagetool
|
||||
# Download appimagetool
|
||||
echo "Downloading appimagetool..."
|
||||
wget -q https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage -O appimagetool
|
||||
chmod +x appimagetool
|
||||
./appimagetool --appimage-extract-and-run "$APPDIR" dist/VoxVera.AppImage
|
||||
|
||||
# Run appimagetool
|
||||
echo "Running appimagetool..."
|
||||
export ARCH
|
||||
mkdir -p voxvera/resources/bin
|
||||
./appimagetool --appimage-extract-and-run "$APPDIR" voxvera/resources/bin/VoxVera-x86_64.AppImage
|
||||
rm appimagetool
|
||||
|
||||
echo "AppImage created at dist/VoxVera.AppImage"
|
||||
echo "AppImage created at voxvera/resources/bin/VoxVera-x86_64.AppImage"
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# Ensure we are in project root
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
ARCH=$(uname -m)
|
||||
# Debian uses amd64 instead of x86_64
|
||||
if [ "$ARCH" == "x86_64" ]; then
|
||||
DEB_ARCH="amd64"
|
||||
elif [ "$ARCH" == "aarch64" ] || [ "$ARCH" == "arm64" ]; then
|
||||
DEB_ARCH="arm64"
|
||||
else
|
||||
DEB_ARCH="$ARCH"
|
||||
fi
|
||||
BINARY=${1:-"voxvera/resources/bin/voxvera-linux-$ARCH"}
|
||||
|
||||
if [ ! -f "$BINARY" ]; then
|
||||
echo "Binary not found: $BINARY." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
VERSION=$(grep "__version__" voxvera/__init__.py | cut -d '"' -f 2)
|
||||
PKG_DIR="build/voxvera_${VERSION}_${DEB_ARCH}"
|
||||
|
||||
echo "Building .deb package for version $VERSION ($DEB_ARCH)..."
|
||||
|
||||
rm -rf "$PKG_DIR"
|
||||
mkdir -p "$PKG_DIR/usr/bin"
|
||||
mkdir -p "$PKG_DIR/DEBIAN"
|
||||
|
||||
cp "$BINARY" "$PKG_DIR/usr/bin/voxvera"
|
||||
chmod +x "$PKG_DIR/usr/bin/voxvera"
|
||||
|
||||
# Copy control file and update version/arch
|
||||
cp packaging/deb/DEBIAN/control "$PKG_DIR/DEBIAN/control"
|
||||
sed -i "s/^Version: .*/Version: $VERSION/" "$PKG_DIR/DEBIAN/control"
|
||||
sed -i "s/^Architecture: .*/Architecture: $DEB_ARCH/" "$PKG_DIR/DEBIAN/control"
|
||||
|
||||
# Build the package
|
||||
mkdir -p voxvera/resources/bin
|
||||
dpkg-deb --build "$PKG_DIR" "voxvera/resources/bin/voxvera_${VERSION}_${DEB_ARCH}.deb"
|
||||
|
||||
echo "Debian package created at voxvera/resources/bin/voxvera_${VERSION}_${DEB_ARCH}.deb"
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# Ensure we are in project root
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# This script is intended to be run on macOS
|
||||
if [ "$(uname)" != "Darwin" ]; then
|
||||
echo "Error: .dmg creation requires macOS." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ARCH=$(uname -m)
|
||||
BINARY=${1:-"voxvera/resources/bin/voxvera-macos-$ARCH"}
|
||||
|
||||
if [ ! -f "$BINARY" ]; then
|
||||
echo "Binary not found: $BINARY." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$(grep "__version__" voxvera/__init__.py | cut -d '"' -f 2)
|
||||
DMG_NAME="VoxVera_${VERSION}_${ARCH}.dmg"
|
||||
STAGING_DIR="build/dmg_staging"
|
||||
|
||||
echo "Building .dmg package for version $VERSION ($ARCH)..."
|
||||
|
||||
rm -rf "$STAGING_DIR"
|
||||
mkdir -p "$STAGING_DIR"
|
||||
|
||||
# For a CLI tool, we just put the binary in the DMG.
|
||||
# In the future, we could create an .app bundle if there's a GUI.
|
||||
cp "$BINARY" "$STAGING_DIR/voxvera"
|
||||
chmod +x "$STAGING_DIR/voxvera"
|
||||
|
||||
# Create the DMG
|
||||
mkdir -p voxvera/resources/bin
|
||||
hdiutil create -volname "VoxVera $VERSION" -srcfolder "$STAGING_DIR" -ov -format UDZO "voxvera/resources/bin/$DMG_NAME"
|
||||
|
||||
echo "DMG created at voxvera/resources/bin/$DMG_NAME"
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# Ensure we are in project root
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
ARCH=$(uname -m)
|
||||
BINARY="voxvera/resources/bin/voxvera-linux-$ARCH"
|
||||
|
||||
if [ ! -f "$BINARY" ]; then
|
||||
echo "Binary not found: $BINARY. Run scripts/build_binaries.sh first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v flatpak-builder &> /dev/null; then
|
||||
echo "flatpak-builder not found. Please install it (e.g., sudo apt install flatpak-builder)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building Flatpak package..."
|
||||
|
||||
BUILD_DIR=build/flatpak-build
|
||||
REPO_DIR=build/flatpak-repo
|
||||
MANIFEST=packaging/flatpak/org.voxvera.VoxVera.yml
|
||||
|
||||
# Clean previous
|
||||
rm -rf "$BUILD_DIR" "$REPO_DIR"
|
||||
|
||||
# Build
|
||||
flatpak-builder --force-clean --repo="$REPO_DIR" "$BUILD_DIR" "$MANIFEST"
|
||||
|
||||
# Generate a standalone .flatpak file
|
||||
mkdir -p voxvera/resources/bin
|
||||
flatpak build-bundle "$REPO_DIR" voxvera/resources/bin/VoxVera-x86_64.flatpak org.voxvera.VoxVera
|
||||
|
||||
echo "Flatpak bundle created at voxvera/resources/bin/VoxVera-x86_64.flatpak"
|
||||
@@ -0,0 +1,9 @@
|
||||
Package: voxvera
|
||||
Version: 0.1.4
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
Maintainer: VoxVera <[email protected]>
|
||||
Description: VoxVera CLI utilities
|
||||
Anonymous message-spreading and flyer-generation tool.
|
||||
Depends: libc6
|
||||
@@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=VoxVera
|
||||
Comment=Anonymous message-spreading and flyer-generation tool
|
||||
Exec=voxvera
|
||||
Icon=org.voxvera.VoxVera
|
||||
Categories=Utility;Network;
|
||||
Terminal=true
|
||||
StartupWMClass=voxvera
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 436 B |
@@ -0,0 +1,27 @@
|
||||
app-id: org.voxvera.VoxVera
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: '23.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
command: voxvera
|
||||
finish-args:
|
||||
- --share=network
|
||||
- --socket=fallback-x11
|
||||
- --socket=wayland
|
||||
- --device=dri
|
||||
- --share=ipc
|
||||
- --filesystem=xdg-run/tor:ro # If using external Tor
|
||||
modules:
|
||||
- name: voxvera
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- install -D voxvera /app/bin/voxvera
|
||||
- install -D org.voxvera.VoxVera.desktop /app/share/applications/org.voxvera.VoxVera.desktop
|
||||
- install -D org.voxvera.VoxVera.png /app/share/icons/hicolor/512x512/apps/org.voxvera.VoxVera.png
|
||||
sources:
|
||||
- type: file
|
||||
path: ../../voxvera/resources/bin/voxvera-linux-x86_64
|
||||
dest-filename: voxvera
|
||||
- type: file
|
||||
path: org.voxvera.VoxVera.desktop
|
||||
- type: file
|
||||
path: org.voxvera.VoxVera.png
|
||||
+3
-2
@@ -14,7 +14,7 @@ voxvera = ["templates/**", "src/**", "resources/**", "locales/**", "resources/bi
|
||||
|
||||
[project]
|
||||
name = "voxvera"
|
||||
version = "0.1.3"
|
||||
version = "0.1.8"
|
||||
description = "VoxVera CLI utilities"
|
||||
readme = "README.md"
|
||||
authors = [{name = "VoxVera"}]
|
||||
@@ -26,9 +26,10 @@ dependencies = [
|
||||
"Pillow",
|
||||
"jsmin",
|
||||
"htmlmin2",
|
||||
"pypdf",
|
||||
"wcwidth",
|
||||
"onionshare-cli",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
test = [
|
||||
"pytest",
|
||||
|
||||
@@ -6,3 +6,5 @@ jsmin
|
||||
htmlmin2
|
||||
pypdf
|
||||
wcwidth
|
||||
onionshare-cli
|
||||
|
||||
@@ -19,17 +19,24 @@ mkdir -p voxvera/resources/bin
|
||||
|
||||
echo "Building VoxVera binary for $(uname)..."
|
||||
|
||||
echo "Ensuring Tor binaries are downloaded..."
|
||||
bash scripts/download_tor.sh
|
||||
|
||||
# Use python module to call pyinstaller
|
||||
PY_CMD="python3 -m PyInstaller"
|
||||
|
||||
# Build the binary
|
||||
ARCH=$(uname -m)
|
||||
OS_NAME=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
BINARY_NAME="voxvera-$OS_NAME-$ARCH"
|
||||
|
||||
$PY_CMD --onefile \
|
||||
--add-data "voxvera/locales:voxvera/locales" \
|
||||
--add-data "voxvera/src:voxvera/src" \
|
||||
--add-data "voxvera/templates:voxvera/templates" \
|
||||
--add-data "voxvera/resources:voxvera/resources" \
|
||||
--add-data "requirements.txt:." \
|
||||
--name "voxvera-$(uname -s | tr '[:upper:]' '[:lower:]')" \
|
||||
--name "$BINARY_NAME" \
|
||||
--distpath voxvera/resources/bin \
|
||||
voxvera/cli.py
|
||||
|
||||
|
||||
+13
-6
@@ -1,12 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="${VERSION:-13.5.18}"
|
||||
BASE_URL="https://www.torproject.org/dist/torbrowser"
|
||||
VERSION="${VERSION:-15.0.7}"
|
||||
BASE_URL="https://dist.torproject.org/torbrowser"
|
||||
|
||||
ARCH=$(uname -m)
|
||||
if [ "$ARCH" == "aarch64" ] || [ "$ARCH" == "arm64" ]; then
|
||||
TOR_ARCH="aarch64"
|
||||
else
|
||||
TOR_ARCH="x86_64"
|
||||
fi
|
||||
|
||||
case "$(uname -s)" in
|
||||
Linux*) PLATFORM=linux; ARCHIVE="tor-expert-bundle-linux-x86_64-${VERSION}.tar.gz"; EXE=tor;;
|
||||
Darwin*) PLATFORM=mac; ARCHIVE="tor-expert-bundle-macos-x86_64-${VERSION}.tar.gz"; EXE=tor;;
|
||||
Linux*) PLATFORM=linux; ARCHIVE="tor-expert-bundle-linux-${TOR_ARCH}-${VERSION}.tar.gz"; EXE=tor;;
|
||||
Darwin*) PLATFORM=mac; ARCHIVE="tor-expert-bundle-macos-${TOR_ARCH}-${VERSION}.tar.gz"; EXE=tor;;
|
||||
MINGW*|MSYS*|CYGWIN*) PLATFORM=win; ARCHIVE="tor-expert-bundle-windows-x86_64-${VERSION}.tar.gz"; EXE=tor.exe;;
|
||||
*) echo "Unsupported OS" >&2; exit 1;;
|
||||
esac
|
||||
@@ -26,10 +33,10 @@ DEST="$(dirname "$0")/../voxvera/resources/tor/$PLATFORM"
|
||||
mkdir -p "$DEST"
|
||||
|
||||
TOR_BIN=$(find "$TMPDIR" -type f -name "$EXE" | head -n 1)
|
||||
OBFS_BIN=$(find "$TMPDIR" -type f -name "obfs4proxy*" | head -n 1)
|
||||
OBFS_BIN=$(find "$TMPDIR" -type f \( -name "lyrebird*" -o -name "obfs4proxy*" \) | head -n 1)
|
||||
|
||||
if [[ -z "$TOR_BIN" || -z "$OBFS_BIN" ]]; then
|
||||
echo "Failed to locate tor or obfs4proxy in archive" >&2
|
||||
echo "Failed to locate tor or lyrebird/obfs4proxy in archive" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Executable
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
+8
-14
@@ -378,7 +378,7 @@
|
||||
</style>
|
||||
<script>
|
||||
// VoxVera Multi-Language Engine
|
||||
const locales = {"ar": {"meta": {"language_name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629", "flag": "\ud83c\uddf8\ud83c\udde6", "direction": "rtl"}, "web": {"tear_off_tor_browser": "\u0627\u0633\u062a\u062e\u062f\u0645 \u0645\u062a\u0635\u0641\u062d Tor", "tear_off_desc": "\u0644\u0644\u0645\u0633\u0627\u0639\u062f\u0629 \u0641\u064a \u0646\u0634\u0631 \u0647\u0630\u0647 \u0627\u0644\u0631\u0633\u0627\u0644\u0629\u060c \u0627\u0646\u062a\u0642\u0644 \u0625\u0644\u0649", "tear_off_footer": "\u0627\u0646\u0642\u0631 \u0641\u0648\u0642 \u0627\u0644\u0632\u0631 \u0623\u062f\u0646\u0627\u0647 \u0644\u0644\u0637\u0628\u0627\u0639\u0629", "built_with": "\u0628\u0646\u064a \u0628\u0648\u0627\u0633\u0637\u0629", "print_button": "\u0627\u0637\u0628\u0639 \u0627\u0644\u0645\u0646\u0634\u0648\u0631", "download_button": "\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0623\u062f\u0627\u0629 \u0648\u0627\u0644\u0643\u0648\u062f", "dl_portable": "Portable (Source + Dependencies)", "dl_linux": "Linux Standalone", "dl_windows": "Windows Standalone (.exe)", "dl_macos": "macOS Standalone", "dl_source": "Source Code Only (.zip)"}, "landing": {"title": "\u0633\u0631\u064a \u0644\u0644\u063a\u0627\u064a\u0629", "subtitle": "~~\u0644\u0627~~ \u0644\u0644\u0646\u0634\u0631", "headline": "\u0639\u0645\u0644\u064a\u0629 VOX VERA", "content": "\u062a\u062d\u0631\u0631 \u0645\u0646 \u0627\u0644\u0631\u0642\u0627\u0628\u0629 \u0645\u0639 VoxVera. \u0623\u062f\u0627\u0629 \u062a\u0633\u0648\u064a\u0642 \u0639\u0635\u0627\u0628\u0627\u062a \u0645\u062c\u0647\u0648\u0644\u0629 \u0648\u0646\u0634\u0631 \u0631\u0633\u0627\u0626\u0644. \u0633\u0648\u0627\u0621 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0623\u0648 \u0641\u064a \u0627\u0644\u0639\u0627\u0644\u0645 \u0627\u0644\u0645\u0627\u062f\u064a\u060c \u064a\u0645\u0643\u0646\u0647\u0627 \u062a\u0645\u0643\u064a\u0646\u0643 \u0645\u0646 \u0646\u0634\u0631 \u0623\u0641\u0643\u0627\u0631\u0643 \u0628\u062c\u0631\u0623\u0629\u060c \u0645\u062d\u0645\u064a\u0629 \u0628\u0625\u062e\u0641\u0627\u0621 \u0627\u0644\u0647\u0648\u064a\u0629 \u0627\u0644\u0643\u0627\u0645\u0644 (\u0625\u0630\u0627 \u0643\u0646\u062a \u062a\u0633\u062a\u0636\u064a\u0641 \u0639\u0628\u0631 Tor). \u0642\u0645 \u0628\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0643\u0648\u062f\u060c \u0648\u0635\u0645\u0645 \u0645\u0648\u0642\u0639 \u0645\u0646\u0634\u0648\u0631\u060c \u0648\u0627\u0633\u062a\u0636\u064a\u0641\u0647 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0648\u0642\u0645 \u0628\u062a\u0636\u062e\u064a\u0645 \u0631\u0633\u0627\u0644\u062a\u0643.\n\n\u0627\u0633\u062a\u062e\u062f\u0645 \u0627\u0644\u0642\u0648\u0629 \u0627\u0644\u0645\u064a\u0645\u064a\u0629 \u0644\u0645\u0634\u0627\u0631\u0643\u0629 \u0623\u0641\u0643\u0627\u0631\u0643 \u0641\u064a \u0645\u062f\u0631\u0633\u062a\u0643\u060c \u0623\u0648 \u0645\u0643\u0627\u0646 \u0639\u0645\u0644\u0643\u060c \u0623\u0648 \u0645\u062c\u062a\u0645\u0639\u0627\u062a\u0643 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a\u060c \u0623\u0648 \u062d\u062a\u0649 \u0639\u0644\u0649 \u0645\u0633\u062a\u0648\u0649 \u0627\u0644\u0639\u0627\u0644\u0645. \u062a\u0636\u0645\u0646 VoxVera \u062a\u0631\u062f\u062f \u0635\u062f\u0649 \u0631\u0633\u0627\u0644\u062a\u0643 \u0628\u0639\u064a\u062f\u0627\u064b \u0648\u0639\u0644\u0649 \u0646\u0637\u0627\u0642 \u0648\u0627\u0633\u0639\u060c \u0645\u0639 \u0623\u0642\u0633\u0627\u0645 \u0642\u0627\u0628\u0644\u0629 \u0644\u0644\u0646\u0632\u0639 \u062a\u062a\u0645\u064a\u0632 \u0628\u0631\u0648\u0627\u0628\u0637 \u0641\u0631\u064a\u062f\u0629 \u0648\u0623\u0643\u0648\u0627\u062f QR \u0644\u0633\u0647\u0648\u0644\u0629 \u0625\u0639\u0627\u062f\u0629 \u0627\u0644\u0637\u0628\u0627\u0639\u0629.\n\n\u064a\u0645\u0643\u0646 \u0627\u0644\u062d\u0641\u0627\u0638 \u0639\u0644\u0649 \u0627\u0644\u062e\u0635\u0648\u0635\u064a\u0629. \u064a\u0645\u0643\u0646 \u0645\u0634\u0627\u0631\u0643\u0629 \u0627\u0644\u0645\u0646\u0634\u0648\u0631\u0627\u062a \u0639\u0628\u0631 \u0634\u0628\u0643\u0629 Tor\u060c \u0645\u0645\u0627 \u064a\u062d\u0645\u064a \u0627\u0644\u0645\u0636\u064a\u0641 \u0648\u0627\u0644\u0645\u0633\u062a\u062e\u062f\u0645\u064a\u0646 \u0645\u0646 \u0627\u0644\u0631\u0642\u0627\u0628\u0629.\n\n\u0627\u0646\u0636\u0645 \u0625\u0644\u064a\u0646\u0627 \u0641\u064a \u062b\u0648\u0631\u0629 \u062a\u0642\u062f\u0631 \u0627\u0644\u062d\u0642\u064a\u0642\u0629 \u0648\u0627\u0644\u0634\u0641\u0627\u0641\u064a\u0629. \u0645\u0639\u0627\u064b\u060c \u064a\u0645\u0643\u0646\u0646\u0627 \u0628\u0646\u0627\u0621 \u0634\u0628\u0643\u0629 \u0645\u0646 \u0627\u0644\u0645\u0648\u0627\u0637\u0646\u064a\u0646 \u0627\u0644\u0645\u0633\u062a\u0646\u064a\u0631\u064a\u0646 \u0627\u0644\u0630\u064a\u0646 \u0644\u0627 \u064a\u062e\u0634\u0648\u0646 \u0627\u0644\u062a\u062d\u062f\u062b.", "url_message": "\u0627\u062d\u0635\u0644 \u0639\u0644\u0649 VoxVera \u0648\u0627\u0628\u062f\u062Line truncated
|
||||
const locales = {"ar": {"meta": {"language_name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629", "flag": "\ud83c\uddf8\ud83c\udde6", "direction": "rtl"}, "web": {"tear_off_tor_browser": "\u0627\u0633\u062a\u062e\u062f\u0645 \u0645\u062a\u0635\u0641\u062d Tor", "tear_off_desc": "\u0644\u0644\u0645\u0633\u0627\u0639\u062f\u0629 \u0641\u064a \u0646\u0634\u0631 \u0647\u0630\u0647 \u0627\u0644\u0631\u0633\u0627\u0644\u0629\u060c \u0627\u0646\u062a\u0642\u0644 \u0625\u0644\u0649", "tear_off_footer": "\u0627\u0646\u0642\u0631 \u0641\u0648\u0642 \u0627\u0644\u0632\u0631 \u0623\u062f\u0646\u0627\u0647 \u0644\u0644\u0637\u0628\u0627\u0639\u0629", "built_with": "\u0628\u0646\u064a \u0628\u0648\u0627\u0633\u0637\u0629", "print_button": "\u0627\u0637\u0628\u0639 \u0627\u0644\u0645\u0646\u0634\u0648\u0631", "download_button": "\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0623\u062f\u0627\u0629 \u0648\u0627\u0644\u0643\u0648\u062f"}, "landing": {"title": "TOP SECRET", "subtitle": "DO ~~NOT~~ DISTRIBUTE", "headline": "OPERATION VOX VERA", "content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message. \n\nUse memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting. \n\nPrivacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.\n\nJoin us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.\n", "url_message": "Get VoxVera and start spreading your message.", "name": "Vox Vera Printable Flyers"}}, "de": {"meta": {"language_name": "Deutsch", "flag": "\ud83c\udde9\ud83c\uddea"}, "web": {"tear_off_tor_browser": "Tor-Browser verwenden", "tear_off_desc": "um diese Nachricht zu verbreiten, gehen Sie zu", "tear_off_footer": "Klicken Sie auf die Schaltfl\u00e4che unten zum Drucken", "built_with": "erstellt mit", "print_button": "Flyer drucken", "download_button": "Tool & Quellcode herunterladen"}, "landing": {"title": "TOP SECRET", "subtitle": "DO ~~NOT~~ DISTRIBUTE", "headline": "OPERATION VOX VERA", "content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message. \n\nUse memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting. \n\nPrivacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.\n\nJoin us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.\n", "url_message": "Get VoxVera and start spreading your message.", "name": "Vox Vera Printable Flyers"}}, "en": {"meta": {"language_name": "English", "flag": "\ud83c\uddfa\ud83c\uddf8"}, "web": {"tear_off_tor_browser": "Use Tor Browser", "tear_off_desc": "to help spread this message go to", "tear_off_footer": "click the button below to print", "built_with": "built with", "print_button": "Print this page", "download_button": "Download Source Code"}, "landing": {"title": "TOP SECRET", "subtitle": "DO ~~NOT~~ DISTRIBUTE", "headline": "OPERATION VOX VERA", "content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message. \n\nUse memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting. \n\nPrivacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.\n\nJoin us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.\n", "url_message": "Get VoxVera and start spreading your message.", "name": "Vox Vera Printable Flyers"}}, "es": {"meta": {"language_name": "Espa\u00f1ol", "flag": "\ud83c\uddea\ud83c\uddf8"}, "web": {"tear_off_tor_browser": "Use el Navegador Tor",Line truncated
|
||||
|
||||
function t(path, lang) {
|
||||
const parts = path.split('.');
|
||||
@@ -557,13 +557,14 @@
|
||||
<h1 id="operation-title-2" data-i18n="landing.headline">OPERATION VOX VERA</h1>
|
||||
<hr>
|
||||
<div class="message">
|
||||
<div class="text-container" id="main-content" contenteditable="false" data-i18n="landing.content">Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.
|
||||
<div class="text-container" id="main-content" contenteditable="false" data-i18n="landing.content">Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.
|
||||
|
||||
Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.
|
||||
Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.
|
||||
|
||||
Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.
|
||||
|
||||
Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.</div>
|
||||
Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.
|
||||
</div>
|
||||
<div class="qr-code-body">
|
||||
|
||||
<div class="qr-code-url">
|
||||
@@ -582,16 +583,9 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
</div>
|
||||
<div class="button-bar">
|
||||
<button class="print-button" onclick="window.print()" data-i18n="web.print_button">Print this page</button>
|
||||
<div class="dropdown">
|
||||
<button class="download-button" data-i18n="web.download_button">Download</button>
|
||||
<div class="dropdown-content">
|
||||
<a href="download/voxvera-portable.zip" download data-i18n="web.dl_portable">Portable (Source + Dependencies)</a>
|
||||
<a href="download/voxvera-linux" download data-i18n="web.dl_linux">Linux Standalone</a>
|
||||
<a href="download/voxvera-windows.exe" download data-i18n="web.dl_windows">Windows Standalone (.exe)</a>
|
||||
<a href="download/voxvera-macos" download data-i18n="web.dl_macos">macOS Standalone</a>
|
||||
<a href="download/voxvera-source.zip" download data-i18n="web.dl_source">Source Code Only (.zip)</a>
|
||||
</div>
|
||||
</div>
|
||||
<a id="download-link" href="download/voxvera-source.zip" download>
|
||||
<button class="download-button" data-i18n="web.download_button">Download Source Code</button>
|
||||
</a>
|
||||
<select class="lang-select" id="lang-selector" onchange="setLanguage(this.value)"></select>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+8
-3
@@ -112,10 +112,15 @@ def test_check_all_present(capsys, monkeypatch):
|
||||
|
||||
|
||||
def test_check_missing(capsys, monkeypatch):
|
||||
def fake_which(cmd):
|
||||
return None if cmd == "onionshare-cli" else "/usr/bin/" + cmd
|
||||
import builtins
|
||||
original_import = builtins.__import__
|
||||
|
||||
monkeypatch.setattr(shutil, "which", fake_which)
|
||||
def fake_import(name, *args, **kwargs):
|
||||
if name == "onionshare_cli":
|
||||
raise ImportError("Mocked missing")
|
||||
return original_import(name, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(builtins, "__import__", fake_import)
|
||||
cli.main(["check"])
|
||||
captured = capsys.readouterr()
|
||||
assert "onionshare-cli" in captured.out
|
||||
|
||||
+9
-3
@@ -46,7 +46,11 @@ def check_tor_running():
|
||||
|
||||
def check_onionshare_cli():
|
||||
"""Check if onionshare-cli is installed."""
|
||||
return shutil.which("onionshare-cli") is not None
|
||||
try:
|
||||
import onionshare_cli # noqa: F401
|
||||
return True
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
@@ -222,12 +226,14 @@ class TestFullWorkflow:
|
||||
host_dir = tmp_path / "host" / "reachtest"
|
||||
assert host_dir.is_dir(), f"Build did not create {host_dir}"
|
||||
|
||||
# Launch onionshare-cli directly against the built host directory
|
||||
# Launch onionshare-cli directly against the built host directory using our internal wrapper
|
||||
logfile = host_dir / "onionshare.log"
|
||||
log_fh = open(logfile, "w")
|
||||
|
||||
cmd = [
|
||||
"onionshare-cli",
|
||||
sys.executable,
|
||||
"-m", "voxvera.cli",
|
||||
"_internal_onionshare",
|
||||
"--website",
|
||||
"--public",
|
||||
"--persistent",
|
||||
|
||||
@@ -115,7 +115,11 @@ def test_portable_bundle_integrity(tmp_path, monkeypatch):
|
||||
shutil.copytree(REPO_ROOT / "voxvera" / "locales", voxvera_dir / "locales")
|
||||
os.makedirs(voxvera_dir / "vendor", exist_ok=True)
|
||||
(voxvera_dir / "vendor" / "anchor.txt").write_text("anchor")
|
||||
(project_root / "voxvera-run.sh").write_text("echo run")
|
||||
|
||||
# Copy real scripts from repo
|
||||
for script in ["voxvera-run.sh", "uninstall.sh", "uninstall.ps1", "install.sh", "install.ps1", "setup.sh"]:
|
||||
if (REPO_ROOT / script).exists():
|
||||
shutil.copy(REPO_ROOT / script, project_root / script)
|
||||
|
||||
# ROOT in cli.py points to the voxvera/ directory
|
||||
monkeypatch.setattr(cli, "ROOT", voxvera_dir)
|
||||
@@ -133,7 +137,9 @@ def test_portable_bundle_integrity(tmp_path, monkeypatch):
|
||||
assert any("voxvera/locales/en.json" in n for n in names)
|
||||
# Verify vendored libs
|
||||
assert any("voxvera/vendor/anchor.txt" in n for n in names)
|
||||
# Verify launchers
|
||||
assert "voxvera.bat" in names
|
||||
# Verify uninstallers
|
||||
assert "uninstall.sh" in names
|
||||
assert "uninstall.ps1" in names
|
||||
|
||||
# Verify root scripts
|
||||
assert "voxvera-run.sh" in names
|
||||
@@ -0,0 +1,22 @@
|
||||
$ErrorActionPreference = 'SilentlyContinue'
|
||||
|
||||
Write-Host "Uninstalling VoxVera..."
|
||||
|
||||
# 1. Uninstall via pipx
|
||||
if (Get-Command pipx -ErrorAction SilentlyContinue) {
|
||||
pipx uninstall voxvera | Out-Null
|
||||
}
|
||||
|
||||
# 2. Uninstall via pip
|
||||
if (Get-Command pip -ErrorAction SilentlyContinue) {
|
||||
pip uninstall -y voxvera | Out-Null
|
||||
}
|
||||
|
||||
# 3. Remove manual binary
|
||||
$home = [Environment]::GetFolderPath('UserProfile')
|
||||
$exePath = Join-Path $home '.local\bin\voxvera.exe'
|
||||
if (Test-Path $exePath) {
|
||||
Remove-Item $exePath -Force
|
||||
}
|
||||
|
||||
Write-Host "`nVoxVera has been uninstalled."
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "Uninstalling VoxVera..."
|
||||
|
||||
# 1. Uninstall via pipx if present
|
||||
if command -v pipx >/dev/null 2>&1; then
|
||||
if pipx list | grep -q "package voxvera"; then
|
||||
echo "Removing voxvera via pipx..."
|
||||
pipx uninstall voxvera
|
||||
fi
|
||||
fi
|
||||
|
||||
# 2. Uninstall via pip if present
|
||||
if command -v pip >/dev/null 2>&1; then
|
||||
echo "Attempting to remove voxvera via pip..."
|
||||
pip uninstall -y voxvera >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# 3. Remove manual binary installation
|
||||
BINARY_PATH="$HOME/.local/bin/voxvera"
|
||||
if [ -f "$BINARY_PATH" ]; then
|
||||
echo "Removing manual binary at $BINARY_PATH..."
|
||||
rm "$BINARY_PATH"
|
||||
fi
|
||||
|
||||
# 4. Remove configuration and host data (Optional - ask user)
|
||||
# We will leave the host data by default to prevent data loss,
|
||||
# but we can remove the internal src config.
|
||||
# ROOT_DIR="$(dirname "$(readlink -f "$0")")" # This might not be reliable if run via curl
|
||||
|
||||
echo ""
|
||||
echo "VoxVera has been uninstalled."
|
||||
echo "Note: Your generated sites in ~/voxvera-exports or within the source folder were not removed."
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
__version__ = "0.1.3"
|
||||
__version__ = "0.1.8"
|
||||
|
||||
# Add the vendor directory to sys.path as a fallback.
|
||||
# For standard installs (pip/pipx), the installed dependencies in the venv
|
||||
|
||||
+107
-75
@@ -64,6 +64,14 @@ def t(path: str, **kwargs) -> str:
|
||||
|
||||
|
||||
def require_cmd(cmd: str):
|
||||
if cmd == "onionshare-cli":
|
||||
try:
|
||||
import onionshare_cli # noqa: F401
|
||||
return True
|
||||
except ImportError:
|
||||
print("Required tool 'onionshare-cli' not bundled or installed.", file=sys.stderr)
|
||||
return False
|
||||
|
||||
if shutil.which(cmd) is None:
|
||||
print(
|
||||
f"Required command '{cmd}' not found. Please install it.", file=sys.stderr
|
||||
@@ -75,10 +83,11 @@ def require_cmd(cmd: str):
|
||||
def check_deps():
|
||||
console = Console()
|
||||
|
||||
# External CLI tools still required at runtime
|
||||
cli_tools = ["onionshare-cli"]
|
||||
# External CLI tools still required at runtime (none anymore if bundled)
|
||||
cli_tools = []
|
||||
# Python packages used by the build pipeline
|
||||
py_packages = {
|
||||
"onionshare-cli": "onionshare_cli",
|
||||
"qrcode": "qrcode",
|
||||
"PIL (Pillow)": "PIL",
|
||||
"jsmin": "jsmin",
|
||||
@@ -385,11 +394,12 @@ def copy_template(name: str) -> str:
|
||||
|
||||
|
||||
def bundle_source(dest_zip: Path):
|
||||
"""Bundle only the VoxVera source code and root scripts (no binaries or vendor)."""
|
||||
"""Bundle the VoxVera source code, dependencies (vendor), and root scripts."""
|
||||
with zipfile.ZipFile(dest_zip, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
||||
# 1. Include the voxvera/ source directory (excluding vendor, host, bin, pycache)
|
||||
# 1. Include the voxvera/ source directory (excluding host, bin, pycache)
|
||||
# We DO include vendor here so the source zip is fully functional offline.
|
||||
for root, dirs, files in os.walk(ROOT):
|
||||
if "__pycache__" in root or "host" in root or "vendor" in root or "resources/bin" in root:
|
||||
if "__pycache__" in root or "host" in root or "resources/bin" in root:
|
||||
continue
|
||||
for file in files:
|
||||
file_path = Path(root) / file
|
||||
@@ -399,7 +409,8 @@ def bundle_source(dest_zip: Path):
|
||||
# 2. Include the run scripts and root files
|
||||
root_files = [
|
||||
"voxvera-run.sh", "voxvera-install.sh", "README.md",
|
||||
"requirements.txt", "setup.sh", "install.sh", "CONTRIBUTING.md", "LICENSE"
|
||||
"requirements.txt", "setup.sh", "install.sh", "uninstall.sh",
|
||||
"install.ps1", "uninstall.ps1", "CONTRIBUTING.md", "LICENSE"
|
||||
]
|
||||
for script in root_files:
|
||||
script_path = ROOT.parent / script
|
||||
@@ -410,43 +421,10 @@ def bundle_source(dest_zip: Path):
|
||||
|
||||
|
||||
def bundle_portable(dest_zip: Path):
|
||||
"""Bundle the entire VoxVera source, standalone binaries, and dependencies into a ZIP."""
|
||||
with zipfile.ZipFile(dest_zip, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
||||
# 1. Include standalone binaries from resources/bin if they exist
|
||||
bin_dir = ROOT / "resources" / "bin"
|
||||
if bin_dir.exists():
|
||||
for bin_file in bin_dir.iterdir():
|
||||
if bin_file.is_file():
|
||||
zipf.write(bin_file, bin_file.name)
|
||||
|
||||
# 2. Include the voxvera/ source directory
|
||||
for root, dirs, files in os.walk(ROOT):
|
||||
if "__pycache__" in root or "host" in root or "vendor" in root or "resources/bin" in root:
|
||||
if "vendor" in root: # We DO want vendor
|
||||
pass
|
||||
else:
|
||||
continue
|
||||
for file in files:
|
||||
file_path = Path(root) / file
|
||||
arcname = file_path.relative_to(ROOT.parent)
|
||||
zipf.write(file_path, arcname)
|
||||
|
||||
# 3. Include the run scripts and root files
|
||||
root_files = [
|
||||
"voxvera-run.sh", "voxvera-install.sh", "README.md",
|
||||
"requirements.txt", "setup.sh", "install.sh"
|
||||
]
|
||||
for script in root_files:
|
||||
script_path = ROOT.parent / script
|
||||
if script_path.exists():
|
||||
zipf.write(script_path, script)
|
||||
|
||||
# 4. Create a simple Windows batch launcher if it doesn't exist
|
||||
win_launcher = "voxvera.bat"
|
||||
launcher_content = "@echo off\npython -m voxvera.cli manage\npause"
|
||||
zipf.writestr(win_launcher, launcher_content)
|
||||
|
||||
print(f"Portable bundle created at {dest_zip}")
|
||||
"""Bundle the entire VoxVera source and dependencies into a ZIP (Viral distribution)."""
|
||||
# For now, we prioritize source code as requested by the user.
|
||||
# We rename it to voxvera-source.zip for clarity.
|
||||
bundle_source(dest_zip)
|
||||
|
||||
|
||||
def build_assets(
|
||||
@@ -472,9 +450,15 @@ def build_assets(
|
||||
l_data = json.load(lf)
|
||||
all_locales[code] = {
|
||||
"meta": l_data.get("meta", {}),
|
||||
"web": l_data.get("web", {})
|
||||
"web": l_data.get("web", {}),
|
||||
"landing": l_data.get("landing", {})
|
||||
}
|
||||
|
||||
# Override landing data with user custom config data so JS doesn't overwrite it
|
||||
for field in ["title", "subtitle", "headline", "content", "url_message"]:
|
||||
if field in data and data[field]:
|
||||
all_locales[code]["landing"][field] = data[field]
|
||||
|
||||
html = html.replace("{{locales}}", json.dumps(all_locales))
|
||||
|
||||
# 1. Statically replace localization tokens {{t_web_...}}
|
||||
@@ -488,15 +472,17 @@ def build_assets(
|
||||
translation = re.sub(r"~~(.*?)~~", r'<span class="redacted">\1</span>', str(translation))
|
||||
html = html.replace(f"{{{{t_web_{token}}}}}", translation)
|
||||
|
||||
# Landing tokens (should be cleared for regular flyers)
|
||||
landing_tokens = lang_data.get("landing", {})
|
||||
for token in landing_tokens.keys():
|
||||
html = html.replace(f"{{{{t_landing_{token}}}}}", "")
|
||||
# 2. Statically replace landing tokens with user content OR localized defaults
|
||||
# We want the user-provided data from config to take precedence.
|
||||
landing_fields = ["title", "subtitle", "headline", "content", "url_message"]
|
||||
landing_defaults = lang_data.get("landing", {})
|
||||
|
||||
# Explicitly handle content and url_message if they were t_landing'd
|
||||
# We want the user's custom content to win
|
||||
html = html.replace("{{t_landing_content}}", "{{content}}")
|
||||
html = html.replace("{{t_landing_url_message}}", "{{url_message}}")
|
||||
for field in landing_fields:
|
||||
# Use data from config if available, otherwise use localized default
|
||||
value = data.get(field, landing_defaults.get(field, ""))
|
||||
# Support Markdown-style redaction ~~text~~
|
||||
val_str = re.sub(r"~~(.*?)~~", r'<span class="redacted">\1</span>', str(value))
|
||||
html = html.replace(f"{{{{t_landing_{field}}}}}", val_str)
|
||||
|
||||
# 2. Statically replace config placeholders {{key}}
|
||||
for key, value in data.items():
|
||||
@@ -509,16 +495,16 @@ def build_assets(
|
||||
dest = ROOT / "host" / folder_name
|
||||
os.makedirs(dest, exist_ok=True)
|
||||
|
||||
# Update download link to point to our viral portable bundle
|
||||
html = html.replace("download/download.zip", "download/voxvera-portable.zip")
|
||||
# Update download link to point to our viral source bundle
|
||||
html = html.replace("download/download.zip", "download/voxvera-source.zip")
|
||||
|
||||
with open(dest / "index.html", "w") as fh:
|
||||
fh.write(html)
|
||||
|
||||
# Create the viral portable bundle
|
||||
# Create the viral source bundle
|
||||
download_dir = dest / "download"
|
||||
os.makedirs(download_dir, exist_ok=True)
|
||||
bundle_portable(download_dir / "voxvera-portable.zip")
|
||||
bundle_source(download_dir / "voxvera-source.zip")
|
||||
|
||||
if download_path:
|
||||
shutil.copy(download_path, download_dir / "extra-content.zip")
|
||||
@@ -582,10 +568,29 @@ def get_tor_ports():
|
||||
return socks_port, ctl_port
|
||||
|
||||
|
||||
def serve(config_path: str) -> str | None:
|
||||
if not require_cmd("onionshare-cli"):
|
||||
def _internal_onionshare():
|
||||
"""Hidden command to run the bundled onionshare-cli."""
|
||||
import platform
|
||||
|
||||
# Try to add bundled Tor to PATH so OnionShare can find it
|
||||
system = platform.system().lower()
|
||||
platform_dir = "win" if "windows" in system else "mac" if "darwin" in system else "linux"
|
||||
tor_dir = ROOT / "resources" / "tor" / platform_dir
|
||||
if tor_dir.exists():
|
||||
os.environ["PATH"] = str(tor_dir) + os.pathsep + os.environ.get("PATH", "")
|
||||
|
||||
try:
|
||||
from onionshare_cli import main
|
||||
# sys.argv is ['voxvera', '_internal_onionshare', '--website', ...]
|
||||
# We need to make it look like ['onionshare-cli', '--website', ...]
|
||||
sys.argv = ["onionshare-cli"] + sys.argv[2:]
|
||||
sys.exit(main())
|
||||
except ImportError:
|
||||
print("Error: onionshare-cli not bundled or installed.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def serve(config_path: str) -> str | None:
|
||||
socks, ctl = get_tor_ports()
|
||||
|
||||
folder_name = load_config(config_path)["folder_name"]
|
||||
@@ -600,8 +605,11 @@ def serve(config_path: str) -> str | None:
|
||||
env["TOR_SOCKS_PORT"] = socks
|
||||
env["TOR_CONTROL_PORT"] = ctl
|
||||
|
||||
# Run the bundled onionshare-cli via our hidden command
|
||||
# sys.argv[0] is the current executable (python script or PyInstaller binary)
|
||||
cmd = [
|
||||
"onionshare-cli",
|
||||
sys.argv[0],
|
||||
"_internal_onionshare",
|
||||
"--website",
|
||||
"--public",
|
||||
"--persistent",
|
||||
@@ -612,6 +620,12 @@ def serve(config_path: str) -> str | None:
|
||||
"-v",
|
||||
str(dir_path),
|
||||
]
|
||||
|
||||
# If running via 'python -m voxvera.cli', sys.argv[0] might be the script path,
|
||||
# so we should ensure it's executed correctly if not a PyInstaller binary.
|
||||
if not getattr(sys, 'frozen', False) and sys.argv[0].endswith('.py'):
|
||||
cmd = [sys.executable, sys.argv[0]] + cmd[1:]
|
||||
|
||||
log_fh = open(logfile, "w")
|
||||
proc = subprocess.Popen(cmd, stdout=log_fh, stderr=subprocess.STDOUT, env=env)
|
||||
|
||||
@@ -876,38 +890,50 @@ def build_site():
|
||||
"landing": l_data.get("landing", {})
|
||||
}
|
||||
|
||||
# Override landing data with user custom config data so JS doesn't overwrite it
|
||||
for field in ["title", "subtitle", "headline", "content", "url_message"]:
|
||||
if field in data and data[field]:
|
||||
all_locales[code]["landing"][field] = data[field]
|
||||
|
||||
html = html.replace("{{locales}}", json.dumps(all_locales))
|
||||
|
||||
# 1. Statically replace localization tokens
|
||||
# 1. Statically replace localization tokens (web tokens)
|
||||
# Get the language from config if available, else default to en
|
||||
current_lang = data.get("lang", "en")
|
||||
lang_data = all_locales.get(current_lang, all_locales.get("en", {}))
|
||||
|
||||
for category in ["web", "landing"]:
|
||||
tokens = lang_data.get(category, {})
|
||||
for token, translation in tokens.items():
|
||||
# Support Markdown-style redaction ~~text~~ -> <span class="redacted">text</span>
|
||||
translation = re.sub(r"~~(.*?)~~", r'<span class="redacted">\1</span>', str(translation))
|
||||
html = html.replace(f"{{{{t_{category}_{token}}}}}", translation)
|
||||
web_tokens = lang_data.get("web", {})
|
||||
for token, translation in web_tokens.items():
|
||||
# Support Markdown-style redaction ~~text~~ -> <span class="redacted">text</span>
|
||||
translation = re.sub(r"~~(.*?)~~", r'<span class="redacted">\1</span>', str(translation))
|
||||
html = html.replace(f"{{{{t_web_{token}}}}}", translation)
|
||||
|
||||
# 2. Update relative path for download
|
||||
html = html.replace("download/download.zip", "download/voxvera-portable.zip")
|
||||
# 2. Statically replace landing tokens with user content OR localized defaults
|
||||
landing_fields = ["title", "subtitle", "headline", "content", "url_message"]
|
||||
landing_defaults = lang_data.get("landing", {})
|
||||
|
||||
# 3. Inject site-specific config data (handles any remaining {{key}} placeholders)
|
||||
for field in landing_fields:
|
||||
# Use data from config if available, otherwise use localized default
|
||||
value = data.get(field, landing_defaults.get(field, ""))
|
||||
# Support Markdown-style redaction ~~text~~
|
||||
val_str = re.sub(r"~~(.*?)~~", r'<span class="redacted">\1</span>', str(value))
|
||||
html = html.replace(f"{{{{t_landing_{field}}}}}", val_str)
|
||||
|
||||
# 3. Update relative path for download
|
||||
html = html.replace("download/download.zip", "download/voxvera-source.zip")
|
||||
|
||||
# 4. Inject site-specific config data (handles any remaining {{key}} placeholders)
|
||||
for key, value in data.items():
|
||||
html = html.replace(f"{{{{{key}}}}}", str(value))
|
||||
|
||||
with open(site_dir / "index.html", "w") as fh:
|
||||
fh.write(html)
|
||||
|
||||
# 3. Create the download assets in site/download/
|
||||
# 5. Create the download assets in site/download/
|
||||
download_dir = site_dir / "download"
|
||||
os.makedirs(download_dir, exist_ok=True)
|
||||
|
||||
# Bundle portable version (source + vendor + binaries)
|
||||
bundle_portable(download_dir / "voxvera-portable.zip")
|
||||
|
||||
# Bundle source version (source only)
|
||||
# Bundle source version
|
||||
bundle_source(download_dir / "voxvera-source.zip")
|
||||
|
||||
# Copy standalone binaries if they exist
|
||||
@@ -1249,8 +1275,14 @@ def main(argv=None):
|
||||
sub.add_parser("build-docs", help="Generate localized documentation from templates")
|
||||
sub.add_parser("build-site", help="Refresh the main site/ directory assets and bundle")
|
||||
sub.add_parser("manage", help="Manage VoxVera servers interactively")
|
||||
sub.add_parser("_internal_onionshare", help=argparse.SUPPRESS)
|
||||
|
||||
args, unknown_args = parser.parse_known_args(argv)
|
||||
|
||||
if args.command == "_internal_onionshare":
|
||||
_internal_onionshare()
|
||||
return
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
config_path = Path(args.config).resolve()
|
||||
|
||||
# Determine language
|
||||
|
||||
@@ -80,12 +80,7 @@
|
||||
"tear_off_footer": "انقر فوق الزر أدناه للطباعة",
|
||||
"built_with": "بني بواسطة",
|
||||
"print_button": "اطبع المنشور",
|
||||
"download_button": "تحميل الأداة والكود",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "تحميل الأداة والكود"
|
||||
},
|
||||
"landing": {
|
||||
"title": "سري للغاية",
|
||||
|
||||
@@ -79,12 +79,7 @@
|
||||
"tear_off_footer": "Klicken Sie auf die Schaltfläche unten zum Drucken",
|
||||
"built_with": "erstellt mit",
|
||||
"print_button": "Flyer drucken",
|
||||
"download_button": "Tool & Quellcode herunterladen",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "Tool & Quellcode herunterladen"
|
||||
},
|
||||
"landing": {
|
||||
"title": "STRENG GEHEIM",
|
||||
|
||||
@@ -79,12 +79,7 @@
|
||||
"tear_off_footer": "click the button below to print",
|
||||
"built_with": "built with",
|
||||
"print_button": "Print this page",
|
||||
"download_button": "Download",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "Download Source Code"
|
||||
},
|
||||
"landing": {
|
||||
"title": "TOP SECRET",
|
||||
|
||||
@@ -79,12 +79,7 @@
|
||||
"tear_off_footer": "haga clic en el botón de abajo para imprimir",
|
||||
"built_with": "construido con",
|
||||
"print_button": "Imprimir volante",
|
||||
"download_button": "Descargar Herramienta y Código",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "Descargar Herramienta y Código"
|
||||
},
|
||||
"landing": {
|
||||
"title": "ALTO SECRETO",
|
||||
|
||||
@@ -80,12 +80,7 @@
|
||||
"tear_off_footer": "برای چاپ روی دکمه زیر کلیک کنید",
|
||||
"built_with": "ساخته شده با",
|
||||
"print_button": "چاپ اعلامیه",
|
||||
"download_button": "دانلود ابزار و کد منبع",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "دانلود ابزار و کد منبع"
|
||||
},
|
||||
"landing": {
|
||||
"title": "بهکلی محرمانه",
|
||||
|
||||
@@ -80,12 +80,7 @@
|
||||
"tear_off_footer": "cliquez sur le bouton ci-dessous pour imprimer",
|
||||
"built_with": "créé avec",
|
||||
"print_button": "Imprimer le flyer",
|
||||
"download_button": "Télécharger l'outil et le code",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "Télécharger l'outil et le code"
|
||||
},
|
||||
"landing": {
|
||||
"title": "TOP SECRET",
|
||||
|
||||
@@ -80,12 +80,7 @@
|
||||
"tear_off_footer": "לחץ על הכפתור למטה להדפסה",
|
||||
"built_with": "נבנה עם",
|
||||
"print_button": "הדפס פלייר",
|
||||
"download_button": "הורד כלי וקוד מקור",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "הורד כלי וקוד מקור"
|
||||
},
|
||||
"landing": {
|
||||
"title": "סודי ביותר",
|
||||
|
||||
@@ -80,12 +80,7 @@
|
||||
"tear_off_footer": "प्रिंट करने के लिए नीचे दिए गए बटन पर क्लिक करें",
|
||||
"built_with": "बनाया गया",
|
||||
"print_button": "फ्लायर प्रिंट करें",
|
||||
"download_button": "टूल और कोड डाउनलोड करें",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "टूल और कोड डाउनलोड करें"
|
||||
},
|
||||
"landing": {
|
||||
"title": "अति गोपनीय",
|
||||
|
||||
@@ -80,12 +80,7 @@
|
||||
"tear_off_footer": "下のボタンをクリックして印刷してください",
|
||||
"built_with": "built with",
|
||||
"print_button": "フライヤーを印刷",
|
||||
"download_button": "ツールとソースをダウンロード",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "ツールとソースをダウンロード"
|
||||
},
|
||||
"landing": {
|
||||
"title": "極秘事項",
|
||||
|
||||
@@ -80,12 +80,7 @@
|
||||
"tear_off_footer": "clique no botão abaixo para imprimir",
|
||||
"built_with": "construído com",
|
||||
"print_button": "Imprimir Panfleto",
|
||||
"download_button": "Baixar Ferramenta e Código",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "Baixar Ferramenta e Código"
|
||||
},
|
||||
"landing": {
|
||||
"title": "ALTO SECRETO",
|
||||
|
||||
@@ -80,12 +80,7 @@
|
||||
"tear_off_footer": "нажмите кнопку ниже, чтобы распечатать",
|
||||
"built_with": "создано с помощью",
|
||||
"print_button": "Печать листовки",
|
||||
"download_button": "Скачать инструмент и код",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "Скачать инструмент и код"
|
||||
},
|
||||
"landing": {
|
||||
"title": "СОВЕРШЕННО СЕКРЕТНО",
|
||||
|
||||
@@ -80,12 +80,7 @@
|
||||
"tear_off_footer": "bonyeza kitufe hapa chini ili kuchapisha",
|
||||
"built_with": "imejengwa na",
|
||||
"print_button": "Chapa Kipeperushi",
|
||||
"download_button": "Pakua Zana na Chanzo",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "Pakua Zana na Chanzo"
|
||||
},
|
||||
"landing": {
|
||||
"title": "SIRI KUBWA",
|
||||
|
||||
@@ -80,12 +80,7 @@
|
||||
"tear_off_footer": "yazdırmak için aşağıdaki düğmeye tıklayın",
|
||||
"built_with": "ile yapıldı",
|
||||
"print_button": "İlanı Yazdır",
|
||||
"download_button": "Aracı ve Kaynak Kodunu İndir",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "Aracı ve Kaynak Kodunu İndir"
|
||||
},
|
||||
"landing": {
|
||||
"title": "ÇOK GİZLİ",
|
||||
|
||||
@@ -80,12 +80,7 @@
|
||||
"tear_off_footer": "点击下方按钮进行打印",
|
||||
"built_with": "构建自",
|
||||
"print_button": "打印传单",
|
||||
"download_button": "下载工具与源码",
|
||||
"dl_portable": "Portable (Source + Dependencies)",
|
||||
"dl_linux": "Linux Standalone",
|
||||
"dl_windows": "Windows Standalone (.exe)",
|
||||
"dl_macos": "macOS Standalone",
|
||||
"dl_source": "Source Code Only (.zip)"
|
||||
"download_button": "下载工具与源码"
|
||||
},
|
||||
"landing": {
|
||||
"title": "绝密",
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -576,16 +576,9 @@
|
||||
</div>
|
||||
<div class="button-bar">
|
||||
<button class="print-button" onclick="window.print()" data-i18n="web.print_button">{{t_web_print_button}}</button>
|
||||
<div class="dropdown">
|
||||
<a id="download-link" href="download/voxvera-source.zip" download>
|
||||
<button class="download-button" data-i18n="web.download_button">{{t_web_download_button}}</button>
|
||||
<div class="dropdown-content">
|
||||
<a href="download/voxvera-portable.zip" download data-i18n="web.dl_portable">Portable (Source + Dependencies)</a>
|
||||
<a href="download/voxvera-linux" download data-i18n="web.dl_linux">Linux Standalone</a>
|
||||
<a href="download/voxvera-windows.exe" download data-i18n="web.dl_windows">Windows Standalone (.exe)</a>
|
||||
<a href="download/voxvera-macos" download data-i18n="web.dl_macos">macOS Standalone</a>
|
||||
<a href="download/voxvera-source.zip" download data-i18n="web.dl_source">Source Code Only (.zip)</a>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<select class="lang-select" id="lang-selector" onchange="setLanguage(this.value)"></select>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
Reference in new issue
Block a user