feat(nix): add unfree package detection and declarative config ref #35

This commit is contained in:
N1C4T
2026-01-13 02:15:57 +04:00
parent c57046a3df
commit 02ac2cf5f9
4 changed files with 58 additions and 59 deletions

View File

@@ -9,7 +9,7 @@ import {
generateArchScript,
generateFedoraScript,
generateOpenSUSEScript,
generateNixScript,
generateNixConfig,
generateFlatpakScript,
generateSnapScript,
generateHomebrewScript,
@@ -21,10 +21,7 @@ interface ScriptOptions {
helper?: 'yay' | 'paru';
}
/**
* Generate a full install script with progress bars, error handling,
* and all the fancy stuff. This is what gets downloaded as .sh file.
*/
// Full install script for download. Nix gets a config file, others get shell scripts.
export function generateInstallScript(options: ScriptOptions): string {
const { distroId, selectedAppIds, helper = 'yay' } = options;
const distro = distros.find(d => d.id === distroId);
@@ -40,7 +37,7 @@ export function generateInstallScript(options: ScriptOptions): string {
case 'arch': return generateArchScript(packages, helper);
case 'fedora': return generateFedoraScript(packages);
case 'opensuse': return generateOpenSUSEScript(packages);
case 'nix': return generateNixScript(packages);
case 'nix': return generateNixConfig(packages);
case 'flatpak': return generateFlatpakScript(packages);
case 'snap': return generateSnapScript(packages);
case 'homebrew': return generateHomebrewScript(packages);
@@ -61,7 +58,7 @@ export function generateSimpleCommand(selectedAppIds: Set<string>, distroId: Dis
case 'arch': return `yay -S --needed --noconfirm ${pkgList}`;
case 'fedora': return `sudo dnf install -y ${pkgList}`;
case 'opensuse': return `sudo zypper install -y ${pkgList}`;
case 'nix': return `nix-env -iA ${packages.filter(p => p.pkg.trim()).map(p => `nixpkgs.${p.pkg.trim()}`).join(' ')}`;
case 'nix': return generateNixConfig(packages);
case 'flatpak': return `flatpak install flathub -y ${pkgList}`;
case 'snap':
if (packages.length === 1) return `sudo snap install ${pkgList}`;

35
src/lib/nixUnfree.ts Normal file
View File

@@ -0,0 +1,35 @@
// Nix unfree package detection - these require allowUnfree = true
export const KNOWN_UNFREE_PACKAGES = new Set([
'discord',
'slack',
'zoom-us',
'teams',
'skypeforlinux',
'google-chrome',
'vivaldi',
'opera',
'spotify',
'steam',
'heroic',
'vscode',
'sublime4',
'jetbrains.idea-ultimate',
'jetbrains.webstorm',
'jetbrains.pycharm-professional',
'nvidia-x11',
'dropbox',
'1password',
'masterpdfeditor',
]);
export function isUnfreePackage(pkg: string): boolean {
const cleanPkg = pkg.trim().toLowerCase();
if (KNOWN_UNFREE_PACKAGES.has(cleanPkg)) return true;
// Nested packages like jetbrains.idea-ultimate
for (const unfree of KNOWN_UNFREE_PACKAGES) {
if (cleanPkg.includes(unfree)) return true;
}
return false;
}

View File

@@ -6,7 +6,7 @@ export { generateDebianScript } from './debian';
export { generateArchScript } from './arch';
export { generateFedoraScript } from './fedora';
export { generateOpenSUSEScript } from './opensuse';
export { generateNixScript } from './nix';
export { generateNixConfig } from './nix';
export { generateFlatpakScript } from './flatpak';
export { generateSnapScript } from './snap';
export { generateHomebrewScript } from './homebrew';

View File

@@ -1,55 +1,22 @@
// Nix script - nix-env
// Nix declarative config generator
import { generateAsciiHeader, generateSharedUtils, escapeShellString, type PackageInfo } from './shared';
import { type PackageInfo } from './shared';
import { isUnfreePackage } from '../nixUnfree';
export function generateNixScript(packages: PackageInfo[]): string {
return generateAsciiHeader('Nix', packages.length) + generateSharedUtils(packages.length) + `
is_installed() { nix-env -q 2>/dev/null | grep -q "$1"; }
export function generateNixConfig(packages: PackageInfo[]): string {
const validPackages = packages.filter(p => p.pkg.trim());
if (validPackages.length === 0) return '# No packages selected';
install_pkg() {
local name=$1 attr=$2
CURRENT=$((CURRENT + 1))
if is_installed "$attr"; then
skip "$name"
SKIPPED+=("$name")
return 0
fi
show_progress $CURRENT $TOTAL "$name"
local start=$(date +%s)
local output
if output=$(with_retry nix-env -iA "nixpkgs.$attr"); then
local elapsed=$(($(date +%s) - start))
update_avg_time $elapsed
printf "\\r\\033[K"
timing "$name" "$elapsed"
SUCCEEDED+=("$name")
else
printf "\\r\\033[K\${RED}\${NC} %s\\n" "$name"
if echo "$output" | grep -q "attribute.*not found"; then
echo -e " \${DIM}Attribute not found\${NC}"
fi
FAILED+=("$name")
fi
}
# ─────────────────────────────────────────────────────────────────────────────
command -v nix-env &>/dev/null || { error "nix-env not found"; exit 1; }
info "Updating channels..."
with_retry nix-channel --update >/dev/null && success "Updated" || warn "Update failed"
echo
info "Installing $TOTAL packages"
echo
${packages.filter(p => p.pkg.trim()).map(({ app, pkg }) => `install_pkg "${escapeShellString(app.name)}" "${pkg.trim()}"`).join('\n')}
print_summary
echo
info "Restart your shell for new commands."
`;
const sortedPkgs = validPackages.map(p => p.pkg.trim()).sort();
const packageList = sortedPkgs.map(pkg => ` ${pkg}`).join('\n');
// Add unfree warning if needed
const unfreePkgs = sortedPkgs.filter(pkg => isUnfreePackage(pkg));
const unfreeComment = unfreePkgs.length > 0
? `# Unfree: ${unfreePkgs.join(', ')}\n# Requires: nixpkgs.config.allowUnfree = true;\n\n`
: '';
return `${unfreeComment}environment.systemPackages = with pkgs; [
${packageList}
];`;
}