diff --git a/.github/workflows/site.yaml b/.github/workflows/site.yaml new file mode 100644 index 00000000..ca519739 --- /dev/null +++ b/.github/workflows/site.yaml @@ -0,0 +1,67 @@ +name: Deploy site +on: + push: + branches: + - scratch/deploy-site + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install Hugo + shell: bash + run: | + tdir=$(mktemp -d) + tgz=$tdir/hugo.tar.gz + bin=$HOME/bin + mkdir -p "$bin" + + url=https://github.com/gohugoio/hugo/releases/download + curl -fSsL --retry 3 \ + "$url/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz" \ + >"$tgz" + printf '%s %s\n' "$HUGO_SHA" "$tgz" | sha256sum --check >/dev/null + + tar --to-stdout --extract -zf "$tgz" hugo >"$bin"/hugo + chmod +x "$bin/hugo" + "$bin"/hugo version + echo "$bin" >>"$GITHUB_PATH" + env: + HUGO_VERSION: '0.133.0' + HUGO_SHA: '372530e2de9ae74087a987ca841429390a055123b8a4dec665cc601f10dc8e6e' + - uses: actions/configure-pages@v5 + id: pages + - name: Check URL match + shell: bash + run: | + url=$(grep '^baseURL:' docs/site/hugo.yaml) + test "${{ steps.pages.outputs.base_url }}/" = "${url#baseURL: }" + - name: Build site + shell: bash + run: make -C docs/site build + env: + HUGO_ENVIRONMENT: production + HUGO_CACHEDIR: ${{ runner.temp }}/hugo_cache + TZ: America/New_York + - name: Upload site + uses: actions/upload-pages-artifact@v3 + with: + path: ./docs/site/public + deploy: + needs: build + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index 945b3b2c..1bcc616c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # pkgr -For additional details of how to use pkgr, please see the [user manual](https://github.com/metrumresearchgroup/pkgr/wiki/user_manual) +Documentation for `pkgr` is available at +. -# What is pkgr? +## What is pkgr? `pkgr` is a rethinking of the way packages are managed in R. Namely, it embraces the declarative philosophy of defining _ideal state_ of the entire system, and working @@ -10,7 +11,7 @@ towards achieving that objective. Furthermore, `pkgr` is built with a focus on r and auditability of what is going on, a vital component for the pharmaceutical sciences + enterprises. -# Why pkgr? +## Why pkgr? `install.packages` and friends such as `remotes::install_github` have a subtle weakness -- they are not good at controlling desired global state. There are some knobs that @@ -31,14 +32,14 @@ the same set of responsibilities (dealing with dataframes + dealing with other l As such, it is becoming increasingly difficult to manage the _set_ of packages in a transparent and robust way. -### how does it compare with pak can be read about [here](https://github.com/metrumresearchgroup/pkgr/issues/222#issuecomment-576340217) - +> [!NOTE] +> How pkgr compares with pak can be read about [here](https://github.com/metrumresearchgroup/pkgr/issues/222#issuecomment-576340217). ## pkgr in action [![asciicast](https://asciinema.org/a/wgcPBvCMtEwhpdW793MBjgSi2.svg)](https://asciinema.org/a/wgcPBvCMtEwhpdW793MBjgSi2) -# Getting Started +## Getting Started ### OSX and Linux installation @@ -56,13 +57,17 @@ Pkgr for Windows is supported, but we have not yet published on a Windows-compat - The destination folder should be on your Windows PATH. You may need to [modify your Windows PATH environment variable](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/) to make this happen. - If you do not already have a preferred way of accomplishing the above, we suggest creating a folder called "apps" in your home directory, then adding the "apps" directory to your PATH. From there, simply make sure that the `pkgr.exe` file from the tarball ends up in your "apps/" directory. -# How it works +## How it works + +> [!NOTE] +> For additional details of how to use pkgr, please see the +> [user manual](https://github.com/metrumresearchgroup/pkgr/wiki/user_manual). `pkgr` is a command line utility with several top level commands. The two primary commands are: ```bash pkgr plan # show what would happen if install is run -pkgr install # install the packages specified in pkgr.config +pkgr install # install the packages specified in pkgr.yml ``` The actions are controlled by a configuration file that specifies the desired global state, namely, by defining the top level packages a user cares about, as well as specific configuration customizations. @@ -184,7 +189,7 @@ Logging: overwrite: true ``` -# pkgr and [packrat](https://rstudio.github.io/packrat/) and renv +## pkgr and [packrat](https://rstudio.github.io/packrat/) and renv **Pkgr is not a replacement for Packrat/renv -- Pkgr is complementary to packrat/renv**. @@ -207,3 +212,23 @@ Pkgr solves these issues by: - Providing timely error messages and halting the installation process immediately when something goes wrong during the installation process (such as a package not being available, a repository being unreachable, etc.) +## Development + +To run the test suite, you can invoke [scripts/run-unit-tests][ru] and +[scripts/run-integration-tests][ri] directly or via `make vt-test`. + +After updating a subcommand, regenerate the Markdown documentation at +[docs/commands][dc] by running `make vt-gen-docs`. See `make vt-help` and +[internal/valtools/README.md][vr] for more details on the validation tooling. + +The setup for building the documentation site is described in +[docs/site/README.md][dr]. + + + + +[dc]: https://github.com/metrumresearchgroup/pkgr/blob/main/docs/commands +[dr]: https://github.com/metrumresearchgroup/pkgr/blob/main/docs/site/README.md +[ri]: https://github.com/metrumresearchgroup/pkgr/blob/main/scripts/run-integration-tests +[ru]: https://github.com/metrumresearchgroup/pkgr/blob/main/scripts/run-unit-tests +[vr]: https://github.com/metrumresearchgroup/pkgr/blob/main/internal/valtools/README.md diff --git a/cmd/add.go b/cmd/add.go index 445e3418..bf968574 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -26,18 +26,22 @@ import ( // installCmd represents the R CMD install command var addCmd = &cobra.Command{ - Use: "add [package name1] [package name2] [package name3] ...", - Short: "add one or more packages", - Long: ` - add package/s to the configuration file and optionally install -`, + Use: "add [flags] [...]", + Short: "Add packages to the configuration file", + Long: `Add the specified packages to the 'Packages' section of the +configuration file.`, + Example: ` # Add mrgsolve and bbr to list of packages + pkgr add mrgsolve bbr + # Add rlang and then do installation + # (same result as following up with 'pkgr install' call) + pkgr add --install rlang`, RunE: rAdd, } var install bool func init() { - addCmd.Flags().BoolVar(&install, "install", false, "install package/s after adding") + addCmd.Flags().BoolVar(&install, "install", false, "run install after updating config") RootCmd.AddCommand(addCmd) } diff --git a/cmd/clean.go b/cmd/clean.go index bb3cf86d..9ac98ff1 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -26,10 +26,22 @@ var cleanAll bool // CleanCmd represents the clean command var CleanCmd = &cobra.Command{ - Use: "clean", - Short: "clean up cached information", - Long: "clean up cached source files and binaries, as well as the saved package database.", - RunE: clean, + Use: `clean [flags]`, + Short: "Clean cached information", + Long: `This subcommand is an entry point for cleaning two categories of cached +data: + + * source and binary tarballs + + Use the 'cache' subcommand to remove these. + + * package databases with information about the packages available from + repositories + + Use the 'pkgdbs' subcommand to remove these. + +To remove cached data for both categories, pass the --all flag.`, + RunE: clean, } func init() { diff --git a/cmd/cleanCache.go b/cmd/cleanCache.go index 627a3a2b..923c19c3 100644 --- a/cmd/cleanCache.go +++ b/cmd/cleanCache.go @@ -35,22 +35,27 @@ var reposToClear string // cacheCmd represents the cache command var cleanCacheCmd = &cobra.Command{ Use: "cache", - Short: "Subcommand to clean cached source and binary files.", - Long: `This command is a subcommand of the "clean" command. - - Using this command deletes cached source and binary files. Use the - --src and --binary options to specify which repos to clean each - file type from. - - `, + Short: "Clean cached package tarballs", + Long: `Delete cached tarballs for source and binary packages. Both source and +binary files are deleted if neither the --src-only or --binaries-only flags +is specified. + +By default, files for all repositories are deleted unless specific +repositories are specified via the --repos option. Note that the value must +match the directory name in the cache, including the unique ID that is +appended to the repository name.`, + Example: ` # Clean binary files for all repos + pkgr clean cache --binaries-only + # Clean binaries files for MPN-889df4238bae repo + pkgr clean cache --repos=MPN-889df4238bae --binaries-only`, RunE: cache, } func init() { //cleanCacheCmd.Flags().BoolVar(&cleanAllFromCache, "all", true, "Clean both src and binary files from the cache.") - cleanCacheCmd.Flags().BoolVar(&srcOnly, "src-only", false, "Clean only src files from the cache") - cleanCacheCmd.Flags().BoolVar(&binariesOnly, "binaries-only", false, "Clean only binary files from the cache") - cleanCacheCmd.Flags().StringVar(&reposToClear, "repos", "ALL", "Comma separated list of repositories to be cleaned. Defaults to all.") + cleanCacheCmd.Flags().BoolVar(&srcOnly, "src-only", false, "clean only source files from the cache") + cleanCacheCmd.Flags().BoolVar(&binariesOnly, "binaries-only", false, "clean only binary files from the cache") + cleanCacheCmd.Flags().StringVar(&reposToClear, "repos", "ALL", "comma-separated list of repositories to be cleaned. Defaults to all.") CleanCmd.AddCommand(cleanCacheCmd) } diff --git a/cmd/cleanPkgdb.go b/cmd/cleanPkgdb.go index 288e3968..09a51246 100644 --- a/cmd/cleanPkgdb.go +++ b/cmd/cleanPkgdb.go @@ -29,17 +29,19 @@ var pkgdbsToClearArgument string // pkgdbCmd represents the pkgdb command var pkgdbsCmd = &cobra.Command{ Use: "pkgdbs", - Short: "Subcommand to clean cached pkgdbs", - Long: `This command parses the currently-cached pkgdbs and removes all - of them by default, or specific ones if desired. Identify specific repos using the "repos" argument, i.e. - pkgr clean pkgdbs --repos="CRAN,r_validated" - Repo names should match names in the pkgr.yml file.`, + Short: "Clean cached package databases", + Long: `Delete cached package databases. By default, remove cached databases for +every repository listed in the active configuration file. If the --repos option +is passed, remove only the cached databases for those repositories. Repo names +should match the names in the configuration file.`, + Example: ` # Clean package databases for CRAN and MPN + pkgr clean pkgdbs --repos=CRAN,MPN`, RunE: executeCommand, } func init() { - pkgdbsCmd.Flags().StringVar(&pkgdbsToClearArgument, "repos", "ALL", "Set the repos you wish to clear the pkgdbs for.") + pkgdbsCmd.Flags().StringVar(&pkgdbsToClearArgument, "repos", "ALL", "clear databases for these repos") CleanCmd.AddCommand(pkgdbsCmd) } diff --git a/cmd/inspect.go b/cmd/inspect.go index cd7ed567..f74da871 100644 --- a/cmd/inspect.go +++ b/cmd/inspect.go @@ -29,11 +29,30 @@ import ( // inspectCmd shows the install inspect var inspectCmd = &cobra.Command{ - Use: "inspect", - Short: "inspect a full installation", - Long: ` - see the inspect for an install - `, + Use: "inspect --deps [flags] [...]", + Short: "Inspect package dependencies", + Long: `The inspect subcommand provides an entry point for displaying +information that can be gathered by examining the configuration file, the +associated package database, and the library. The current focus is on +inspecting package dependencies (triggered by passing --deps). + +Note: If the configuration file has 'Suggests: true', that does not affect +the set of dependencies listed for any particular package. Instead the set +of suggested packages is included in the top-level package set.`, + Example: ` # Show all dependencies as a tree + pkgr --loglevel=fatal inspect --deps --tree + # Show dependency tree, restricting roots to the named packages + pkgr --loglevel=fatal inspect --deps --tree processx here + + # Output a JSON record where each item maps a package to its direct + # and indirect dependencies + pkgr --loglevel=fatal inspect --deps + # Do the same, but filter to records for the named packages + pkgr --loglevel=fatal inspect --deps processx here + + # Output a JSON record where each item maps a package to + # the packages that have it as a dependency + pkgr --loglevel=fatal inspect --deps --reverse`, RunE: inspect, } @@ -115,8 +134,14 @@ func init() { inspectCmd.Flags().BoolVar(&showDeps, "deps", false, "show dependency tree") inspectCmd.Flags().BoolVar(&reverse, "reverse", false, "show reverse dependencies") inspectCmd.Flags().BoolVar(&tree, "tree", false, "show full recursive dependency tree") - inspectCmd.Flags().BoolVar(&toJson, "json", false, "output as clean json") + inspectCmd.Flags().BoolVar(&toJson, "json", false, + // Point the user to --loglevel because it also suppresses logging + // upstream of the inspect() call. + "suppress non-fatal logging (note: prefer --loglevel=fatal to this flag)") inspectCmd.Flags().BoolVar(&installedFrom, "installed-from", false, "show package installation source") + // Don't advertise this until work is done to improve it. + inspectCmd.Flags().MarkHidden("installed-from") + RootCmd.AddCommand(inspectCmd) } diff --git a/cmd/install.go b/cmd/install.go index f40cf9a1..b8ad05ab 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -35,10 +35,16 @@ import ( // installCmd represents the R CMD install command var installCmd = &cobra.Command{ Use: "install", - Short: "install a package", - Long: ` - install a package -`, + Short: "Install packages", + Long: `Create the library defined by the configuration file. + +See for details on +the configuration file.`, + Example: ` # Create or update library defined by pkgr.yml + pkgr install + # Install new packages and dependencies but don't update packages that already + # exist in the library. + pkgr install --no-update`, RunE: rInstall, } diff --git a/cmd/load.go b/cmd/load.go index 83b58d14..d57d5e76 100644 --- a/cmd/load.go +++ b/cmd/load.go @@ -21,9 +21,20 @@ import ( // loadCmd represents the load command var loadCmd = &cobra.Command{ Use: "load", - Short: "Checks that installed packages can be loaded", - Long: `Attempts to load user packages specified in pkgr.yml to validate that each package has been installed -successfully and can be used. Use the --all flag to load all packages in the user-library dependency tree instead of just user-level packages.`, + Short: "Check that installed packages can be loaded", + Long: `Load packages specified in the configuration file to validate that +each package has been installed successfully and can be used. + +**Execution environment**. This subcommand runs R with the same settings +that R would use if you invoked 'R' from the current working directory. It +relies on that environment being configured to find packages in the library +path specified in the configuration file (via 'Library' or 'Lockfile: +Type'). Pass the --json argument to confirm that the package is being +loaded from the expected library.`, + Example: ` # Load packages listed in config file + pkgr load --json + # Load the above packages and all their dependencies + pkgr load --json --all`, Run: func(cmd *cobra.Command, args []string) { all := viper.GetBool("all") json := viper.GetBool("json") @@ -52,9 +63,9 @@ successfully and can be used. Use the --all flag to load all packages in the use func init() { RootCmd.AddCommand(loadCmd) - loadCmd.Flags().Bool("all", false, "load user packages as well as their dependencies") + loadCmd.Flags().Bool("all", false, "load all packages in dependency tree") viper.BindPFlag("all", loadCmd.LocalFlags().Lookup("all")) //There doesn't seem to be a way to bind local flags. - loadCmd.Flags().Bool("json", false, "output a JSON object of package info at the end") + loadCmd.Flags().Bool("json", false, "output results as a JSON object") viper.BindPFlag("json", loadCmd.LocalFlags().Lookup("json")) //There doesn't seem to be a way to bind local flags. // loadCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } diff --git a/cmd/plan.go b/cmd/plan.go index 45b7fd66..83490c77 100644 --- a/cmd/plan.go +++ b/cmd/plan.go @@ -47,10 +47,14 @@ import ( // planCmd shows the install plan var planCmd = &cobra.Command{ Use: "plan", - Short: "plan a full installation", - Long: ` - see the plan for an install - `, + Short: "Display plan for installation", + Long: `Preview an installation with the current configuration. This subcommand +is commonly invoked before running 'pkgr install' to confirm that the +configuration is behaving as intended. + +The output includes details about which repositories particular packages would +be retrieved from, the library that packages would be installed into, and which +packages would be installed or updated.`, RunE: plan, } diff --git a/cmd/remove.go b/cmd/remove.go index ed57088a..c8c25c86 100644 --- a/cmd/remove.go +++ b/cmd/remove.go @@ -26,11 +26,10 @@ import ( // installCmd represents the R CMD install command var removeCmd = &cobra.Command{ - Use: "remove [package name1] [package name2] [package name3] ...", - Short: "remove one or more packages", - Long: ` - remove package/s from the configuration file -`, + Use: "remove [flags] [...]", + Short: "Remove packages from the configuration file", + Long: `Remove the specified packages from the 'Packages' section of the +configuration file.`, RunE: rRemove, } diff --git a/cmd/root.go b/cmd/root.go index dbbe387d..d28905df 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -38,7 +38,7 @@ var update bool // RootCmd represents the base command when called without any subcommands var RootCmd = &cobra.Command{ Use: "pkgr", - Short: "package manager", + Short: "A package manager for R", Version: VERSION, } @@ -67,6 +67,7 @@ func rootInit() { // this is added to maintain legacy compatibility in case people use this flag RootCmd.PersistentFlags().BoolVar(&update, "update", false, "whether to update installed packages") + RootCmd.PersistentFlags().MarkHidden("update") // this replaces the update RootCmd.PersistentFlags().Bool("no-update", false, "don't update installed packages") @@ -86,6 +87,7 @@ func rootInit() { RootCmd.PersistentFlags().Bool("preview", false, "preview action, but don't actually run command") _ = viper.BindPFlag("preview", RootCmd.PersistentFlags().Lookup("preview")) + RootCmd.PersistentFlags().MarkHidden("preview") RootCmd.PersistentFlags().Bool("debug", false, "use debug mode") _ = viper.BindPFlag("debug", RootCmd.PersistentFlags().Lookup("debug")) @@ -96,13 +98,13 @@ func rootInit() { RootCmd.PersistentFlags().String("library", "", "library to install packages") _ = viper.BindPFlag("library", RootCmd.PersistentFlags().Lookup("library")) - RootCmd.PersistentFlags().Bool("no-rollback", cfg.NoRollback, "Disable rollback") + RootCmd.PersistentFlags().Bool("no-rollback", cfg.NoRollback, "disable rollback") _ = viper.BindPFlag("norollback", RootCmd.PersistentFlags().Lookup("no-rollback")) RootCmd.PersistentFlags().Bool("no-secure", cfg.NoSecure, "disable TLS certificate verification") _ = viper.BindPFlag("nosecure", RootCmd.PersistentFlags().Lookup("no-secure")) - RootCmd.PersistentFlags().Bool("strict", cfg.Strict, "Enable strict mode") + RootCmd.PersistentFlags().Bool("strict", cfg.Strict, "enable strict mode") _ = viper.BindPFlag("strict", RootCmd.PersistentFlags().Lookup("strict")) } diff --git a/cmd/run.go b/cmd/run.go index 3e1f3db0..af34d99b 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -25,11 +25,29 @@ var pkg string // checkCmd represents the R CMD check command var runCmd = &cobra.Command{ - Use: "run R", - Short: "Run R with the configuration settings used with other R commands", - Long: ` - allows for interactive use and debugging based on the configuration specified by pkgr - `, + Use: "run", + Short: "Launch R session with config settings", + Long: `Start an interactive R session based on the settings defined in the +configuration file. + + * Use the R executable defined by the 'RPath' value, if any. + + * Set the library paths so that packages come from only the + configuration's library and the library bundled with the R + installation. + + * If the --pkg option is passed, set the environment variables defined in + the package's 'Customizations' entry.`, + Example: ` # Launch an R session, setting values based on pkgr.yml + pkgr run + # Also setting environment variables specified for dplyr: + # + # Customizations: + # Packages: + # - dplyr: + # Env: + # [...] + pkgr run --pkg=dplyr`, RunE: rRun, } diff --git a/docs/commands/pkgr.md b/docs/commands/pkgr.md index d82f1d3e..8afbe658 100644 --- a/docs/commands/pkgr.md +++ b/docs/commands/pkgr.md @@ -1,6 +1,6 @@ ## pkgr -package manager +A package manager for R ### Options @@ -11,24 +11,22 @@ package manager --library string library to install packages --logjson log as json --loglevel string level for logging - --no-rollback Disable rollback + --no-rollback disable rollback --no-secure disable TLS certificate verification --no-update don't update installed packages - --preview preview action, but don't actually run command - --strict Enable strict mode + --strict enable strict mode --threads int number of threads to execute with - --update whether to update installed packages -v, --version print the version ``` ### SEE ALSO -* [pkgr add](pkgr_add.md) - add one or more packages -* [pkgr clean](pkgr_clean.md) - clean up cached information -* [pkgr inspect](pkgr_inspect.md) - inspect a full installation -* [pkgr install](pkgr_install.md) - install a package -* [pkgr load](pkgr_load.md) - Checks that installed packages can be loaded -* [pkgr plan](pkgr_plan.md) - plan a full installation -* [pkgr remove](pkgr_remove.md) - remove one or more packages -* [pkgr run](pkgr_run.md) - Run R with the configuration settings used with other R commands +* [pkgr add](pkgr_add.md) - Add packages to the configuration file +* [pkgr clean](pkgr_clean.md) - Clean cached information +* [pkgr inspect](pkgr_inspect.md) - Inspect package dependencies +* [pkgr install](pkgr_install.md) - Install packages +* [pkgr load](pkgr_load.md) - Check that installed packages can be loaded +* [pkgr plan](pkgr_plan.md) - Display plan for installation +* [pkgr remove](pkgr_remove.md) - Remove packages from the configuration file +* [pkgr run](pkgr_run.md) - Launch R session with config settings diff --git a/docs/commands/pkgr_add.md b/docs/commands/pkgr_add.md index 83b1905b..3ccba0f0 100644 --- a/docs/commands/pkgr_add.md +++ b/docs/commands/pkgr_add.md @@ -1,22 +1,31 @@ ## pkgr add -add one or more packages +Add packages to the configuration file ### Synopsis +Add the specified packages to the 'Packages' section of the +configuration file. - add package/s to the configuration file and optionally install +``` +pkgr add [flags] [...] +``` +### Examples ``` -pkgr add [package name1] [package name2] [package name3] ... [flags] + # Add mrgsolve and bbr to list of packages + pkgr add mrgsolve bbr + # Add rlang and then do installation + # (same result as following up with 'pkgr install' call) + pkgr add --install rlang ``` ### Options ``` -h, --help help for add - --install install package/s after adding + --install run install after updating config ``` ### Options inherited from parent commands @@ -27,16 +36,14 @@ pkgr add [package name1] [package name2] [package name3] ... [flags] --library string library to install packages --logjson log as json --loglevel string level for logging - --no-rollback Disable rollback + --no-rollback disable rollback --no-secure disable TLS certificate verification --no-update don't update installed packages - --preview preview action, but don't actually run command - --strict Enable strict mode + --strict enable strict mode --threads int number of threads to execute with - --update whether to update installed packages ``` ### SEE ALSO -* [pkgr](pkgr.md) - package manager +* [pkgr](pkgr.md) - A package manager for R diff --git a/docs/commands/pkgr_clean.md b/docs/commands/pkgr_clean.md index 6726caac..a2871780 100644 --- a/docs/commands/pkgr_clean.md +++ b/docs/commands/pkgr_clean.md @@ -1,10 +1,22 @@ ## pkgr clean -clean up cached information +Clean cached information ### Synopsis -clean up cached source files and binaries, as well as the saved package database. +This subcommand is an entry point for cleaning two categories of cached +data: + + * source and binary tarballs + + Use the 'cache' subcommand to remove these. + + * package databases with information about the packages available from + repositories + + Use the 'pkgdbs' subcommand to remove these. + +To remove cached data for both categories, pass the --all flag. ``` pkgr clean [flags] @@ -25,18 +37,16 @@ pkgr clean [flags] --library string library to install packages --logjson log as json --loglevel string level for logging - --no-rollback Disable rollback + --no-rollback disable rollback --no-secure disable TLS certificate verification --no-update don't update installed packages - --preview preview action, but don't actually run command - --strict Enable strict mode + --strict enable strict mode --threads int number of threads to execute with - --update whether to update installed packages ``` ### SEE ALSO -* [pkgr](pkgr.md) - package manager -* [pkgr clean cache](pkgr_clean_cache.md) - Subcommand to clean cached source and binary files. -* [pkgr clean pkgdbs](pkgr_clean_pkgdbs.md) - Subcommand to clean cached pkgdbs +* [pkgr](pkgr.md) - A package manager for R +* [pkgr clean cache](pkgr_clean_cache.md) - Clean cached package tarballs +* [pkgr clean pkgdbs](pkgr_clean_pkgdbs.md) - Clean cached package databases diff --git a/docs/commands/pkgr_clean_cache.md b/docs/commands/pkgr_clean_cache.md index 4e22dbef..e751d051 100644 --- a/docs/commands/pkgr_clean_cache.md +++ b/docs/commands/pkgr_clean_cache.md @@ -1,28 +1,38 @@ ## pkgr clean cache -Subcommand to clean cached source and binary files. +Clean cached package tarballs ### Synopsis -This command is a subcommand of the "clean" command. +Delete cached tarballs for source and binary packages. Both source and +binary files are deleted if neither the --src-only or --binaries-only flags +is specified. - Using this command deletes cached source and binary files. Use the - --src and --binary options to specify which repos to clean each - file type from. - - +By default, files for all repositories are deleted unless specific +repositories are specified via the --repos option. Note that the value must +match the directory name in the cache, including the unique ID that is +appended to the repository name. ``` pkgr clean cache [flags] ``` +### Examples + +``` + # Clean binary files for all repos + pkgr clean cache --binaries-only + # Clean binaries files for MPN-889df4238bae repo + pkgr clean cache --repos=MPN-889df4238bae --binaries-only +``` + ### Options ``` - --binaries-only Clean only binary files from the cache + --binaries-only clean only binary files from the cache -h, --help help for cache - --repos string Comma separated list of repositories to be cleaned. Defaults to all. (default "ALL") - --src-only Clean only src files from the cache + --repos string comma-separated list of repositories to be cleaned. Defaults to all. (default "ALL") + --src-only clean only source files from the cache ``` ### Options inherited from parent commands @@ -33,16 +43,14 @@ pkgr clean cache [flags] --library string library to install packages --logjson log as json --loglevel string level for logging - --no-rollback Disable rollback + --no-rollback disable rollback --no-secure disable TLS certificate verification --no-update don't update installed packages - --preview preview action, but don't actually run command - --strict Enable strict mode + --strict enable strict mode --threads int number of threads to execute with - --update whether to update installed packages ``` ### SEE ALSO -* [pkgr clean](pkgr_clean.md) - clean up cached information +* [pkgr clean](pkgr_clean.md) - Clean cached information diff --git a/docs/commands/pkgr_clean_pkgdbs.md b/docs/commands/pkgr_clean_pkgdbs.md index 98fb80c5..5114c4f1 100644 --- a/docs/commands/pkgr_clean_pkgdbs.md +++ b/docs/commands/pkgr_clean_pkgdbs.md @@ -1,23 +1,30 @@ ## pkgr clean pkgdbs -Subcommand to clean cached pkgdbs +Clean cached package databases ### Synopsis -This command parses the currently-cached pkgdbs and removes all - of them by default, or specific ones if desired. Identify specific repos using the "repos" argument, i.e. - pkgr clean pkgdbs --repos="CRAN,r_validated" - Repo names should match names in the pkgr.yml file. +Delete cached package databases. By default, remove cached databases for +every repository listed in the active configuration file. If the --repos option +is passed, remove only the cached databases for those repositories. Repo names +should match the names in the configuration file. ``` pkgr clean pkgdbs [flags] ``` +### Examples + +``` + # Clean package databases for CRAN and MPN + pkgr clean pkgdbs --repos=CRAN,MPN +``` + ### Options ``` -h, --help help for pkgdbs - --repos string Set the repos you wish to clear the pkgdbs for. (default "ALL") + --repos string clear databases for these repos (default "ALL") ``` ### Options inherited from parent commands @@ -28,16 +35,14 @@ pkgr clean pkgdbs [flags] --library string library to install packages --logjson log as json --loglevel string level for logging - --no-rollback Disable rollback + --no-rollback disable rollback --no-secure disable TLS certificate verification --no-update don't update installed packages - --preview preview action, but don't actually run command - --strict Enable strict mode + --strict enable strict mode --threads int number of threads to execute with - --update whether to update installed packages ``` ### SEE ALSO -* [pkgr clean](pkgr_clean.md) - clean up cached information +* [pkgr clean](pkgr_clean.md) - Clean cached information diff --git a/docs/commands/pkgr_inspect.md b/docs/commands/pkgr_inspect.md index 4e67db54..8935d07f 100644 --- a/docs/commands/pkgr_inspect.md +++ b/docs/commands/pkgr_inspect.md @@ -1,26 +1,49 @@ ## pkgr inspect -inspect a full installation +Inspect package dependencies ### Synopsis +The inspect subcommand provides an entry point for displaying +information that can be gathered by examining the configuration file, the +associated package database, and the library. The current focus is on +inspecting package dependencies (triggered by passing --deps). - see the inspect for an install - +Note: If the configuration file has 'Suggests: true', that does not affect +the set of dependencies listed for any particular package. Instead the set +of suggested packages is included in the top-level package set. ``` -pkgr inspect [flags] +pkgr inspect --deps [flags] [...] +``` + +### Examples + +``` + # Show all dependencies as a tree + pkgr --loglevel=fatal inspect --deps --tree + # Show dependency tree, restricting roots to the named packages + pkgr --loglevel=fatal inspect --deps --tree processx here + + # Output a JSON record where each item maps a package to its direct + # and indirect dependencies + pkgr --loglevel=fatal inspect --deps + # Do the same, but filter to records for the named packages + pkgr --loglevel=fatal inspect --deps processx here + + # Output a JSON record where each item maps a package to + # the packages that have it as a dependency + pkgr --loglevel=fatal inspect --deps --reverse ``` ### Options ``` - --deps show dependency tree - -h, --help help for inspect - --installed-from show package installation source - --json output as clean json - --reverse show reverse dependencies - --tree show full recursive dependency tree + --deps show dependency tree + -h, --help help for inspect + --json suppress non-fatal logging (note: prefer --loglevel=fatal to this flag) + --reverse show reverse dependencies + --tree show full recursive dependency tree ``` ### Options inherited from parent commands @@ -31,16 +54,14 @@ pkgr inspect [flags] --library string library to install packages --logjson log as json --loglevel string level for logging - --no-rollback Disable rollback + --no-rollback disable rollback --no-secure disable TLS certificate verification --no-update don't update installed packages - --preview preview action, but don't actually run command - --strict Enable strict mode + --strict enable strict mode --threads int number of threads to execute with - --update whether to update installed packages ``` ### SEE ALSO -* [pkgr](pkgr.md) - package manager +* [pkgr](pkgr.md) - A package manager for R diff --git a/docs/commands/pkgr_install.md b/docs/commands/pkgr_install.md index 6ea3e110..4680dcf6 100644 --- a/docs/commands/pkgr_install.md +++ b/docs/commands/pkgr_install.md @@ -1,17 +1,28 @@ ## pkgr install -install a package +Install packages ### Synopsis +Create the library defined by the configuration file. - install a package - +See for details on +the configuration file. ``` pkgr install [flags] ``` +### Examples + +``` + # Create or update library defined by pkgr.yml + pkgr install + # Install new packages and dependencies but don't update packages that already + # exist in the library. + pkgr install --no-update +``` + ### Options ``` @@ -26,16 +37,14 @@ pkgr install [flags] --library string library to install packages --logjson log as json --loglevel string level for logging - --no-rollback Disable rollback + --no-rollback disable rollback --no-secure disable TLS certificate verification --no-update don't update installed packages - --preview preview action, but don't actually run command - --strict Enable strict mode + --strict enable strict mode --threads int number of threads to execute with - --update whether to update installed packages ``` ### SEE ALSO -* [pkgr](pkgr.md) - package manager +* [pkgr](pkgr.md) - A package manager for R diff --git a/docs/commands/pkgr_load.md b/docs/commands/pkgr_load.md index ff9cc160..7a618d44 100644 --- a/docs/commands/pkgr_load.md +++ b/docs/commands/pkgr_load.md @@ -1,22 +1,38 @@ ## pkgr load -Checks that installed packages can be loaded +Check that installed packages can be loaded ### Synopsis -Attempts to load user packages specified in pkgr.yml to validate that each package has been installed -successfully and can be used. Use the --all flag to load all packages in the user-library dependency tree instead of just user-level packages. +Load packages specified in the configuration file to validate that +each package has been installed successfully and can be used. + +**Execution environment**. This subcommand runs R with the same settings +that R would use if you invoked 'R' from the current working directory. It +relies on that environment being configured to find packages in the library +path specified in the configuration file (via 'Library' or 'Lockfile: +Type'). Pass the --json argument to confirm that the package is being +loaded from the expected library. ``` pkgr load [flags] ``` +### Examples + +``` + # Load packages listed in config file + pkgr load --json + # Load the above packages and all their dependencies + pkgr load --json --all +``` + ### Options ``` - --all load user packages as well as their dependencies + --all load all packages in dependency tree -h, --help help for load - --json output a JSON object of package info at the end + --json output results as a JSON object ``` ### Options inherited from parent commands @@ -27,16 +43,14 @@ pkgr load [flags] --library string library to install packages --logjson log as json --loglevel string level for logging - --no-rollback Disable rollback + --no-rollback disable rollback --no-secure disable TLS certificate verification --no-update don't update installed packages - --preview preview action, but don't actually run command - --strict Enable strict mode + --strict enable strict mode --threads int number of threads to execute with - --update whether to update installed packages ``` ### SEE ALSO -* [pkgr](pkgr.md) - package manager +* [pkgr](pkgr.md) - A package manager for R diff --git a/docs/commands/pkgr_plan.md b/docs/commands/pkgr_plan.md index 7ee71e11..b825355e 100644 --- a/docs/commands/pkgr_plan.md +++ b/docs/commands/pkgr_plan.md @@ -1,12 +1,16 @@ ## pkgr plan -plan a full installation +Display plan for installation ### Synopsis +Preview an installation with the current configuration. This subcommand +is commonly invoked before running 'pkgr install' to confirm that the +configuration is behaving as intended. - see the plan for an install - +The output includes details about which repositories particular packages would +be retrieved from, the library that packages would be installed into, and which +packages would be installed or updated. ``` pkgr plan [flags] @@ -27,16 +31,14 @@ pkgr plan [flags] --library string library to install packages --logjson log as json --loglevel string level for logging - --no-rollback Disable rollback + --no-rollback disable rollback --no-secure disable TLS certificate verification --no-update don't update installed packages - --preview preview action, but don't actually run command - --strict Enable strict mode + --strict enable strict mode --threads int number of threads to execute with - --update whether to update installed packages ``` ### SEE ALSO -* [pkgr](pkgr.md) - package manager +* [pkgr](pkgr.md) - A package manager for R diff --git a/docs/commands/pkgr_remove.md b/docs/commands/pkgr_remove.md index 591ec73a..063685a8 100644 --- a/docs/commands/pkgr_remove.md +++ b/docs/commands/pkgr_remove.md @@ -1,15 +1,14 @@ ## pkgr remove -remove one or more packages +Remove packages from the configuration file ### Synopsis - - remove package/s from the configuration file - +Remove the specified packages from the 'Packages' section of the +configuration file. ``` -pkgr remove [package name1] [package name2] [package name3] ... [flags] +pkgr remove [flags] [...] ``` ### Options @@ -26,16 +25,14 @@ pkgr remove [package name1] [package name2] [package name3] ... [flags] --library string library to install packages --logjson log as json --loglevel string level for logging - --no-rollback Disable rollback + --no-rollback disable rollback --no-secure disable TLS certificate verification --no-update don't update installed packages - --preview preview action, but don't actually run command - --strict Enable strict mode + --strict enable strict mode --threads int number of threads to execute with - --update whether to update installed packages ``` ### SEE ALSO -* [pkgr](pkgr.md) - package manager +* [pkgr](pkgr.md) - A package manager for R diff --git a/docs/commands/pkgr_run.md b/docs/commands/pkgr_run.md index cdd87a4b..ded0b05d 100644 --- a/docs/commands/pkgr_run.md +++ b/docs/commands/pkgr_run.md @@ -1,15 +1,38 @@ ## pkgr run -Run R with the configuration settings used with other R commands +Launch R session with config settings ### Synopsis +Start an interactive R session based on the settings defined in the +configuration file. - allows for interactive use and debugging based on the configuration specified by pkgr - + * Use the R executable defined by the 'RPath' value, if any. + + * Set the library paths so that packages come from only the + configuration's library and the library bundled with the R + installation. + + * If the --pkg option is passed, set the environment variables defined in + the package's 'Customizations' entry. + +``` +pkgr run [flags] +``` + +### Examples ``` -pkgr run R [flags] + # Launch an R session, setting values based on pkgr.yml + pkgr run + # Also setting environment variables specified for dplyr: + # + # Customizations: + # Packages: + # - dplyr: + # Env: + # [...] + pkgr run --pkg=dplyr ``` ### Options @@ -27,16 +50,14 @@ pkgr run R [flags] --library string library to install packages --logjson log as json --loglevel string level for logging - --no-rollback Disable rollback + --no-rollback disable rollback --no-secure disable TLS certificate verification --no-update don't update installed packages - --preview preview action, but don't actually run command - --strict Enable strict mode + --strict enable strict mode --threads int number of threads to execute with - --update whether to update installed packages ``` ### SEE ALSO -* [pkgr](pkgr.md) - package manager +* [pkgr](pkgr.md) - A package manager for R diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 00000000..78f16378 --- /dev/null +++ b/docs/config.md @@ -0,0 +1,399 @@ + +## Overview + +The package library to be created is defined by a configuration file. +`pkgr` reads the configuration from `pkgr.yml` in the current working +directory unless another location is specified via its `--config` +option. + +Here's an example of a minimal configuration: + +```yaml {filename="pkgr.yml"} +Version: 1 +Packages: + - here +Repos: + - MPN: https://mpn.metworx.com/snapshots/stable/2024-06-12 +Library: lib +``` + +That instructs `pkgr` to install `here` and its dependencies, where to +popuplate the library (`lib/` under the current working directory), +and where to get the package and its dependencies from (MPN's +2024-06-12 snapshot). + +Running `pkgr install` from the directory that contains that +`pkgr.yml` file creates the library with `here` and its dependency +`rprojroot`: + +``` +lib/ +|-- here/ +`-- rprojroot/ +``` + +### Path handling + +Several values in the configuration may be paths (e.g., `Library` and +`Descriptions`). + + * A `~` in the path is expanded to the home directory. + + * If the path is a relative path, it is interpreted as relative to + the *configuration file*. This is typically the same location from + which `pkgr` was invoked but may differ if an alternative + configuration file was specified with `--config`. + +### Expansion of environment variables + +If a dollar sign precedes a word (with optional brackets around the +word), the sequence is replaced by the value of the environment +variable with that name. If that variable is undefined, it is +replaced by an empty string. + +This is most commonly used to set `Rpath` based on an environment +variable (e.g., `${R_EXE_4_3}`). + +> [!CAUTION] +> Dollar signs should be avoided in configuration unless they +> reference an environment variable, as there is no support for +> escaping the dollar sign to disable the expansion. + +## Primary sections + +The sections below are the most commonly used. + +### Customizations + +Specify package-specific or repo-specific customizations. + +#### Package customizations + +Package-level customization is specified as a list of items under +`Customizations: Packages`. + + * **Env**: a set of environment variables to set when installing the + package. This is most commonly used to set `R_MAKEVARS_USER`. + + ```yaml {filename="Example"} + Customizations: + Packages: + - RCurl: + Env: + R_MAKEVARS_USER: ~/.R/Makevars-RCurl + ``` + + * **Repo**: install the package from this repository (must match a + key under top-level `Repos` section) + + ```yaml {filename="Example"} + Customizations: + Packages: + - here: + Repo: PPM + ``` + + * **Suggests**: whether to install the suggested dependencies for a + package + + ```yaml {filename="Example"} + Customizations: + Packages: + - here: + Suggests: true + ``` + + * **Type**: type of package, "source" or "binary", to install for a + package + + Installing a binary package is faster, but the CRAN-like repository + must have a binary available for the package, which is often not + the case. + + ```yaml {filename="Example"} + Customizations: + Packages: + - here: + Type: binary + ``` + +#### Repo customizations + + * **Type**: type of packages, "source" or "binary", to install from + the specified repository + + The default type is "source" on Linux and "binary" on macOS and + Windows. + + > [!NOTE] + > It is common for a repository to be missing binaries available + > for your setup (platform, architecture, and version of R). If + > binaries are unavailable and you're running macOS or Windows, you + > must customize the repository to have type "source" and ensure + > your machine has the necessary system requirements to build the + > package from source. + + ```yaml {filename="Example"} + Customizations: + Repos: + - MPN: + Type: source + ``` + + + +### Descriptions + +Install the dependencies listed in the specified R package +[DESCRIPTION][desc] files. + +[desc]: https://r-pkgs.org/description.html + +This main use case for this is R package development, as it avoids +duplicating the list of dependencies under the `Packages` section of +the `pkgr` configuration. + +```yaml {filename="Example"} +Descriptions: + - DESCRIPTION +``` + +### Library + +Directory in which to install packages. + +A directory specified via the `--library` command-line option +overrides the value specified in the configuration. + +Either this section or `Lockfile` must be present. `Library` takes +precedence over `Lockfile` if both are specified. + +```yaml {filename="Example"} +Library: lib +``` + +### Lockfile + +Use the same library location as +[renv](https://rstudio.github.io/renv) or +[packrat](https://github.com/rstudio/packrat). + +> [!WARNING] +> Packrat was superseded by renv. It is in maintenance-only mode. + +`pkgr` invokes `renv` to find the library, as its location depends on +a number of settings. If `renv` has not yet been initialized for a +project, `pkgr` tries to query the `renv`, if any, available in the +default library paths. However, the recommended approach is to +initialize `renv` *before* calling `pkgr install` (e.g., by invoking +`renv::init(bare = TRUE)`). + +Either this section or `Library` must be present. `Library` takes +precedence over `Lockfile` if both are specified. + +```yaml {filename="Example"} +Lockfile: + Type: renv +``` + +### Packages + +Packages to install. They must be available from at least one of the +CRAN-like repositories listed under the `Repos` section. + +Any of their dependencies are installed automatically. By default, +suggested dependencies are not included (but see `Suggests` section). + +```yaml {filename="Example"} +Packages: + - here + - rlang +``` + +### Repos + +CRAN-like repositories from which to retrieve the packages listed in +`Packages`. + +> [!CAUTION] +> The order is important. Unless customized in the `Customizations` +> section, a package is downloaded from the first repository that +> contains it. + +> [!TIP] +> To create a library with a reproducible set of packages, use +> CRAN-like repositories that snapshot packages at a particular date, +> such as [Metrum Package Network][mpn] and +> [Posit Package Manager][ppm]. + +[mpn]: https://mpn.metworx.com +[ppm]: https://packagemanager.posit.co + +```yaml {filename="Example"} +Repos: + - MPN: https://mpn.metworx.com/snapshots/stable/2024-06-12 + - PPM: https://packagemanager.posit.co/cran/2024-06-12 +``` + +### RPath + +Which R executable to use. Defaults to the highest priority R +executable in `PATH` (i.e. the same R that would launch if you invoked +`R` from the current shell). + +```yaml {filename="Example"} +Rpath: /opt/R/4.2.3/bin/R +``` + +### Suggests + +Install the suggested dependencies for all packages listed in +`Packages`. + +To do so only for some packages, use a package-specific customizations +entry (see `Customizations` section). + +```yaml {filename="Example"} +Suggests: true +``` + +### Version + +The configuration format version. This should always be `1`. + +```yaml {filename="Example"} +Version: 1 +``` + +## Other sections + +These sections are not as commonly used as the ones above. + +### Cache + +Store downloaded packages and built binaries in this directory rather +than the default platform-specific directory (e.g., +`$HOME/.cache/pkgr/` on Linux). + +```yaml {filename="Example"} +Cache: cache +``` + +### IgnorePackages + +Do not install the specified packages even if they are a required +dependency. + +```yaml {filename="Example"} +IgnorePackages: +- foo +``` + + + + +### Logging + +Configure logging behavior. + + * **all**: path for all commands to write log records to + + * **install**: path for `pkgr install` to write log records to + instead of the one specified by **all**. + + * **overwrite**: overwrite the above files instead of the default + behavior of appending to them + + + + +```yaml {filename="Example"} +Logging: + all: pkgr-all.log + install: pkgr-install.log + overwrite: true +``` + +### NoRecommended + +By default `pkgr intall` considers [recommended packages][rp] when +installing and updating a library. If `NoRecommended` is set to +`true`, recommended packages are ignored unless they are explicitly +listed under the `Packages` section. + +[rp]: https://rstudio.github.io/r-manuals/r-admin/Obtaining-R.html#using-subversion-and-rsync + +```yaml {filename="Example"} +NoRecommended: true +``` + +### NoRollback + +By default `pkgr install` restores the library to its original state +if the installation fails. Set `NoRollback` to `true` to disable that +behavior. + +```yaml {filename="Example"} +NoRollback: true +``` + +### NoSecure + +Disable the default TLS certificate verification by setting +`NoSecure: true`. + +> [!CAUTION] +> Disable TLS certificate verification is not recommended. If you +> need this in a particular case, consider passing the `--no-secure` +> command-line flag instead. + +```yaml {filename="Example"} +NoSecure: true +``` + +### NoUpdate + +Disable the default behavior of updating packages that are already +present in the library. + +This can be set for a single call by passing the `--no-update` +command-line flag instead. + +```yaml {filename="Example"} +NoUpdate: true +``` + +### Strict + +`pkgr install` creates the library directory if needed. Set `Strict` +to `true` to make it abort if the library directory does not exist. + +This can be set for a single call by passing the `--strict` +command-line flag instead. + +```yaml {filename="Example"} +Strict: true +``` + +### Tarballs + +Install packages from the specified source tarball. + +```yaml {filename="Example"} +Tarballs: + - packages/R6_2.5.1.tar.gz + - packages/glue_1.7.0.tar.gz +``` + +### Threads + +Number of workers to use for installing packages. The default is to +use up to eight workers, considering the number of workers on the +machine and any configured Linux CPU quotas. + +A value specified via the `--threads` command-line option overrides +the value specified in the configuration. + +```yaml {filename="Example"} +Threads: 2 +``` diff --git a/docs/site/.gitignore b/docs/site/.gitignore new file mode 100644 index 00000000..d6a86d88 --- /dev/null +++ b/docs/site/.gitignore @@ -0,0 +1,7 @@ +/.hugo_build.lock +/content/**/*.md +!/content/docs/_index.md +/local.mk +/public/ +/resources/ +/static/windows.png diff --git a/docs/site/Makefile b/docs/site/Makefile new file mode 100644 index 00000000..4faf6155 --- /dev/null +++ b/docs/site/Makefile @@ -0,0 +1,60 @@ +-include local.mk + +HTTP_PORT ?= 1313 + +version := $(shell git describe --tags --always HEAD) +export HUGO_PARAMS_PKGRVERSION ?= $(version) + +.PHONY: help +help: + $(info Primary targets:) + $(info * build: build site under public/) + $(info * serve: build and serve the site locally with 'hugo server') + @: + +.PHONY: build +build: prep + hugo --gc --minify + +.PHONY: serve +serve: prep + hugo server -p '$(HTTP_PORT)' + +copied_files = content/_index.md +copied_files += content/docs/config.md +copied_files += content/news.md +copied_files += static/windows.png + +.PHONY: prep +prep: commands +prep: $(copied_files) + rm -rf public resources + +command_dir = content/docs/commands + +.PHONY: commands +commands: + rm -rf '$(command_dir)' + ./scripts/ingest-command-docs '$(command_dir)' + +content/_index.md: ../../README.md + sed 's|docs/images/windows_tarball_picture\.png|windows.png|' '$<' >'$@' + +content/docs/config.md: ../config.md + printf -- '---\ntitle: "Configuration"\nweight: 10\n---\n\n' >'$@' + cat '$<' >>'$@' + +content/news.md: ./scripts/add-gh-links +content/news.md: ../../NEWS.md + printf -- '---\ntitle: "News"\n---\n\n' >'$@' + sed 's/^# pkgr/##/' '$<' | \ + ./scripts/add-gh-links https://github.com/metrumresearchgroup/pkgr \ + >>'$@' + +static/windows.png: ../images/windows_tarball_picture.png + cp '$<' '$@' + +.PHONY: clean +clean: + rm -rf public resources $(command_dir) + rm -f $(copied_files) diff --git a/docs/site/README.md b/docs/site/README.md new file mode 100644 index 00000000..a7a210ec --- /dev/null +++ b/docs/site/README.md @@ -0,0 +1,57 @@ +This directory contains files for building the GitHub Pages site with +[Hugo][h] and the [Hextra][x] theme. The site is published on release +via GitHub Actions ([workflow](/.github/workflows/site.yaml)). + +[h]: https://gohugo.io/ +[x]: https://imfing.github.io/hextra/ + +## Previewing the site + + * Install Hugo () + + * Run `make serve` to prepare the necessary files and launch + [Hugo's web server](https://gohugo.io/commands/hugo_server/) + +## Components + + * [hugo.yaml](./hugo.yaml): site configuration + + * [Hugo docs](https://gohugo.io/getting-started/configuration/) + + * [Hextra docs](https://imfing.github.io/hextra/docs/guide/configuration/) + + * [go.mod](./go.mod): defines Hextra as a Hugo module + + * [Hugo docs](https://gohugo.io/hugo-modules/) + + * [Hextra docs](https://imfing.github.io/hextra/docs/getting-started/#setup-hextra-as-hugo-module) + + * [content/][c]: Markdown content of the site + + Most of the content is pulled from other spots in the repository by + the [Makefile][m], including [NEWS.md](/NEWS.md) and the + command-line Markdown documentation at + [docs/commands/](/docs/commands). + + * [Hugo docs](https://gohugo.io/content-management/) + + * [layouts/](./layouts): templates for rendering the site pages + + Most of the layouts are defined by the Hextra theme. The files + here override particular templates. They are derived from the + corresponding Hextra template. The header of each template + documents what was changed. + + * [Hugo docs](https://gohugo.io/templates/) + + * [scripts/](./scripts): custom scripts used by the [Makefile][m] to + process the Markdown content before writing it to [content/][c] + + This is responsible for things like adding PR links in the NEWS + page rendered on the site. + + * [static/](./static): this directory contains the logos and any + other images used on the site + +[c]: ./content +[m]: ./Makefile diff --git a/docs/site/content/docs/_index.md b/docs/site/content/docs/_index.md new file mode 100644 index 00000000..5d180eec --- /dev/null +++ b/docs/site/content/docs/_index.md @@ -0,0 +1,25 @@ +--- +title: Documentation +toc: false +--- + +`pkgr` is a command-line interface for managing R packages. This +documentation describes that [interface][commands] and the +[configuration file][config] that controls its operation. + +{{< cards >}} + {{< card link="config" title="Configuration" icon="document-text" + subtitle="Define the package library to create" >}} +{{< /cards >}} + +{{< cards >}} + {{< card link="commands/plan" title="Key subcommand: plan" icon="terminal" + subtitle="Preview what would be installed or updated" >}} + {{< card link="commands/install" title="Key subcommand: install" icon="terminal" + subtitle="Create or update the library" >}} +{{< /cards >}} + +[commands]: {{< relref "/docs/commands" >}} +[config]: {{< relref "/docs/config" >}} +[install]: {{< relref "/docs/commands/install" >}} +[plan]: {{< relref "/docs/commands/plan" >}} diff --git a/docs/site/go.mod b/docs/site/go.mod new file mode 100644 index 00000000..40fd050f --- /dev/null +++ b/docs/site/go.mod @@ -0,0 +1,5 @@ +module github.com/metrumresearchgroup/pkgr/docs/site + +go 1.21.4 + +require github.com/imfing/hextra v0.8.2 // indirect diff --git a/docs/site/go.sum b/docs/site/go.sum new file mode 100644 index 00000000..ed34ce09 --- /dev/null +++ b/docs/site/go.sum @@ -0,0 +1,4 @@ +github.com/imfing/hextra v0.8.1 h1:HPeuyBTxaG7o2PHT9XRgFTVbCVGhJbZ0t7PjApA32F8= +github.com/imfing/hextra v0.8.1/go.mod h1:cEfel3lU/bSx7lTE/+uuR4GJaphyOyiwNR3PTqFTXpI= +github.com/imfing/hextra v0.8.2 h1:/IykSIAywgKfhKUBgAW+dCCjrJWJNny4jr9qvdXfch0= +github.com/imfing/hextra v0.8.2/go.mod h1:cEfel3lU/bSx7lTE/+uuR4GJaphyOyiwNR3PTqFTXpI= diff --git a/docs/site/hugo.yaml b/docs/site/hugo.yaml new file mode 100644 index 00000000..a042ee4a --- /dev/null +++ b/docs/site/hugo.yaml @@ -0,0 +1,67 @@ +title: pkgr +baseURL: https://metrumresearchgroup.github.io/pkgr/ +languageCode: en-us +markup: + highlight: + noClasses: false + goldmark: + extensions: + typographer: + # Prevent --option in command pages from being rendered with an en-dash. + disable: true + parser: + attribute: + block: true +menu: + main: + - name: Docs + pageRef: /docs + weight: 10 + - name: News + pageRef: /news + weight: 20 + - name: Search + weight: 30 + params: + type: search + - name: GitHub + weight: 40 + url: https://github.com/metrumresearchgroup/pkgr + params: + icon: github + sidebar: + - identifier: wikimanual + name: "User manual (pkgr wiki) ↗" + url: "https://github.com/metrumresearchgroup/pkgr/wiki/user_manual" + weight: 100 + - identifier: repos + name: Snapshot repos + params: + type: separator + weight: 110 + - identifier: mpn + name: "Metrum Package Network ↗" + url: "https://mpn.metworx.com" + weight: 120 + - identifier: ppm + name: "Posit Package Manager ↗" + url: "https://packagemanager.posit.co" + weight: 130 + +disableKinds: + - taxonomy + - term + +module: + imports: + - path: github.com/imfing/hextra +params: + navbar: + displayTitle: false + displayLogo: true + logo: + path: logo.png + width: 45 + theme: + default: system + displayToggle: false diff --git a/docs/site/layouts/_default/_markup/render-blockquote.html b/docs/site/layouts/_default/_markup/render-blockquote.html new file mode 100644 index 00000000..5204b6a9 --- /dev/null +++ b/docs/site/layouts/_default/_markup/render-blockquote.html @@ -0,0 +1,47 @@ +{{/* + This is combines the types from the example at + with the + classes from Hextra's layouts/shortcodes/callout.html + (blob: 6b56bcb2fff52b58af2a34da0bd8e5784108bd2b). +*/}} +{{ if eq .Type "alert" }} + {{ $emojis := dict + "caution" ":exclamation:" + "important" ":information_source:" + "note" ":information_source:" + "tip" ":bulb:" + "warning" ":information_source:" + }} + + {{ $orange := "hx-border-orange-100 hx-bg-orange-50 hx-text-orange-800 dark:hx-border-orange-400/30 dark:hx-bg-orange-400/20 dark:hx-text-orange-300" }} + {{ $blue := "hx-border-blue-200 hx-bg-blue-100 hx-text-blue-900 dark:hx-border-blue-200/30 dark:hx-bg-blue-900/30 dark:hx-text-blue-200" }} + {{ $yellow := "hx-border-yellow-100 hx-bg-yellow-50 hx-text-yellow-900 dark:hx-border-yellow-200/30 dark:hx-bg-yellow-700/30 dark:hx-text-yellow-200" }} + {{ $red := "hx-border-red-200 hx-bg-red-100 hx-text-red-900 dark:hx-border-red-200/30 dark:hx-bg-red-900/30 dark:hx-text-red-200" }} + + {{ $classes := dict + "caution" $red + "important" $orange + "note" $blue + "tip" $blue + "warning" $yellow + }} + + {{ $class := (index $classes .AlertType) }} +
+
+
+ {{ transform.Emojify (index $emojis .AlertType) }} +
+
+ +
+
+ {{ .Text | safeHTML }} +
+
+
+{{ else }} +
+ {{ .Text | safeHTML }} +
+{{ end }} diff --git a/docs/site/layouts/command/list.html b/docs/site/layouts/command/list.html new file mode 100644 index 00000000..74a60b73 --- /dev/null +++ b/docs/site/layouts/command/list.html @@ -0,0 +1,25 @@ +{{/* + This is modified from Hextra's layouts/docs/list.html + (blob: a91882ae16796158309d93c44a3df571b98dfb11) + + Changes: + + * don't insert title as H1 heading +*/}} +{{ define "main" }} +
+ {{ partial "sidebar.html" (dict "context" .) }} + {{ partial "toc.html" . }} +
+
+ {{ partial "breadcrumb.html" . }} +
+ {{ .Content }} +
+ {{ partial "components/last-updated.html" . }} + {{ partial "components/pager.html" . }} + {{ partial "components/comments.html" . }} +
+
+
+{{ end }} diff --git a/docs/site/layouts/command/single.html b/docs/site/layouts/command/single.html new file mode 100644 index 00000000..51891e3e --- /dev/null +++ b/docs/site/layouts/command/single.html @@ -0,0 +1,25 @@ +{{/* + This is modified from Hextra's layouts/docs/single.html + (blob: a91882ae16796158309d93c44a3df571b98dfb11) + + Changes: + + * don't insert title as H1 heading +*/}} +{{ define "main" }} +
+ {{ partial "sidebar.html" (dict "context" .) }} + {{ partial "toc.html" . }} +
+
+ {{ partial "breadcrumb.html" . }} +
+ {{ .Content }} +
+ {{ partial "components/last-updated.html" . }} + {{ partial "components/pager.html" . }} + {{ partial "components/comments.html" . }} +
+
+
+{{ end }} diff --git a/docs/site/layouts/partials/favicons.html b/docs/site/layouts/partials/favicons.html new file mode 100644 index 00000000..2d659c3b --- /dev/null +++ b/docs/site/layouts/partials/favicons.html @@ -0,0 +1,13 @@ +{{/* + This is modified from Hextra's layouts/partials/favicons.html + (blob: 66a80188e72c094884b37ff9720d669a6e03824f) + + Changes: + + * remove links to favicon.svg and favicon-dark.svg +*/}} + + + + + diff --git a/docs/site/layouts/partials/footer.html b/docs/site/layouts/partials/footer.html new file mode 100644 index 00000000..787c46fe --- /dev/null +++ b/docs/site/layouts/partials/footer.html @@ -0,0 +1,35 @@ +{{/* + This is modified from Hextra's layouts/partials/footer.html + (blob: 61cbb1133d633b0389f9d6ce70cd99b8568a7bad) + + Changes: + + * remove support for toggling (handled in navbar instead) + + * decrease height of footer + + * use custom credit line that 1) credits Hugo in addition to + Hextra and 2) is right aligned +*/}} + +{{- $footerWidth := "hx-max-w-screen-xl" -}} +{{- with .Site.Params.footer.width -}} + {{ if eq . "wide" -}} + {{ $footerWidth = "hx-max-w-[90rem]" -}} + {{ else if eq . "full" -}} + {{ $footerWidth = "max-w-full" -}} + {{ end -}} +{{- end -}} + +
+
+ + Site built with + Hugo + and + Hextra + +
+
diff --git a/docs/site/layouts/partials/navbar.html b/docs/site/layouts/partials/navbar.html new file mode 100644 index 00000000..ed4574da --- /dev/null +++ b/docs/site/layouts/partials/navbar.html @@ -0,0 +1,89 @@ +{{/* + This is modified from Hextra's layouts/partials/navbar.html + (blob: 04adb27e4f170601cc82f9d8ecf30baf50ffbe5a) + + Changes: + + * add pkgr version + + * don't blur + + * add dark/light theme toggle (but don't guard it with + site.Params.theme.displayToggle so that Hexta's sidebar can be + used without any changes) +*/}} + +{{- $logoPath := .Site.Params.navbar.logo.path | default "images/logo.svg" -}} +{{- $logoLink := .Site.Params.navbar.logo.link | default .Site.Home.RelPermalink -}} +{{- $logoWidth := .Site.Params.navbar.logo.width | default "20" -}} +{{- $logoHeight := .Site.Params.navbar.logo.height | default "20" -}} +{{- $logoDarkPath := .Site.Params.navbar.logo.dark | default $logoPath -}} + +{{- $pkgrVersion := .Site.Params.pkgrversion | default "" -}} + +{{- $navWidth := "hx-max-w-[90rem]" -}} +{{- with .Site.Params.navbar.width -}} + {{ if eq . "normal" -}} + {{ $navWidth = "hx-max-w-screen-xl" -}} + {{ else if eq . "full" -}} + {{ $navWidth = "max-w-full" -}} + {{ end -}} +{{- end -}} + + diff --git a/docs/site/layouts/partials/theme-toggle.html b/docs/site/layouts/partials/theme-toggle.html new file mode 100644 index 00000000..2da946d4 --- /dev/null +++ b/docs/site/layouts/partials/theme-toggle.html @@ -0,0 +1,29 @@ +{{/* + This is modified from Hextra's layouts/partials/favicons.html + (blob: 6a939b56760c8f2b7ca442463ee41b47a65b6091) + + Changes: + + * increase size of icons +*/}} +{{- $hideLabel := .hideLabel | default false -}} + +{{- $changeTheme := (T "changeTheme") | default "Change theme" -}} +{{- $light := (T "light") | default "Light" -}} +{{- $dark := (T "dark") | default "Dark" -}} + + + diff --git a/docs/site/scripts/add-gh-links b/docs/site/scripts/add-gh-links new file mode 100755 index 00000000..e4420dd0 --- /dev/null +++ b/docs/site/scripts/add-gh-links @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# Copyright 2024 Metrum Research Group +# SPDX-License-Identifier: MIT +"""usage: {} + +Read markdown content from stdin and write it to stdout replacing + + * #NNN with [#NNN](/issue/NNN) + + * commit IDs with links to [](/commit/) + + If something looks like a commit but isn't a known commit in the + current repository, it's left as is. + +This script must be executed from a Git repository. +""" + +import fileinput +import re +import subprocess as sp +import sys + +link_re = re.compile(r"\B#([1-9][0-9]*)\b") + + +def link_issues(url, string): + return link_re.sub(rf"[#\1]({url}/issues/\1)", string) + + +commit_re = re.compile(r"\b([0-9a-f]{7,})\b") + + +# Note: This runs a git process per commit candidate. If this were to +# be used in a case where there is a large amount of input and +# performance matters, consider rewriting to use cat-file and a single +# process. +def replace_commit(matchobj): + fullmatch = matchobj.group(0) + commit = matchobj.group(1) + + try: + p = sp.run( + ["git", "rev-parse", "--verify", "--quiet", commit + "^{commit}"], + check=True, + capture_output=True, + encoding="utf-8", + ) + except sp.CalledProcessError as err: + if err.returncode == 1: + # This doesn't resolve to a known commit. + return fullmatch + raise + + resolved = p.stdout.rstrip("\n") + return fullmatch.replace(commit, f"[{commit}]({url}/commit/{resolved})") + + +def link_commits(url, string): + return commit_re.sub(replace_commit, string) + + +if __name__ == "__main__": + if len(sys.argv) != 2: + sys.stderr.write(__doc__.format(sys.argv[0])) + sys.exit(1) + + url = sys.argv[1] + if url.endswith("/"): + url = url[:-1] + + for line in fileinput.input("-"): + line = link_issues(url, line) + line = link_commits(url, line) + sys.stdout.write(line) diff --git a/docs/site/scripts/format-command-flags b/docs/site/scripts/format-command-flags new file mode 100755 index 00000000..e9c2a413 --- /dev/null +++ b/docs/site/scripts/format-command-flags @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +""" +Read Cobra command markdown on stdin and replace option code blocks +with a list of options. + +For example, convert + + ``` + --json json tree of output, if possible + -v, --verbose verbose output + ``` + +to + + * `--json`: json tree of output, if possible + + * `-v, --verbose`: verbose output + +""" + +import fileinput +import sys + + +def format_option(line): + line = line.strip() + if not line: + return "" + if line == "```": + return "\n" + + try: + # Note: This relies on the "-o, --option value" part 1) _not_ + # containing two spaces and 2) being separated from the + # description by at least two spaces. + opts, desc = line.split(" ", maxsplit=1) + except ValueError: + raise ValueError(f"option line in unexpected format: {line}") from None + + return f" * `{opts}`: {desc}\n\n" + + +if __name__ == "__main__": + in_options = False + for line in fileinput.input("-"): + if line.startswith("#"): + in_options = line.startswith("### Options") + sys.stdout.write(line) + continue + + if in_options: + line = format_option(line) + sys.stdout.write(line) diff --git a/docs/site/scripts/ingest-command-docs b/docs/site/scripts/ingest-command-docs new file mode 100755 index 00000000..0881ad2f --- /dev/null +++ b/docs/site/scripts/ingest-command-docs @@ -0,0 +1,58 @@ +#!/bin/sh + +# Copy generated command docs from contents/commands/ with the +# following modifications: +# +# * set title metadata to name of subcommand +# +# * promote each heading (e.g., H2 becomes H1) +# +# * adjust the link to the other markdown files to work in the Hugo +# context +# +# * tweak the headings of top-level command page to make it a more +# suitable landing page for /commands +# +# * replace "option" with "flag" in title. +# +# Although "option" is arguably the better term in general, 1) +# cobra puts these under "flags" in its command-line help output +# and 2) cobra adds a "[flag]" value to the usage, so staying with +# "flag" is better for consistency. +# +# * put inherited flags as subhead of under flags heading +# +# * extract option lines from code block and reformat as bullets + +set -eu + +test -x scripts/format-command-flags || { + printf >&2 'script must be executed from docs/site/ directory\n' + exit 1 +} + +outdir=content/docs/commands +mkdir -p "$outdir" +for f in ../commands/*.md +do + if test "$f" = ../commands/pkgr.md + then + dest=$outdir/_index.md + title=Commands + seename=Subcommands + else + base=$(basename "$f") + cmd=${base%.md} + title=$(printf '%s\n' "$cmd" | sed 's/pkgr_//' | sed 's/_/ /g') + dest=$outdir/${base#pkgr_} + seename='See also' + fi + printf -- '---\ntitle: "%s"\ntype: "command"\n---\n\n' "$title" >"$dest" + ./scripts/format-command-flags <"$f" | \ + sed 's/^##/#/' | \ + sed -E 's|\[pkgr\]\(pkgr\.md\).*|[pkgr]({{< relref "/docs/commands/" >}}) - top-level entry point |' | \ + sed 's|(pkgr_\(.*\)\.md)|({{< relref "/docs/commands/\1" >}})|' | \ + sed 's/# Options inherited from parent commands/## Inherited/' | \ + sed 's/# Options/# Flags/' | \ + sed "s/# SEE ALSO/# $seename/" >>"$dest" +done diff --git a/docs/site/static/apple-touch-icon-120x120.png b/docs/site/static/apple-touch-icon-120x120.png new file mode 100644 index 00000000..1e9fe20c Binary files /dev/null and b/docs/site/static/apple-touch-icon-120x120.png differ diff --git a/docs/site/static/apple-touch-icon-152x152.png b/docs/site/static/apple-touch-icon-152x152.png new file mode 100644 index 00000000..c4d2527d Binary files /dev/null and b/docs/site/static/apple-touch-icon-152x152.png differ diff --git a/docs/site/static/apple-touch-icon-180x180.png b/docs/site/static/apple-touch-icon-180x180.png new file mode 100644 index 00000000..1063ce61 Binary files /dev/null and b/docs/site/static/apple-touch-icon-180x180.png differ diff --git a/docs/site/static/apple-touch-icon-60x60.png b/docs/site/static/apple-touch-icon-60x60.png new file mode 100644 index 00000000..2b577822 Binary files /dev/null and b/docs/site/static/apple-touch-icon-60x60.png differ diff --git a/docs/site/static/apple-touch-icon-76x76.png b/docs/site/static/apple-touch-icon-76x76.png new file mode 100644 index 00000000..3f55d615 Binary files /dev/null and b/docs/site/static/apple-touch-icon-76x76.png differ diff --git a/docs/site/static/apple-touch-icon.png b/docs/site/static/apple-touch-icon.png new file mode 100644 index 00000000..1063ce61 Binary files /dev/null and b/docs/site/static/apple-touch-icon.png differ diff --git a/docs/site/static/favicon-16x16.png b/docs/site/static/favicon-16x16.png new file mode 100644 index 00000000..038940a5 Binary files /dev/null and b/docs/site/static/favicon-16x16.png differ diff --git a/docs/site/static/favicon-32x32.png b/docs/site/static/favicon-32x32.png new file mode 100644 index 00000000..5e64c7c4 Binary files /dev/null and b/docs/site/static/favicon-32x32.png differ diff --git a/docs/site/static/favicon.ico b/docs/site/static/favicon.ico new file mode 100644 index 00000000..bac9784b Binary files /dev/null and b/docs/site/static/favicon.ico differ diff --git a/docs/site/static/logo.png b/docs/site/static/logo.png new file mode 100644 index 00000000..eb7e3b72 Binary files /dev/null and b/docs/site/static/logo.png differ