From 3f892a567414c7a59f1de761bdccaf400f669cef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Mar 2026 21:49:21 +0000 Subject: [PATCH 01/12] Initial plan From 2f27f9efa0c8e12c2ba6fbc150f8429e1932c409 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Mar 2026 21:57:14 +0000 Subject: [PATCH 02/12] fix filesystem security issues: remove chown -R and restore /usr/local/bin ownership after install Co-authored-by: yihui <163582+yihui@users.noreply.github.com> --- R/install.R | 12 +++++++++--- tools/install-bin-unix.sh | 11 +++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/R/install.R b/R/install.R index f83808aed..15f1e4ae7 100644 --- a/R/install.R +++ b/R/install.R @@ -113,7 +113,8 @@ install_tinytex = function( switch( os, 'unix' = { - check_local_bin() + restore_local_bin = check_local_bin() + if (is.function(restore_local_bin)) on.exit(restore_local_bin(), add = TRUE) if (os_index != 3 && !any(dir_exists(c('~/bin', '~/.local/bin')))) on.exit(message( 'You may have to restart your system after installing TinyTeX to make sure ', '~/bin appears in your PATH variable (https://github.com/rstudio/tinytex/issues/16).' @@ -284,14 +285,19 @@ valid_path = function(x) grepl('^[!-~]+$', x) # check if /usr/local/bin on macOS is writable check_local_bin = function() { - if (os_index != 3 || is_writable(p <- '/usr/local/bin')) return() + if (os_index != 3 || is_writable(p <- '/usr/local/bin')) return(invisible(NULL)) message( 'The directory ', p, ' is not writable. I recommend that you make it writable. ', 'See https://github.com/rstudio/tinytex/issues/24 for more info.' ) if (!dir_exists(p)) osascript(paste('mkdir -p', p)) + # save current ownership before changing it (to restore later) + prev_owner = system2('stat', c('-f', '%Su:%Sg', p), stdout = TRUE) user = system2('whoami', stdout = TRUE) - osascript(sprintf('chown -R %s:admin %s', user, p)) + osascript(sprintf('chown %s:admin %s', user, p)) + # return a cleanup function to restore the original ownership (only if prev_owner is valid) + if (length(prev_owner) == 1 && grepl('^[^:]+:[^:]+$', prev_owner)) + function() osascript(sprintf('chown %s %s', prev_owner, p)) } osascript = function(cmd) { diff --git a/tools/install-bin-unix.sh b/tools/install-bin-unix.sh index fd4555d03..f30309b63 100755 --- a/tools/install-bin-unix.sh +++ b/tools/install-bin-unix.sh @@ -129,11 +129,18 @@ if [ $OSNAME = 'Darwin' ]; then echo "Admin privilege (password) is required to create the directory /usr/local/bin:" sudo mkdir -p /usr/local/bin fi - # change owner of the dir + # temporarily change owner of the dir to allow creating symlinks if [ ! -w /usr/local/bin ]; then echo "Admin privilege (password) is required to make /usr/local/bin writable:" - sudo chown -R `whoami`:admin /usr/local/bin + PREV_OWNER=$(stat -f "%Su:%Sg" /usr/local/bin) + sudo chown `whoami`:admin /usr/local/bin fi fi ./tlmgr path add + +# Restore original ownership of /usr/local/bin on macOS +if [ "$OSNAME" = 'Darwin' ] && [ -n "${PREV_OWNER-}" ]; then + echo "Admin privilege (password) is required to restore ownership of /usr/local/bin:" + sudo chown ${PREV_OWNER} /usr/local/bin +fi From 31aaa1f370e62a990dfaecc30445cd905d50ba44 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Thu, 26 Mar 2026 23:08:38 -0500 Subject: [PATCH 03/12] update license year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 353c85d0c..a3834cb47 100644 --- a/LICENSE +++ b/LICENSE @@ -1,2 +1,2 @@ -YEAR: 2017-2024 +YEAR: 2017-2026 COPYRIGHT HOLDER: Yihui Xie and Posit Software, PBC From eea0101161fac874dc02991e2cda9159cc799cb0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 06:38:47 +0000 Subject: [PATCH 04/12] macOS: use /etc/paths.d/TinyTeX instead of symlinks in /usr/local/bin Agent-Logs-Url: https://github.com/rstudio/tinytex/sessions/13dd76fe-b745-4b61-8faf-aa3314531d3e Co-authored-by: yihui <163582+yihui@users.noreply.github.com> --- R/install.R | 40 +++++++++++++++------------------------ R/tlmgr.R | 27 ++++++++++++++++++++++---- tools/install-bin-unix.sh | 26 +++++++------------------ tools/install-unx.sh | 17 +++++++++++++---- 4 files changed, 58 insertions(+), 52 deletions(-) diff --git a/R/install.R b/R/install.R index 65f8b0af7..42628f799 100644 --- a/R/install.R +++ b/R/install.R @@ -33,8 +33,10 @@ #' installed. By default, a vector of all currently installed LaTeX packages #' if an existing installation of TinyTeX is found. If you want a fresh #' installation, you may use \code{extra_packages = NULL}. -#' @param add_path Whether to run the command \command{tlmgr path add} to add -#' the bin path of TeX Live to the system environment variable \var{PATH}. +#' @param add_path Whether to add the bin path of TeX Live to the system +#' environment variable \var{PATH}. On macOS, this writes the bin path to +#' \file{/etc/paths.d/TinyTeX}; on Linux, it runs \command{tlmgr path add}; +#' on Windows, it runs \command{tlmgr path add}. #' @references See the TinyTeX documentation (\url{https://yihui.org/tinytex/}) #' for the default installation directories on different platforms. #' @note If you really want to disable the installation, you may set the @@ -113,8 +115,6 @@ install_tinytex = function( switch( os, 'unix' = { - restore_local_bin = check_local_bin() - if (is.function(restore_local_bin)) on.exit(restore_local_bin(), add = TRUE) if (os_index != 3 && !any(dir_exists(c('~/bin', '~/.local/bin')))) on.exit(message( 'You may have to restart your system after installing TinyTeX to make sure ', '~/bin appears in your PATH variable (https://github.com/rstudio/tinytex/issues/16).' @@ -291,23 +291,6 @@ win_app_dir = function(s) { # test if path is pure ASCII and has no spaces valid_path = function(x) grepl('^[!-~]+$', x) -# check if /usr/local/bin on macOS is writable -check_local_bin = function() { - if (os_index != 3 || is_writable(p <- '/usr/local/bin')) return(invisible(NULL)) - message( - 'The directory ', p, ' is not writable. I recommend that you make it writable. ', - 'See https://github.com/rstudio/tinytex/issues/24 for more info.' - ) - if (!dir_exists(p)) osascript(paste('mkdir -p', p)) - # save current ownership before changing it (to restore later) - prev_owner = system2('stat', c('-f', '%Su:%Sg', p), stdout = TRUE) - user = system2('whoami', stdout = TRUE) - osascript(sprintf('chown %s:admin %s', user, p)) - # return a cleanup function to restore the original ownership (only if prev_owner is valid) - if (length(prev_owner) == 1 && grepl('^[^:]+:[^:]+$', prev_owner)) - function() osascript(sprintf('chown %s %s', prev_owner, p)) -} - osascript = function(cmd) { if (system(sprintf( "/usr/bin/osascript -e 'do shell script \"%s\" with administrator privileges'", cmd @@ -593,9 +576,10 @@ download_installer = function(file, version) { #' #' The function \code{copy_tinytex()} copies the existing TinyTeX installation #' to another directory (e.g., a portable device like a USB stick). The function -#' \code{use_tinytex()} runs \command{tlmgr path add} to add the copy of TinyTeX -#' in an existing folder to the \code{PATH} variable of the current system, so -#' that you can use utilities such as \command{tlmgr} and \command{pdflatex}, +#' \code{use_tinytex()} adds the copy of TinyTeX in an existing folder to the +#' \code{PATH} variable of the current system (on macOS via +#' \file{/etc/paths.d/TinyTeX}; on other platforms via \command{tlmgr path add}), +#' so that you can use utilities such as \command{tlmgr} and \command{pdflatex}, #' etc. #' @param from The root directory of the TinyTeX installation. For #' \code{copy_tinytex()}, the default value \code{tinytex_root()} should be a @@ -638,7 +622,13 @@ use_tinytex = function(from = select_dir('Select TinyTeX Directory')) { if (length(d) != 1) stop("The directory '", from, "' does not contain TinyTeX.") p = file.path(d, 'tlmgr') if (os == 'windows') p = paste0(p, '.bat') - if (system2(p, c('path', 'add')) != 0) stop( + if (is_macos()) { + # on macOS, add the bin dir to /etc/paths.d instead of using symlinks in /usr/local/bin + tmp = tempfile(tmpdir = '/tmp') + writeLines(normalizePath(d), tmp) + osascript(sprintf('cp %s /etc/paths.d/TinyTeX', tmp)) + unlink(tmp) + } else if (system2(p, c('path', 'add')) != 0) stop( "Failed to add '", d, "' to your system's environment variable PATH. You may ", "consider the fallback approach, i.e., set options(tinytex.tlmgr.path = '", p, "')." ) diff --git a/R/tlmgr.R b/R/tlmgr.R index 3e94540e2..532b084f2 100644 --- a/R/tlmgr.R +++ b/R/tlmgr.R @@ -259,12 +259,31 @@ delete_tlpdb_files = function() { } #' @param action On Unix, add/remove symlinks of binaries to/from the system's -#' \code{PATH}. On Windows, add/remove the path to the TeXLive binary -#' directory to/from the system environment variable \code{PATH}. +#' \code{PATH}. On macOS, add/remove the TinyTeX bin path to/from +#' \file{/etc/paths.d/TinyTeX}. On Windows, add/remove the path to the +#' TeXLive binary directory to/from the system environment variable +#' \code{PATH}. #' @rdname tlmgr #' @export -tlmgr_path = function(action = c('add', 'remove')) - tlmgr(c('path', match.arg(action)), .quiet = TRUE) +tlmgr_path = function(action = c('add', 'remove')) { + action = match.arg(action) + if (is_macos()) { + paths_file = '/etc/paths.d/TinyTeX' + if (action == 'add') { + f = getOption('tinytex.tlmgr.path', find_tlmgr(extra = TRUE)) + if (length(f) == 0 || !file_test('-x', f)) return(invisible(1L)) + bin = normalizePath(dirname(f)) + tmp = tempfile(tmpdir = '/tmp') + writeLines(bin, tmp) + osascript(sprintf('cp %s %s', tmp, paths_file)) + unlink(tmp) + } else { + osascript(sprintf('rm -f %s', paths_file)) + } + return(invisible(0L)) + } + tlmgr(c('path', action), .quiet = TRUE) +} #' @rdname tlmgr #' @export diff --git a/tools/install-bin-unix.sh b/tools/install-bin-unix.sh index da071bbc2..dc1873595 100755 --- a/tools/install-bin-unix.sh +++ b/tools/install-bin-unix.sh @@ -125,23 +125,11 @@ fi ./tlmgr postaction install script xetex # GH issue #313 if [ $OSNAME = 'Darwin' ]; then - # create the dir if it doesn't exist - if [ ! -d /usr/local/bin ]; then - echo "Admin privilege (password) is required to create the directory /usr/local/bin:" - sudo mkdir -p /usr/local/bin - fi - # temporarily change owner of the dir to allow creating symlinks - if [ ! -w /usr/local/bin ]; then - echo "Admin privilege (password) is required to make /usr/local/bin writable:" - PREV_OWNER=$(stat -f "%Su:%Sg" /usr/local/bin) - sudo chown `whoami`:admin /usr/local/bin - fi -fi - -./tlmgr path add - -# Restore original ownership of /usr/local/bin on macOS -if [ "$OSNAME" = 'Darwin' ] && [ -n "${PREV_OWNER-}" ]; then - echo "Admin privilege (password) is required to restore ownership of /usr/local/bin:" - sudo chown ${PREV_OWNER} /usr/local/bin + # add TinyTeX's bin path to /etc/paths.d instead of creating symlinks in /usr/local/bin + # (at this point we are in $TEXDIR/bin/*/ after the `cd` above) + TINYTEX_BINDIR="$(pwd)" + echo "Admin privilege (password) is required to set up the PATH for TinyTeX:" + printf '%s\n' "$TINYTEX_BINDIR" | sudo tee /etc/paths.d/TinyTeX > /dev/null +else + ./tlmgr path add fi diff --git a/tools/install-unx.sh b/tools/install-unx.sh index 39020f806..c90d29030 100755 --- a/tools/install-unx.sh +++ b/tools/install-unx.sh @@ -29,10 +29,19 @@ rm -r $OLDPWD/install-tl-* $TEXDIR/bin/*/tlmgr install $(download https://tinytex.yihui.org/pkgs-custom.txt | tr '\n' ' ') -if [ "$1" = '--admin' ]; then - if [ "$2" != '--no-path' ]; then - sudo $TEXDIR/bin/*/tlmgr path add +if [ $(uname) = 'Darwin' ]; then + # write TinyTeX's bin path to /etc/paths.d instead of creating symlinks in /usr/local/bin; + # skip only when --admin AND --no-path are both given (i.e. ! (admin && no-path)) + if [ "$1" != '--admin' ] || [ "$2" != '--no-path' ]; then + echo "Admin privilege (password) is required to set up the PATH for TinyTeX:" + printf '%s\n' "$(ls -d $TEXDIR/bin/*/)" | sudo tee /etc/paths.d/TinyTeX > /dev/null || true fi else - $TEXDIR/bin/*/tlmgr path add || true + if [ "$1" = '--admin' ]; then + if [ "$2" != '--no-path' ]; then + sudo $TEXDIR/bin/*/tlmgr path add + fi + else + $TEXDIR/bin/*/tlmgr path add || true + fi fi From 76970e114769d7758f05bd09d7541b3a42bd2450 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:58:01 +0000 Subject: [PATCH 05/12] Address review comments: factor macos_path/find_tinytex_bin, idempotency, osascript returns status, roxygenize Agent-Logs-Url: https://github.com/rstudio/tinytex/sessions/3a5f54ec-a104-4514-843a-93ea370973bf Co-authored-by: yihui <163582+yihui@users.noreply.github.com> --- R/install.R | 37 +++++++++++++++++++++++++------------ R/tlmgr.R | 38 ++++++++++++++------------------------ man/copy_tinytex.Rd | 6 +++--- man/install_tinytex.Rd | 4 ++-- man/tlmgr.Rd | 8 +++++--- tools/install-bin-unix.sh | 7 ++++--- tools/install-unx.sh | 7 +++++-- 7 files changed, 58 insertions(+), 49 deletions(-) diff --git a/R/install.R b/R/install.R index 42628f799..b1359fcbe 100644 --- a/R/install.R +++ b/R/install.R @@ -34,9 +34,7 @@ #' if an existing installation of TinyTeX is found. If you want a fresh #' installation, you may use \code{extra_packages = NULL}. #' @param add_path Whether to add the bin path of TeX Live to the system -#' environment variable \var{PATH}. On macOS, this writes the bin path to -#' \file{/etc/paths.d/TinyTeX}; on Linux, it runs \command{tlmgr path add}; -#' on Windows, it runs \command{tlmgr path add}. +#' environment variable \var{PATH}. See \code{\link{tlmgr_path}()}. #' @references See the TinyTeX documentation (\url{https://yihui.org/tinytex/}) #' for the default installation directories on different platforms. #' @note If you really want to disable the installation, you may set the @@ -292,12 +290,32 @@ win_app_dir = function(s) { valid_path = function(x) grepl('^[!-~]+$', x) osascript = function(cmd) { - if (system(sprintf( + ret = system(sprintf( "/usr/bin/osascript -e 'do shell script \"%s\" with administrator privileges'", cmd - )) != 0) warning( + )) + if (ret != 0) warning( "Please run this command in your Terminal (password required):\n sudo ", cmd, call. = FALSE ) + invisible(ret) +} + +# add/remove TinyTeX's bin path to/from /etc/paths.d/TinyTeX on macOS; +# if adding and the file already contains the desired path, skip the operation +macos_path = function(dir = NULL, add = TRUE) { + paths_file = '/etc/paths.d/TinyTeX' + if (add) { + if (is.null(dir) || dir == '') return(invisible(1L)) + if (file.exists(paths_file) && + identical(trimws(readLines(paths_file, warn = FALSE)), dir)) + return(invisible(0L)) + tmp = tempfile() + on.exit(unlink(tmp), add = TRUE) + writeLines(dir, tmp) + invisible(osascript(sprintf('cp %s %s', tmp, paths_file))) + } else { + invisible(osascript(sprintf('rm -f %s', paths_file))) + } } install_tinytex_source = function(repo = '', dir, version, add_path, extra_packages) { @@ -577,8 +595,7 @@ download_installer = function(file, version) { #' The function \code{copy_tinytex()} copies the existing TinyTeX installation #' to another directory (e.g., a portable device like a USB stick). The function #' \code{use_tinytex()} adds the copy of TinyTeX in an existing folder to the -#' \code{PATH} variable of the current system (on macOS via -#' \file{/etc/paths.d/TinyTeX}; on other platforms via \command{tlmgr path add}), +#' \code{PATH} variable of the current system via \code{\link{tlmgr_path}()}, #' so that you can use utilities such as \command{tlmgr} and \command{pdflatex}, #' etc. #' @param from The root directory of the TinyTeX installation. For @@ -623,11 +640,7 @@ use_tinytex = function(from = select_dir('Select TinyTeX Directory')) { p = file.path(d, 'tlmgr') if (os == 'windows') p = paste0(p, '.bat') if (is_macos()) { - # on macOS, add the bin dir to /etc/paths.d instead of using symlinks in /usr/local/bin - tmp = tempfile(tmpdir = '/tmp') - writeLines(normalizePath(d), tmp) - osascript(sprintf('cp %s /etc/paths.d/TinyTeX', tmp)) - unlink(tmp) + macos_path(normalizePath(d)) } else if (system2(p, c('path', 'add')) != 0) stop( "Failed to add '", d, "' to your system's environment variable PATH. You may ", "consider the fallback approach, i.e., set options(tinytex.tlmgr.path = '", p, "')." diff --git a/R/tlmgr.R b/R/tlmgr.R index 532b084f2..169a2e32a 100644 --- a/R/tlmgr.R +++ b/R/tlmgr.R @@ -59,12 +59,16 @@ tlmgr = function(args = character(), usermode = FALSE, ..., .quiet = FALSE) { # check if it is necessary to add ~/Library/TinyTeX/bin/*/ to PATH #' @importFrom xfun is_linux is_unix is_macos is_windows with_ext -tweak_path = function() { - # check tlmgr exists under the default installation dir of TinyTeX, or the - # global option tinytex.tlmgr.path +# return the bin directory of TinyTeX (empty string if not found) +find_tinytex_bin = function() { f = getOption('tinytex.tlmgr.path', find_tlmgr(extra = TRUE)) - if (length(f) == 0 || !file_test('-x', f)) return() - bin = normalizePath(dirname(f)) + if (length(f) == 0 || !file_test('-x', f)) return('') + normalizePath(dirname(f)) +} + +tweak_path = function() { + bin = find_tinytex_bin() + if (bin == '') return() # if the pdftex from TinyTeX is already on PATH, no need to adjust the PATH if ((p <- Sys.which('pdftex')) != '') { p2 = with_ext(file.path(bin, 'pdftex'), xfun::file_ext(p)) @@ -258,30 +262,16 @@ delete_tlpdb_files = function() { )) } -#' @param action On Unix, add/remove symlinks of binaries to/from the system's -#' \code{PATH}. On macOS, add/remove the TinyTeX bin path to/from -#' \file{/etc/paths.d/TinyTeX}. On Windows, add/remove the path to the -#' TeXLive binary directory to/from the system environment variable +#' @param action On macOS, add/remove the TinyTeX bin path to/from +#' \file{/etc/paths.d/TinyTeX}. On other Unix systems, add/remove symlinks of +#' binaries to/from the system's \code{PATH}. On Windows, add/remove the path +#' to the TeXLive binary directory to/from the system environment variable #' \code{PATH}. #' @rdname tlmgr #' @export tlmgr_path = function(action = c('add', 'remove')) { action = match.arg(action) - if (is_macos()) { - paths_file = '/etc/paths.d/TinyTeX' - if (action == 'add') { - f = getOption('tinytex.tlmgr.path', find_tlmgr(extra = TRUE)) - if (length(f) == 0 || !file_test('-x', f)) return(invisible(1L)) - bin = normalizePath(dirname(f)) - tmp = tempfile(tmpdir = '/tmp') - writeLines(bin, tmp) - osascript(sprintf('cp %s %s', tmp, paths_file)) - unlink(tmp) - } else { - osascript(sprintf('rm -f %s', paths_file)) - } - return(invisible(0L)) - } + if (is_macos()) return(invisible(macos_path(find_tinytex_bin(), action == 'add'))) tlmgr(c('path', action), .quiet = TRUE) } diff --git a/man/copy_tinytex.Rd b/man/copy_tinytex.Rd index 96eca0b78..d4db5842a 100644 --- a/man/copy_tinytex.Rd +++ b/man/copy_tinytex.Rd @@ -30,9 +30,9 @@ TinyTeX after copying it.} \description{ The function \code{copy_tinytex()} copies the existing TinyTeX installation to another directory (e.g., a portable device like a USB stick). The function -\code{use_tinytex()} runs \command{tlmgr path add} to add the copy of TinyTeX -in an existing folder to the \code{PATH} variable of the current system, so -that you can use utilities such as \command{tlmgr} and \command{pdflatex}, +\code{use_tinytex()} adds the copy of TinyTeX in an existing folder to the +\code{PATH} variable of the current system via \code{\link{tlmgr_path}()}, +so that you can use utilities such as \command{tlmgr} and \command{pdflatex}, etc. } \note{ diff --git a/man/install_tinytex.Rd b/man/install_tinytex.Rd index 6e3c4ae3c..b32f82a59 100644 --- a/man/install_tinytex.Rd +++ b/man/install_tinytex.Rd @@ -58,8 +58,8 @@ installed. By default, a vector of all currently installed LaTeX packages if an existing installation of TinyTeX is found. If you want a fresh installation, you may use \code{extra_packages = NULL}.} -\item{add_path}{Whether to run the command \command{tlmgr path add} to add -the bin path of TeX Live to the system environment variable \var{PATH}.} +\item{add_path}{Whether to add the bin path of TeX Live to the system +environment variable \var{PATH}. See \code{\link{tlmgr_path}()}.} \item{packages}{Whether to reinstall all currently installed packages.} diff --git a/man/tlmgr.Rd b/man/tlmgr.Rd index ddc25be75..531e5e05d 100644 --- a/man/tlmgr.Rd +++ b/man/tlmgr.Rd @@ -101,9 +101,11 @@ format and hyphenation files after updating \pkg{tlmgr}.} (where \verb{HASH} is an MD5 hash) under the \file{tlpkg} directory of the root directory of TeX Live after updating.} -\item{action}{On Unix, add/remove symlinks of binaries to/from the system's -\code{PATH}. On Windows, add/remove the path to the TeXLive binary -directory to/from the system environment variable \code{PATH}.} +\item{action}{On macOS, add/remove the TinyTeX bin path to/from +\file{/etc/paths.d/TinyTeX}. On other Unix systems, add/remove symlinks of +binaries to/from the system's \code{PATH}. On Windows, add/remove the path +to the TeXLive binary directory to/from the system environment variable +\code{PATH}.} \item{url}{The URL of the CTAN mirror. If \code{NULL}, show the current repository, otherwise set the repository. See the \code{repository} diff --git a/tools/install-bin-unix.sh b/tools/install-bin-unix.sh index dc1873595..d625d777b 100755 --- a/tools/install-bin-unix.sh +++ b/tools/install-bin-unix.sh @@ -127,9 +127,10 @@ fi if [ $OSNAME = 'Darwin' ]; then # add TinyTeX's bin path to /etc/paths.d instead of creating symlinks in /usr/local/bin # (at this point we are in $TEXDIR/bin/*/ after the `cd` above) - TINYTEX_BINDIR="$(pwd)" - echo "Admin privilege (password) is required to set up the PATH for TinyTeX:" - printf '%s\n' "$TINYTEX_BINDIR" | sudo tee /etc/paths.d/TinyTeX > /dev/null + if ! grep -qxF "$(pwd)" /etc/paths.d/TinyTeX 2>/dev/null; then + echo "Admin privilege (password) is required to set up the PATH for TinyTeX:" + printf '%s\n' "$(pwd)" | sudo tee /etc/paths.d/TinyTeX > /dev/null + fi else ./tlmgr path add fi diff --git a/tools/install-unx.sh b/tools/install-unx.sh index c90d29030..7e70bc031 100755 --- a/tools/install-unx.sh +++ b/tools/install-unx.sh @@ -33,8 +33,11 @@ if [ $(uname) = 'Darwin' ]; then # write TinyTeX's bin path to /etc/paths.d instead of creating symlinks in /usr/local/bin; # skip only when --admin AND --no-path are both given (i.e. ! (admin && no-path)) if [ "$1" != '--admin' ] || [ "$2" != '--no-path' ]; then - echo "Admin privilege (password) is required to set up the PATH for TinyTeX:" - printf '%s\n' "$(ls -d $TEXDIR/bin/*/)" | sudo tee /etc/paths.d/TinyTeX > /dev/null || true + BINDIR="$(ls -d $TEXDIR/bin/*/)" + if ! grep -qxF "$BINDIR" /etc/paths.d/TinyTeX 2>/dev/null; then + echo "Admin privilege (password) is required to set up the PATH for TinyTeX:" + printf '%s\n' "$BINDIR" | sudo tee /etc/paths.d/TinyTeX > /dev/null || true + fi fi else if [ "$1" = '--admin' ]; then From 14a151f34c72846edd747bc77b2657c60c5c7107 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 17:37:37 +0000 Subject: [PATCH 06/12] Address new review comments: revert install-unx.sh, --no-path flag, macos_path refactor Agent-Logs-Url: https://github.com/rstudio/tinytex/sessions/4ed6cb3b-42e3-4296-9381-ea23c5541e30 Co-authored-by: yihui <163582+yihui@users.noreply.github.com> --- R/install.R | 23 ++++++++++++++--------- R/tlmgr.R | 5 +++-- tools/install-bin-unix.sh | 11 +++++++++-- tools/install-unx.sh | 20 ++++---------------- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/R/install.R b/R/install.R index b1359fcbe..a06ace254 100644 --- a/R/install.R +++ b/R/install.R @@ -297,25 +297,27 @@ osascript = function(cmd) { "Please run this command in your Terminal (password required):\n sudo ", cmd, call. = FALSE ) - invisible(ret) + ret } # add/remove TinyTeX's bin path to/from /etc/paths.d/TinyTeX on macOS; # if adding and the file already contains the desired path, skip the operation -macos_path = function(dir = NULL, add = TRUE) { +macos_path = function(dir = NULL, action = 'add') { paths_file = '/etc/paths.d/TinyTeX' - if (add) { - if (is.null(dir) || dir == '') return(invisible(1L)) + add = action == 'add' + cmd = if (add) { + if (is.null(dir) || dir == '') return(1L) if (file.exists(paths_file) && - identical(trimws(readLines(paths_file, warn = FALSE)), dir)) - return(invisible(0L)) + identical(readLines(paths_file, warn = FALSE), dir)) + return(0L) tmp = tempfile() on.exit(unlink(tmp), add = TRUE) writeLines(dir, tmp) - invisible(osascript(sprintf('cp %s %s', tmp, paths_file))) + sprintf('cp %s %s', tmp, paths_file) } else { - invisible(osascript(sprintf('rm -f %s', paths_file))) + sprintf('rm -f %s', paths_file) } + osascript(cmd) } install_tinytex_source = function(repo = '', dir, version, add_path, extra_packages) { @@ -640,7 +642,10 @@ use_tinytex = function(from = select_dir('Select TinyTeX Directory')) { p = file.path(d, 'tlmgr') if (os == 'windows') p = paste0(p, '.bat') if (is_macos()) { - macos_path(normalizePath(d)) + if (macos_path(normalizePath(d)) != 0) stop( + "Failed to add '", d, "' to /etc/paths.d/TinyTeX. You may consider the fallback ", + "approach, i.e., set options(tinytex.tlmgr.path = '", p, "')." + ) } else if (system2(p, c('path', 'add')) != 0) stop( "Failed to add '", d, "' to your system's environment variable PATH. You may ", "consider the fallback approach, i.e., set options(tinytex.tlmgr.path = '", p, "')." diff --git a/R/tlmgr.R b/R/tlmgr.R index 169a2e32a..fa694e59d 100644 --- a/R/tlmgr.R +++ b/R/tlmgr.R @@ -271,8 +271,9 @@ delete_tlpdb_files = function() { #' @export tlmgr_path = function(action = c('add', 'remove')) { action = match.arg(action) - if (is_macos()) return(invisible(macos_path(find_tinytex_bin(), action == 'add'))) - tlmgr(c('path', action), .quiet = TRUE) + if (is_macos()) { + invisible(macos_path(find_tinytex_bin(), action)) + } else tlmgr(c('path', action), .quiet = TRUE) } #' @rdname tlmgr diff --git a/tools/install-bin-unix.sh b/tools/install-bin-unix.sh index d625d777b..a6df72651 100755 --- a/tools/install-bin-unix.sh +++ b/tools/install-bin-unix.sh @@ -127,9 +127,16 @@ fi if [ $OSNAME = 'Darwin' ]; then # add TinyTeX's bin path to /etc/paths.d instead of creating symlinks in /usr/local/bin # (at this point we are in $TEXDIR/bin/*/ after the `cd` above) - if ! grep -qxF "$(pwd)" /etc/paths.d/TinyTeX 2>/dev/null; then + if [ "$1" != '--no-path' ] && ! grep -qxF "$(pwd)" /etc/paths.d/TinyTeX 2>/dev/null; then echo "Admin privilege (password) is required to set up the PATH for TinyTeX:" - printf '%s\n' "$(pwd)" | sudo tee /etc/paths.d/TinyTeX > /dev/null + if printf '%s\n' "$(pwd)" | sudo tee /etc/paths.d/TinyTeX > /dev/null; then + # /etc/paths.d is not picked up until the shell is restarted; export PATH now + export PATH="$PATH:$(pwd)" + else + echo "To set up PATH manually, run the following command and add it to your shell startup profile (e.g. ~/.zshrc):" + echo " export PATH=\$PATH:$(pwd)" + export PATH="$PATH:$(pwd)" + fi fi else ./tlmgr path add diff --git a/tools/install-unx.sh b/tools/install-unx.sh index 7e70bc031..39020f806 100755 --- a/tools/install-unx.sh +++ b/tools/install-unx.sh @@ -29,22 +29,10 @@ rm -r $OLDPWD/install-tl-* $TEXDIR/bin/*/tlmgr install $(download https://tinytex.yihui.org/pkgs-custom.txt | tr '\n' ' ') -if [ $(uname) = 'Darwin' ]; then - # write TinyTeX's bin path to /etc/paths.d instead of creating symlinks in /usr/local/bin; - # skip only when --admin AND --no-path are both given (i.e. ! (admin && no-path)) - if [ "$1" != '--admin' ] || [ "$2" != '--no-path' ]; then - BINDIR="$(ls -d $TEXDIR/bin/*/)" - if ! grep -qxF "$BINDIR" /etc/paths.d/TinyTeX 2>/dev/null; then - echo "Admin privilege (password) is required to set up the PATH for TinyTeX:" - printf '%s\n' "$BINDIR" | sudo tee /etc/paths.d/TinyTeX > /dev/null || true - fi +if [ "$1" = '--admin' ]; then + if [ "$2" != '--no-path' ]; then + sudo $TEXDIR/bin/*/tlmgr path add fi else - if [ "$1" = '--admin' ]; then - if [ "$2" != '--no-path' ]; then - sudo $TEXDIR/bin/*/tlmgr path add - fi - else - $TEXDIR/bin/*/tlmgr path add || true - fi + $TEXDIR/bin/*/tlmgr path add || true fi From 67441ce163bbccf7b7246b569464ff5f36d75998 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:02:14 +0000 Subject: [PATCH 07/12] Address new review comments: --no-path for any OS, quote paths, unlink only on success, consolidate stop() Agent-Logs-Url: https://github.com/rstudio/tinytex/sessions/27d65823-31ba-4ff1-ba16-17c9c18bd877 Co-authored-by: yihui <163582+yihui@users.noreply.github.com> --- R/install.R | 19 ++++++++++--------- tools/install-bin-unix.sh | 35 +++++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/R/install.R b/R/install.R index a06ace254..a6c523141 100644 --- a/R/install.R +++ b/R/install.R @@ -311,13 +311,14 @@ macos_path = function(dir = NULL, action = 'add') { identical(readLines(paths_file, warn = FALSE), dir)) return(0L) tmp = tempfile() - on.exit(unlink(tmp), add = TRUE) writeLines(dir, tmp) - sprintf('cp %s %s', tmp, paths_file) + sprintf('cp \\"%s\\" \\"%s\\"', tmp, paths_file) } else { - sprintf('rm -f %s', paths_file) + sprintf('rm -f \\"%s\\"', paths_file) } - osascript(cmd) + ret = osascript(cmd) + if (add && ret == 0) unlink(tmp) + ret } install_tinytex_source = function(repo = '', dir, version, add_path, extra_packages) { @@ -642,11 +643,11 @@ use_tinytex = function(from = select_dir('Select TinyTeX Directory')) { p = file.path(d, 'tlmgr') if (os == 'windows') p = paste0(p, '.bat') if (is_macos()) { - if (macos_path(normalizePath(d)) != 0) stop( - "Failed to add '", d, "' to /etc/paths.d/TinyTeX. You may consider the fallback ", - "approach, i.e., set options(tinytex.tlmgr.path = '", p, "')." - ) - } else if (system2(p, c('path', 'add')) != 0) stop( + ret = macos_path(normalizePath(d)) + } else { + ret = system2(p, c('path', 'add')) + } + if (ret != 0) stop( "Failed to add '", d, "' to your system's environment variable PATH. You may ", "consider the fallback approach, i.e., set options(tinytex.tlmgr.path = '", p, "')." ) diff --git a/tools/install-bin-unix.sh b/tools/install-bin-unix.sh index a6df72651..a77ea14b0 100755 --- a/tools/install-bin-unix.sh +++ b/tools/install-bin-unix.sh @@ -124,20 +124,27 @@ fi [ $OSNAME != "Darwin" ] && ./tlmgr option sys_bin $BINDIR ./tlmgr postaction install script xetex # GH issue #313 -if [ $OSNAME = 'Darwin' ]; then - # add TinyTeX's bin path to /etc/paths.d instead of creating symlinks in /usr/local/bin - # (at this point we are in $TEXDIR/bin/*/ after the `cd` above) - if [ "$1" != '--no-path' ] && ! grep -qxF "$(pwd)" /etc/paths.d/TinyTeX 2>/dev/null; then - echo "Admin privilege (password) is required to set up the PATH for TinyTeX:" - if printf '%s\n' "$(pwd)" | sudo tee /etc/paths.d/TinyTeX > /dev/null; then - # /etc/paths.d is not picked up until the shell is restarted; export PATH now - export PATH="$PATH:$(pwd)" - else - echo "To set up PATH manually, run the following command and add it to your shell startup profile (e.g. ~/.zshrc):" - echo " export PATH=\$PATH:$(pwd)" - export PATH="$PATH:$(pwd)" +NO_PATH=0 +for arg in "$@"; do + case "$arg" in --no-path) NO_PATH=1 ;; esac +done + +if [ $NO_PATH -eq 0 ]; then + if [ $OSNAME = 'Darwin' ]; then + # add TinyTeX's bin path to /etc/paths.d instead of creating symlinks in /usr/local/bin + # (at this point we are in $TEXDIR/bin/*/ after the `cd` above) + if ! grep -qxF "$(pwd)" /etc/paths.d/TinyTeX 2>/dev/null; then + echo "Admin privilege (password) is required to set up the PATH for TinyTeX:" + if printf '%s\n' "$(pwd)" | sudo tee /etc/paths.d/TinyTeX > /dev/null; then + # /etc/paths.d is not picked up until the shell is restarted; export PATH now + export PATH="$PATH:$(pwd)" + else + echo "To set up PATH manually, run the following command and add it to your shell startup profile (e.g. ~/.zshrc):" + echo " export PATH=\$PATH:$(pwd)" + export PATH="$PATH:$(pwd)" + fi fi + else + ./tlmgr path add fi -else - ./tlmgr path add fi From 6f220656a804c6d0ed4727e145a2e44b95ef6fde Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Fri, 27 Mar 2026 13:17:10 -0500 Subject: [PATCH 08/12] one liners if-else --- R/install.R | 8 ++------ R/tlmgr.R | 3 +-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/R/install.R b/R/install.R index a6c523141..e799aa8c3 100644 --- a/R/install.R +++ b/R/install.R @@ -642,12 +642,8 @@ use_tinytex = function(from = select_dir('Select TinyTeX Directory')) { if (length(d) != 1) stop("The directory '", from, "' does not contain TinyTeX.") p = file.path(d, 'tlmgr') if (os == 'windows') p = paste0(p, '.bat') - if (is_macos()) { - ret = macos_path(normalizePath(d)) - } else { - ret = system2(p, c('path', 'add')) - } - if (ret != 0) stop( + ret = if (is_macos()) macos_path(normalizePath(d)) else system2(p, c('path', 'add')) + if (ret != 0) warning( "Failed to add '", d, "' to your system's environment variable PATH. You may ", "consider the fallback approach, i.e., set options(tinytex.tlmgr.path = '", p, "')." ) diff --git a/R/tlmgr.R b/R/tlmgr.R index fa694e59d..9438c2802 100644 --- a/R/tlmgr.R +++ b/R/tlmgr.R @@ -62,8 +62,7 @@ tlmgr = function(args = character(), usermode = FALSE, ..., .quiet = FALSE) { # return the bin directory of TinyTeX (empty string if not found) find_tinytex_bin = function() { f = getOption('tinytex.tlmgr.path', find_tlmgr(extra = TRUE)) - if (length(f) == 0 || !file_test('-x', f)) return('') - normalizePath(dirname(f)) + if (length(f) == 0 || !file_test('-x', f)) '' else normalizePath(dirname(f)) } tweak_path = function() { From 42fbe7f07d12cdec8e055211faf7e616eaa6f563 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Fri, 27 Mar 2026 13:19:21 -0500 Subject: [PATCH 09/12] TeXLive -> TeX Live --- R/tlmgr.R | 2 +- man/tlmgr.Rd | 2 +- tools/install-windows.ps1 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R/tlmgr.R b/R/tlmgr.R index 9438c2802..d670630fb 100644 --- a/R/tlmgr.R +++ b/R/tlmgr.R @@ -264,7 +264,7 @@ delete_tlpdb_files = function() { #' @param action On macOS, add/remove the TinyTeX bin path to/from #' \file{/etc/paths.d/TinyTeX}. On other Unix systems, add/remove symlinks of #' binaries to/from the system's \code{PATH}. On Windows, add/remove the path -#' to the TeXLive binary directory to/from the system environment variable +#' to the TeX Live binary directory to/from the system environment variable #' \code{PATH}. #' @rdname tlmgr #' @export diff --git a/man/tlmgr.Rd b/man/tlmgr.Rd index 531e5e05d..32e09620c 100644 --- a/man/tlmgr.Rd +++ b/man/tlmgr.Rd @@ -104,7 +104,7 @@ root directory of TeX Live after updating.} \item{action}{On macOS, add/remove the TinyTeX bin path to/from \file{/etc/paths.d/TinyTeX}. On other Unix systems, add/remove symlinks of binaries to/from the system's \code{PATH}. On Windows, add/remove the path -to the TeXLive binary directory to/from the system environment variable +to the TeX Live binary directory to/from the system environment variable \code{PATH}.} \item{url}{The URL of the CTAN mirror. If \code{NULL}, show the current diff --git a/tools/install-windows.ps1 b/tools/install-windows.ps1 index 628bc6149..406d42018 100644 --- a/tools/install-windows.ps1 +++ b/tools/install-windows.ps1 @@ -24,7 +24,7 @@ Add-Content tinytex.profile 'TEXMFVAR $TEXMFSYSVAR' # download the custom package list Invoke-WebRequest 'https://tinytex.yihui.org/pkgs-custom.txt' -OutFile pkgs-custom.txt -# an automated installation of TeXLive (infrastructure only) +# an automated installation of TeX Live (infrastructure only) cd install-tl-* (Get-Content install-tl-windows.bat) -notmatch '^\s*pause\s*$' | Set-Content install-tl-windows.bat mkdir TinyTeX @@ -39,7 +39,7 @@ ni .tinytex | Out-Null del install-tl.log, install-tl, install-tl-windows.bat -ErrorAction SilentlyContinue cd .. -# TeXLive installed to ./TinyTeX; move it to APPDATA +# TeX Live installed to ./TinyTeX; move it to APPDATA rd $env:APPDATA\TinyTeX -r -fo -ErrorAction SilentlyContinue rd $env:APPDATA\TinyTeX -r -fo -ErrorAction SilentlyContinue move TinyTeX $env:APPDATA From 71d49e32869c9b2b9adf5d44bf343ae9403f457a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:40:14 +0000 Subject: [PATCH 10/12] Check /usr/local/bin writable on macOS; message()/warning() in osascript(); move quote escaping to osascript() Agent-Logs-Url: https://github.com/rstudio/tinytex/sessions/1bca78ef-48fe-4a0f-bf5c-3be15fdc5da8 Co-authored-by: yihui <163582+yihui@users.noreply.github.com> --- R/install.R | 15 +++++++++------ R/tlmgr.R | 11 ++++++----- man/tlmgr.Rd | 9 +++++---- tools/install-bin-unix.sh | 8 +++++--- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/R/install.R b/R/install.R index e799aa8c3..234fe8cfa 100644 --- a/R/install.R +++ b/R/install.R @@ -290,16 +290,19 @@ win_app_dir = function(s) { valid_path = function(x) grepl('^[!-~]+$', x) osascript = function(cmd) { + message("Requesting admin privilege to run: sudo ", cmd) + escaped = gsub('"', '\\"', cmd, fixed = TRUE) ret = system(sprintf( - "/usr/bin/osascript -e 'do shell script \"%s\" with administrator privileges'", cmd + "/usr/bin/osascript -e 'do shell script \"%s\" with administrator privileges'", escaped )) if (ret != 0) warning( - "Please run this command in your Terminal (password required):\n sudo ", - cmd, call. = FALSE + "Please run the above command in your Terminal (password required).", call. = FALSE ) ret } +macos_local_bin_writable = function() file.access('/usr/local/bin', 2) == 0 + # add/remove TinyTeX's bin path to/from /etc/paths.d/TinyTeX on macOS; # if adding and the file already contains the desired path, skip the operation macos_path = function(dir = NULL, action = 'add') { @@ -312,9 +315,9 @@ macos_path = function(dir = NULL, action = 'add') { return(0L) tmp = tempfile() writeLines(dir, tmp) - sprintf('cp \\"%s\\" \\"%s\\"', tmp, paths_file) + sprintf('cp "%s" "%s"', tmp, paths_file) } else { - sprintf('rm -f \\"%s\\"', paths_file) + sprintf('rm -f "%s"', paths_file) } ret = osascript(cmd) if (add && ret == 0) unlink(tmp) @@ -642,7 +645,7 @@ use_tinytex = function(from = select_dir('Select TinyTeX Directory')) { if (length(d) != 1) stop("The directory '", from, "' does not contain TinyTeX.") p = file.path(d, 'tlmgr') if (os == 'windows') p = paste0(p, '.bat') - ret = if (is_macos()) macos_path(normalizePath(d)) else system2(p, c('path', 'add')) + ret = if (is_macos() && !macos_local_bin_writable()) macos_path(normalizePath(d)) else system2(p, c('path', 'add')) if (ret != 0) warning( "Failed to add '", d, "' to your system's environment variable PATH. You may ", "consider the fallback approach, i.e., set options(tinytex.tlmgr.path = '", p, "')." diff --git a/R/tlmgr.R b/R/tlmgr.R index d670630fb..c3bd916c2 100644 --- a/R/tlmgr.R +++ b/R/tlmgr.R @@ -261,16 +261,17 @@ delete_tlpdb_files = function() { )) } -#' @param action On macOS, add/remove the TinyTeX bin path to/from -#' \file{/etc/paths.d/TinyTeX}. On other Unix systems, add/remove symlinks of -#' binaries to/from the system's \code{PATH}. On Windows, add/remove the path -#' to the TeX Live binary directory to/from the system environment variable +#' @param action On macOS, if \file{/usr/local/bin} is not writable, +#' add/remove the TinyTeX bin path to/from \file{/etc/paths.d/TinyTeX}; +#' otherwise (or on other Unix systems), add/remove symlinks of binaries +#' to/from the system's \code{PATH}. On Windows, add/remove the path to the +#' TeX Live binary directory to/from the system environment variable #' \code{PATH}. #' @rdname tlmgr #' @export tlmgr_path = function(action = c('add', 'remove')) { action = match.arg(action) - if (is_macos()) { + if (is_macos() && !macos_local_bin_writable()) { invisible(macos_path(find_tinytex_bin(), action)) } else tlmgr(c('path', action), .quiet = TRUE) } diff --git a/man/tlmgr.Rd b/man/tlmgr.Rd index 32e09620c..aa5b96d1b 100644 --- a/man/tlmgr.Rd +++ b/man/tlmgr.Rd @@ -101,10 +101,11 @@ format and hyphenation files after updating \pkg{tlmgr}.} (where \verb{HASH} is an MD5 hash) under the \file{tlpkg} directory of the root directory of TeX Live after updating.} -\item{action}{On macOS, add/remove the TinyTeX bin path to/from -\file{/etc/paths.d/TinyTeX}. On other Unix systems, add/remove symlinks of -binaries to/from the system's \code{PATH}. On Windows, add/remove the path -to the TeX Live binary directory to/from the system environment variable +\item{action}{On macOS, if \file{/usr/local/bin} is not writable, +add/remove the TinyTeX bin path to/from \file{/etc/paths.d/TinyTeX}; +otherwise (or on other Unix systems), add/remove symlinks of binaries +to/from the system's \code{PATH}. On Windows, add/remove the path to the +TeX Live binary directory to/from the system environment variable \code{PATH}.} \item{url}{The URL of the CTAN mirror. If \code{NULL}, show the current diff --git a/tools/install-bin-unix.sh b/tools/install-bin-unix.sh index a77ea14b0..a7be1830f 100755 --- a/tools/install-bin-unix.sh +++ b/tools/install-bin-unix.sh @@ -131,9 +131,11 @@ done if [ $NO_PATH -eq 0 ]; then if [ $OSNAME = 'Darwin' ]; then - # add TinyTeX's bin path to /etc/paths.d instead of creating symlinks in /usr/local/bin - # (at this point we are in $TEXDIR/bin/*/ after the `cd` above) - if ! grep -qxF "$(pwd)" /etc/paths.d/TinyTeX 2>/dev/null; then + if [ -w /usr/local/bin ]; then + ./tlmgr path add + elif ! grep -qxF "$(pwd)" /etc/paths.d/TinyTeX 2>/dev/null; then + # add TinyTeX's bin path to /etc/paths.d instead of creating symlinks in /usr/local/bin + # (at this point we are in $TEXDIR/bin/*/ after the `cd` above) echo "Admin privilege (password) is required to set up the PATH for TinyTeX:" if printf '%s\n' "$(pwd)" | sudo tee /etc/paths.d/TinyTeX > /dev/null; then # /etc/paths.d is not picked up until the shell is restarted; export PATH now From 598df3e4c5cc116bf6d230d91ad3c927a251e322 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Fri, 27 Mar 2026 14:15:27 -0500 Subject: [PATCH 11/12] refactor --- R/install.R | 8 ++++++-- R/tlmgr.R | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/R/install.R b/R/install.R index 234fe8cfa..bc98ed440 100644 --- a/R/install.R +++ b/R/install.R @@ -301,7 +301,11 @@ osascript = function(cmd) { ret } -macos_local_bin_writable = function() file.access('/usr/local/bin', 2) == 0 +# on macOS, if the user doesn't have write permission to /usr/local/bin, we use +# /etc/paths.d instead +use_paths_d = function() { + is_macos() && file.access('/usr/local/bin', 2) != 0 +} # add/remove TinyTeX's bin path to/from /etc/paths.d/TinyTeX on macOS; # if adding and the file already contains the desired path, skip the operation @@ -645,7 +649,7 @@ use_tinytex = function(from = select_dir('Select TinyTeX Directory')) { if (length(d) != 1) stop("The directory '", from, "' does not contain TinyTeX.") p = file.path(d, 'tlmgr') if (os == 'windows') p = paste0(p, '.bat') - ret = if (is_macos() && !macos_local_bin_writable()) macos_path(normalizePath(d)) else system2(p, c('path', 'add')) + ret = if (use_paths_d()) macos_path(normalizePath(d)) else system2(p, c('path', 'add')) if (ret != 0) warning( "Failed to add '", d, "' to your system's environment variable PATH. You may ", "consider the fallback approach, i.e., set options(tinytex.tlmgr.path = '", p, "')." diff --git a/R/tlmgr.R b/R/tlmgr.R index c3bd916c2..798f92100 100644 --- a/R/tlmgr.R +++ b/R/tlmgr.R @@ -271,7 +271,7 @@ delete_tlpdb_files = function() { #' @export tlmgr_path = function(action = c('add', 'remove')) { action = match.arg(action) - if (is_macos() && !macos_local_bin_writable()) { + if (use_paths_d()) { invisible(macos_path(find_tinytex_bin(), action)) } else tlmgr(c('path', action), .quiet = TRUE) } From 864bb51af26d8b65c301b569c02b2755050495c8 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Fri, 27 Mar 2026 14:17:13 -0500 Subject: [PATCH 12/12] rewrap --- DESCRIPTION | 2 +- R/tlmgr.R | 11 +++++------ man/tlmgr.Rd | 11 +++++------ 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 9ab36f98c..9641932fe 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: tinytex Type: Package Title: Helper Functions to Install and Maintain TeX Live, and Compile LaTeX Documents -Version: 0.58.6 +Version: 0.58.7 Authors@R: c( person("Yihui", "Xie", role = c("aut", "cre", "cph"), email = "xie@yihui.name", comment = c(ORCID = "0000-0003-0645-5666")), person(given = "Posit Software, PBC", role = c("cph", "fnd")), diff --git a/R/tlmgr.R b/R/tlmgr.R index 798f92100..bd3fbcc32 100644 --- a/R/tlmgr.R +++ b/R/tlmgr.R @@ -261,12 +261,11 @@ delete_tlpdb_files = function() { )) } -#' @param action On macOS, if \file{/usr/local/bin} is not writable, -#' add/remove the TinyTeX bin path to/from \file{/etc/paths.d/TinyTeX}; -#' otherwise (or on other Unix systems), add/remove symlinks of binaries -#' to/from the system's \code{PATH}. On Windows, add/remove the path to the -#' TeX Live binary directory to/from the system environment variable -#' \code{PATH}. +#' @param action On macOS, if \file{/usr/local/bin} is not writable, add/remove +#' the TinyTeX bin path to/from \file{/etc/paths.d/TinyTeX}; otherwise (or on +#' other Unix systems), add/remove symlinks of binaries to/from the system's +#' \code{PATH}. On Windows, add/remove the path to the TeX Live binary +#' directory to/from the system environment variable \code{PATH}. #' @rdname tlmgr #' @export tlmgr_path = function(action = c('add', 'remove')) { diff --git a/man/tlmgr.Rd b/man/tlmgr.Rd index aa5b96d1b..7169c9121 100644 --- a/man/tlmgr.Rd +++ b/man/tlmgr.Rd @@ -101,12 +101,11 @@ format and hyphenation files after updating \pkg{tlmgr}.} (where \verb{HASH} is an MD5 hash) under the \file{tlpkg} directory of the root directory of TeX Live after updating.} -\item{action}{On macOS, if \file{/usr/local/bin} is not writable, -add/remove the TinyTeX bin path to/from \file{/etc/paths.d/TinyTeX}; -otherwise (or on other Unix systems), add/remove symlinks of binaries -to/from the system's \code{PATH}. On Windows, add/remove the path to the -TeX Live binary directory to/from the system environment variable -\code{PATH}.} +\item{action}{On macOS, if \file{/usr/local/bin} is not writable, add/remove +the TinyTeX bin path to/from \file{/etc/paths.d/TinyTeX}; otherwise (or on +other Unix systems), add/remove symlinks of binaries to/from the system's +\code{PATH}. On Windows, add/remove the path to the TeX Live binary +directory to/from the system environment variable \code{PATH}.} \item{url}{The URL of the CTAN mirror. If \code{NULL}, show the current repository, otherwise set the repository. See the \code{repository}