File: version.R

package info (click to toggle)
r-cran-usethis 3.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,228 kB
  • sloc: sh: 26; makefile: 17; cpp: 6; ansic: 3
file content (159 lines) | stat: -rw-r--r-- 4,222 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#' Increment package version
#'
#' @description
#'

#' usethis supports semantic versioning, which is described in more detail in
#' the [version
#' section](https://r-pkgs.org/lifecycle.html#sec-lifecycle-version-number) of [R
#' Packages](https://r-pkgs.org). A version number breaks down like so:
#'
#' ```
#' <major>.<minor>.<patch>       (released version)
#' <major>.<minor>.<patch>.<dev> (dev version)
#' ```

#' `use_version()` increments the "Version" field in `DESCRIPTION`, adds a new
#' heading to `NEWS.md` (if it exists), commits those changes (if package uses
#' Git), and optionally pushes (if safe to do so). It makes the same update to a
#' line like `PKG_version = "x.y.z";` in `src/version.c` (if it exists).
#'

#' `use_dev_version()` increments to a development version, e.g. from 1.0.0 to
#' 1.0.0.9000. If the existing version is already a development version with
#' four components, it does nothing. Thin wrapper around `use_version()`.
#'

#' @param which A string specifying which level to increment, one of: "major",
#'   "minor", "patch", "dev". If `NULL`, user can choose interactively.

#'
#' @seealso The [version
#'   section](https://r-pkgs.org/lifecycle.html#sec-lifecycle-version-number) of [R
#'   Packages](https://r-pkgs.org).
#'
#' @examples
#' \dontrun{
#' ## for interactive selection, do this:
#' use_version()
#'
#' ## request a specific type of increment
#' use_version("minor")
#' use_dev_version()
#' }
#'
#' @name use_version
NULL

#' @rdname use_version
#' @param push If `TRUE`, also attempts to push the commits to the remote
#'   branch.
#' @export
use_version <- function(which = NULL, push = FALSE) {
  if (is.null(which) && !is_interactive()) {
    return(invisible(FALSE))
  }

  check_is_package("use_version()")
  challenge_uncommitted_changes(
    msg = "There are uncommitted changes and you're about to bump version"
  )

  new_ver <- choose_version("What should the new version be?", which)
  if (is.null(new_ver)) {
    return(invisible(FALSE))
  }

  proj_desc_field_update("Version", new_ver, overwrite = TRUE)
  if (names(new_ver) == "dev") {
    use_news_heading("(development version)")
  } else {
    use_news_heading(new_ver)
  }

  use_c_version(new_ver)

  git_ask_commit(
    glue("Increment version number to {new_ver}"),
    untracked = TRUE,
    push = push,
    paths = c("DESCRIPTION", "NEWS.md", path("src", "version.c"))
  )

  invisible(TRUE)
}

#' @rdname use_version
#' @export
use_dev_version <- function(push = FALSE) {
  check_is_package("use_dev_version()")
  if (is_dev_version()) {
    return(invisible())
  }
  use_version(which = "dev", push = push)
}

choose_version <- function(message, which = NULL) {
  versions <- bump_version()
  rtypes <- names(versions)
  which <- which %||% rtypes
  which <- arg_match(which, values = rtypes, multiple = TRUE)
  versions <- versions[which]

  if (length(versions) == 1) {
    return(versions)
  }

  choice <- utils::menu(
    choices = glue(
      "{format(names(versions), justify = 'right')} --> {versions}"
    ),
    title = glue(
      "Current version is {proj_version()}.\n",
      "{message} (0 to exit)"
    )
  )

  if (choice == 0) {
    invisible()
  } else {
    # Not using `[[` even though there is only 1 `choice`,
    # because that removes the names from `versions`
    versions[choice]
  }
}

bump_version <- function(ver = proj_version()) {
  bumps <- c("major", "minor", "patch", "dev")
  vapply(bumps, bump_, character(1), ver = ver)
}

bump_ <- function(x, ver) {
  d <- desc::desc(text = paste0("Version: ", ver))
  suppressMessages(d$bump_version(x)$get("Version")[[1]])
}

use_c_version <- function(ver) {
  version_path <- proj_path("src", "version.c")

  if (!file_exists(version_path)) {
    return()
  }

  hint <- glue("{project_name()}_version")
  ui_bullets(c(
    "v" = "Setting {.field {hint}} to {.val {ver}} {.path {pth(version_path)}}."
  ))

  lines <- read_utf8(version_path)

  re <- glue("(^.*{project_name()}_version = \")([0-9.]+)(\";$)")
  lines <- gsub(re, glue("\\1{ver}\\3"), lines)

  write_utf8(version_path, lines)
}

is_dev_version <- function(version = proj_version()) {
  ver <- package_version(version)
  length(unlist(ver)) > 3
}