Skip to content
Closed
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
12 changes: 12 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Shell scripts must always use LF line endings
# CRLF breaks shebang lines in Linux containers (e.g. Docker builds)
*.sh text eol=lf

# JS/TS and other text files use native line endings
*.js text
*.ts text
*.json text
*.yml text
*.yaml text
*.md text
*.toml text
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ COPY plugins/ ./plugins/
COPY scripts/ ./scripts/

# Install default plugin dependencies (apt packages + post-install hooks)
RUN scripts/install-plugin-deps.sh
RUN sh scripts/install-plugin-deps.sh

ENV NODE_ENV=production
ENV CAMOFOX_PORT=9377
Expand Down
4 changes: 3 additions & 1 deletion Dockerfile.ci
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ COPY plugins/ ./plugins/
COPY scripts/ ./scripts/

# Install default plugin dependencies
RUN scripts/install-plugin-deps.sh
# Note: sh is used explicitly (./scripts/) because COPY from Windows
# does not preserve +x permission bits in Docker build contexts.
RUN sh scripts/install-plugin-deps.sh

ENV NODE_ENV=production
ENV CAMOFOX_PORT=9377
Expand Down
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,39 @@ make up ARCH=x86_64
make up VERSION=135.0.1 RELEASE=beta.24
```

#### Windows

On Windows, `make` is not available. Use the included `build.ps1` PowerShell script instead:

```powershell
# Build and start
.\build.ps1 up

# Stop and remove the container
.\build.ps1 down

# Build image only
.\build.ps1 build

# Force a clean rebuild
.\build.ps1 reset

# Download binaries only (without building)
.\build.ps1 fetch

# Override architecture
.\build.ps1 up -Arch x86_64
.\build.ps1 up -Arch aarch64
```

> **Note:** PowerShell 7+ (`pwsh`) is recommended but `powershell.exe` (Windows PowerShell 5.1) also works. The script requires Docker Desktop for Windows with the WSL2 backend.
>
> **Line endings:** This project includes a `.gitattributes` file that forces Unix (`LF`) line endings for `.sh` files. If you've already cloned the repo and get `sh: not found` or `set: Illegal option -` errors during `docker build`, run:
> ```powershell
> Get-ChildItem -Recurse *.sh | ForEach-Object { (Get-Content $_) -join "`n" + "`n" | Set-Content $_ -NoNewline }
> ```
> This converts shell scripts to LF line endings. Future clones will handle this automatically thanks to `.gitattributes`.

> **WARNING: Do not run `docker build` directly.** The Dockerfile uses bind mounts to pull pre-downloaded binaries from `dist/`. Always use `make up` (or `make fetch` then `make build`) -- it downloads the binaries first.

### Fly.io
Expand Down
171 changes: 171 additions & 0 deletions build.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Build and manage camofox-browser on Windows (PowerShell alternative to Makefile).

.DESCRIPTION
Provides the same targets as the Makefile for Windows users without make:
build - Download Camoufox + yt-dlp, then build the Docker image.
up - Build (if needed) and run the container.
down - Stop and remove the container.
reset - Full rebuild from scratch.
clean - Remove downloaded binaries.
fetch - Download Camoufox + yt-dlp binaries only.

.PARAMETER Target
The action to perform: build, up, down, reset, clean, fetch (default: build).

.PARAMETER Arch
Target architecture: x86_64 or aarch64 (default: x86_64).

.PARAMETER CamoufoxVersion
Camoufox version (default: 135.0.1).

.PARAMETER CamoufoxRelease
Camoufox release channel (default: beta.24).

.PARAMETER ContainerName
Docker container name (default: camofox-browser).

.PARAMETER HostPort
Host port to map (default: 9377).

.EXAMPLE
.\build.ps1 up # Build + run
.\build.ps1 down # Stop container
.\build.ps1 fetch # Download binaries only
#>

param(
[ValidateSet('build', 'up', 'down', 'reset', 'clean', 'fetch')]
[string]$Target = 'build',

[ValidateSet('x86_64', 'aarch64')]
[string]$Arch = 'x86_64',

[string]$CamoufoxVersion = '135.0.1',
[string]$CamoufoxRelease = 'beta.24',
[string]$ContainerName = 'camofox-browser',
[int]$HostPort = 9377
)

$ErrorActionPreference = 'Stop'
$ProjectRoot = Split-Path -Parent $PSCommandPath
$DistDir = Join-Path $ProjectRoot 'dist'
$CamoufoxZip = Join-Path $DistDir "camoufox-$Arch.zip"
$YtDlpBin = Join-Path $DistDir "yt-dlp-$Arch"
$ImageTag = "camofox-browser:$CamoufoxVersion-$Arch"
$ContainerPort = 9377

# Map architecture to upstream release filenames
if ($Arch -eq 'aarch64') {
$CamoufoxArch = 'arm64'
$YtDlpSuffix = '_aarch64'
} else {
$CamoufoxArch = 'x86_64'
$YtDlpSuffix = ''
}

$CamoufoxUrl = "https://github.com/daijro/camoufox/releases/download/v$CamoufoxVersion-$CamoufoxRelease/camoufox-$CamoufoxVersion-$CamoufoxRelease-lin.$CamoufoxArch.zip"
$YtDlpUrl = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux$YtDlpSuffix"

function Write-Step {
param([string]$Message)
Write-Host ">>> $Message" -ForegroundColor Cyan
}

function Invoke-Fetch {
Write-Step "Creating dist directory..."
New-Item -ItemType Directory -Path $DistDir -Force | Out-Null

if (-not (Test-Path $CamoufoxZip)) {
Write-Step "Downloading Camoufox browser ($CamoufoxArch)..."
Write-Host " URL: $CamoufoxUrl"
curl.exe -L -o $CamoufoxZip $CamoufoxUrl
Write-Host " Downloaded: $(Get-Item $CamoufoxZip | Select-Object -ExpandProperty Length) bytes"
} else {
Write-Host " [SKIP] Camoufox already downloaded"
}

if (-not (Test-Path $YtDlpBin)) {
Write-Step "Downloading yt-dlp ($Arch)..."
Write-Host " URL: $YtDlpUrl"
curl.exe -L -o $YtDlpBin $YtDlpUrl
Write-Host " Downloaded: $(Get-Item $YtDlpBin | Select-Object -ExpandProperty Length) bytes"
} else {
Write-Host " [SKIP] yt-dlp already downloaded"
}
}

function Invoke-Build {
Invoke-Fetch

Write-Step "Building Docker image: $ImageTag"
docker build `
--build-arg "ARCH=$Arch" `
--build-arg "CAMOUFOX_VERSION=$CamoufoxVersion" `
--build-arg "CAMOUFOX_RELEASE=$CamoufoxRelease" `
-t $ImageTag `
-f (Join-Path $ProjectRoot 'Dockerfile') `
$ProjectRoot
}

function Invoke-Up {
# Check if image exists
$imageExists = docker images -q $ImageTag 2>$null
if (-not $imageExists) {
Write-Step "Image not found — building first..."
Invoke-Build
}

# Stop & remove existing container
docker stop $ContainerName 2>$null | Out-Null
docker rm $ContainerName 2>$null | Out-Null

Write-Step "Starting container: $ContainerName on port $HostPort"
docker run -d `
--restart unless-stopped `
--name $ContainerName `
-p "${HostPort}:${ContainerPort}" `
$ImageTag

Write-Host "Container started. Server should be available at http://localhost:$HostPort" -ForegroundColor Green
Write-Host "Check logs: docker logs $ContainerName" -ForegroundColor Gray
}

function Invoke-Down {
Write-Step "Stopping container: $ContainerName"
docker stop $ContainerName 2>$null
docker rm $ContainerName 2>$null
Write-Host "Container stopped and removed." -ForegroundColor Green
}

function Invoke-Reset {
Invoke-Down

Write-Step "Removing Docker image: $ImageTag"
docker rmi $ImageTag 2>$null

Invoke-Build
Invoke-Up
}

function Invoke-Clean {
Write-Step "Removing dist directory..."
if (Test-Path $DistDir) {
Remove-Item -Recurse -Force $DistDir
Write-Host "Removed: $DistDir" -ForegroundColor Green
} else {
Write-Host "Nothing to clean." -ForegroundColor Yellow
}
}

# --- Main dispatch ---
switch ($Target) {
'build' { Invoke-Build }
'up' { Invoke-Up }
'down' { Invoke-Down }
'reset' { Invoke-Reset }
'clean' { Invoke-Clean }
'fetch' { Invoke-Fetch }
}