[mirror] Opinionated R package quickstart
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Changed AGENTS template, more tests

VisruthSK 2a8412aa 790c361e

+264 -40
+12 -7
AGENTS.md
··· 1 1 # General 2 - Read DESCRIPTION, README 3 - Red/green TDD via usethis::use_test() 2 + - Read DESCRIPTION, README 3 + - Red/green TDD 4 4 5 5 # Personality 6 6 Literal, direct, concise, high-signal, non-empathic. No hedging, both-sidesing, closing summaries, or offers. Only ask questions if functionally blocked. 7 7 8 8 # R Dev Rules 9 - No manual edits to .Rd or NAMESPACE. 10 - Use devtools::document(), test(), check(). 11 - Prefer Base R, existing dep closure. Request permission for new deps which make code better. 12 - Add deps via usethis::use_import_from(), use_package() 13 - air format ., jarl check . --fix --allow-dirty, all tests, and R CMD check pass before you claim to be done. 9 + - No manual edits: `.Rd`, `NAMESPACE` 10 + - Use `devtools::document()`, `test()`, `check()` 11 + - Deps: prefer Base R or current closure. Permission required for new deps to make code better 12 + - Add deps via `usethis::use_import_from()` or `use_package()` 13 + - Completion Pipeline (ordered, all must pass): 14 + 1. `air format .` 15 + 2. `jarl check . --fix --allow-dirty` 16 + 3. `devtools::document()` 17 + 4. `devtools::check()` 18 + 5. Report `covr::package_coverage()`
+2 -2
R/bootstrapper.R
··· 120 120 setup_dependabot() 121 121 } 122 122 if (setup_AGENTS) { 123 - setup_AGENTS() 123 + setup_agents() 124 124 } 125 125 126 126 try_air_jarl_format() ··· 249 249 } else { 250 250 FALSE 251 251 } 252 - if (selected_fn) { 252 + if (!isFALSE(selected_fn)) { 253 253 switch( 254 254 selected_fn, 255 255 use_mit_license = usethis::use_mit_license(),
+12 -7
inst/templates/AGENTS.md
··· 1 1 # General 2 - Read DESCRIPTION, README 3 - Red/green TDD via usethis::use_test() 2 + - Read DESCRIPTION, README 3 + - Red/green TDD 4 4 5 5 # Personality 6 6 Literal, direct, concise, high-signal, non-empathic. No hedging, both-sidesing, closing summaries, or offers. Only ask questions if functionally blocked. 7 7 8 8 # R Dev Rules 9 - No manual edits to .Rd or NAMESPACE. 10 - Use devtools::document(), test(), check(). 11 - Prefer Base R, existing dep closure. Request permission for new deps which make code better. 12 - Add deps via usethis::use_import_from(), use_package() 13 - air format ., jarl check . --fix --allow-dirty, all tests, and R CMD check pass before you claim to be done. 9 + - No manual edits: `.Rd`, `NAMESPACE` 10 + - Use `devtools::document()`, `test()`, `check()` 11 + - Deps: prefer Base R or current closure. Permission required for new deps to make code better 12 + - Add deps via `usethis::use_import_from()` or `use_package()` 13 + - Completion Pipeline (ordered, all must pass): 14 + 1. `air format .` 15 + 2. `jarl check . --fix --allow-dirty` 16 + 3. `devtools::document()` 17 + 4. `devtools::check()` 18 + 5. Report `covr::package_coverage()`
+238 -24
tests/testthat/test-bootstrapper.R
··· 111 111 ) 112 112 113 113 testthat::local_mocked_bindings( 114 - configure_gha = function() { 114 + setup_gha = function() { 115 115 calls$sections <<- c(calls$sections, "gha") 116 116 NULL 117 117 }, 118 - configure_dependabot = function() { 118 + setup_dependabot = function() { 119 119 calls$sections <<- c(calls$sections, "dependabot") 120 120 NULL 121 121 }, 122 - configure_agents = function() { 122 + setup_agents = function() { 123 123 calls$sections <<- c(calls$sections, "agents") 124 124 NULL 125 125 }, ··· 162 162 ) 163 163 164 164 testthat::local_mocked_bindings( 165 - configure_gha = function() { 165 + setup_gha = function() { 166 166 called <<- TRUE 167 167 NULL 168 168 }, 169 - configure_dependabot = function() { 169 + setup_dependabot = function() { 170 170 called <<- TRUE 171 171 NULL 172 172 }, 173 - configure_agents = function() { 173 + setup_agents = function() { 174 174 called <<- TRUE 175 175 NULL 176 176 }, ··· 205 205 ) 206 206 207 207 testthat::local_mocked_bindings( 208 - configure_gha = function() NULL, 209 - configure_dependabot = function() NULL, 210 - configure_agents = function() { 208 + setup_gha = function() NULL, 209 + setup_dependabot = function() NULL, 210 + setup_agents = function() { 211 211 called <<- TRUE 212 212 NULL 213 213 }, ··· 220 220 expect_true(called) 221 221 }) 222 222 223 - test_that("configure_gha runs expected usethis, replacement, and air/jarl calls", { 224 - configure_gha <- getFromNamespace("configure_gha", "bootstrapper") 223 + test_that("setup_gha runs expected usethis, replacement, and template calls", { 224 + setup_gha <- getFromNamespace("setup_gha", "bootstrapper") 225 225 actions <- list( 226 226 github_actions = list(), 227 227 replacements = character(), ··· 255 255 ) 256 256 NULL 257 257 }, 258 - write_to_path = function(text, filepath) { 259 - actions$writes <<- c(actions$writes, filepath) 258 + copy_template_file = function(template_file, destination) { 259 + actions$writes <<- c(actions$writes, destination) 260 + expect_true(template_file %in% c("extensions.json", "jarl.toml")) 260 261 NULL 261 262 }, 262 263 .package = "bootstrapper" 263 264 ) 264 265 265 - expect_null(configure_gha()) 266 + expect_null(setup_gha()) 266 267 expect_length(actions$github_actions, 3) 267 268 expect_identical(actions$github_actions[[1]][[1]], "check-standard") 268 269 expect_true(isTRUE(actions$github_actions[[1]]$badge)) ··· 286 287 ) 287 288 }) 288 289 289 - test_that("configure_dependabot writes dependabot config", { 290 - configure_dependabot <- getFromNamespace( 291 - "configure_dependabot", 290 + test_that("setup_dependabot copies dependabot template", { 291 + setup_dependabot <- getFromNamespace( 292 + "setup_dependabot", 292 293 "bootstrapper" 293 294 ) 294 - captured <- list(path = NULL, text = NULL) 295 + captured <- list(template_file = NULL, destination = NULL) 296 + 297 + testthat::local_mocked_bindings( 298 + copy_template_file = function(template_file, destination) { 299 + captured$template_file <<- template_file 300 + captured$destination <<- destination 301 + NULL 302 + }, 303 + .package = "bootstrapper" 304 + ) 305 + 306 + expect_null(setup_dependabot()) 307 + expect_identical(captured$template_file, "dependabot.yml") 308 + expect_identical(captured$destination, fs::path(".github", "dependabot.yml")) 309 + }) 310 + 311 + test_that("setup_agents copies AGENTS template", { 312 + setup_agents <- getFromNamespace("setup_agents", "bootstrapper") 313 + captured <- list(template_file = NULL, destination = NULL) 295 314 296 315 testthat::local_mocked_bindings( 297 - write_to_path = function(text, filepath) { 298 - captured$path <<- filepath 299 - captured$text <<- text 316 + copy_template_file = function(template_file, destination) { 317 + captured$template_file <<- template_file 318 + captured$destination <<- destination 300 319 NULL 301 320 }, 302 321 .package = "bootstrapper" 303 322 ) 304 323 305 - expect_null(configure_dependabot()) 306 - expect_identical(captured$path, fs::path(".github", "dependabot.yml")) 307 - expect_true(any(grepl("^version: 2$", captured$text))) 324 + expect_null(setup_agents()) 325 + expect_identical(captured$template_file, "AGENTS.md") 326 + expect_identical(captured$destination, "AGENTS.md") 308 327 }) 309 328 310 329 test_that("pkg_setup rethrows a generic message when test setup fails", { ··· 351 370 expect_true(any(grepl("No license selected", messages, fixed = TRUE))) 352 371 }) 353 372 373 + test_that("use_license applies selected license in interactive mode", { 374 + used_mit <- FALSE 375 + warned <- FALSE 376 + 377 + testthat::local_mocked_bindings( 378 + is_interactive = function() TRUE, 379 + .package = "bootstrapper" 380 + ) 381 + 382 + testthat::local_mocked_bindings( 383 + menu = function(choices, title = NULL, graphics = FALSE) { 384 + expect_identical(title, "License") 385 + expect_true("MIT" %in% choices) 386 + 1L 387 + }, 388 + .package = "utils" 389 + ) 390 + 391 + testthat::local_mocked_bindings( 392 + ui_info = function(message, ...) NULL, 393 + ui_warn = function(message, ...) { 394 + warned <<- TRUE 395 + NULL 396 + }, 397 + use_mit_license = function(...) { 398 + used_mit <<- TRUE 399 + NULL 400 + }, 401 + .package = "usethis" 402 + ) 403 + 404 + expect_null(bootstrapper::use_license()) 405 + expect_true(used_mit) 406 + expect_false(warned) 407 + }) 408 + 409 + test_that("use_license dispatches all remaining license helpers", { 410 + called <- character() 411 + current_label <- NULL 412 + label_to_fn <- c( 413 + "GPL" = "use_gpl_license", 414 + "GPL-3" = "use_gpl3_license", 415 + "LGPL" = "use_lgpl_license", 416 + "AGPL" = "use_agpl_license", 417 + "AGPL-3" = "use_agpl3_license", 418 + "Apache-2.0" = "use_apl2_license", 419 + "Apache" = "use_apache_license", 420 + "CC BY" = "use_ccby_license", 421 + "CC0" = "use_cc0_license", 422 + "Proprietary" = "use_proprietary_license" 423 + ) 424 + 425 + testthat::local_mocked_bindings( 426 + is_interactive = function() TRUE, 427 + .package = "bootstrapper" 428 + ) 429 + 430 + testthat::local_mocked_bindings( 431 + menu = function(choices, title = NULL, graphics = FALSE) { 432 + match(current_label, choices) 433 + }, 434 + .package = "utils" 435 + ) 436 + 437 + testthat::local_mocked_bindings( 438 + ui_info = function(message, ...) NULL, 439 + ui_warn = function(message, ...) NULL, 440 + use_mit_license = function(...) { 441 + called <<- c(called, "use_mit_license") 442 + NULL 443 + }, 444 + use_gpl_license = function(...) { 445 + called <<- c(called, "use_gpl_license") 446 + NULL 447 + }, 448 + use_gpl3_license = function(...) { 449 + called <<- c(called, "use_gpl3_license") 450 + NULL 451 + }, 452 + use_lgpl_license = function(...) { 453 + called <<- c(called, "use_lgpl_license") 454 + NULL 455 + }, 456 + use_agpl_license = function(...) { 457 + called <<- c(called, "use_agpl_license") 458 + NULL 459 + }, 460 + use_agpl3_license = function(...) { 461 + called <<- c(called, "use_agpl3_license") 462 + NULL 463 + }, 464 + use_apl2_license = function(...) { 465 + called <<- c(called, "use_apl2_license") 466 + NULL 467 + }, 468 + use_apache_license = function(...) { 469 + called <<- c(called, "use_apache_license") 470 + NULL 471 + }, 472 + use_ccby_license = function(...) { 473 + called <<- c(called, "use_ccby_license") 474 + NULL 475 + }, 476 + use_cc0_license = function(...) { 477 + called <<- c(called, "use_cc0_license") 478 + NULL 479 + }, 480 + use_proprietary_license = function(...) { 481 + called <<- c(called, "use_proprietary_license") 482 + NULL 483 + }, 484 + .package = "usethis" 485 + ) 486 + 487 + for (label in names(label_to_fn)) { 488 + current_label <- label 489 + called <- character() 490 + 491 + expect_null(bootstrapper::use_license()) 492 + expect_identical(called, unname(label_to_fn[[label]])) 493 + } 494 + }) 495 + 496 + test_that("is_interactive mirrors base interactive", { 497 + is_interactive <- getFromNamespace("is_interactive", "bootstrapper") 498 + expect_identical(is_interactive(), interactive()) 499 + }) 500 + 501 + test_that("copy_template_file creates parent directories and copies content", { 502 + tmp <- tempfile("bootstrapper-template-copy-") 503 + dir.create(tmp) 504 + old <- setwd(tmp) 505 + on.exit(setwd(old), add = TRUE) 506 + 507 + copy_template_file <- getFromNamespace("copy_template_file", "bootstrapper") 508 + destination <- fs::path("nested", ".github", "dependabot.yml") 509 + 510 + expect_false(file.exists(destination)) 511 + expect_null(copy_template_file("dependabot.yml", destination)) 512 + expect_true(file.exists(destination)) 513 + writeLines("stale", destination) 514 + expect_null(copy_template_file("dependabot.yml", destination)) 515 + expect_identical( 516 + readLines(destination, warn = FALSE), 517 + readLines( 518 + fs::path_package("bootstrapper", "templates", "dependabot.yml"), 519 + warn = FALSE 520 + ) 521 + ) 522 + }) 523 + 524 + test_that("try_air_jarl_format runs both commands and returns status", { 525 + try_air_jarl_format <- getFromNamespace("try_air_jarl_format", "bootstrapper") 526 + commands <- character() 527 + 528 + testthat::local_mocked_bindings( 529 + system2 = function(command, args, stdout = FALSE, stderr = FALSE, ...) { 530 + commands <<- c(commands, paste(command, paste(args, collapse = " "))) 531 + 0L 532 + }, 533 + .package = "base" 534 + ) 535 + 536 + status <- try_air_jarl_format() 537 + 538 + expect_identical( 539 + commands, 540 + c("air format .", "jarl check . --fix --allow-dirty") 541 + ) 542 + expect_identical(status, c(air = TRUE, jarl = TRUE)) 543 + }) 544 + 545 + test_that("try_air_jarl_format marks failed commands as FALSE", { 546 + try_air_jarl_format <- getFromNamespace("try_air_jarl_format", "bootstrapper") 547 + 548 + testthat::local_mocked_bindings( 549 + system2 = function(command, args, stdout = FALSE, stderr = FALSE, ...) { 550 + stop("tool missing") 551 + }, 552 + .package = "base" 553 + ) 554 + 555 + status <- try_air_jarl_format() 556 + expect_identical(status, c(air = FALSE, jarl = FALSE)) 557 + }) 558 + 354 559 test_that("file helpers create directories and replace text", { 355 560 tmp <- tempfile("bootstrapper-helpers-") 356 561 dir.create(tmp) ··· 375 580 expect_true(find_replace_in_file("alpha", "ALPHA", "one.txt")) 376 581 expect_identical(readLines("one.txt", warn = FALSE), c("ALPHA", "beta")) 377 582 expect_false(find_replace_in_file("gamma", "GAMMA", "one.txt")) 583 + 584 + writeLines(c("abc123", "abc999"), "regex.txt") 585 + expect_true(find_replace_in_file( 586 + "^abc[0-9]+$", 587 + "ID", 588 + "regex.txt", 589 + fixed = FALSE 590 + )) 591 + expect_identical(readLines("regex.txt", warn = FALSE), c("ID", "ID")) 378 592 379 593 dir.create("sub") 380 594 writeLines("token", fs::path("sub", "a.yaml"))