|
| 1 | +--- |
| 2 | +output: hugodown::hugo_document |
| 3 | + |
| 4 | +slug: rapp-0-3-0 |
| 5 | +title: Rapp 0.3.0 |
| 6 | +date: 2026-02-18 |
| 7 | +author: Tomasz Kalinowski |
| 8 | +description: > |
| 9 | + Rapp is an R front-end (like Rscript) that turns simple scripts into polished CLIs, |
| 10 | + with automatic argument parsing, generated help, and support for |
| 11 | + commands and installable launchers. |
| 12 | +
|
| 13 | +photo: |
| 14 | + url: https://unsplash.com/photos/two-yellow-red-blue-papers-Ay7Nkvc49ag |
| 15 | + author: Carolina Garcia Tavizon |
| 16 | + |
| 17 | + |
| 18 | +# one of: "deep-dive", "learn", "package", "programming", "roundup", or "other" |
| 19 | +categories: [package] |
| 20 | +tags: [r-lib, package, programming, yaml] |
| 21 | +--- |
| 22 | + |
| 23 | +We're excited to share our first tidyverse blog post for Rapp, alongside the `0.3.0` release. Rapp helps you turn R scripts into polished command-line tools, with argument parsing and help generation built in. |
| 24 | + |
| 25 | +## Why a command-line interface for R? |
| 26 | + |
| 27 | +A command-line interface (CLI) lets you run programs from a terminal, without opening an IDE or starting an interactive R session. This is useful when you want to: |
| 28 | + |
| 29 | +- automate tasks via cron jobs, scheduled tasks, or CI/CD pipelines |
| 30 | +- chain R scripts together with other tools in data pipelines |
| 31 | +- let others run your R code without needing to know R |
| 32 | +- package reusable tools that feel native to the terminal |
| 33 | +- expose specific actions through a clean interface that LLM agents can invoke |
| 34 | + |
| 35 | +There are several established packages for building CLIs in R, including argparse, optparse, and docopt, where you explicitly parse and handle command-line arguments in code. Rapp takes a different approach: it derives the CLI surface from the structure of your R script and injects values at runtime, so you never need to handle CLI arguments manually. |
| 36 | + |
| 37 | +## How Rapp works |
| 38 | + |
| 39 | +At its core, Rapp is an alternative front-end to R: a drop-in replacement for `Rscript` that automatically turns common R expression patterns into command-line options, switches, positional arguments, and subcommands. You write normal R code and Rapp handles the CLI surface. |
| 40 | + |
| 41 | +Rapp also uses special `#|` comments (similar to Quarto's YAML-in-comments syntax) to add metadata such as help descriptions and short aliases. |
| 42 | + |
| 43 | +## A tiny example |
| 44 | + |
| 45 | +Here's a complete Rapp script (from the package examples), a coin flipper: |
| 46 | + |
| 47 | +```r |
| 48 | +#!/usr/bin/env Rapp |
| 49 | +#| name: flip-coin |
| 50 | +#| description: | |
| 51 | +#| Flip a coin. |
| 52 | + |
| 53 | +#| description: Number of coin flips |
| 54 | +#| short: 'n' |
| 55 | +flips <- 1L |
| 56 | + |
| 57 | +sep <- " " |
| 58 | +wrap <- TRUE |
| 59 | + |
| 60 | +seed <- NA_integer_ |
| 61 | +if (!is.na(seed)) { |
| 62 | + set.seed(seed) |
| 63 | +} |
| 64 | + |
| 65 | +cat(sample(c("heads", "tails"), flips, TRUE), sep = sep, fill = wrap) |
| 66 | +``` |
| 67 | + |
| 68 | +Let's break down how Rapp interprets this script: |
| 69 | + |
| 70 | +| R code | Generated CLI option | What it does | |
| 71 | +|--------|------------|-------------| |
| 72 | +| `flips <- 1L` | `--flips` or `-n` | Integer option with default of 1 | |
| 73 | +| `sep <- " "` | `--sep` | String option with default of `" "` | |
| 74 | +| `wrap <- TRUE` | `--wrap` / `--no-wrap` | Boolean toggle (TRUE/FALSE becomes on/off) | |
| 75 | +| `seed <- NA_integer_` | `--seed` | Optional integer (NA means "not set") | |
| 76 | + |
| 77 | +The `#| short: 'n'` comment adds `-n` as a short alias for `--flips`. The `#!/usr/bin/env Rapp` line (called a "shebang") lets you run the script directly on macOS and Linux without typing `Rapp` first. |
| 78 | + |
| 79 | +### Running the script |
| 80 | + |
| 81 | +With Rapp installed and `flip-coin` available on your `PATH` (see [Get started](#get-started) below), you can run the app from the terminal: |
| 82 | + |
| 83 | +```sh |
| 84 | +flip-coin -n 3 |
| 85 | +#> heads tails heads |
| 86 | + |
| 87 | +flip-coin --seed 42 -n 5 |
| 88 | +#> tails heads tails tails heads |
| 89 | +``` |
| 90 | + |
| 91 | +### Auto-generated help |
| 92 | + |
| 93 | +Rapp generates `--help` from your script (and `--help-yaml` if you want a machine-readable spec): |
| 94 | + |
| 95 | +```sh |
| 96 | +flip-coin --help |
| 97 | +``` |
| 98 | + |
| 99 | +```text |
| 100 | +Usage: flip-coin [OPTIONS] |
| 101 | +
|
| 102 | +Flip a coin. |
| 103 | +
|
| 104 | +Options: |
| 105 | + -n, --flips <FLIPS> Number of coin flips [default: 1] [type: integer] |
| 106 | + --sep <SEP> [default: " "] [type: string] |
| 107 | + --wrap / --no-wrap [default: true] Disable with `--no-wrap`. |
| 108 | + --seed <SEED> [default: NA] [type: integer] |
| 109 | +``` |
| 110 | + |
| 111 | +::: {.callout-warning} |
| 112 | +## Breaking change in 0.3.0: positional arguments are now required by default |
| 113 | + |
| 114 | +If you're upgrading from an earlier version of Rapp, note that positional arguments are now **required** unless explicitly marked optional. |
| 115 | + |
| 116 | +```r |
| 117 | +# Before 0.3.0: this positional was optional |
| 118 | +name <- NULL |
| 119 | + |
| 120 | +# In 0.3.0+: add this comment to keep it optional |
| 121 | +#| required: false |
| 122 | +name <- NULL |
| 123 | +``` |
| 124 | + |
| 125 | +If your scripts use positional arguments with `NULL` defaults that should remain optional, add `#| required: false` above them. |
| 126 | +::: |
| 127 | + |
| 128 | +## Highlights in 0.3.0 |
| 129 | + |
| 130 | +Rapp will be new to most readers, so rather than listing every change, here are the main ideas (and what's improved in 0.3.0). |
| 131 | + |
| 132 | +### Options, switches, and repeatable flags from plain R |
| 133 | + |
| 134 | +Rapp recognizes a small set of "declarative" patterns at the top level of your script: |
| 135 | + |
| 136 | +- Scalar literals like `flips <- 1L` become options like `--flips 10`. |
| 137 | +- Logical defaults like `wrap <- TRUE` become toggles like `--wrap` / `--no-wrap`. |
| 138 | +- `#| short: n` adds a short alias like `-n` (new in 0.3.0). |
| 139 | +- `c()` and `list()` defaults declare repeatable options (new in 0.3.0): callers can supply the same flag multiple times and values are appended. |
| 140 | + |
| 141 | +### Subcommands with `switch()` |
| 142 | + |
| 143 | +Rapp can now turn a `switch()` block into subcommands (and you can nest `switch()` blocks for nested commands). Here's a small sketch of a `todo`-style app: |
| 144 | + |
| 145 | +```r |
| 146 | +#!/usr/bin/env Rapp |
| 147 | +#| name: todo |
| 148 | +#| description: Manage a simple todo list. |
| 149 | + |
| 150 | +#| description: Path to the todo list file. |
| 151 | +#| short: s |
| 152 | +store <- ".todo.yml" |
| 153 | + |
| 154 | +switch( |
| 155 | + command <- "", |
| 156 | + |
| 157 | + #| description: Display the todos |
| 158 | + list = { |
| 159 | + limit <- 30L |
| 160 | + # ... |
| 161 | + }, |
| 162 | + |
| 163 | + #| description: Add a new todo |
| 164 | + add = { |
| 165 | + task <- NULL |
| 166 | + # ... |
| 167 | + } |
| 168 | +) |
| 169 | +``` |
| 170 | + |
| 171 | +Help is scoped to the command you're asking about, so `todo --help` lists the commands, and `todo list --help` shows just the options/arguments for `list` (plus any parent/global options). |
| 172 | + |
| 173 | +### Installable launchers for package CLIs |
| 174 | + |
| 175 | +A big part of sharing CLI tools is making them easy to run after installation. In `0.3.0`, `install_pkg_cli_apps()` installs lightweight launchers for scripts in a package's `exec/` directory that use either `#!/usr/bin/env Rapp` or `#!/usr/bin/env Rscript`: |
| 176 | + |
| 177 | +```r |
| 178 | +Rapp::install_pkg_cli_apps("mypackage") |
| 179 | +``` |
| 180 | + |
| 181 | +(There's also `uninstall_pkg_cli_apps()` to remove a package's launchers.) |
| 182 | + |
| 183 | +## Get started |
| 184 | + |
| 185 | +Here's the quickest path to your first Rapp script: |
| 186 | + |
| 187 | +```r |
| 188 | +# 1. Install the package |
| 189 | +install.packages("Rapp") |
| 190 | +``` |
| 191 | + |
| 192 | +```r |
| 193 | +# 2. Install the command-line launcher |
| 194 | +Rapp::install_pkg_cli_apps("Rapp") |
| 195 | +``` |
| 196 | + |
| 197 | +Then create a script (e.g., `hello.R`): |
| 198 | + |
| 199 | +```{.r filename="hello.R"} |
| 200 | +#!/usr/bin/env Rapp |
| 201 | +#| name: hello |
| 202 | +#| description: Say hello |
| 203 | + |
| 204 | +name <- "world" |
| 205 | +cat("Hello,", name, "\n") |
| 206 | +``` |
| 207 | + |
| 208 | +And run it: |
| 209 | + |
| 210 | +```sh |
| 211 | +Rapp hello.R --name "R users" |
| 212 | +#> Hello, R users |
| 213 | +``` |
| 214 | + |
| 215 | +### Learn more |
| 216 | + |
| 217 | +To dig deeper into Rapp: |
| 218 | + |
| 219 | +- browse examples in the package: `system.file("examples", package = "Rapp")` |
| 220 | +- read the full documentation: <https://github.com/r-lib/Rapp> |
| 221 | +- note that Rapp requires R ≥ 4.1.0 |
| 222 | + |
| 223 | +If you try Rapp, we'd love feedback! We especially want to hear about your experiences with edge cases in argument parsing, help output, and how commands should feel. Issues and ideas are welcome at <https://github.com/r-lib/Rapp/issues>. |
0 commit comments