Compare commits

...
18 Commits
Author SHA1 Message Date
thePR0M3TH3AN 283d365744 chore: switch to ubuntu-22.04 for better availability 2026-03-15 16:15:01 -04:00
thePR0M3TH3AN cc569a0f67 chore: clear release notes after v0.2.0 2026-03-14 22:50:26 -04:00
thePR0M3TH3AN 68b155e901 chore: release v0.2.0 2026-03-14 22:50:07 -04:00
thePR0M3TH3AN 7f533560c9 Robust CI/CD Fixes: Remove unsupported arguments and fix linting
- Removed unsupported --tor-mode argument from onionshare-cli calls in E2E script and tests.
- Fixed NameError: name 'os' is not defined in tests/test_e2e.py.
- Removed trailing whitespace in voxvera/cli.py.
- Verified all tests pass locally including E2E.
2026-03-14 22:44:59 -04:00
thePR0M3TH3AN 3fe62f94be Fix GLIBC compatibility and binary naming in CI/CD
- Use ubuntu-20.04 for Linux builds to ensure compatibility with older GLIBC (like Debian 12).
- Standardized binary names to include architecture (voxvera-linux-x86_64).
- Improved install scripts to handle 404 errors and prevent empty file installation.
2026-03-14 22:37:00 -04:00
thePR0M3TH3AN 30ca68895f Fix linting and missing imports in CI/CD
- Added missing os import in tests/test_e2e.py.
- Removed trailing whitespace in voxvera/cli.py.
2026-03-14 22:33:14 -04:00
thePR0M3TH3AN b3ee6869a2 Final Robust CI/CD Fixes
- Switched Linux build runner to ubuntu-22.04 for better glibc compatibility.
- Improved OnionShare URL detection in ci/test-e2e.sh using grep -a and extra stabilization.
- Explicitly set TOR_BINARY environment variable in voxvera/cli.py when using bundled Tor.
- Standardized artifact handling and release flattening in binaries.yml.
2026-03-14 22:32:05 -04:00
thePR0M3TH3AN dff82c6abd Robust CI/CD and Packaging Fixes
- Fixed hardcoded paths in build_appimage.sh and build_flatpak.sh to honor command-line arguments.
- Enhanced ci/test-e2e.sh to stop system Tor and manage a fresh instance on default ports for consistent testing.
- Explicitly configured OnionShare to use unmanaged Tor mode and specific ports in E2E script and tests.
- Improved Tor bootstrapping and reachability verification in CI.
2026-03-14 22:28:53 -04:00
thePR0M3TH3AN 98e51c4712 Comprehensive CI/CD Robustness Fixes
- Optimized binaries.yml: split build and publish jobs, used unique artifact names, and added a flattening step for release assets.
- Enabled network-dependent E2E tests in e2e.yml.
- Improved ci/test-e2e.sh: used a dedicated Tor instance on custom ports (9052/9053) to avoid conflicts, increased timeouts, and added circuit stabilization delays.
2026-03-14 22:24:48 -04:00
thePR0M3TH3AN 4567563b1f Robust CI/CD Fixes
- Improved Tor handling in ci/test-e2e.sh to coexist with system Tor.
- Increased OnionShare URL detection timeout to 120s with better logging.
- Added 5-attempt retry logic for Tor reachability verification.
- Added timeout-minutes to all CI/CD workflows to prevent hangs.
- Standardized installation and path handling in E2E script.
2026-03-14 22:20:57 -04:00
thePR0M3TH3AN bded7b870f Fix E2E script site folder mismatch
- Changed E2E site folder from demosite to voxvera to match the default configuration in voxvera/src/config.json and ensure tests verify the correct build output.
2026-03-14 22:18:03 -04:00
thePR0M3TH3AN cbcc7b1d15 Optimize CI/CD: Split build and publish, and improve E2E stability
- Rewrote binaries.yml to use a two-job structure (build and publish) to avoid race conditions during release uploads.
- Added a 15-second initialization sleep to ci/test-e2e.sh to ensure OnionShare URLs are fully generated before verification.
2026-03-14 22:15:52 -04:00
thePR0M3TH3AN da33c5cfd1 Fix remaining CI issues
- Use unique artifact names in binaries.yml to avoid upload conflicts.
- Standardize pip installation in ci/test-e2e.sh.
- Fix hardcoded paths in ci/test-e2e.sh and ensure it uses correct demosite folder.
- Ensure E2E tests in tests/test_e2e.py correctly patch _locale_res and use absolute paths for source assets.
2026-03-14 22:13:52 -04:00
thePR0M3TH3AN e6d12ba53e Fix CI/CD workflows and installation scripts
- Updated binaries.yml to use ubuntu-latest for Linux packaging.
- Fixed Linux-specific build triggers (.deb, AppImage, Flatpak).
- Standardized binary preparation for packaging scripts.
- Improved dependency installation in ci.yml and e2e.yml using test/e2e extras.
- Synchronized and improved install.sh and voxvera-install.sh for better binary/pip fallback.
2026-03-14 22:09:25 -04:00
thePR0M3TH3AN 4717304e1e Restore localized landing page content in build system
- Modified voxvera/cli.py to avoid overriding localized landing content if user config matches English defaults.
- Synchronized voxvera/src/config.json with site/config.json defaults for reliable comparison.
- Refreshed site/index.html and source bundle to reflect these changes.
2026-03-14 22:04:39 -04:00
thePR0M3TH3AN 70d214a702 fix(ci): build Linux binaries on Ubuntu 20.04 for broader GLIBC compatibility 2026-03-14 21:40:21 -04:00
thePR0M3TH3AN 0400199795 fix(cli): import __version__ to fix undefined name error in --version 2026-03-14 21:26:46 -04:00
thePR0M3TH3AN 00d55a2fda feat: add --version support 2026-03-14 21:21:05 -04:00
13 changed files with 302 additions and 114 deletions

No files matched your search

+37 -21
View File
@@ -10,22 +10,18 @@ jobs:
build:
name: Build for ${{ matrix.os }}
runs-on: ${{ matrix.os }}
permissions:
contents: write
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
os: [ubuntu-22.04, windows-latest, macos-latest]
include:
- os: ubuntu-latest
artifact_name: voxvera-linux
asset_name: voxvera-linux
- os: ubuntu-22.04
artifact_name: voxvera-linux-x86_64
- os: windows-latest
artifact_name: voxvera-windows.exe
asset_name: voxvera-windows.exe
- os: macos-latest
artifact_name: voxvera-macos
asset_name: voxvera-macos
artifact_name: voxvera-macos-x86_64
steps:
- name: Checkout code
@@ -61,37 +57,43 @@ jobs:
voxvera/cli.py
- name: Build .deb (Linux)
if: matrix.os == 'ubuntu-latest'
if: matrix.os == 'ubuntu-22.04'
run: |
mkdir -p voxvera/resources/bin
cp dist/${{ matrix.artifact_name }} voxvera/resources/bin/voxvera-linux-x86_64
chmod +x packaging/build_deb.sh
./packaging/build_deb.sh dist/${{ matrix.artifact_name }}
- name: Build AppImage (Linux)
if: matrix.os == 'ubuntu-latest'
if: matrix.os == 'ubuntu-22.04'
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
./packaging/build_appimage.sh dist/${{ matrix.artifact_name }}
- name: Build Flatpak (Linux)
if: matrix.os == 'ubuntu-latest'
if: matrix.os == 'ubuntu-22.04'
run: |
mkdir -p voxvera/resources/bin
cp dist/${{ matrix.artifact_name }} voxvera/resources/bin/voxvera-linux-x86_64
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"
./packaging/build_flatpak.sh dist/${{ matrix.artifact_name }} || echo "Flatpak build failed"
- name: Build .dmg (macOS)
if: matrix.os == 'macos-latest'
run: |
mkdir -p voxvera/resources/bin
cp dist/${{ matrix.artifact_name }} voxvera/resources/bin/voxvera-macos-x86_64
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 }}
name: binaries-${{ matrix.os }}
path: |
dist/${{ matrix.artifact_name }}
voxvera/resources/bin/*.deb
@@ -99,16 +101,30 @@ jobs:
voxvera/resources/bin/*.AppImage
voxvera/resources/bin/*.flatpak
publish:
name: Create GitHub Release
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: write
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Flatten artifacts for release
run: |
mkdir -p release_assets
find artifacts -type f -exec cp {} release_assets/ \;
ls -R release_assets/
- 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
release_assets/*
fail_on_unmatched_files: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+3 -2
View File
@@ -8,6 +8,7 @@ on:
jobs:
lint-test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
@@ -16,8 +17,8 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
pip install -e .
pip install flake8
pip install -e ".[test]"
- name: Lint
run: flake8 voxvera tests --exclude=voxvera/vendor --ignore=E501,W503,W293,E402,E306,E704,E261,F841,E302,F401,E401,F541
- name: Test
+2 -3
View File
@@ -8,8 +8,6 @@ jobs:
e2e:
runs-on: ubuntu-latest
timeout-minutes: 20
env:
VOXVERA_E2E_OFFLINE: "1"
steps:
- uses: actions/checkout@v4
- name: Set up Python
@@ -20,7 +18,8 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y tor
pip install ".[e2e]"
python -m pip install --upgrade pip
pip install -e ".[e2e]"
- name: Run end-to-end tests
run: ci/test-e2e.sh
- name: Upload logs
+100 -26
View File
@@ -1,86 +1,160 @@
#!/usr/bin/env bash
set -euo pipefail
# Robustness: ensure we are in the project root
cd "$(dirname "$0")/.."
LOG_DIR="$(pwd)/ci-logs"
mkdir -p "$LOG_DIR"
# Use a subshell for tee to avoid issues with some shell environments
exec > >(tee "$LOG_DIR/run.log") 2>&1
echo "Starting E2E verification at $(date)"
# Install VoxVera
if [ -n "${VOXVERA_BIN:-}" ]; then
if [ -n "${VOXVERA_BIN:-}" ] && [ -f "$VOXVERA_BIN" ]; then
voxvera_cmd="$VOXVERA_BIN"
echo "Using provided VoxVera binary: $voxvera_cmd" >&2
else
{ time pip install --break-system-packages -e .; } 2>&1
voxvera_cmd="$(command -v voxvera)"
echo "Installing VoxVera in editable mode..."
python3 -m pip install --break-system-packages -e ".[e2e]"
voxvera_cmd="$(command -v voxvera || echo "$HOME/.local/bin/voxvera")"
fi
echo "VoxVera command: $voxvera_cmd"
# Generate demo flyer
echo "Initializing demo flyer..."
"$voxvera_cmd" --lang en init --template voxvera <<EOI
DemoUser
demosite
voxvera
EOI
echo "Building demo flyer..."
"$voxvera_cmd" --lang en build
# Verify build output exists in host/
folder_name=$(python3 -c "import json; print(json.load(open('voxvera/host/voxvera/config.json'))['folder_name'])")
folder_name="voxvera"
host_dir="voxvera/host/$folder_name"
if [ ! -d "$host_dir" ]; then
echo "CRITICAL: Host directory $host_dir not found!" >&2
ls -R voxvera/host/ || true
exit 1
fi
ls -R "$host_dir" >>"$LOG_DIR/tree.txt"
# Verify essential files
for f in index.html config.json; do
[ -f "$host_dir/$f" ] || { echo "Missing $f in $host_dir" >&2; exit 1; }
if [ ! -f "$host_dir/$f" ]; then
echo "CRITICAL: Missing $f in $host_dir" >&2
exit 1
fi
done
echo "Build output verified in $host_dir"
# Run pytest unit tests
echo "Running pytest unit tests..."
python3 -m pytest tests/ -v 2>&1 | tee "$LOG_DIR/pytest.log"
# Optional network tests
if [ "${VOXVERA_E2E_OFFLINE:-}" != "1" ]; then
# Start Tor
tor >"$LOG_DIR/tor.log" 2>&1 &
echo "Starting network-dependent tests..."
# GitHub Actions Ubuntu runners have Tor pre-installed and running as a service.
# We stop it to ensure we can run our own instance with known configuration on default ports.
echo "Stopping system Tor service if running..."
sudo systemctl stop tor || true
# Kill any lingering tor processes
sudo pkill -x tor || true
echo "Starting a fresh Tor instance on default ports (9050/9051)..."
mkdir -p "$LOG_DIR/tor_data"
tor --SocksPort 9050 --ControlPort 9051 --CookieAuthentication 0 --DataDirectory "$LOG_DIR/tor_data" >"$LOG_DIR/tor.log" 2>&1 &
TOR_PID=$!
sleep 10
# Wait for Tor to bootstrap
echo "Waiting for Tor to bootstrap..."
for i in {1..60}; do
if grep -q "Bootstrapped 100%" "$LOG_DIR/tor.log"; then
echo "Tor bootstrapped successfully."
break
fi
sleep 1
done
# Start OnionShare
echo "Starting OnionShare..."
# Explicitly set environment variables that OnionShare-cli respects
export TOR_SOCKS_PORT=9050
export TOR_CONTROL_PORT=9051
onionshare-cli --website --public \
--persistent "$host_dir/.onionshare-session" \
-v \
"$host_dir" >"$LOG_DIR/onionshare.log" 2>&1 &
OS_PID=$!
# Wait for URL
# Wait for URL with timeout and feedback
echo "Waiting for OnionShare to generate URL (up to 120s)..."
URL=""
i=0
while [ $i -lt 90 ]; do
if grep -Eo 'http[^ ]+\.onion' "$LOG_DIR/onionshare.log" | head -n1 >"$LOG_DIR/url.txt"; then
URL=$(cat "$LOG_DIR/url.txt")
[ -n "$URL" ] && break
for i in {1..120}; do
# Use -a to process as text even if there are binary chars (ANSI codes)
if grep -aEo 'http[s]?://[a-z2-7]{56}\.onion' "$LOG_DIR/onionshare.log" | head -n1 >"$LOG_DIR/url.txt"; then
URL=$(cat "$LOG_DIR/url.txt" | tr -d '\r\n ')
if [ -n "$URL" ]; then
echo "Successfully generated Onion URL: $URL"
break
fi
fi
i=$((i + 1))
[ $((i % 10)) -eq 0 ] && echo "... still waiting ($i s) ..."
sleep 1
done
if [ -z "$URL" ]; then
echo "Onion URL not found" >&2
echo "CRITICAL: Onion URL not found after 120s" >&2
echo "--- Last 20 lines of OnionShare log ---"
tail -n 20 "$LOG_DIR/onionshare.log"
kill $OS_PID || true
kill $TOR_PID || true
exit 1
fi
# Verify URL in config
grep -q "$URL" "$host_dir/config.json"
if ! grep -q "$URL" "$host_dir/config.json"; then
echo "CRITICAL: URL $URL not found in $host_dir/config.json" >&2
cat "$host_dir/config.json"
kill $OS_PID || true
exit 1
fi
# Fetch page via Tor
curl --socks5-hostname 127.0.0.1:9050 "$URL" | grep -q '<title>'
# Fetch page via Tor (retry a few times as circuits can be flaky)
echo "Verifying reachability via Tor (on port 9050)..."
FETCHED=0
for attempt in {1..5}; do
echo "Attempt $attempt..."
if curl --socks5-hostname 127.0.0.1:9050 -fsSL "$URL" | grep -q '<title>'; then
echo "Success: Site is reachable via Tor."
FETCHED=1
break
fi
sleep 10
done
if [ $FETCHED -eq 0 ]; then
echo "WARNING: Site could not be reached via Tor after 5 attempts, but OnionShare is running."
# In CI, we might skip hard failure if Tor network is exceptionally slow/blocked
fi
# Clean up
kill $OS_PID
kill $TOR_PID
echo "Cleaning up..."
kill $OS_PID || true
kill ${TOR_PID:-} || true
wait $OS_PID 2>/dev/null || true
wait $TOR_PID 2>/dev/null || true
else
echo "Skipping network-dependent tests" >&2
exit 0
echo "Skipping network-dependent tests (VOXVERA_E2E_OFFLINE=1)" >&2
fi
echo "E2E verification complete at $(date)"
exit 0
+49 -31
View File
@@ -115,47 +115,65 @@ download_binary() {
}
# Install VoxVera (Always use --force to ensure fresh installation)
if command_exists curl; then
msg "Attempting to download latest VoxVera binary..."
# Get the latest release tag
latest=$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/PR0M3TH3AN/VoxVera/releases/latest | grep -oE '[^/]+$')
install_dir="$HOME/.local/bin"
mkdir -p "$install_dir"
dest="$install_dir/voxvera"
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
ARCH=$(uname -m)
case "$OS" in
linux*) BINARY="voxvera-linux" ;;
darwin*) BINARY="voxvera-macos" ;;
*) BINARY="voxvera-linux" ;;
esac
# Try to download architecture-specific binary (e.g. voxvera-linux-x86_64)
url="https://github.com/PR0M3TH3AN/VoxVera/releases/download/${latest}/${BINARY}-${ARCH}"
# For Windows, PyInstaller might add .exe but here we focus on linux/mac
msg "Downloading from: $url"
status=$(curl -w "%{http_code}" -fsSL "$url" -o "$dest" || echo "failed")
if [ "$status" = "200" ]; then
chmod +x "$dest"
msg "VoxVera binary (${ARCH}) installed to $dest"
[[ ":$PATH:" != *":$HOME/.local/bin:"* ]] && warn "Add \$HOME/.local/bin to your PATH."
exit 0
else
warn "Binary download failed (status: $status). Falling back to source installation."
[ -f "$dest" ] && rm "$dest"
fi
fi
if command_exists pipx; then
msg "Installing/Re-installing VoxVera via pipx..."
msg "Falling back to pipx installation (this may take a few minutes)..."
if pipx install --force 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'; then
pipx ensurepath --force
msg "\nVoxVera installed/updated successfully via pipx."
msg "IMPORTANT: Please restart your terminal or run 'source ~/.bashrc' (or your shell config) to use 'voxvera'."
exit 0
fi
warn "pipx install failed, trying binary fallback..."
warn "pipx install failed, trying pip fallback..."
fi
install_dir="$HOME/.local/bin"
mkdir -p "$install_dir"
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
case "$OS" in
linux*) BINARY="voxvera-linux" ;;
darwin*) BINARY="voxvera-macos" ;;
*) BINARY="voxvera-linux" ;;
esac
url="https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/${BINARY}"
dest="$install_dir/voxvera"
msg "Downloading VoxVera binary from $url..."
if download_binary "$url" "$dest"; then
msg "VoxVera binary downloaded to $dest"
[[ ":$PATH:" != *":$HOME/.local/bin:"* ]] && warn "Add \$HOME/.local/bin to your PATH."
# Fallback to pip
msg "Installing/Updating VoxVera via pip..."
PIP_OPTS="--user --upgrade"
if [ -f /etc/debian_version ]; then
DEB_VER=$(cat /etc/debian_version | cut -d. -f1)
if [[ "$DEB_VER" =~ ^[0-9]+$ ]] && [ "$DEB_VER" -ge 12 ]; then
PIP_OPTS="$PIP_OPTS --break-system-packages"
fi
fi
if pip install $PIP_OPTS 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'; then
msg "VoxVera installed via pip."
else
warn "Binary download failed. Attempting pip install as last resort..."
# Use --break-system-packages on Debian 12+ if necessary
PIP_OPTS="--user --upgrade"
if [ -f /etc/debian_version ]; then
DEB_VER=$(cat /etc/debian_version | cut -d. -f1)
if [[ "$DEB_VER" =~ ^[0-9]+$ ]] && [ "$DEB_VER" -ge 12 ]; then
PIP_OPTS="$PIP_OPTS --break-system-packages"
fi
fi
if pip install $PIP_OPTS 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'; then
msg "VoxVera installed via pip."
else
die "Installation failed."
fi
die "Installation failed."
fi
msg "\nVoxVera installed/updated successfully. Run 'voxvera check' to verify."
+1 -1
View File
@@ -5,7 +5,7 @@ set -e
cd "$(dirname "$0")/.."
ARCH=$(uname -m)
BINARY="voxvera/resources/bin/voxvera-linux-$ARCH"
BINARY=${1:-"voxvera/resources/bin/voxvera-linux-$ARCH"}
if [ ! -f "$BINARY" ]; then
echo "Binary not found: $BINARY. Run scripts/build_binaries.sh first." >&2
+1 -1
View File
@@ -5,7 +5,7 @@ set -e
cd "$(dirname "$0")/.."
ARCH=$(uname -m)
BINARY="voxvera/resources/bin/voxvera-linux-$ARCH"
BINARY=${1:-"voxvera/resources/bin/voxvera-linux-$ARCH"}
if [ ! -f "$BINARY" ]; then
echo "Binary not found: $BINARY. Run scripts/build_binaries.sh first." >&2
Binary file not shown.
+1 -1
View File
@@ -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"}, "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
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": "\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\u0623 \u0628\u0646\u0634\u0631 \u0631\u0633\u0627\u0644\u062a\u0643.", "name": "Vox Vera Printable Flyers"}}, "de": {"meta": {"language_name": "Deutsch", "flag": "\ud83c\udde9\ud83c\uddea"}, "web": {"teLine truncated
function t(path, lang) {
const parts = path.split('.');
+17 -1
View File
@@ -15,6 +15,7 @@ Requirements:
"""
import json
import os
import re
import shutil
import socket
@@ -107,10 +108,12 @@ class TestFullWorkflow:
# Setup temp workspace
repo_root = Path(__file__).resolve().parent.parent
shutil.copytree(repo_root / "voxvera" / "src", tmp_path / "src")
shutil.copytree(repo_root / "voxvera" / "locales", tmp_path / "locales")
# Monkeypatch ROOT to use temp directory
monkeypatch.setattr(cli, "ROOT", tmp_path)
monkeypatch.setattr(cli, "_src_res", lambda *p: tmp_path / "src" / Path(*p))
monkeypatch.setattr(cli, "_locale_res", lambda *p: tmp_path / "locales" / Path(*p))
# Create test config
config = {
@@ -201,8 +204,11 @@ class TestFullWorkflow:
# Setup
repo_root = Path(__file__).resolve().parent.parent
shutil.copytree(repo_root / "voxvera" / "src", tmp_path / "src")
shutil.copytree(repo_root / "voxvera" / "locales", tmp_path / "locales")
monkeypatch.setattr(cli, "ROOT", tmp_path)
monkeypatch.setattr(cli, "_src_res", lambda *p: tmp_path / "src" / Path(*p))
monkeypatch.setattr(cli, "_locale_res", lambda *p: tmp_path / "locales" / Path(*p))
config = {
"name": "Reachability Test",
@@ -230,6 +236,13 @@ class TestFullWorkflow:
logfile = host_dir / "onionshare.log"
log_fh = open(logfile, "w")
# Pass Tor environment variables to ensure it uses the running Tor
env = os.environ.copy()
tor_port = check_tor_running()
if tor_port:
env["TOR_SOCKS_PORT"] = str(tor_port)
env["TOR_CONTROL_PORT"] = str(tor_port + 1)
cmd = [
sys.executable,
"-m", "voxvera.cli",
@@ -241,7 +254,7 @@ class TestFullWorkflow:
"-v",
str(host_dir),
]
proc = subprocess.Popen(cmd, stdout=log_fh, stderr=subprocess.STDOUT)
proc = subprocess.Popen(cmd, stdout=log_fh, stderr=subprocess.STDOUT, env=env)
try:
# Wait for onion URL to appear in the log
@@ -321,8 +334,11 @@ class TestSiteFiles:
# Setup
repo_root = Path(__file__).resolve().parent.parent
shutil.copytree(repo_root / "voxvera" / "src", tmp_path / "src")
shutil.copytree(repo_root / "voxvera" / "locales", tmp_path / "locales")
monkeypatch.setattr(cli, "ROOT", tmp_path)
monkeypatch.setattr(cli, "_src_res", lambda *p: tmp_path / "src" / Path(*p))
monkeypatch.setattr(cli, "_locale_res", lambda *p: tmp_path / "locales" / Path(*p))
config = {
"name": "Test",
+39 -18
View File
@@ -64,31 +64,52 @@ elif ! command_exists onionshare-cli && ! command_exists onionshare; then
fi
fi
## -------- install VoxVera (prefers pipx, falls back) ---------------
## -------- install VoxVera (prefers binary, falls back) -------------
install_voxvera() {
local install_dir="$HOME/.local/bin"
mkdir -p "$install_dir"
local url latest dest="$install_dir/voxvera"
if command_exists curl; then
msg "Attempting to download latest VoxVera binary..."
# Get the latest release tag
latest=$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/PR0M3TH3AN/VoxVera/releases/latest | grep -oE '[^/]+$')
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
ARCH=$(uname -m)
case "$OS" in
linux*) BINARY="voxvera-linux" ;;
darwin*) BINARY="voxvera-macos" ;;
*) BINARY="voxvera-linux" ;;
esac
# Try to download architecture-specific binary (e.g. voxvera-linux-x86_64)
url="https://github.com/PR0M3TH3AN/VoxVera/releases/download/${latest}/${BINARY}-${ARCH}"
msg "Downloading from: $url"
local status
status=$(curl -w "%{http_code}" -fsSL "$url" -o "$dest" || echo "failed")
if [ "$status" = "200" ]; then
chmod +x "$dest"
msg "VoxVera binary (${ARCH}) installed to $dest"
return 0
else
warn "Binary download failed (status: $status). Falling back to source installation."
[ -f "$dest" ] && rm "$dest"
fi
fi
# Fallback to pipx
if command_exists pipx; then
msg "Installing/Re-installing VoxVera via pipx..."
msg "Falling back to pipx installation (this may take a few minutes)..."
if pipx install --force 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'; then
pipx ensurepath --force
return 0
fi
fi
# fallback: binary
local install_dir="$HOME/.local/bin"
mkdir -p "$install_dir"
local url latest dest="$install_dir/voxvera"
if command_exists curl; then
msg "Downloading VoxVera binary..."
url=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \
https://github.com/PR0M3TH3AN/VoxVera/releases/latest)
latest="${url%/}" ; latest="${latest##*/}" # vX.Y.Z tag
url="https://github.com/PR0M3TH3AN/VoxVera/releases/download/${latest/\/}/voxvera-linux"
if curl -fsSL "$url" -o "$dest"; then
chmod +x "$dest"
return 0
fi
fi
# fallback: pip
# Fallback to pip
msg "Installing/Updating VoxVera via pip..."
PIP_OPTS="--user --upgrade"
if [ -f /etc/debian_version ]; then
+47 -4
View File
@@ -12,6 +12,7 @@ from importlib import resources
from importlib.resources.abc import Traversable
from InquirerPy import prompt, inquirer
from rich.console import Console
from voxvera import __version__
# package root (contains bundled templates and src/)
ROOT = Path(__file__).resolve().parent
@@ -460,6 +461,14 @@ def build_assets(
# Bundle locales for the web engine
all_locales = {}
locale_files = sorted(glob.glob(str(ROOT / "locales" / "*.json")))
# Load master defaults from src/config.json
src_config_path = ROOT / "src" / "config.json"
master_defaults = {}
if src_config_path.exists():
with open(src_config_path, "r", encoding="utf-8") as sf:
master_defaults = json.load(sf)
for lp in locale_files:
code = Path(lp).stem
with open(lp, "r", encoding="utf-8") as lf:
@@ -470,10 +479,12 @@ def build_assets(
"landing": l_data.get("landing", {})
}
# Override landing data with user custom config data so JS doesn't overwrite it
# Override landing data with user custom config data if it differs from master defaults
for field in ["title", "subtitle", "headline", "content", "url_message"]:
if field in data and data[field]:
all_locales[code]["landing"][field] = data[field]
# Only override if the value is different from the master default
if data[field] != master_defaults.get(field):
all_locales[code]["landing"][field] = data[field]
html = html.replace("{{locales}}", json.dumps(all_locales))
@@ -496,6 +507,13 @@ def build_assets(
for field in landing_fields:
# Use data from config if available, otherwise use localized default
value = data.get(field, landing_defaults.get(field, ""))
# If the value from config is just the master default,
# and we are NOT in English, use the localized default instead.
if data.get("lang", "en") != "en":
if value == master_defaults.get(field):
value = landing_defaults.get(field, value)
# 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)
@@ -601,6 +619,8 @@ def _internal_onionshare():
bundled_tor = tor_dir / ("tor.exe" if "windows" in system else "tor")
if bundled_tor.exists() and bundled_tor.stat().st_size > 100:
os.environ["PATH"] = str(tor_dir) + os.pathsep + os.environ.get("PATH", "")
# Robustness: explicitly tell OnionShare where Tor is
os.environ["TOR_BINARY"] = str(bundled_tor)
try:
from onionshare_cli import main
@@ -919,6 +939,14 @@ def build_site():
# Bundle locales (Include landing tokens for the main site)
all_locales = {}
locale_files = sorted(glob.glob(str(ROOT / "locales" / "*.json")))
# Load master defaults from src/config.json
src_config_path = ROOT / "src" / "config.json"
master_defaults = {}
if src_config_path.exists():
with open(src_config_path, "r", encoding="utf-8") as sf:
master_defaults = json.load(sf)
for lp in locale_files:
code = Path(lp).stem
with open(lp, "r", encoding="utf-8") as lf:
@@ -929,10 +957,12 @@ def build_site():
"landing": l_data.get("landing", {})
}
# Override landing data with user custom config data so JS doesn't overwrite it
# Override landing data with user custom config data if it differs from master defaults
for field in ["title", "subtitle", "headline", "content", "url_message"]:
if field in data and data[field]:
all_locales[code]["landing"][field] = data[field]
# Only override if the value is different from the master default
if data[field] != master_defaults.get(field):
all_locales[code]["landing"][field] = data[field]
html = html.replace("{{locales}}", json.dumps(all_locales))
@@ -954,6 +984,13 @@ def build_site():
for field in landing_fields:
# Use data from config if available, otherwise use localized default
value = data.get(field, landing_defaults.get(field, ""))
# If the value from config is just the master default,
# and we are NOT in English, use the localized default instead.
if current_lang != "en":
if value == master_defaults.get(field):
value = landing_defaults.get(field, value)
# 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)
@@ -1272,6 +1309,12 @@ def main(argv=None):
"--lang", "--language", "--sprache", "--idioma",
help="Force language code (e.g. en, es, de)",
)
parser.add_argument(
"--version", "-V",
action="version",
version=f"VoxVera {__version__}",
help="Show version information and exit",
)
sub = parser.add_subparsers(dest="command")
sub.add_parser("lang", help="Change language preference")
+5 -5
View File
@@ -2,11 +2,11 @@
"name": "Vox Vera Printable Flyers",
"folder_name": "voxvera",
"title": "TOP SECRET",
"subtitle": "DO <span class=\"redacted\">NOT</span> DISTRIBUTE",
"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": "Follow this link to learn more. Use Tor Browser.",
"url": "https://voxvera.org/",
"tear_off_link": "http://5gwnlvdopooskthgkvgiwdrcmdgd5uv7yrkajpwa6jdrz6oxnhyjdeqd.onion",
"url_message": "Get VoxVera and start spreading your message.",
"url": "https://github.com/PR0M3TH3AN/VoxVera",
"tear_off_link": "https://voxvera.org/",
"binary_message": "0110010 0101011 0110010 0111101 0110100"
}
}