Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,4 @@ Config/testthat/edition: 3
Encoding: UTF-8
Language: en-US
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.2
RoxygenNote: 7.3.3
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export(add_dockerfile_with_renv_heroku)
export(add_dockerfile_with_renv_shinyproxy)
export(add_empty_file)
export(add_fct)
export(add_github_action)
export(add_gitlab_ci)
export(add_html_template)
export(add_js_file)
export(add_js_handler)
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

## New features / user-visible changes

- New `add_github_action()` and `add_gitlab_ci()` helpers generate minimal deployment CI for fresh `{golem}` apps.
- The `add_dockerfile_with_renv_*` function now generates a multi-stage Dockerfile by default (use `single_file = FALSE` to retain the previous behavior).
- The `add_dockerfile_with_renv_*` function now creates a Dockerfile that sets `golem.app.prod = TRUE` by default (use `set_golem.app.prod = FALSE` to retain the previous behavior).
- Print functions have be reworked standardized using the `{cli}` package (@ilyaZar, #89)
Expand Down
268 changes: 268 additions & 0 deletions R/add_ci_files.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
#' Add deployment CI for GitHub Actions
#'
#' Creates a minimal GitHub Actions workflow for deploying a `{golem}` app via
#' `{rsconnect}`. If needed, this function also creates a root `app.R` and
#' `.rscignore` by calling [add_positconnect_file()].
#'
#' @inheritParams add_module
#'
#' @export
#'
#' @return The path to the created workflow, invisibly.
add_github_action <- function(
golem_wd = get_golem_wd(),
open = TRUE,
pkg
) {
signal_arg_is_deprecated(
pkg,
fun = as.character(
sys.call()[[1]]
),
"pkg"
)

add_deploy_ci_(
template = "github-action-template.yml",
output = fs_path(
golem_wd,
".github",
"workflows",
"shiny-deploy.yaml"
),
golem_wd = golem_wd,
open = open
)
}

#' Add deployment CI for GitLab
#'
#' Creates a minimal GitLab CI file for deploying a `{golem}` app via
#' `{rsconnect}`. If needed, this function also creates a root `app.R` and
#' `.rscignore` by calling [add_positconnect_file()].
#'
#' @inheritParams add_module
#'
#' @export
#'
#' @return The path to the created workflow, invisibly.
add_gitlab_ci <- function(
golem_wd = get_golem_wd(),
open = TRUE,
pkg
) {
signal_arg_is_deprecated(
pkg,
fun = as.character(
sys.call()[[1]]
),
"pkg"
)

add_deploy_ci_(
template = "gitlab-ci-template.yml",
output = fs_path(
golem_wd,
".gitlab-ci.yml"
),
golem_wd = golem_wd,
open = open
)
}

add_deploy_ci_ <- function(
template,
output,
golem_wd = get_golem_wd(),
open = TRUE
) {
golem_wd <- fs_path_abs(
golem_wd
)

ensure_deploy_entrypoint_(
golem_wd = golem_wd
)

if (
fs_file_exists(
output
)
) {
cli_alert_info(
sprintf(
"The '%s'-file already exists.",
basename(
output
)
)
)
return(
open_or_go_to(
output,
open
)
)
}

fs_dir_create(
path_dir(
output
),
recurse = TRUE
)

writeLines(
render_ci_template_(
template = template,
golem_wd = golem_wd
),
con = output
)

if (
basename(
output
) ==
"shiny-deploy.yaml"
) {
ensure_github_gitignore_(
golem_wd = golem_wd
)
usethis_use_build_ignore(
".github"
)
} else {
usethis_use_build_ignore(
".gitlab-ci.yml"
)
}

cat_created(
output
)
open_or_go_to(
output,
open
)
}

render_ci_template_ <- function(
template,
golem_wd = get_golem_wd()
) {
app_name <- get_golem_name(
golem_wd = golem_wd
)

template_lines <- readLines(
golem_sys(
"utils",
template
),
warn = FALSE
)

gsub(
"__APPNAME__",
app_name,
template_lines,
fixed = TRUE
)
}

ensure_deploy_entrypoint_ <- function(
golem_wd = get_golem_wd()
) {
app_file <- fs_path(
golem_wd,
"app.R"
)
rscignore_file <- fs_path(
golem_wd,
".rscignore"
)

if (
!fs_file_exists(
app_file
)
) {
add_positconnect_file(
golem_wd = golem_wd,
open = FALSE
)
return(
invisible(
golem_wd
)
)
}

if (
!fs_file_exists(
rscignore_file
)
) {
add_rscignore_file(
golem_wd = golem_wd,
open = FALSE
)
}

invisible(
golem_wd
)
}

ensure_github_gitignore_ <- function(
golem_wd = get_golem_wd()
) {
where <- fs_path(
golem_wd,
".github",
".gitignore"
)

if (
!fs_file_exists(
where
)
) {
writeLines(
"*.html",
con = where
)
return(
invisible(
where
)
)
}

content <- readLines(
where,
warn = FALSE
)

if (
!"*.html" %in%
content
) {
writeLines(
c(
content,
"*.html"
),
con = where
)
}

invisible(
where
)
}

path_dir <- function(path) {
dirname(path)
}
8 changes: 5 additions & 3 deletions inst/shinyexample/dev/02_dev.R
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,10 @@ covrpage::covrpage()
usethis::use_github()

# GitHub Actions
usethis::use_github_action()
# Chose one of the three
# Add actions workflow for golem based deployment of Shiny apps
golem::add_github_action()
# Or use the generic usethis GitHub Actions helpers
# Choose one of the three
# See https://usethis.r-lib.org/reference/use_github_action.html
usethis::use_github_action_check_release()
usethis::use_github_action_check_standard()
Expand All @@ -82,7 +84,7 @@ usethis::use_circleci_badge()
usethis::use_jenkins()

# GitLab CI
usethis::use_gitlab_ci()
golem::add_gitlab_ci()

# You're now set! ----
# go to dev/03_deploy.R
Expand Down
72 changes: 72 additions & 0 deletions inst/utils/github-action-template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
push:
branches: [main, master]

name: shiny-deploy.yaml

permissions: read-all

jobs:
shiny-deploy:
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4

- uses: r-lib/actions/setup-pandoc@v2

- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true

- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::rsconnect, local::.
needs: shiny

- name: Validate deployment settings
env:
APPNAME: __APPNAME__
ACCOUNT: your-account-name
SERVER: shinyapps.io
RSCONNECT_USER: ${{ secrets.RSCONNECT_USER }}
RSCONNECT_TOKEN: ${{ secrets.RSCONNECT_TOKEN }}
RSCONNECT_SECRET: ${{ secrets.RSCONNECT_SECRET }}
run: |
required <- c("RSCONNECT_USER", "RSCONNECT_TOKEN", "RSCONNECT_SECRET")
missing <- required[vapply(required, function(x) Sys.getenv(x) == "", logical(1))]
if (length(missing) > 0) {
stop(
sprintf(
"Missing GitHub Actions secrets: %s",
paste(missing, collapse = ", ")
),
call. = FALSE
)
}
if (Sys.getenv("ACCOUNT") == "your-account-name") {
stop(
"Set ACCOUNT in the workflow before deploying.",
call. = FALSE
)
}
if (Sys.getenv("SERVER") == "") {
stop(
"Set SERVER in the workflow before deploying.",
call. = FALSE
)
}
shell: Rscript {0}

- name: Authorize and deploy app
env:
APPNAME: __APPNAME__
ACCOUNT: your-account-name
SERVER: shinyapps.io
run: |
rsconnect::setAccountInfo("${{ secrets.RSCONNECT_USER }}", "${{ secrets.RSCONNECT_TOKEN }}", "${{ secrets.RSCONNECT_SECRET }}")
rsconnect::deployApp(appDir = ".", appPrimaryDoc = "app.R", appName = Sys.getenv("APPNAME"), account = Sys.getenv("ACCOUNT"), server = Sys.getenv("SERVER"))
shell: Rscript {0}
Loading
Loading