Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 24 additions & 25 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,46 +44,43 @@ jobs:
- name: Install dependencies
run: uv sync --all-extras

- name: Build binary
run: uv run python scripts/build.py --skip-deps

- name: Get version
id: version
shell: bash
run: |
VERSION=$(grep '^version' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
echo "version=$VERSION" >> $GITHUB_OUTPUT

- name: Rename binary (Unix)
if: runner.os != 'Windows'
run: |
mv dist/parallel-cli dist/parallel-cli-${{ matrix.platform }}

- name: Rename binary (Windows)
if: runner.os == 'Windows'
run: |
mv dist/parallel-cli.exe dist/parallel-cli-${{ matrix.platform }}.exe
- name: Build binary
run: uv run python scripts/build.py --skip-deps

- name: Generate checksum (Unix)
if: runner.os != 'Windows'
- name: List build output
shell: bash
run: |
cd dist
shasum -a 256 parallel-cli-${{ matrix.platform }} | cut -d' ' -f1 > parallel-cli-${{ matrix.platform }}.sha256

- name: Generate checksum (Windows)
if: runner.os == 'Windows'
shell: pwsh
echo "Build output:"
ls -la dist/
echo ""
echo "Archive contents:"
if [ -f "dist/parallel-cli-${{ matrix.platform }}.zip" ]; then
unzip -l "dist/parallel-cli-${{ matrix.platform }}.zip" | head -20
fi

- name: Verify checksum file exists
shell: bash
run: |
cd dist
$hash = (Get-FileHash -Algorithm SHA256 parallel-cli-${{ matrix.platform }}.exe).Hash.ToLower()
"$hash" | Out-File -FilePath parallel-cli-${{ matrix.platform }}.exe.sha256 -NoNewline
if [ ! -f "dist/parallel-cli-${{ matrix.platform }}.zip.sha256" ]; then
echo "Error: Checksum file not found"
exit 1
fi
echo "Checksum: $(cat dist/parallel-cli-${{ matrix.platform }}.zip.sha256)"

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.platform }}
path: |
dist/parallel-cli-${{ matrix.platform }}*
dist/parallel-cli-${{ matrix.platform }}.zip
dist/parallel-cli-${{ matrix.platform }}.zip.sha256
retention-days: 1

release:
Expand All @@ -99,7 +96,9 @@ jobs:
merge-multiple: true

- name: List artifacts
run: ls -la artifacts/
run: |
echo "Downloaded artifacts:"
ls -la artifacts/

- name: Upload to Release
uses: softprops/action-gh-release@v2
Expand Down
25 changes: 13 additions & 12 deletions MAINTAINERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ parallel-cli --version

### Supported Platforms

| Platform | Runner | Binary Name |
|----------|--------|-------------|
| macOS Apple Silicon | `macos-15` | `parallel-cli-darwin-arm64` |
| macOS Intel | `macos-15-large` | `parallel-cli-darwin-x64` |
| Linux x64 | `ubuntu-latest` | `parallel-cli-linux-x64` |
| Windows x64 | `windows-latest` | `parallel-cli-windows-x64.exe` |
| Platform | Runner | Archive Name |
|----------|--------|--------------|
| macOS Apple Silicon | `macos-15` | `parallel-cli-darwin-arm64.zip` |
| macOS Intel | `macos-15-large` | `parallel-cli-darwin-x64.zip` |
| Linux x64 | `ubuntu-latest` | `parallel-cli-linux-x64.zip` |
| Windows x64 | `windows-latest` | `parallel-cli-windows-x64.zip` |

Note: Linux arm64 is not supported (no GitHub-hosted ARM64 runners available).

Expand All @@ -56,13 +56,14 @@ Note: Linux arm64 is not supported (no GitHub-hosted ARM64 runners available).
# Build for current platform
uv run scripts/build.py

# Test the binary
./dist/parallel-cli --version
# Test the binary (onedir mode - binary is in a folder)
./dist/parallel-cli/parallel-cli --version
```

### Binary Size

Expect ~100-150 MB per binary (includes Python runtime and all dependencies).
Expect ~35-50 MB per platform archive (zip). The standalone CLI uses PyInstaller onedir mode
for fast startup (~0.2s). The archive extracts to ~80 MB on disk.

## PyPI Publishing

Expand Down Expand Up @@ -173,9 +174,9 @@ gh workflow run publish.yml -f test_pypi=true

### Install script fails

1. Check if binaries exist on the release
2. Verify checksum files are present (`.sha256`)
1. Check if zip archives exist on the release
2. Verify checksum files are present (`.zip.sha256`)
3. Test the download URL manually:
```bash
curl -fsSL https://github.com/parallel-web/parallel-web-tools/releases/download/v0.0.1/parallel-cli-darwin-arm64
curl -fsSL https://github.com/parallel-web/parallel-web-tools/releases/download/v0.0.1/parallel-cli-darwin-arm64.zip -o test.zip
```
86 changes: 51 additions & 35 deletions install-cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# Parallel CLI Installer
#
# Installs the standalone parallel-cli binary (includes bundled Python runtime).
# Installs the standalone parallel-cli (includes bundled Python runtime).
# Automatically detects your platform (macOS/Linux, x64/arm64) and downloads
# the appropriate pre-built binary.
#
Expand All @@ -16,7 +16,8 @@
set -e

VERSION="${1:-latest}"
INSTALL_DIR="${HOME}/.local/bin"
INSTALL_DIR="${HOME}/.local/share/parallel-cli"
BIN_DIR="${HOME}/.local/bin"
REPO="parallel-web/parallel-web-tools"
BINARY_NAME="parallel-cli"
GITHUB_RELEASES="https://github.com/${REPO}/releases"
Expand Down Expand Up @@ -55,6 +56,12 @@ else
exit 1
fi

# Check for unzip
if ! command -v unzip >/dev/null 2>&1; then
print_error "unzip is required but not installed"
exit 1
fi

# Download function that works with both curl and wget
download() {
local url="$1"
Expand Down Expand Up @@ -148,22 +155,22 @@ setup_path() {
;;
*)
print_warning "Unknown shell: $shell_name"
print_warning "Add ${INSTALL_DIR} to your PATH manually"
print_warning "Add ${BIN_DIR} to your PATH manually"
return
;;
esac

# Check if already in PATH
if [[ ":$PATH:" == *":${INSTALL_DIR}:"* ]]; then
if [[ ":$PATH:" == *":${BIN_DIR}:"* ]]; then
return
fi

print_status "Adding ${INSTALL_DIR} to PATH in ${rc_file}..."
print_status "Adding ${BIN_DIR} to PATH in ${rc_file}..."

if [ "$shell_name" = "fish" ]; then
echo "set -gx PATH ${INSTALL_DIR} \$PATH" >> "$rc_file"
echo "set -gx PATH ${BIN_DIR} \$PATH" >> "$rc_file"
else
echo "export PATH=\"${INSTALL_DIR}:\$PATH\"" >> "$rc_file"
echo "export PATH=\"${BIN_DIR}:\$PATH\"" >> "$rc_file"
fi

print_warning "Restart your shell or run: source ${rc_file}"
Expand All @@ -172,9 +179,9 @@ setup_path() {
# Main
main() {
echo ""
echo "╔════════════════════════════════════════════╗"
echo " Parallel CLI Installer "
echo "╚════════════════════════════════════════════╝"
echo "========================================"
echo " Parallel CLI Installer "
echo "========================================"
echo ""

# Detect platform
Expand All @@ -189,27 +196,22 @@ main() {
fi
print_status "Installing version: ${VERSION}"

# Build download URLs
local binary_url="${GITHUB_RELEASES}/download/${VERSION}/${BINARY_NAME}-${platform}"
local checksum_url="${binary_url}.sha256"

# Windows binary has .exe extension
if [[ "$platform" == windows-* ]]; then
binary_url="${binary_url}.exe"
checksum_url="${binary_url}.sha256"
fi
# Build download URLs - now downloading a zip archive
local archive_name="${BINARY_NAME}-${platform}.zip"
local archive_url="${GITHUB_RELEASES}/download/${VERSION}/${archive_name}"
local checksum_url="${archive_url}.sha256"

# Create install directory and temp directory
mkdir -p "$INSTALL_DIR"
# Create directories
mkdir -p "$BIN_DIR"
local tmp_dir
tmp_dir=$(mktemp -d)
local tmp_file="${tmp_dir}/${BINARY_NAME}"
local tmp_archive="${tmp_dir}/${archive_name}"

# Download binary
print_status "Downloading ${BINARY_NAME}..."
if ! download "$binary_url" "$tmp_file" 2>/dev/null; then
print_error "Failed to download binary"
print_error "URL: ${binary_url}"
# Download archive
print_status "Downloading ${archive_name}..."
if ! download "$archive_url" "$tmp_archive" 2>/dev/null; then
print_error "Failed to download archive"
print_error "URL: ${archive_url}"
print_error ""
print_error "This could mean:"
print_error " - The release doesn't exist yet"
Expand All @@ -229,9 +231,9 @@ main() {
local actual_checksum

if [ "$(uname -s)" = "Darwin" ]; then
actual_checksum=$(shasum -a 256 "$tmp_file" | cut -d' ' -f1)
actual_checksum=$(shasum -a 256 "$tmp_archive" | cut -d' ' -f1)
else
actual_checksum=$(sha256sum "$tmp_file" | cut -d' ' -f1)
actual_checksum=$(sha256sum "$tmp_archive" | cut -d' ' -f1)
fi

if [ "$actual_checksum" != "$expected_checksum" ]; then
Expand All @@ -246,19 +248,33 @@ main() {
print_warning "Checksum file not available, skipping verification"
fi

# Install binary
local install_path="${INSTALL_DIR}/${BINARY_NAME}"
mv "$tmp_file" "$install_path"
chmod +x "$install_path"
# Remove old installation if exists
if [ -d "$INSTALL_DIR" ]; then
print_status "Removing previous installation..."
rm -rf "$INSTALL_DIR"
fi

# Extract archive
print_status "Extracting archive..."
unzip -q "$tmp_archive" -d "$tmp_dir"

# Move to install directory
mv "${tmp_dir}/parallel-cli" "$INSTALL_DIR"
rm -rf "$tmp_dir"

print_success "Installed to ${install_path}"
# Create symlink in bin directory
local symlink_path="${BIN_DIR}/${BINARY_NAME}"
rm -f "$symlink_path"
ln -s "${INSTALL_DIR}/${BINARY_NAME}" "$symlink_path"

print_success "Installed to ${INSTALL_DIR}"
print_success "Symlinked to ${symlink_path}"

# Setup PATH
setup_path

# Verify installation
export PATH="${INSTALL_DIR}:$PATH"
export PATH="${BIN_DIR}:$PATH"
if command -v "${BINARY_NAME}" >/dev/null 2>&1; then
echo ""
print_success "Installation complete!"
Expand Down
53 changes: 35 additions & 18 deletions parallel-web-tools.spec
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,28 @@ a = Analysis(
'parallel_web_tools.core.batch',
'parallel_web_tools.core.runner',
'parallel_web_tools.core.schema',
# CLI
'parallel_web_tools.core.research',
'parallel_web_tools.core.result',
# CLI (standalone mode - no planner)
'parallel_web_tools.cli',
'parallel_web_tools.cli.commands',
'parallel_web_tools.cli.planner',
# Processors (for local file/db enrichment)
# Note: bigquery processor not included - requires sqlalchemy-bigquery driver
# Processors (CSV only in standalone - DuckDB/BigQuery require pip install)
'parallel_web_tools.processors',
'parallel_web_tools.processors.csv',
'parallel_web_tools.processors.duckdb',
# Note: Deploy commands (bigquery, snowflake) are NOT included in standalone CLI
# They require: pip install parallel-web-tools[snowflake] or [bigquery]
# Note: These features are NOT included in standalone CLI:
# - enrich plan (interactive wizard) - requires questionary, duckdb
# - enrich run with YAML config - requires pyyaml
# - DuckDB processor - requires duckdb, polars
# - BigQuery processor - requires sqlalchemy-bigquery
# - Deploy commands - requires snowflake-connector or gcloud
# For these features: pip install parallel-web-tools[all]
# Dependencies that might not be auto-detected
'click',
'questionary',
'rich',
'yaml',
'dotenv',
'pydantic',
'httpx',
'parallel',
'duckdb',
'sqlalchemy',
'pandas',
'certifi',
],
hookspath=[],
Expand All @@ -65,31 +64,49 @@ a = Analysis(
'tkinter',
'matplotlib',
'PIL',
'numpy.tests',
'pandas.tests',
'numpy',
'pandas',
'polars',
'pyarrow',
'duckdb',
'sqlalchemy',
'questionary',
'prompt_toolkit',
'yaml',
'pyyaml',
],
noarchive=False,
)

pyz = PYZ(a.pure)

# Use onedir mode for faster startup (no extraction needed)
# Distribution is a folder instead of single file, but startup is ~0.2s vs ~1s
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
[], # Don't include binaries/datas in EXE (they go in COLLECT)
exclude_binaries=True,
name='parallel-cli',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='parallel-cli',
)
2 changes: 1 addition & 1 deletion parallel_web_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
run_tasks,
)

__version__ = "0.0.7"
__version__ = "0.0.8"

__all__ = [
# Auth
Expand Down
Loading