Skip to content

Commit

Permalink
generate title from path; update title from server
Browse files Browse the repository at this point in the history
  • Loading branch information
aronatkins committed Nov 1, 2023
1 parent 48c7970 commit 019920f
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 191 deletions.
116 changes: 60 additions & 56 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
content was incorrectly published to a new location rather than reusing an
existing deployment. (#981, #1007, #1013, #1019)

* Generated application names are always normalized and lower-cased when
deploying to shinyapps.io. The name is derived from an incoming title, when
provided, and otherwise from the content path. (#1022)
* When `deployApp()` is not given `appName`, the name is generated from an
incoming title, when provided. When the title is not provided, a title is
generated from the content path, which is then used to generate the
normalized application name. (#1022)

* The application title recorded in the deployment record is updated with
server data. (#1008)

* `showLogs()`, `configureApp()`, `setProperty()`, and `unsetProperty()`
search for the application by name when there are no matching deployment
Expand Down Expand Up @@ -40,7 +44,7 @@

* Ignore `.env`, `.venv`, and `venv` files only when they reference Python
virtual environments. (#972)

* `deployApp()` and `writeManifest()` accept optional `envManagement`,
`envManagementR`, and `envManagementPy` arguments. These args specify whether
Posit Connect should install packages in the package cache.
Expand Down Expand Up @@ -105,29 +109,29 @@

* rsconnect requires renv 1.0.0.

* `deployApp()` and `writeManifest()` now respect renv lock files, if present.
If you don't want to use these lockfiles, and instead return the previous
behaviour of snapshotting on every deploy, add your `renv.lock` to
* `deployApp()` and `writeManifest()` now respect renv lock files, if present.
If you don't want to use these lockfiles, and instead return the previous
behaviour of snapshotting on every deploy, add your `renv.lock` to
`.rscignore` (#671). Learn more `?appDependencies()`.
Additionally, `deployApp()` and `writeManifest()` now use renv to capture app
dependencies, rather than packrat. If this causes a previously working deploy
to fail, please file an issue then set `options(rsconnect.packrat = TRUE)` to

Additionally, `deployApp()` and `writeManifest()` now use renv to capture app
dependencies, rather than packrat. If this causes a previously working deploy
to fail, please file an issue then set `options(rsconnect.packrat = TRUE)` to
revert to the previous behaviour.

* `deployApp()`'s `quarto` argument now takes values `TRUE`, `FALSE` or
* `deployApp()`'s `quarto` argument now takes values `TRUE`, `FALSE` or
`NA`. The previous value (a path to a quarto binary) is now ignored,
and instead we automatically figure out the package from `QUARTO_PATH` and
`PATH` env vars (#658). `deploySite()` now supports quarto websites (#813).

* `deployApp()` gains a new `envVars` argument which takes a vector of the
names of environment variables that should be securely copied to the server.
* `deployApp()` gains a new `envVars` argument which takes a vector of the
names of environment variables that should be securely copied to the server.
The names (not values) of these environment variables are also saved in the
deployment record and will be updated each time you re-deploy the app (#667).
This currently only works with Connect, but we hope to add support to
This currently only works with Connect, but we hope to add support to
Posit cloud and shinyapps.io in the future.
* rsconnect gains two new functions for understanding and updating the

* rsconnect gains two new functions for understanding and updating the
environment variables that your apps currently use. `listServerEnvVars()`
will return a data frame of applications, with a `envVars` list-column
giving the names of the environment variables used by each application.
Expand All @@ -137,7 +141,7 @@

## Lifecycle changes

* Non-libcurl `rsconnect.http` options have been deprecated. This allows us to
* Non-libcurl `rsconnect.http` options have been deprecated. This allows us to
focus our efforts on a single backend, rather than spreading development
efforts across five. The old backends will remain available for at least 2
years, but if you are using them because libcurl doesn't work for you, please
Expand Down Expand Up @@ -166,19 +170,19 @@
* Uploading large files to RPubs works once more (#450).

* When recording details about deployments to Posit Cloud, appId now represents
the content id (as seen in URLs of the format
the content id (as seen in URLs of the format
`https://posit.cloud/content/{id}`) instead of the application id.

* Deployment records no longer contain the time the app was deployed (`when`)
or when it's metadata was last synced (`lastSyncTime`) as these variables
are not very useful, and they lead to uninteresting diffs if you have
committed the deployment records to git (#770). A `version` field has been
are not very useful, and they lead to uninteresting diffs if you have
committed the deployment records to git (#770). A `version` field has been
added to deployment DCF files to facilitate future file format changes, if
needed. Its value for this release is `1`.,

* `accounts()` returns a zero-row data frame if no accounts are registered.

* `accountInfo()` and `removeAccount()` no longer require `account` be
* `accountInfo()` and `removeAccount()` no longer require `account` be
supplied (#666).

* `accountInfo()` and `servers()` redact sensitive information (secrets,
Expand All @@ -187,8 +191,8 @@

* `addServer()` includes the port in the default server name, if present.

* `appDependencies()` includes implicit dependencies, and returns an additional
column giving the Repository (#670). Its documentation contains more
* `appDependencies()` includes implicit dependencies, and returns an additional
column giving the Repository (#670). Its documentation contains more
information about how dependency discovery works, and how you can control
it, if needed.

Expand All @@ -197,13 +201,13 @@

* `connectApiUser()` now clearly requires an `apiKey` (#741).

* `deployApp()` output has been thoroughly reviewed and tweaked. As well as
* `deployApp()` output has been thoroughly reviewed and tweaked. As well as
general polish it now gives you more information about what it has discovered
about the deployment, like the app name, account & server, and which files
are included in the bundle (#669).

* `deployApp()` is more aggressive about saving deployment data, which should
make it less likely that you need to repeat yourself after a failed
make it less likely that you need to repeat yourself after a failed
deployment. In particular, it now saves both before and after uploading the
contents (#677) and it saves when you're updating content originally created
by someone else (#270).
Expand All @@ -212,70 +216,70 @@
visibility of an app deployed to posit.cloud (#838).

* `deployApp()` now uses a stricter policy for determining whether or not
a locally installed package can be successfully installed on the deployment
server. This means that you're more likely to get a clean failure prior to
a locally installed package can be successfully installed on the deployment
server. This means that you're more likely to get a clean failure prior to
deployment (#659).

* `deployApp()` will now detect if you're attempting to publish to an app
that has been deleted and will prompt you to create a new app (#226).

* `deployApp()` includes some new conveniences for large uploads including
reporting the size of the bundle you're uploading and showing a progress bar
reporting the size of the bundle you're uploading and showing a progress bar
in interactive sessions (#754).

* `deployApp()` now follows redirects, which should make it more robust to your
server moving to a new url (#674).

* `deployApp()` uses simpler logic for determining whether it should create a
new app or update an existing app. Now `appName`, `account`, and `server` are
used to find existing deployments. If none are found, it will create a new
deployment; if one is found, it'll be updated; if more than one are found, it
new app or update an existing app. Now `appName`, `account`, and `server` are
used to find existing deployments. If none are found, it will create a new
deployment; if one is found, it'll be updated; if more than one are found, it
will prompt you to disambiguate (#666).

* `deployApp()` improves account resolution from `account` and `server`
arguments by giving specific recommendations on the values that you might use
in the case of ambiguity or lack of matches (#666). Additionally, you'll now
receive a clear error if you accidentally provide something other than a
* `deployApp()` improves account resolution from `account` and `server`
arguments by giving specific recommendations on the values that you might use
in the case of ambiguity or lack of matches (#666). Additionally, you'll now
receive a clear error if you accidentally provide something other than a
string or `NULL` to these arguments.

* `deployApp()` now generates an interactive prompt to select `account`/`server`
(if no previous deployments) or `appName`/`account`/`server` (if multiple
previous deployments) (#691).
(if no previous deployments) or `appName`/`account`/`server` (if multiple
previous deployments) (#691).

* `deployApp()` now advertises which startup scripts are run at the normal
`logLevel`, and it evaluates each script in its own environment (#542).

* `deployApp()` now derives `appName` from `appDir` and `appPrimaryDoc`,
never using the title (#538). It now only simplifies the path if you are
publishing to shinyapps.io, since its restrictions on application names are
* `deployApp()` now derives `appName` from `appDir` and `appPrimaryDoc`,
never using the title (#538). It now only simplifies the path if you are
publishing to shinyapps.io, since its restrictions on application names are
much tighter than those of Posit Connect.

* `deployApp()` will now warn if `appFiles` or `appManifestFiles` contain
files that don't exist, rather than silently ignoring them (#706).

* `deployApp()` excludes temporary backup files (names starting or ending
with `~`) when automatically determining files to bundle (#111) as well as
directories that are likely to be Python virtual environments (#632).
Additionally, ignore rules are always now applied to all directories;
previously some (like `.Rproj.user` and `"manifest.json"`) were only
applied to the root directory. It correctly handles `.rscignore` files
(i.e. as documented) (#568).
* `deployApp()` excludes temporary backup files (names starting or ending
with `~`) when automatically determining files to bundle (#111) as well as
directories that are likely to be Python virtual environments (#632).
Additionally, ignore rules are always now applied to all directories;
previously some (like `.Rproj.user` and `"manifest.json"`) were only
applied to the root directory. It correctly handles `.rscignore` files
(i.e. as documented) (#568).

* `deployApp(appSourceDoc)` has been deprecated; it did the same job as
`recordDir`.

* `deployDoc()` includes a `.Rprofile` in the bundle, if one is found in the
* `deployDoc()` includes a `.Rprofile` in the bundle, if one is found in the
same directory as the document.

* `lint()` should have fewer false positives for path problems:
the relative path linter has been removed (#244) and the case-sensitive
the relative path linter has been removed (#244) and the case-sensitive
linter now only checks strings containing a `/` (#611).

* New `listDeploymentFiles()`, which supsersedes `listBundleFiles()`.
It now errors when if the bundle is either too large or contains too many
It now errors when if the bundle is either too large or contains too many
files, rather than silently truncating as before (#684).

* `serverInfo()` and `removeServer()` no longer require a `server` when
* `serverInfo()` and `removeServer()` no longer require a `server` when
called interactively.

* `showMetrics()` once again returns a correctly named data frame (#528).
Expand Down Expand Up @@ -315,11 +319,11 @@
* Shiny applications and Shiny documents no longer include an implicit
dependency on [`ragg`](https://ragg.r-lib.org) when that package is present
in the local environment. This reverts a change introduced in 0.8.27.

Shiny applications should add an explicit dependency on `ragg` (usually with
a `library("ragg")` statement) to see it used by `shiny::renderPlot` (via
`shiny::plotPNG`).

The documentation for `shiny::plotPNG` explains the use of `ragg`. (#598)

* Fix bug that prevented publishing or writing manifests for non-Quarto content
Expand All @@ -341,8 +345,8 @@
an `.Rmd` file will successfully deploy as Quarto content instead of falling
back to R Markdown. (#601)
* If the `ragg` package is installed locally, it is now added as an implicit
dependency to `shiny` apps since `shiny::renderPlot()` now uses it by default
(when available). This way, `shiny` apps won't have to add `library(ragg)` to
dependency to `shiny` apps since `shiny::renderPlot()` now uses it by default
(when available). This way, `shiny` apps won't have to add `library(ragg)` to
get consistent (higher-quality) PNG images when deployed. (#598)

# rsconnect 0.8.26
Expand Down
53 changes: 13 additions & 40 deletions R/deploymentTarget.R
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,12 @@ findDeploymentTarget <- function(
# Otherwise, identify a target account (given just one available or prompted
# by the user), generate a name, and locate the deployment.
accountDetails <- findAccountInfo(account, server, error_call = error_call)
appName <- generateDefaultName(
recordPath = recordPath,
title = appTitle,
server = accountDetails$server
appTitle <- generateTitle(recordPath = recordPath, title = appTitle)
appName <- generateAppName(
appTitle = appTitle,
appPath = recordPath,
account = accountDetails$name,
unique = FALSE
)
return(findDeploymentTargetByAppName(
recordPath = recordPath,
Expand Down Expand Up @@ -319,25 +321,7 @@ updateDeployment <- function(previous, appTitle = NULL, envVars = NULL) {
)
}

normalizeName <- function(name, server = NULL) {
if (is.null(name)) {
return("")
}

if (isShinyappsServer(server)) {
name <- tolower(name)
}

# Replace non-alphanumerics with underscores, trim to length 64
name <- gsub("[^[:alnum:]_-]+", "_", name, perl = TRUE)
name <- gsub("_+", "_", name)
if (nchar(name) > 64) {
name <- substr(name, 1, 64)
}

name
}

# Generate a title from a content path.
titleFromPath <- function(recordPath) {
if (isDocumentPath(recordPath)) {
title <- file_path_sans_ext(basename(recordPath))
Expand All @@ -354,24 +338,13 @@ titleFromPath <- function(recordPath) {
title
}

# Determine name given a file or directory path and (optional) title.
#
# Prefer generating the name from the incoming title when provided and
# fall-back to one derived from the target filename.
#
# Name is guaranteed to conform to [a-zA-Z0-9_-]{0,64}. Minimum length is
# enforced by the server.
#
# Names produced for Shinyapps.io deployments are lower-cased.
generateDefaultName <- function(recordPath, title = NULL, server = NULL) {
name <- normalizeName(title, server = server)

if (nchar(name) < 3) {
title <- titleFromPath(recordPath)
name <- normalizeName(title, server = server)
# Generate a title when a title was not already specified.
generateTitle <- function(recordPath, title = NULL) {
if (is.null(title)) {
titleFromPath(recordPath)
} else {
title
}

name
}

shouldUpdateApp <- function(application,
Expand Down
2 changes: 1 addition & 1 deletion R/deployments.R
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ saveDeployment <- function(recordDir,
addToHistory = TRUE) {
deployment <- deploymentRecord(
name = deployment$name,
title = deployment$title,
title = application$title %||% deployment$title,
username = deployment$username,
account = deployment$account,
server = deployment$server,
Expand Down
13 changes: 1 addition & 12 deletions R/title.R
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,7 @@ generateAppName <- function(appTitle, appPath = NULL, account = NULL, unique = T
# if we wound up with too few characters, try generating from the directory
# name instead
if (nchar(name) < 3 && !is.null(appPath) && file.exists(appPath)) {
# strip extension if present
base <- basename(appPath)
if (nzchar(tools::file_ext(base))) {
base <- file_path_sans_ext(base)

# if we stripped an extension and the name is now "index", use the parent
# folder's name
if (identical(base, "index")) {
base <- basename(dirname(appPath))
}
}
name <- munge(base)
name <- munge(titleFromPath(appPath))
}

# validate that we wound up with a valid name
Expand Down
Loading

0 comments on commit 019920f

Please sign in to comment.