diff --git a/zen/README.md b/zen/README.md new file mode 100644 index 00000000..ed7acb1b --- /dev/null +++ b/zen/README.md @@ -0,0 +1,15 @@ +--- +title: Zen Browser +homepage: https://zen-browser.app +tagline: | + A calmer internet, without tracking +description: | + Zen Browser is a privacy-focused Firefox fork that provides a calmer internet experience without tracking. It runs on Windows, macOS, and Linux, and aims to provide a more private and distraction-free browsing experience. +--- + +## Cheat Sheet + +> Zen Browser is a privacy-focused Firefox fork that provides a calmer internet +> experience without tracking. + +### Starting Zen Browser diff --git a/zen/install.ps1 b/zen/install.ps1 new file mode 100644 index 00000000..8e0b3be4 --- /dev/null +++ b/zen/install.ps1 @@ -0,0 +1,121 @@ +#!/usr/bin/env pwsh + +# Package-specific variables +$pkg_cmd_name = "zen" +$pkg_dst = "$HOME\.local\opt\zen" +$pkg_dst_cmd = "$HOME\.local\opt\zen\zen.exe" +$pkg_src = "$HOME\.local\opt\zen-v$Env:WEBI_VERSION" +$pkg_src_cmd = "$HOME\.local\opt\zen-v$Env:WEBI_VERSION\zen.exe" + +# Version check function +function pkg_get_current_version { + try { + $output = & zen --version 2>$null + if ($output -match '(\d+\.\d+\.\d+)') { + return $Matches[1] + } + } catch { + return $null + } +} + +# Pre-install tasks +function pkg_pre_install { + # Standard webi pre-install tasks + webi_check + webi_download + webi_extract +} + +# Install function - specific to Zen Browser +function pkg_install { + # Create versioned directory + $parent_dir = Split-Path -Parent $pkg_src + New-Item -ItemType Directory -Force -Path $parent_dir | Out-Null + Remove-Item -Recurse -Force -Path $pkg_src -ErrorAction SilentlyContinue + New-Item -ItemType Directory -Force -Path $pkg_src | Out-Null + + # Handle zip extraction + if ($Env:WEBI_PKG_FILE -like "*.zip") { + # Look for extracted content + $extracted_items = Get-ChildItem -Path $Env:WEBI_TMP | Where-Object { $_.Name -ne $Env:WEBI_PKG_FILE } + + if ($extracted_items.Count -gt 0) { + # Check if there's a single directory that contains all files + $main_dir = $extracted_items | Where-Object { $_.PSIsContainer } | Select-Object -First 1 + + if ($null -ne $main_dir) { + # Move contents from the main directory + Get-ChildItem -Path $main_dir.FullName | ForEach-Object { + Move-Item -Path $_.FullName -Destination $pkg_src + } + } else { + # Move all extracted items directly + $extracted_items | ForEach-Object { + Move-Item -Path $_.FullName -Destination $pkg_src + } + } + } else { + Write-Warning "No files found after extraction. Installation may be incomplete." + } + } + + # Verify the executable exists + if (-not (Test-Path $pkg_src_cmd)) { + Write-Warning "Executable not found at $pkg_src_cmd. Installation may be incomplete." + } +} + +# Post-install tasks +function pkg_post_install { + # Update PATH + $bin_dir = Split-Path -Parent $pkg_dst_cmd + webi_path_add $bin_dir + + # Create symlink to the installed version + $dst_parent = Split-Path -Parent $pkg_dst_cmd + New-Item -ItemType Directory -Force -Path $dst_parent | Out-Null + + # Remove existing symlink or file if it exists + if (Test-Path $pkg_dst_cmd) { + Remove-Item $pkg_dst_cmd -Force + } + + # Create new symlink + # Try symbolic link first, fall back to copy if it fails + try { + New-Item -ItemType SymbolicLink -Path $pkg_dst_cmd -Target $pkg_src_cmd -ErrorAction Stop + } catch { + Write-Warning "Could not create symbolic link. Copying file instead." + Copy-Item -Path $pkg_src_cmd -Destination $pkg_dst_cmd -Force + } + + # Add to ~/.local/bin if it doesn't exist + $local_bin = "$HOME\.local\bin\zen.exe" + if (-not (Test-Path $local_bin)) { + $local_bin_dir = Split-Path -Parent $local_bin + New-Item -ItemType Directory -Force -Path $local_bin_dir | Out-Null + + # Try symbolic link first, fall back to copy if it fails + try { + New-Item -ItemType SymbolicLink -Path $local_bin -Target $pkg_dst_cmd -ErrorAction Stop + } catch { + Write-Warning "Could not create symbolic link in .local/bin. Copying file instead." + Copy-Item -Path $pkg_dst_cmd -Destination $local_bin -Force + } + } +} + +# Success message +function pkg_done_message { + Write-Output "Zen Browser v$Env:WEBI_VERSION installed successfully!" + Write-Output "" + Write-Output "To run Zen Browser:" + Write-Output " zen" + Write-Output "" + Write-Output "Configuration directory: $Env:APPDATA\zen\" + Write-Output "" + Write-Output "For more information:" + Write-Output " - Documentation: https://docs.zen-browser.app/" + Write-Output " - GitHub: https://github.com/zen-browser/desktop" +} \ No newline at end of file diff --git a/zen/install.sh b/zen/install.sh new file mode 100644 index 00000000..e260c555 --- /dev/null +++ b/zen/install.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +# Package-specific variables +pkg_cmd_name="zen" +pkg_dst="$HOME/.local/opt/zen" +pkg_dst_cmd="$HOME/.local/opt/zen/zen" +pkg_src="$HOME/.local/opt/zen-v$WEBI_VERSION" +pkg_src_cmd="$HOME/.local/opt/zen-v$WEBI_VERSION/zen" + +# Version check function +pkg_get_current_version() { + # zen 1.0.0 => 1.0.0 + echo "$(zen --version 2> /dev/null | head -n 1 | sed 's/^.*[^0-9]\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*$/\1/')" +} + +# Pre-install tasks +pkg_pre_install() { + # Standard webi pre-install tasks + webi_check + webi_download + + # AppImage doesn't need extraction + if [[ $WEBI_PKG_FILE != *".AppImage" ]]; then + webi_extract + fi +} + +# Install function - specific to Zen Browser +pkg_install() { + # Move extracted files to the versioned directory + mkdir -p "$(dirname $pkg_src)" + rm -rf "$pkg_src" + mkdir -p "$pkg_src" + + # Handle different file formats + if [[ $WEBI_PKG_FILE == *".AppImage" ]]; then + # AppImage: copy the single file and make executable + if [[ $WEBI_PKG_FILE == *"-x86_64.AppImage" ]] || [[ $WEBI_PKG_FILE == *"-aarch64.AppImage" ]]; then + # Handle AppImage files with different naming patterns + cp "$WEBI_TMP/$WEBI_PKG_FILE" "$pkg_src_cmd" + else + # Regular AppImage naming + cp "$WEBI_TMP/$WEBI_PKG_FILE" "$pkg_src_cmd" + fi + chmod +x "$pkg_src_cmd" + elif [[ $WEBI_PKG_FILE == *".tar.xz" ]]; then + # Tar archive: move extracted directory contents + local extracted_dir=$(ls -1 "$WEBI_TMP" | grep -v "^$WEBI_PKG_FILE$" | head -n 1) + if [ -n "$extracted_dir" ]; then + mv "$WEBI_TMP/$extracted_dir"/* "$pkg_src/" 2> /dev/null || mv "$WEBI_TMP/$extracted_dir"/* "$pkg_src" + else + # If no subdirectory, move all files + mv "$WEBI_TMP"/* "$pkg_src/" 2> /dev/null || true + fi + + # Ensure the zen binary is executable + chmod +x "$pkg_src_cmd" 2> /dev/null || true + + # If zen binary doesn't exist, look for other executables + if [ ! -f "$pkg_src_cmd" ]; then + local zen_exe=$(find "$pkg_src" -type f -name "zen*" -executable | head -n 1) + if [ -n "$zen_exe" ]; then + ln -sf "$zen_exe" "$pkg_src_cmd" + fi + fi + fi + + # Final check to ensure the executable exists + if [ ! -f "$pkg_src_cmd" ]; then + echo "Error: Could not find zen executable. Installation may be incomplete." + echo "Files in $pkg_src:" + ls -la "$pkg_src" + return 1 + fi +} + +# Post-install tasks +pkg_post_install() { + # Update PATH + webi_path_add "$(dirname $pkg_dst_cmd)" + + # Create symlink to the installed version + mkdir -p "$(dirname $pkg_dst_cmd)" + ln -sf "$pkg_src_cmd" "$pkg_dst_cmd" + + # Create a convenience symlink in ~/.local/bin if it doesn't exist + mkdir -p "$HOME/.local/bin" + if [[ ! -e "$HOME/.local/bin/zen" ]]; then + ln -sf "$pkg_dst_cmd" "$HOME/.local/bin/zen" + fi +} + +# Success message +pkg_done_message() { + echo "Zen Browser v$WEBI_VERSION installed successfully!" + echo "" + echo "To run Zen Browser:" + echo " zen" + echo "" + echo "Configuration directory: ~/.config/zen/" + echo "" + echo "For more information:" + echo " - Documentation: https://docs.zen-browser.app/" + echo " - GitHub: https://github.com/zen-browser/desktop" +} diff --git a/zen/releases.js b/zen/releases.js new file mode 100644 index 00000000..821a4459 --- /dev/null +++ b/zen/releases.js @@ -0,0 +1,281 @@ +'use strict'; +'use strict'; + +var github = require('../_common/github.js'); + +/** + * @typedef {Object} BrowserAsset + * @property {string} name - Name of the release asset + * @property {string} browser_download_url - Download URL + */ + +/** + * @typedef {Object} Release + * @property {string} version - Version of the release + * @property {string} tag_name - Tag name of the release + * @property {boolean} prerelease - Whether this is a prerelease + * @property {string} published_at - Publication date + * @property {BrowserAsset[]} assets - Release assets + */ + +/** + * Fetches Zen Browser releases and formats them for webi installer + * @param {Object} request - Request object for the GitHub API + * @returns {Promise} Formatted releases + */ +module.exports = function (request) { + var owner = 'zen-browser'; + var repo = 'desktop'; + + return github(request, owner, repo).then(function (all) { + // Process each release and its assets + var releases = all.releases.map(function (release) { + // Set release properties + var version = release.version || release.tag_name; + if (version.startsWith('v')) { + version = version.slice(1); + } + + var channel = release.prerelease === true ? 'beta' : 'stable'; + var date = release.published_at || release.date || ''; + if (date.includes('T')) { + date = date.split('T')[0]; + } + + // Determine if the version contains prerelease indicators + if (!release.prerelease && version.match(/-(alpha|beta|rc|pre)/i)) { + channel = 'beta'; + } + + // Process each asset and map to the correct format + var mappedAssets = []; + + release.assets.forEach(function (asset) { + var assetInfo = { + name: asset.name, + version: version, + lts: false, // Zen browser doesn't use LTS concept + channel: channel, + date: date, + download: asset.browser_download_url, + os: '', + arch: '', + ext: '', + format: '', + }; + + // Linux tar.xz files + if (asset.name.match(/zen\.linux-(x86_64|aarch64)\.tar\.xz$/)) { + assetInfo.os = 'linux'; + assetInfo.ext = 'tar.xz'; + assetInfo.format = 'tar'; + + if (asset.name.includes('aarch64')) { + assetInfo.arch = 'arm64'; + } else { + assetInfo.arch = 'amd64'; + } + + mappedAssets.push(assetInfo); + } + // Linux AppImage files + else if (asset.name.match(/zen-(x86_64|aarch64)\.AppImage$/)) { + assetInfo.os = 'linux'; + assetInfo.ext = 'AppImage'; + assetInfo.format = 'bin'; + + if (asset.name.includes('aarch64')) { + assetInfo.arch = 'arm64'; + } else { + assetInfo.arch = 'amd64'; + } + + mappedAssets.push(assetInfo); + } + // Windows zip files + else if (asset.name.match(/zen\.windows-(x86_64|aarch64)\.zip$/)) { + assetInfo.os = 'windows'; + assetInfo.ext = 'zip'; + assetInfo.format = 'zip'; + + if (asset.name.includes('aarch64')) { + assetInfo.arch = 'arm64'; + } else { + assetInfo.arch = 'amd64'; + } + + mappedAssets.push(assetInfo); + } + // macOS dmg files + else if (asset.name.match(/zen\.macos-(x86_64|aarch64)\.dmg$/)) { + assetInfo.os = 'macos'; + assetInfo.ext = 'dmg'; + assetInfo.format = 'dmg'; + + if (asset.name.includes('aarch64')) { + assetInfo.arch = 'arm64'; + } else { + assetInfo.arch = 'amd64'; + } + + mappedAssets.push(assetInfo); + } + }); + + return mappedAssets; + }); + + // Flatten the array of arrays and filter out empty entries + var flatReleases = releases.flat().filter(Boolean); + + // Sort releases by date (newest first) + flatReleases.sort(function (a, b) { + return new Date(b.date) - new Date(a.date); + }); + + return { + releases: flatReleases, + }; + }); +}; + +// For testing the script directly +if (module === require.main) { + module.exports(require('http')).then(function (all) { + all = require('../_webi/normalize.js')(all); + console.info(JSON.stringify(all, null, 2)); + }); +} +var github = require('../_common/github.js'); + +/** + * @typedef {Object} BuildInfo + * @property {string} version - Version of the release + * @property {string} arch - Architecture (e.g., 'amd64') + * @property {string} channel - Release channel (e.g., 'stable', 'beta') + * @property {string} date - Release date + * @property {string} download - Download URL + * @property {string} ext - File extension (e.g., 'tar.xz') + * @property {string} format - Format type (e.g., 'tar') + * @property {boolean} lts - Whether this is an LTS release + * @property {string} name - Name of the release asset + * @property {string} os - Operating system (e.g., 'linux') + */ + +/** + * Fetches Zen Browser releases and filters for appropriate installer formats + * @param {Object} request - Request object for the GitHub API + * @returns {Promise<{releases: BuildInfo[]}>} Filtered releases + */ +module.exports = function (request) { + var owner = 'zen-browser'; + var repo = 'desktop'; + + return github(request, owner, repo).then(function (all) { + // Array to store filtered releases + var filteredReleases = []; + + // Process each release + for (var i = 0; i < all.releases.length; i++) { + var release = all.releases[i]; + + // Set release channel based on prerelease status + var channel = release.prerelease === true ? 'beta' : 'stable'; + var version = release.version || release.tag_name; + var date = release.published_at || release.date || '1970-01-01'; + + // Process all assets + for (var j = 0; j < release.assets.length; j++) { + var asset = release.assets[j]; + var buildInfo = null; + + // Linux tar.xz files + if (asset.name.match(/zen\.linux-(x86_64|aarch64)\.tar\.xz$/)) { + var linuxArch = asset.name.includes('aarch64') ? 'arm64' : 'amd64'; + buildInfo = { + name: asset.name, + version: version, + lts: false, // Zen browser doesn't use LTS concept + channel: channel, + date: date, + os: 'linux', + arch: linuxArch, + ext: 'tar.xz', + format: 'tar', + download: asset.browser_download_url, + }; + } + // Linux AppImage files + else if (asset.name.match(/zen-(x86_64|aarch64)\.AppImage$/)) { + var appImageArch = asset.name.includes('aarch64') ? 'arm64' : 'amd64'; + buildInfo = { + name: asset.name, + version: version, + lts: false, + channel: channel, + date: date, + os: 'linux', + arch: appImageArch, + ext: 'AppImage', + format: 'bin', + download: asset.browser_download_url, + }; + } + // Windows zip files + else if (asset.name.match(/zen\.windows-(x86_64|aarch64)\.zip$/)) { + var windowsArch = asset.name.includes('aarch64') ? 'arm64' : 'amd64'; + buildInfo = { + name: asset.name, + version: version, + lts: false, + channel: channel, + date: date, + os: 'windows', + arch: windowsArch, + ext: 'zip', + format: 'zip', + download: asset.browser_download_url, + }; + } + // macOS dmg files + else if (asset.name.match(/zen\.macos-(x86_64|aarch64)\.dmg$/)) { + var macosArch = asset.name.includes('aarch64') ? 'arm64' : 'amd64'; + buildInfo = { + name: asset.name, + version: version, + lts: false, + channel: channel, + date: date, + os: 'macos', + arch: macosArch, + ext: 'dmg', + format: 'dmg', + download: asset.browser_download_url, + }; + } + + // Add to filtered releases if we found a matching asset + if (buildInfo) { + filteredReleases.push(buildInfo); + } + } + } + + // Sort releases by date, newest first + filteredReleases.sort(function (a, b) { + return new Date(b.date) - new Date(a.date); + }); + + return { + releases: filteredReleases, + }; + }); +}; + +// For testing the script directly +if (module === require.main) { + module.exports().then(function (all) { + all = require('../_webi/normalize.js')(all); + console.info(JSON.stringify(all, null, 2)); + }); +}