1- #!/usr/bin/env -S pkgx --quiet deno^2.1 run --ext=ts --allow-sys=uid --allow-run --allow-env=PKGX_DIR,HOMEBREW_PREFIX,HOME --allow-read=/usr/local/pkgs
1+ #!/usr/bin/env -S pkgx --quiet deno^2.1 run --ext=ts --allow-sys=uid --allow-run --allow-env=PKGX_DIR,HOMEBREW_PREFIX,HOME --allow-read=/usr/local/pkgs,${HOME}/.local/pkgs
22import { dirname , fromFileUrl , join } from "jsr:@std/path@^1" ;
33import { ensureDir , existsSync } from "jsr:@std/fs@^1" ;
4- import { parse as parse_args } from "jsr:@std/flags@0.224.0 " ;
4+ import { parseArgs } from "jsr:@std/cli@^1 " ;
55import * as semver from "jsr:@std/semver@^1" ;
66
77function standardPath ( ) {
8- const basePath = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" ;
9- // for pkgm installed via homebrew
10- const homebrew = `${ Deno . env . get ( "HOMEBREW_PREFIX" ) || "/opt/homebrew" } /bin` ;
11- if ( Deno . build . os === "darwin" ) {
12- return `${ homebrew } :${ basePath } ` ;
13- } else {
14- return basePath ;
8+ let path = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" ;
9+
10+ // for pkgx installed via homebrew
11+ let homebrewPrefix = "" ;
12+ switch ( Deno . build . os ) {
13+ case "darwin" :
14+ homebrewPrefix = "/opt/homebrew" ; // /usr/local is already in the path
15+ break ;
16+ case "linux" :
17+ homebrewPrefix = `/home/linuxbrew/.linuxbrew:${
18+ Deno . env . get ( "HOME" )
19+ } /.linuxbrew`;
20+ break ;
21+ }
22+ if ( homebrewPrefix ) {
23+ homebrewPrefix = Deno . env . get ( "HOMEBREW_PREFIX" ) ?? homebrewPrefix ;
24+ path = `${ homebrewPrefix } /bin:${ path } ` ;
1525 }
26+
27+ return path ;
1628}
1729
18- const parsedArgs = parse_args ( Deno . args , {
30+ const parsedArgs = parseArgs ( Deno . args , {
1931 alias : {
2032 v : "version" ,
2133 h : "help" ,
@@ -34,7 +46,11 @@ if (parsedArgs.help) {
3446 switch ( parsedArgs . _ [ 0 ] ) {
3547 case "install" :
3648 case "i" :
37- await install ( args ) ;
49+ await install ( args , "/usr/local" ) ;
50+ break ;
51+ case "local-install" :
52+ case "li" :
53+ await install ( args , `${ Deno . env . get ( "HOME" ) ! } /.local` ) ;
3854 break ;
3955 case "uninstall" :
4056 case "rm" :
@@ -49,12 +65,12 @@ if (parsedArgs.help) {
4965 Deno . exit ( 1 ) ;
5066 break ;
5167 case "sudo-install" : {
52- const [ pkgx_dir , runtime_env , ...paths ] = args ;
68+ const [ pkgx_dir , runtime_env , basePath , ...paths ] = args ;
5369 const parsed_runtime_env = JSON . parse ( runtime_env ) as Record <
5470 string ,
5571 Record < string , string >
5672 > ;
57- await sudo_install ( pkgx_dir , paths , parsed_runtime_env ) ;
73+ await sudo_install ( pkgx_dir , paths , parsed_runtime_env , basePath ) ;
5874 break ;
5975 }
6076 default :
@@ -67,7 +83,7 @@ if (parsedArgs.help) {
6783 }
6884}
6985
70- async function install ( args : string [ ] ) {
86+ async function install ( args : string [ ] , basePath : string ) {
7187 if ( args . length === 0 ) {
7288 console . error ( "no packages specified" ) ;
7389 Deno . exit ( 1 ) ;
@@ -106,9 +122,9 @@ async function install(args: string[]) {
106122
107123 const self = fromFileUrl ( import . meta. url ) ;
108124 const pkgx_dir = Deno . env . get ( "PKGX_DIR" ) || `${ Deno . env . get ( "HOME" ) } /.pkgx` ;
109- const needs_sudo = Deno . uid ( ) != 0 ;
125+ const needs_sudo = Deno . uid ( ) != 0 && basePath === "/usr/local" ;
110126
111- const runtime_env = expand_runtime_env ( json . runtime_env ) ;
127+ const runtime_env = expand_runtime_env ( json . runtime_env , basePath ) ;
112128
113129 args = [
114130 "pkgx" ,
@@ -121,9 +137,20 @@ async function install(args: string[]) {
121137 "sudo-install" ,
122138 pkgx_dir ,
123139 runtime_env ,
140+ basePath ,
124141 ...pkg_prefixes ,
125142 ] ;
126- const cmd = needs_sudo ? "/usr/bin/sudo" : args . shift ( ) ! ;
143+ let cmd = "" ;
144+ if ( needs_sudo ) {
145+ cmd = "/usr/bin/sudo" ;
146+ args . unshift (
147+ "-E" , // we already cleared the env, it's safe
148+ "env" ,
149+ `PATH=${ env . PATH } ` ,
150+ ) ;
151+ } else {
152+ cmd = args . shift ( ) ! ;
153+ }
127154 status = await new Deno . Command ( cmd , { args, env, clearEnv : true } )
128155 . spawn ( ) . status ;
129156 Deno . exit ( status . code ) ;
@@ -133,12 +160,13 @@ async function sudo_install(
133160 pkgx_dir : string ,
134161 pkg_prefixes : string [ ] ,
135162 runtime_env : Record < string , Record < string , string > > ,
163+ basePath : string ,
136164) {
137- const dst = "/usr/local" ;
165+ const dst = basePath ;
138166 for ( const pkg_prefix of pkg_prefixes ) {
139- // create /usr/local /pkgs/${prefix}
167+ // create ${dst} /pkgs/${prefix}
140168 await mirror_directory ( join ( dst , "pkgs" ) , pkgx_dir , pkg_prefix ) ;
141- // symlink /usr/local/ pkgs/${prefix} to /usr/local
169+ // symlink ${dst}/ pkgs/${prefix} to ${dst}
142170 if ( ! pkg_prefix . startsWith ( "pkgx.sh/v" ) ) {
143171 // ^^ don’t overwrite ourselves
144172 // ^^ * https://github.com/pkgxdev/pkgm/issues/14
@@ -157,14 +185,14 @@ async function sudo_install(
157185 if ( ! pkg_prefix ) continue ; //FIXME wtf?
158186
159187 for ( const bin of [ "bin" , "sbin" ] ) {
160- const bin_prefix = join ( "/usr/local/ pkgs" , pkg_prefix , bin ) ;
188+ const bin_prefix = join ( ` ${ dst } / pkgs` , pkg_prefix , bin ) ;
161189
162190 if ( ! existsSync ( bin_prefix ) ) continue ;
163191
164192 for await ( const entry of Deno . readDir ( bin_prefix ) ) {
165193 if ( ! entry . isFile ) continue ;
166194
167- const to_stub = join ( "/usr/local" , bin , entry . name ) ;
195+ const to_stub = join ( dst , bin , entry . name ) ;
168196
169197 let sh = `#!/bin/sh\n` ;
170198 for ( const [ key , value ] of Object . entries ( env ) ) {
@@ -280,12 +308,13 @@ async function create_v_symlinks(prefix: string) {
280308
281309function expand_runtime_env (
282310 runtime_env : Record < string , Record < string , string > > ,
311+ basePath : string ,
283312) {
284313 const expanded : Record < string , Record < string , string > > = { } ;
285314 for ( const [ project , env ] of Object . entries ( runtime_env ) ) {
286315 const expanded_env : Record < string , string > = { } ;
287316 for ( const [ key , value ] of Object . entries ( env ) ) {
288- const new_value = value . replaceAll ( / \$ ? { { .* p r e f i x } } / g, "/usr/local" ) ;
317+ const new_value = value . replaceAll ( / \$ ? { { .* p r e f i x } } / g, basePath ) ;
289318 expanded_env [ key ] = new_value ;
290319 }
291320 expanded [ project ] = expanded_env ;
0 commit comments