├── .Rbuildignore ├── .github ├── .gitignore ├── CODE_OF_CONDUCT.md └── workflows │ ├── R-CMD-check-dev.yaml │ ├── R-CMD-check-status.yaml │ ├── R-CMD-check.yaml │ ├── check │ └── action.yml │ ├── commit │ └── action.yml │ ├── covr │ └── action.yml │ ├── custom │ └── after-install │ │ └── action.yml │ ├── dep-matrix │ └── action.yml │ ├── dep-suggests-matrix │ ├── action.R │ └── action.yml │ ├── fledge.yaml │ ├── get-extra │ └── action.yml │ ├── git-identity │ └── action.yml │ ├── install │ └── action.yml │ ├── lock.yaml │ ├── matrix-check │ └── action.yml │ ├── pkgdown-build │ └── action.yml │ ├── pkgdown-deploy │ └── action.yml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ ├── rate-limit │ └── action.yml │ ├── revdep.yaml │ ├── roxygenize │ └── action.yml │ ├── style │ └── action.yml │ ├── update-snapshots │ └── action.yml │ └── versions-matrix │ ├── action.R │ └── action.yml ├── .gitignore ├── .lintr ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── create_indiedown_package.R ├── dr_down.R ├── import.R ├── use_indiedown_fonts.R └── utils.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── cran-comments.md ├── indiedown.Rproj ├── inst ├── WORDLIST ├── dr_down │ ├── test.Rmd │ └── test.pdf └── mypackage │ ├── .gitignore │ ├── DESCRIPTION │ ├── LICENSE │ ├── NAMESPACE │ ├── R │ ├── cd_format_date.R │ ├── cd_knit_chunk_opts.R │ ├── cd_page_title.R │ ├── indiedown.R │ └── indiedown_pdf_document.R │ ├── inst │ ├── indiedown │ │ ├── default.yaml │ │ ├── fonts │ │ │ ├── bold.ttf │ │ │ ├── bolditalic.ttf │ │ │ ├── italic.ttf │ │ │ └── regular.ttf │ │ ├── pre_processor.R │ │ ├── preamble.tex │ │ └── res │ │ │ └── logo.png │ └── rmarkdown │ │ └── templates │ │ └── report │ │ ├── skeleton │ │ ├── .gitignore │ │ └── skeleton.Rmd │ │ └── template.yaml │ └── man │ ├── cd_format_date.Rd │ ├── cd_knit_chunk_opts.Rd │ ├── cd_page_title.Rd │ ├── default.Rd │ ├── indiedown_glue.Rd │ ├── indiedown_path.Rd │ └── mypackage.Rd ├── man ├── create_indiedown_package.Rd ├── dr_down.Rd └── use_indiedown_gfonts.Rd ├── renovate.json ├── tests ├── spelling.R ├── testthat.R └── testthat │ ├── _snaps │ └── create_indiedown_package.md │ ├── test-create_indiedown_package.R │ ├── test-dr_down.R │ └── test-install.R └── vignettes ├── .gitignore ├── customize.Rmd ├── fig ├── rstudio.png ├── status-1.png └── status-2.png ├── indiedown.Rmd └── walkthrough.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^indiedown\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^_pkgdown\.yml$ 5 | ^docs$ 6 | ^pkgdown$ 7 | ^\.github$ 8 | ^README\.html$ 9 | ^\.lintr$ 10 | ^README\.Rmd$ 11 | ^CODE_OF_CONDUCT\.md$ 12 | ^cran-comments\.md$ 13 | ^renovate\.json$ 14 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | /pkg.lock 3 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who 4 | contribute through reporting issues, posting feature requests, updating documentation, 5 | submitting pull requests or patches, and other activities. 6 | 7 | We are committed to making participation in this project a harassment-free experience for 8 | everyone, regardless of level of experience, gender, gender identity and expression, 9 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. 10 | 11 | Examples of unacceptable behavior by participants include the use of sexual language or 12 | imagery, derogatory comments or personal attacks, trolling, public or private harassment, 13 | insults, or other unprofessional conduct. 14 | 15 | Project maintainers have the right and responsibility to remove, edit, or reject comments, 16 | commits, code, wiki edits, issues, and other contributions that are not aligned to this 17 | Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed 18 | from the project team. 19 | 20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by 21 | opening an issue or contacting one or more of the project maintainers. 22 | 23 | This Code of Conduct is adapted from the Contributor Covenant 24 | (http://contributor-covenant.org), version 1.0.0, available at 25 | http://contributor-covenant.org/version/1/0/0/ 26 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check-dev.yaml: -------------------------------------------------------------------------------- 1 | # This workflow calls the GitHub API very frequently. 2 | # Can't be run as part of commits 3 | on: 4 | schedule: 5 | - cron: "0 5 * * *" # 05:00 UTC every day only run on main branch 6 | push: 7 | branches: 8 | - "cran-*" 9 | tags: 10 | - "v*" 11 | 12 | name: rcc dev 13 | 14 | jobs: 15 | matrix: 16 | runs-on: ubuntu-22.04 17 | outputs: 18 | matrix: ${{ steps.set-matrix.outputs.matrix }} 19 | 20 | name: Collect deps 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | 25 | - uses: ./.github/workflows/rate-limit 26 | with: 27 | token: ${{ secrets.GITHUB_TOKEN }} 28 | 29 | - uses: r-lib/actions/setup-r@v2 30 | 31 | - id: set-matrix 32 | uses: ./.github/workflows/dep-matrix 33 | 34 | check-matrix: 35 | runs-on: ubuntu-22.04 36 | needs: matrix 37 | 38 | name: Check deps 39 | 40 | steps: 41 | - name: Install json2yaml 42 | run: | 43 | sudo npm install -g json2yaml 44 | 45 | - name: Check matrix definition 46 | run: | 47 | matrix='${{ needs.matrix.outputs.matrix }}' 48 | echo $matrix 49 | echo $matrix | jq . 50 | echo $matrix | json2yaml 51 | 52 | R-CMD-check-base: 53 | runs-on: ubuntu-22.04 54 | 55 | name: base 56 | 57 | # Begin custom: services 58 | # End custom: services 59 | 60 | strategy: 61 | fail-fast: false 62 | 63 | steps: 64 | - uses: actions/checkout@v4 65 | 66 | - uses: ./.github/workflows/custom/before-install 67 | if: hashFiles('.github/workflows/custom/before-install/action.yml') != '' 68 | 69 | - uses: ./.github/workflows/install 70 | with: 71 | cache-version: rcc-dev-base-1 72 | needs: build, check 73 | extra-packages: "any::rcmdcheck any::remotes ." 74 | token: ${{ secrets.GITHUB_TOKEN }} 75 | 76 | - name: Session info 77 | run: | 78 | options(width = 100) 79 | if (!requireNamespace("sessioninfo", quietly = TRUE)) install.packages("sessioninfo") 80 | pkgs <- installed.packages()[, "Package"] 81 | sessioninfo::session_info(pkgs, include_base = TRUE) 82 | shell: Rscript {0} 83 | 84 | - uses: ./.github/workflows/custom/after-install 85 | if: hashFiles('.github/workflows/custom/after-install/action.yml') != '' 86 | 87 | - uses: ./.github/workflows/update-snapshots 88 | if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository 89 | 90 | - uses: ./.github/workflows/check 91 | with: 92 | results: ${{ matrix.package }} 93 | 94 | R-CMD-check-dev: 95 | needs: 96 | - matrix 97 | - R-CMD-check-base 98 | 99 | runs-on: ubuntu-22.04 100 | 101 | name: 'rcc-dev: ${{ matrix.package }}' 102 | 103 | # Begin custom: services 104 | # End custom: services 105 | 106 | strategy: 107 | fail-fast: false 108 | matrix: ${{fromJson(needs.matrix.outputs.matrix)}} 109 | 110 | steps: 111 | - uses: actions/checkout@v4 112 | 113 | - uses: ./.github/workflows/custom/before-install 114 | if: hashFiles('.github/workflows/custom/before-install/action.yml') != '' 115 | 116 | - uses: ./.github/workflows/install 117 | with: 118 | cache-version: rcc-dev-${{ matrix.package }}-1 119 | needs: build, check 120 | extra-packages: "any::rcmdcheck any::remotes ." 121 | token: ${{ secrets.GITHUB_TOKEN }} 122 | 123 | - name: Install dev version of ${{ matrix.package }} 124 | env: 125 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 126 | run: | 127 | remotes::install_dev("${{ matrix.package }}", "https://cloud.r-project.org", upgrade = "always") 128 | shell: Rscript {0} 129 | 130 | - name: Session info 131 | run: | 132 | options(width = 100) 133 | if (!requireNamespace("sessioninfo", quietly = TRUE)) install.packages("sessioninfo") 134 | pkgs <- installed.packages()[, "Package"] 135 | sessioninfo::session_info(pkgs, include_base = TRUE) 136 | shell: Rscript {0} 137 | 138 | - uses: ./.github/workflows/custom/after-install 139 | if: hashFiles('.github/workflows/custom/after-install/action.yml') != '' 140 | 141 | - uses: ./.github/workflows/update-snapshots 142 | if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository 143 | 144 | - uses: ./.github/workflows/check 145 | with: 146 | results: ${{ matrix.package }} 147 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check-status.yaml: -------------------------------------------------------------------------------- 1 | # Workflow to update the status of a commit for the R-CMD-check workflow 2 | # Necessary because remote PRs cannot update the status of the commit 3 | on: 4 | workflow_run: 5 | workflows: 6 | - rcc 7 | types: 8 | - requested 9 | - completed 10 | 11 | name: rcc-status 12 | 13 | jobs: 14 | rcc-status: 15 | runs-on: ubuntu-24.04 16 | 17 | name: "Update commit status" 18 | 19 | permissions: 20 | contents: read 21 | statuses: write 22 | 23 | steps: 24 | - name: "Update commit status" 25 | # Only run if triggered by rcc workflow 26 | if: github.event.workflow_run.name == 'rcc' 27 | env: 28 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | run: | 30 | set -x 31 | 32 | if [ "${{ github.event.workflow_run.status }}" == "completed" ]; then 33 | if [ "${{ github.event.workflow_run.conclusion }}" == "success" ]; then 34 | state="success" 35 | else 36 | state="failure" 37 | fi 38 | 39 | # Read artifact ID 40 | artifact_id=$(gh api \ 41 | -H "Accept: application/vnd.github+json" \ 42 | -H "X-GitHub-Api-Version: 2022-11-28" \ 43 | repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}/artifacts | jq -r '.artifacts[] | select(.name == "rcc-smoke-sha") | .id') 44 | 45 | if [ -n "${artifact_id}" ]; then 46 | # Download artifact 47 | curl -L -o rcc-smoke-sha.zip \ 48 | -H "Accept: application/vnd.github+json" \ 49 | -H "Authorization: Bearer ${GH_TOKEN}" \ 50 | -H "X-GitHub-Api-Version: 2022-11-28" \ 51 | https://api.github.com/repos/${{ github.repository }}/actions/artifacts/${artifact_id}/zip 52 | 53 | # Unzip artifact 54 | unzip rcc-smoke-sha.zip 55 | 56 | # Read artifact 57 | sha=$(cat rcc-smoke-sha.txt) 58 | 59 | # Clean up 60 | rm rcc-smoke-sha.zip rcc-smoke-sha.txt 61 | fi 62 | else 63 | state="pending" 64 | fi 65 | 66 | if [ -z "${sha}" ]; then 67 | sha=${{ github.event.workflow_run.head_sha }} 68 | fi 69 | 70 | html_url=${{ github.event.workflow_run.html_url }} 71 | description=${{ github.event.workflow_run.name }} 72 | 73 | gh api \ 74 | --method POST \ 75 | -H "Accept: application/vnd.github+json" \ 76 | -H "X-GitHub-Api-Version: 2022-11-28" \ 77 | repos/${{ github.repository }}/statuses/${sha} \ 78 | -f "state=${state}" -f "target_url=${html_url}" -f "description=${description}" -f "context=rcc" 79 | shell: bash 80 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | push: 9 | branches: 10 | - main 11 | - master 12 | - release 13 | - cran-* 14 | pull_request: 15 | branches: 16 | - main 17 | - master 18 | workflow_dispatch: 19 | inputs: 20 | ref: 21 | description: "Branch, tag, or commit to check out" 22 | required: false 23 | default: "main" 24 | versions-matrix: 25 | description: "Create a matrix of R versions" 26 | type: boolean 27 | default: false 28 | dep-suggests-matrix: 29 | description: "Create a matrix of suggested dependencies" 30 | type: boolean 31 | default: false 32 | merge_group: 33 | types: 34 | - checks_requested 35 | schedule: 36 | - cron: "10 1 * * *" 37 | 38 | concurrency: 39 | group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.ref || github.head_ref || github.sha }}-${{ github.base_ref || '' }} 40 | cancel-in-progress: true 41 | 42 | name: rcc 43 | 44 | jobs: 45 | rcc-smoke: 46 | runs-on: ubuntu-24.04 47 | 48 | outputs: 49 | sha: ${{ steps.commit.outputs.sha }} 50 | versions-matrix: ${{ steps.versions-matrix.outputs.matrix }} 51 | dep-suggests-matrix: ${{ steps.dep-suggests-matrix.outputs.matrix }} 52 | 53 | name: "Smoke test: stock R" 54 | 55 | permissions: 56 | contents: write 57 | statuses: write 58 | pull-requests: write 59 | actions: write 60 | 61 | # Begin custom: services 62 | # End custom: services 63 | 64 | steps: 65 | - uses: actions/checkout@v4 66 | with: 67 | ref: ${{ inputs.ref }} 68 | 69 | - name: Update status for rcc 70 | # FIXME: Wrap into action 71 | if: github.event_name == 'workflow_dispatch' 72 | env: 73 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 74 | run: | 75 | # Check status of this workflow 76 | state="pending" 77 | sha=${{ inputs.ref }} 78 | if [ -z "${sha}" ]; then 79 | sha=${{ github.head_ref }} 80 | fi 81 | if [ -z "${sha}" ]; then 82 | sha=${{ github.sha }} 83 | fi 84 | sha=$(git rev-parse ${sha}) 85 | 86 | html_url=$(gh api \ 87 | -H "Accept: application/vnd.github+json" \ 88 | -H "X-GitHub-Api-Version: 2022-11-28" \ 89 | repos/${{ github.repository }}/actions/runs/${{ github.run_id }} | jq -r .html_url) 90 | 91 | description="${{ github.workflow }} / ${{ github.job }}" 92 | 93 | gh api \ 94 | --method POST \ 95 | -H "Accept: application/vnd.github+json" \ 96 | -H "X-GitHub-Api-Version: 2022-11-28" \ 97 | repos/${{ github.repository }}/statuses/${sha} \ 98 | -f "state=${state}" -f "target_url=${html_url}" -f "description=${description}" -f "context=rcc" 99 | shell: bash 100 | 101 | - uses: ./.github/workflows/rate-limit 102 | with: 103 | token: ${{ secrets.GITHUB_TOKEN }} 104 | 105 | - uses: ./.github/workflows/git-identity 106 | 107 | - uses: ./.github/workflows/custom/before-install 108 | if: hashFiles('.github/workflows/custom/before-install/action.yml') != '' 109 | 110 | - uses: ./.github/workflows/install 111 | with: 112 | token: ${{ secrets.GITHUB_TOKEN }} 113 | cache-version: rcc-smoke-2 114 | needs: build, check, website 115 | # Beware of using dev pkgdown here, has brought in dev dependencies in the past 116 | extra-packages: any::rcmdcheck r-lib/roxygen2 any::decor r-lib/styler r-lib/pkgdown deps::. 117 | 118 | - uses: ./.github/workflows/custom/after-install 119 | if: hashFiles('.github/workflows/custom/after-install/action.yml') != '' 120 | 121 | # Must come after the custom after-install workflow 122 | - name: Install package 123 | run: | 124 | _R_SHLIB_STRIP_=true R CMD INSTALL . 125 | shell: bash 126 | 127 | - id: versions-matrix 128 | # Only run for pull requests if the base repo is different from the head repo, not for workflow_dispatch if not requested, always run for other events 129 | if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository) && (github.event_name != 'workflow_dispatch' || inputs.versions-matrix) 130 | uses: ./.github/workflows/versions-matrix 131 | 132 | - id: dep-suggests-matrix 133 | # Not for workflow_dispatch if not requested, always run for other events 134 | if: github.event_name != 'workflow_dispatch' || inputs.dep-suggests-matrix 135 | uses: ./.github/workflows/dep-suggests-matrix 136 | 137 | - uses: ./.github/workflows/update-snapshots 138 | with: 139 | base: ${{ inputs.ref || github.head_ref }} 140 | 141 | - uses: ./.github/workflows/style 142 | 143 | - uses: ./.github/workflows/roxygenize 144 | 145 | - name: Remove config files from previous iteration 146 | run: | 147 | rm -f .github/dep-suggests-matrix.json .github/versions-matrix.json 148 | shell: bash 149 | 150 | - id: commit 151 | uses: ./.github/workflows/commit 152 | with: 153 | token: ${{ secrets.GITHUB_TOKEN }} 154 | 155 | - uses: ./.github/workflows/check 156 | with: 157 | results: ${{ runner.os }}-smoke-test 158 | 159 | - uses: ./.github/workflows/pkgdown-build 160 | if: github.event_name != 'push' 161 | 162 | - uses: ./.github/workflows/pkgdown-deploy 163 | if: github.event_name == 'push' 164 | 165 | # Upload sha as artifact 166 | - run: | 167 | echo -n "${{ steps.commit.outputs.sha }}" > rcc-smoke-sha.txt 168 | shell: bash 169 | 170 | - uses: actions/upload-artifact@v4 171 | with: 172 | name: rcc-smoke-sha 173 | path: rcc-smoke-sha.txt 174 | 175 | - name: Update status for rcc 176 | # FIXME: Wrap into action 177 | if: always() && github.event_name == 'workflow_dispatch' 178 | env: 179 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 180 | run: | 181 | # Check status of this workflow 182 | if [ "${{ job.status }}" == "success" ]; then 183 | state="success" 184 | else 185 | state="failure" 186 | fi 187 | 188 | sha=${{ steps.commit.outputs.sha }} 189 | if [ -z "${sha}" ]; then 190 | sha=${{ inputs.ref }} 191 | fi 192 | if [ -z "${sha}" ]; then 193 | sha=${{ github.head_ref }} 194 | fi 195 | if [ -z "${sha}" ]; then 196 | sha=${{ github.sha }} 197 | fi 198 | sha=$(git rev-parse ${sha}) 199 | 200 | html_url=$(gh api \ 201 | -H "Accept: application/vnd.github+json" \ 202 | -H "X-GitHub-Api-Version: 2022-11-28" \ 203 | repos/${{ github.repository }}/actions/runs/${{ github.run_id }} | jq -r .html_url) 204 | 205 | description="${{ github.workflow }} / ${{ github.job }}" 206 | 207 | gh api \ 208 | --method POST \ 209 | -H "Accept: application/vnd.github+json" \ 210 | -H "X-GitHub-Api-Version: 2022-11-28" \ 211 | repos/${{ github.repository }}/statuses/${sha} \ 212 | -f "state=${state}" -f "target_url=${html_url}" -f "description=${description}" -f "context=rcc" 213 | shell: bash 214 | 215 | rcc-smoke-check-matrix: 216 | runs-on: ubuntu-24.04 217 | 218 | name: "Check matrix" 219 | 220 | needs: 221 | - rcc-smoke 222 | 223 | steps: 224 | - uses: actions/checkout@v4 225 | with: 226 | ref: ${{ needs.rcc-smoke.outputs.sha }} 227 | 228 | - uses: ./.github/workflows/matrix-check 229 | with: 230 | matrix: ${{ needs.rcc-smoke.outputs.versions-matrix }} 231 | 232 | - uses: ./.github/workflows/matrix-check 233 | with: 234 | matrix: ${{ needs.rcc-smoke.outputs.dep-suggests-matrix }} 235 | 236 | rcc-full: 237 | needs: 238 | - rcc-smoke 239 | 240 | runs-on: ${{ matrix.os }} 241 | 242 | if: ${{ needs.rcc-smoke.outputs.versions-matrix != '' }} 243 | 244 | name: 'rcc: ${{ matrix.os }} (${{ matrix.r }}) ${{ matrix.desc }}' 245 | 246 | # Begin custom: services 247 | # End custom: services 248 | 249 | strategy: 250 | fail-fast: false 251 | matrix: ${{fromJson(needs.rcc-smoke.outputs.versions-matrix)}} 252 | 253 | steps: 254 | - uses: actions/checkout@v4 255 | with: 256 | ref: ${{ needs.rcc-smoke.outputs.sha }} 257 | 258 | - uses: ./.github/workflows/custom/before-install 259 | if: hashFiles('.github/workflows/custom/before-install/action.yml') != '' 260 | 261 | - uses: ./.github/workflows/install 262 | with: 263 | r-version: ${{ matrix.r }} 264 | cache-version: rcc-full-1 265 | token: ${{ secrets.GITHUB_TOKEN }} 266 | needs: build, check 267 | 268 | - uses: ./.github/workflows/custom/after-install 269 | if: hashFiles('.github/workflows/custom/after-install/action.yml') != '' 270 | 271 | - name: Must allow NOTEs if packages are missing, even with _R_CHECK_FORCE_SUGGESTS_ 272 | run: | 273 | if (Sys.getenv("RCMDCHECK_ERROR_ON") %in% c("", "note")) { 274 | pkgs <- setdiff(desc::desc_get_deps()$package, "R") 275 | installable <- vapply(pkgs, FUN.VALUE = logical(1), requireNamespace, quietly = TRUE) 276 | if (any(!installable)) { 277 | message("Missing packages: ", paste(pkgs[!installable], collapse = ", ")) 278 | cat('RCMDCHECK_ERROR_ON="warning"\n', file = Sys.getenv("GITHUB_ENV"), append = TRUE) 279 | } 280 | } 281 | shell: Rscript {0} 282 | 283 | - uses: ./.github/workflows/update-snapshots 284 | if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository 285 | 286 | - uses: ./.github/workflows/check 287 | if: ${{ ! matrix.covr }} 288 | with: 289 | results: ${{ runner.os }}-r${{ matrix.r }} 290 | 291 | - uses: ./.github/workflows/covr 292 | if: ${{ matrix.covr }} 293 | with: 294 | token: ${{ secrets.CODECOV_TOKEN }} 295 | 296 | # The status update is taken care of by R-CMD-check-status.yaml 297 | 298 | rcc-suggests: 299 | needs: 300 | - rcc-smoke 301 | 302 | runs-on: ubuntu-22.04 303 | 304 | if: ${{ needs.rcc-smoke.outputs.dep-suggests-matrix != '' }} 305 | 306 | name: Without ${{ matrix.package }} 307 | 308 | # Begin custom: services 309 | # End custom: services 310 | 311 | strategy: 312 | fail-fast: false 313 | matrix: ${{fromJson(needs.rcc-smoke.outputs.dep-suggests-matrix)}} 314 | 315 | steps: 316 | - uses: actions/checkout@v4 317 | 318 | - uses: ./.github/workflows/custom/before-install 319 | if: hashFiles('.github/workflows/custom/before-install/action.yml') != '' 320 | 321 | - uses: ./.github/workflows/install 322 | with: 323 | cache-version: rcc-dev-${{ matrix.package }}-1 324 | needs: build, check 325 | extra-packages: "any::rcmdcheck any::remotes ." 326 | token: ${{ secrets.GITHUB_TOKEN }} 327 | 328 | - name: Remove ${{ matrix.package }} and all strong dependencies 329 | run: | 330 | pkg <- "${{ matrix.package }}" 331 | pkgs <- tools::package_dependencies(pkg, reverse = TRUE)[[1]] 332 | installed <- rownames(utils::installed.packages()) 333 | to_remove <- c(pkg, intersect(pkgs, installed)) 334 | print(to_remove) 335 | remove.packages(to_remove) 336 | shell: Rscript {0} 337 | 338 | - name: Session info 339 | run: | 340 | options(width = 100) 341 | if (!requireNamespace("sessioninfo", quietly = TRUE)) install.packages("sessioninfo") 342 | pkgs <- installed.packages()[, "Package"] 343 | sessioninfo::session_info(pkgs, include_base = TRUE) 344 | shell: Rscript {0} 345 | 346 | - uses: ./.github/workflows/custom/after-install 347 | if: hashFiles('.github/workflows/custom/after-install/action.yml') != '' 348 | 349 | - name: Must allow NOTEs, even with _R_CHECK_FORCE_SUGGESTS_ 350 | run: | 351 | if (Sys.getenv("RCMDCHECK_ERROR_ON") %in% c("", "note")) { 352 | cat('RCMDCHECK_ERROR_ON="warning"\n', file = Sys.getenv("GITHUB_ENV"), append = TRUE) 353 | } 354 | shell: Rscript {0} 355 | 356 | - name: Check env vars 357 | run: | 358 | print(Sys.getenv('_R_CHECK_FORCE_SUGGESTS_')) 359 | print(Sys.getenv('RCMDCHECK_ERROR_ON')) 360 | shell: Rscript {0} 361 | 362 | - uses: ./.github/workflows/check 363 | with: 364 | results: ${{ matrix.package }} 365 | 366 | # The status update is taken care of by R-CMD-check-status.yaml 367 | -------------------------------------------------------------------------------- /.github/workflows/check/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to check an R package" 2 | inputs: 3 | results: 4 | description: Slug for check results 5 | required: true 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - uses: r-lib/actions/check-r-package@v2 11 | with: 12 | # Fails on R 3.6 on Windows, remove when this job is removed? 13 | args: 'c("--no-manual", "--as-cran", "--no-multiarch")' 14 | error-on: ${{ env.RCMDCHECK_ERROR_ON || '"note"' }} 15 | 16 | - name: Show test output 17 | if: always() 18 | run: | 19 | ## -- Show test output -- 20 | echo "::group::Test output" 21 | find check -name '*.Rout*' -exec head -n 1000000 '{}' \; || true 22 | echo "::endgroup::" 23 | shell: bash 24 | 25 | - name: Upload check results 26 | if: failure() 27 | uses: actions/upload-artifact@main 28 | with: 29 | name: ${{ inputs.results }}-results 30 | path: check 31 | -------------------------------------------------------------------------------- /.github/workflows/commit/action.yml: -------------------------------------------------------------------------------- 1 | name: "Action to commit changes to the repository" 2 | inputs: 3 | token: 4 | description: "GitHub token" 5 | required: true 6 | outputs: 7 | sha: 8 | description: "SHA of generated commit" 9 | value: ${{ steps.commit.outputs.sha }} 10 | 11 | runs: 12 | using: "composite" 13 | steps: 14 | - name: Commit if changed, create a PR if protected 15 | id: commit 16 | env: 17 | GITHUB_TOKEN: ${{ inputs.token }} 18 | run: | 19 | set -x 20 | if [ -n "$(git status --porcelain)" ]; then 21 | echo "Changed" 22 | protected=${{ github.ref_protected }} 23 | foreign=${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }} 24 | if [ "${foreign}" = "true" ]; then 25 | # https://github.com/krlmlr/actions-sync/issues/44 26 | echo "Can't push to foreign branch" 27 | elif [ "${protected}" = "true" ]; then 28 | current_branch=$(git branch --show-current) 29 | new_branch=gha-commit-$(git rev-parse --short HEAD) 30 | git checkout -b ${new_branch} 31 | git add . 32 | git commit -m "chore: Auto-update from GitHub Actions"$'\n'$'\n'"Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" 33 | # Force-push, used in only one place 34 | # Alternative: separate branch names for each usage 35 | git push -u origin HEAD -f 36 | 37 | existing_pr=$(gh pr list --state open --base main --head ${new_branch} --json number --jq '.[] | .number') 38 | if [ -n "${existing_pr}" ]; then 39 | echo "Existing PR: ${existing_pr}" 40 | else 41 | gh pr create --base main --head ${new_branch} --title "chore: Auto-update from GitHub Actions" --body "Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" 42 | fi 43 | 44 | gh workflow run rcc -f ref=$(git rev-parse HEAD) 45 | gh pr merge --merge --auto 46 | else 47 | git fetch 48 | if [ -n "${GITHUB_HEAD_REF}" ]; then 49 | git add . 50 | git stash save 51 | git switch ${GITHUB_HEAD_REF} 52 | git merge origin/${GITHUB_BASE_REF} --no-edit 53 | git stash pop 54 | fi 55 | git add . 56 | git commit -m "chore: Auto-update from GitHub Actions"$'\n'$'\n'"Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" 57 | git push -u origin HEAD 58 | 59 | # Only set output if changed 60 | echo sha=$(git rev-parse HEAD) >> $GITHUB_OUTPUT 61 | fi 62 | fi 63 | shell: bash 64 | -------------------------------------------------------------------------------- /.github/workflows/covr/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to run covr for an R package" 2 | inputs: 3 | token: 4 | description: codecov token 5 | required: false 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - name: Run coverage check 11 | run: | 12 | if (dir.exists("tests/testthat")) { 13 | cov <- covr::package_coverage( 14 | quiet = FALSE, 15 | clean = FALSE, 16 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 17 | ) 18 | covr::to_cobertura(cov) 19 | } else { 20 | message("No tests found, coverage not tested.") 21 | } 22 | shell: Rscript {0} 23 | 24 | - uses: codecov/codecov-action@v5 25 | with: 26 | # Fail if token is given 27 | fail_ci_if_error: ${{ inputs.token != '' }} 28 | files: ./cobertura.xml 29 | plugins: noop 30 | disable_search: true 31 | token: ${{ inputs.token }} 32 | 33 | - name: Show testthat output 34 | if: always() 35 | run: | 36 | ## -------------------------------------------------------------------- 37 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 38 | shell: bash 39 | 40 | - name: Upload test results 41 | if: failure() 42 | uses: actions/upload-artifact@v4 43 | with: 44 | name: coverage-test-failures 45 | path: ${{ runner.temp }}/package 46 | -------------------------------------------------------------------------------- /.github/workflows/custom/after-install/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Custom steps to run after R packages are installed' 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Run R CMD check on example package 7 | run: | 8 | options(crayon.enabled = TRUE) 9 | error_on <- "note" 10 | rcmdcheck::rcmdcheck("inst/mypackage", args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "inst/mypackage/check") 11 | shell: Rscript {0} 12 | 13 | - name: Remove check output 14 | run: | 15 | rm -r inst/mypackage/check 16 | shell: bash 17 | -------------------------------------------------------------------------------- /.github/workflows/dep-matrix/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to compute a matrix with all dependent packages" 2 | outputs: 3 | matrix: 4 | description: "Generated matrix" 5 | value: ${{ steps.set-matrix.outputs.matrix }} 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - id: set-matrix 11 | run: | 12 | # Determine package dependencies 13 | # From remotes 14 | read_dcf <- function(path) { 15 | fields <- colnames(read.dcf(path)) 16 | as.list(read.dcf(path, keep.white = fields)[1, ]) 17 | } 18 | 19 | re_match <- function(text, pattern, perl = TRUE, ...) { 20 | 21 | stopifnot(is.character(pattern), length(pattern) == 1, !is.na(pattern)) 22 | text <- as.character(text) 23 | 24 | match <- regexpr(pattern, text, perl = perl, ...) 25 | 26 | start <- as.vector(match) 27 | length <- attr(match, "match.length") 28 | end <- start + length - 1L 29 | 30 | matchstr <- substring(text, start, end) 31 | matchstr[ start == -1 ] <- NA_character_ 32 | 33 | res <- data.frame( 34 | stringsAsFactors = FALSE, 35 | .text = text, 36 | .match = matchstr 37 | ) 38 | 39 | if (!is.null(attr(match, "capture.start"))) { 40 | 41 | gstart <- attr(match, "capture.start") 42 | glength <- attr(match, "capture.length") 43 | gend <- gstart + glength - 1L 44 | 45 | groupstr <- substring(text, gstart, gend) 46 | groupstr[ gstart == -1 ] <- NA_character_ 47 | dim(groupstr) <- dim(gstart) 48 | 49 | res <- cbind(groupstr, res, stringsAsFactors = FALSE) 50 | } 51 | 52 | names(res) <- c(attr(match, "capture.names"), ".text", ".match") 53 | class(res) <- c("tbl_df", "tbl", class(res)) 54 | res 55 | } 56 | 57 | dev_split_ref <- function(x) { 58 | re_match(x, "^(?[^@#]+)(?[@#].*)?$") 59 | } 60 | 61 | has_dev_dep <- function(package) { 62 | cran_url <- "https://cloud.r-project.org" 63 | 64 | refs <- dev_split_ref(package) 65 | url <- file.path(cran_url, "web", "packages", refs[["pkg"]], "DESCRIPTION") 66 | 67 | f <- tempfile() 68 | on.exit(unlink(f)) 69 | 70 | utils::download.file(url, f) 71 | desc <- read_dcf(f) 72 | 73 | url_fields <- c(desc$URL, desc$BugReports) 74 | 75 | if (length(url_fields) == 0) { 76 | return(FALSE) 77 | } 78 | 79 | pkg_urls <- unlist(strsplit(url_fields, "[[:space:]]*,[[:space:]]*")) 80 | 81 | # Remove trailing "/issues" from the BugReports URL 82 | pkg_urls <- sub("/issues$", "", pkg_urls) 83 | 84 | valid_domains <- c("github[.]com", "gitlab[.]com", "bitbucket[.]org") 85 | 86 | parts <- 87 | re_match(pkg_urls, 88 | sprintf("^https?://(?%s)/(?%s)/(?%s)(?:/(?%s))?", 89 | domain = paste0(valid_domains, collapse = "|"), 90 | username = "[^/]+", 91 | repo = "[^/@#]+", 92 | subdir = "[^/@$ ]+" 93 | ) 94 | )[c("domain", "username", "repo", "subdir")] 95 | 96 | # Remove cases which don't match and duplicates 97 | 98 | parts <- unique(stats::na.omit(parts)) 99 | 100 | nrow(parts) == 1 101 | } 102 | 103 | if (!requireNamespace("desc", quietly = TRUE)) { 104 | install.packages("desc") 105 | } 106 | 107 | deps_df <- desc::desc_get_deps() 108 | deps_df <- deps_df[deps_df$type %in% c("Depends", "Imports", "LinkingTo", "Suggests"), ] 109 | 110 | packages <- sort(deps_df$package) 111 | packages <- intersect(packages, rownames(available.packages())) 112 | 113 | valid_dev_dep <- vapply(packages, has_dev_dep, logical(1)) 114 | 115 | # https://github.com/r-lib/remotes/issues/576 116 | valid_dev_dep[packages %in% c("igraph", "duckdb", "logging")] <- FALSE 117 | 118 | deps <- packages[valid_dev_dep] 119 | if (any(!valid_dev_dep)) { 120 | msg <- paste0( 121 | "Could not determine development repository for packages: ", 122 | paste(packages[!valid_dev_dep], collapse = ", ") 123 | ) 124 | writeLines(paste0("::warning::", msg)) 125 | } 126 | 127 | json <- paste0( 128 | '{"package":[', 129 | paste0('"', deps, '"', collapse = ","), 130 | ']}' 131 | ) 132 | writeLines(json) 133 | writeLines(paste0("matrix=", json), Sys.getenv("GITHUB_OUTPUT")) 134 | shell: Rscript {0} 135 | -------------------------------------------------------------------------------- /.github/workflows/dep-suggests-matrix/action.R: -------------------------------------------------------------------------------- 1 | # FIXME: Dynamic lookup by parsing https://svn.r-project.org/R/tags/ 2 | get_deps <- function() { 3 | # Determine package dependencies 4 | if (!requireNamespace("desc", quietly = TRUE)) { 5 | install.packages("desc") 6 | } 7 | 8 | deps_df <- desc::desc_get_deps() 9 | deps_df_optional <- deps_df$package[deps_df$type %in% c("Suggests", "Enhances")] 10 | deps_df_hard <- deps_df$package[deps_df$type %in% c("Depends", "Imports", "LinkingTo")] 11 | deps_df_base <- unlist(tools::standard_package_names(), use.names = FALSE) 12 | 13 | packages <- sort(deps_df_optional) 14 | packages <- intersect(packages, rownames(available.packages())) 15 | 16 | # Too big to fail, or can't be avoided: 17 | off_limits <- c("testthat", "rmarkdown", "rcmdcheck", deps_df_hard, deps_df_base) 18 | off_limits_dep <- unlist(tools::package_dependencies(off_limits, recursive = TRUE, which = "strong")) 19 | setdiff(packages, c(off_limits, off_limits_dep)) 20 | } 21 | 22 | if (Sys.getenv("GITHUB_BASE_REF") != "") { 23 | print(Sys.getenv("GITHUB_BASE_REF")) 24 | system("git fetch origin ${GITHUB_BASE_REF}") 25 | # Use .. to avoid having to fetch the entire history 26 | # https://github.com/krlmlr/actions-sync/issues/45 27 | diff_cmd <- "git diff origin/${GITHUB_BASE_REF}.. -- R/ tests/ | egrep '^[+][^+]' | grep -q ::" 28 | diff_lines <- system(diff_cmd, intern = TRUE) 29 | if (length(diff_lines) > 0) { 30 | writeLines("Changes using :: in R/ or tests/:") 31 | writeLines(diff_lines) 32 | packages <- get_deps() 33 | } else { 34 | writeLines("No changes using :: found in R/ or tests/, not checking without suggested packages") 35 | packages <- character() 36 | } 37 | } else { 38 | writeLines("No GITHUB_BASE_REF, checking without suggested packages") 39 | packages <- get_deps() 40 | } 41 | 42 | if (length(packages) > 0) { 43 | json <- paste0( 44 | '{"package":[', 45 | paste0('"', packages, '"', collapse = ","), 46 | "]}" 47 | ) 48 | writeLines(paste0("matrix=", json), Sys.getenv("GITHUB_OUTPUT")) 49 | writeLines(json) 50 | } else { 51 | writeLines("No suggested packages found.") 52 | } 53 | -------------------------------------------------------------------------------- /.github/workflows/dep-suggests-matrix/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to compute a matrix with all suggested packages" 2 | outputs: 3 | matrix: 4 | description: "Generated matrix" 5 | value: ${{ steps.set-matrix.outputs.matrix }} 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - id: set-matrix 11 | run: | 12 | Rscript ./.github/workflows/dep-suggests-matrix/action.R 13 | shell: bash 14 | -------------------------------------------------------------------------------- /.github/workflows/fledge.yaml: -------------------------------------------------------------------------------- 1 | name: fledge 2 | 3 | on: 4 | # for manual triggers 5 | workflow_dispatch: 6 | inputs: 7 | pr: 8 | description: "Create PR" 9 | required: false 10 | type: boolean 11 | default: false 12 | # daily run 13 | schedule: 14 | - cron: "30 0 * * *" 15 | 16 | concurrency: 17 | group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || '' }}-${{ github.base_ref || '' }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | check_fork: 22 | runs-on: ubuntu-24.04 23 | outputs: 24 | is_forked: ${{ steps.check.outputs.is_forked }} 25 | steps: 26 | - name: Check if the repo is forked 27 | id: check 28 | env: 29 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | run: | 31 | is_forked=$(gh api repos/${{ github.repository }} | jq .fork) 32 | echo "is_forked=${is_forked}" >> $GITHUB_OUTPUT 33 | shell: bash 34 | 35 | fledge: 36 | runs-on: ubuntu-24.04 37 | needs: check_fork 38 | if: needs.check_fork.outputs.is_forked == 'false' 39 | permissions: 40 | contents: write 41 | pull-requests: write 42 | actions: write 43 | env: 44 | FLEDGE_GHA_CI: true 45 | steps: 46 | - uses: actions/checkout@v4 47 | with: 48 | fetch-depth: 0 49 | fetch-tags: true 50 | 51 | - name: Configure Git identity 52 | run: | 53 | env | sort 54 | git config --local user.name "$GITHUB_ACTOR" 55 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 56 | shell: bash 57 | 58 | - name: Update apt 59 | run: | 60 | sudo apt-get update 61 | shell: bash 62 | 63 | - uses: r-lib/actions/setup-r@v2 64 | with: 65 | use-public-rspm: true 66 | 67 | - uses: r-lib/actions/setup-r-dependencies@v2 68 | env: 69 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 70 | with: 71 | pak-version: devel 72 | packages: cynkra/fledge 73 | cache-version: fledge-1 74 | 75 | - name: Count rulesets 76 | # Assume that branch is protected if ruleset exists 77 | id: rulesets 78 | env: 79 | GH_TOKEN: ${{ github.token }} 80 | run: | 81 | n_rulesets=$(gh api repos/${{ github.repository }}/rulesets -q length) 82 | echo "count=${n_rulesets}" >> $GITHUB_OUTPUT 83 | shell: bash 84 | 85 | - name: Switch to branch if branch protection is enabled 86 | if: github.ref_protected == 'true' || inputs.pr == 'true' || steps.rulesets.outputs.count > 0 87 | run: | 88 | git checkout -b fledge 89 | git push -f -u origin HEAD 90 | shell: bash 91 | 92 | - name: Bump version 93 | env: 94 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 95 | run: | 96 | check_default_branch <- ("${{ github.ref_protected == 'true' || inputs.pr == 'true' || steps.rulesets.outputs.count > 0 }}" != "true") 97 | if (fledge::bump_version(which = "dev", no_change_behavior = "noop", check_default_branch = check_default_branch)) { 98 | fledge::finalize_version(push = TRUE) 99 | } 100 | shell: Rscript {0} 101 | 102 | - name: Create and merge PR if branch protection is enabled 103 | if: github.ref_protected == 'true' || inputs.pr == 'true' || steps.rulesets.outputs.count > 0 104 | env: 105 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 106 | run: | 107 | set -ex 108 | if [ -n "$(git diff main --numstat)" ]; then 109 | gh pr create --base main --head fledge --fill-first 110 | gh workflow run rcc -f ref=$(git rev-parse HEAD) 111 | gh pr merge --squash --auto 112 | else 113 | echo "No changes." 114 | fi 115 | shell: bash 116 | 117 | - name: Check release 118 | env: 119 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 120 | run: | 121 | fledge:::release_after_cran_built_binaries() 122 | shell: Rscript {0} 123 | -------------------------------------------------------------------------------- /.github/workflows/get-extra/action.yml: -------------------------------------------------------------------------------- 1 | name: "Action to determine extra packages to be installed" 2 | outputs: 3 | packages: 4 | description: "List of extra packages" 5 | value: ${{ steps.get-extra.outputs.packages }} 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - name: Get extra packages 11 | id: get-extra 12 | run: | 13 | set -x 14 | packages=$( ( grep Config/gha/extra-packages DESCRIPTION || true ) | cut -d " " -f 2) 15 | echo packages=$packages >> $GITHUB_OUTPUT 16 | shell: bash 17 | -------------------------------------------------------------------------------- /.github/workflows/git-identity/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to set up a Git identity" 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Configure Git identity 7 | run: | 8 | env | sort 9 | git config --local user.name "$GITHUB_ACTOR" 10 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 11 | shell: bash 12 | -------------------------------------------------------------------------------- /.github/workflows/install/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to run for installing R packages" 2 | inputs: 3 | token: 4 | description: GitHub token, set to secrets.GITHUB_TOKEN 5 | required: true 6 | r-version: 7 | description: Passed on to r-lib/actions/setup-r@v2 8 | required: false 9 | default: release 10 | install-r: 11 | description: Passed on to r-lib/actions/setup-r@v2 12 | required: false 13 | default: true 14 | needs: 15 | description: Passed on to r-lib/actions/setup-r-dependencies@v2 16 | required: false 17 | default: "" 18 | packages: 19 | description: Passed on to r-lib/actions/setup-r-dependencies@v2 20 | required: false 21 | default: deps::., any::sessioninfo 22 | extra-packages: 23 | description: Passed on to r-lib/actions/setup-r-dependencies@v2 24 | required: false 25 | default: any::rcmdcheck 26 | cache-version: 27 | description: Passed on to r-lib/actions/setup-r-dependencies@v2 28 | required: false 29 | default: 1 30 | 31 | runs: 32 | using: "composite" 33 | steps: 34 | - name: Set environment variables 35 | run: | 36 | echo "R_REMOTES_NO_ERRORS_FROM_WARNINGS=true" | tee -a $GITHUB_ENV 37 | echo "R_KEEP_PKG_SOURCE=yes" | tee -a $GITHUB_ENV 38 | echo "_R_CHECK_SYSTEM_CLOCK_=false" | tee -a $GITHUB_ENV 39 | echo "_R_CHECK_FUTURE_FILE_TIMESTAMPS_=false" | tee -a $GITHUB_ENV 40 | # prevent rgl issues because no X11 display is available 41 | echo "RGL_USE_NULL=true" | tee -a $GITHUB_ENV 42 | # from https://github.com/r-devel/r-dev-web/blob/main/CRAN/QA/Kurt/lib/R/Scripts/check_CRAN_incoming.R 43 | echo "_R_CHECK_CRAN_INCOMING_CHECK_FILE_URIS_=true" | tee -a $GITHUB_ENV 44 | echo "_R_CHECK_CRAN_INCOMING_NOTE_GNU_MAKE_=true" | tee -a $GITHUB_ENV 45 | echo "_R_CHECK_PACKAGE_DEPENDS_IGNORE_MISSING_ENHANCES_=true" | tee -a $GITHUB_ENV 46 | echo "_R_CHECK_CODE_CLASS_IS_STRING_=true" | tee -a $GITHUB_ENV 47 | echo "_R_CHECK_CODOC_VARIABLES_IN_USAGES_=true" | tee -a $GITHUB_ENV 48 | echo "_R_CHECK_CONNECTIONS_LEFT_OPEN_=true" | tee -a $GITHUB_ENV 49 | echo "_R_CHECK_DATALIST_=true" | tee -a $GITHUB_ENV 50 | echo "_R_CHECK_NEWS_IN_PLAIN_TEXT_=true" | tee -a $GITHUB_ENV 51 | echo "_R_CHECK_PACKAGES_USED_CRAN_INCOMING_NOTES_=true" | tee -a $GITHUB_ENV 52 | echo "_R_CHECK_RD_CONTENTS_KEYWORDS_=true" | tee -a $GITHUB_ENV 53 | echo "_R_CHECK_R_DEPENDS_=warn" | tee -a $GITHUB_ENV 54 | echo "_R_CHECK_S3_METHODS_SHOW_POSSIBLE_ISSUES_=true" | tee -a $GITHUB_ENV 55 | echo "_R_CHECK_THINGS_IN_TEMP_DIR_=true" | tee -a $GITHUB_ENV 56 | echo "_R_CHECK_UNDOC_USE_ALL_NAMES_=true" | tee -a $GITHUB_ENV 57 | echo "_R_CHECK_URLS_SHOW_301_STATUS_=true" | tee -a $GITHUB_ENV 58 | echo "_R_CXX_USE_NO_REMAP_=true" | tee -a $GITHUB_ENV 59 | # There is no way to disable recency and frequency checks when the incoming checks are run 60 | # echo "_R_CHECK_CRAN_INCOMING_=true" | tee -a $GITHUB_ENV 61 | echo "_R_CHECK_CRAN_INCOMING_SKIP_LARGE_VERSION_=true" | tee -a $GITHUB_ENV 62 | echo "_R_CHECK_FORCE_SUGGESTS_=false" | tee -a $GITHUB_ENV 63 | shell: bash 64 | 65 | - name: Set environment variables (non-Windows only) 66 | if: runner.os != 'Windows' 67 | run: | 68 | echo "_R_CHECK_BASHISMS_=true" | tee -a $GITHUB_ENV 69 | shell: bash 70 | 71 | - name: Update apt 72 | if: runner.os == 'Linux' 73 | run: | 74 | sudo apt-get update 75 | sudo apt-get install -y aspell 76 | echo "_R_CHECK_CRAN_INCOMING_USE_ASPELL_=true" | tee -a $GITHUB_ENV 77 | shell: bash 78 | 79 | - name: Remove pkg-config@0.29.2 80 | if: runner.os == 'macOS' 81 | run: | 82 | brew uninstall pkg-config@0.29.2 || true 83 | shell: bash 84 | 85 | - uses: r-lib/actions/setup-pandoc@v2 86 | 87 | - uses: r-lib/actions/setup-r@v2 88 | with: 89 | r-version: ${{ inputs.r-version }} 90 | install-r: ${{ inputs.install-r }} 91 | http-user-agent: ${{ matrix.config.http-user-agent }} 92 | use-public-rspm: true 93 | 94 | - id: get-extra 95 | run: | 96 | set -x 97 | packages=$( ( grep Config/gha/extra-packages DESCRIPTION || true ) | cut -d " " -f 2) 98 | echo packages=$packages >> $GITHUB_OUTPUT 99 | shell: bash 100 | 101 | - uses: r-lib/actions/setup-r-dependencies@v2 102 | env: 103 | GITHUB_PAT: ${{ inputs.token }} 104 | with: 105 | pak-version: stable 106 | needs: ${{ inputs.needs }} 107 | packages: ${{ inputs.packages }} 108 | extra-packages: ${{ inputs.extra-packages }} ${{ ( matrix.covr && 'covr xml2' ) || '' }} ${{ steps.get-extra.outputs.packages }} 109 | cache-version: ${{ inputs.cache-version }} 110 | 111 | - name: Add pkg.lock to .gitignore 112 | run: | 113 | set -x 114 | if ! [ -f .github/.gitignore ] || [ -z "$(grep '^/pkg.lock$' .github/.gitignore)" ]; then 115 | echo /pkg.lock >> .github/.gitignore 116 | fi 117 | shell: bash 118 | 119 | - name: Add fake qpdf and checkbashisms 120 | if: runner.os == 'Linux' 121 | run: | 122 | sudo ln -s $(which true) /usr/local/bin/qpdf 123 | sudo ln -s $(which true) /usr/local/bin/checkbashisms 124 | shell: bash 125 | 126 | - name: Install ccache 127 | uses: krlmlr/ccache-action@parallel-dir 128 | with: 129 | max-size: 10G 130 | verbose: 1 131 | save: false 132 | restore: false 133 | 134 | - name: Use ccache for compiling R code, and parallelize 135 | run: | 136 | mkdir -p ~/.R 137 | echo 'CC := ccache $(CC)' >> ~/.R/Makevars 138 | echo 'CXX := ccache $(CXX)' >> ~/.R/Makevars 139 | echo 'CXX11 := ccache $(CXX11)' >> ~/.R/Makevars 140 | echo 'CXX14 := ccache $(CXX14)' >> ~/.R/Makevars 141 | echo 'CXX17 := ccache $(CXX17)' >> ~/.R/Makevars 142 | echo 'MAKEFLAGS = -j2' >> ~/.R/Makevars 143 | cat ~/.R/Makevars 144 | 145 | echo 'CCACHE_SLOPPINESS=locale,time_macros' | tee -a $GITHUB_ENV 146 | 147 | # echo 'CCACHE_DEBUG=true' | tee -a $GITHUB_ENV 148 | # echo "CCACHE_DEBUGDIR=$(dirname $(pwd))/ccache-debug" | tee -a $GITHUB_ENV 149 | # mkdir -p $(dirname $(pwd))/.ccache-debug 150 | 151 | echo 'PKG_BUILD_EXTRA_FLAGS=false' | tee -a $GITHUB_ENV 152 | 153 | # Repair 154 | git rm -rf .ccache || true 155 | rm -rf .ccache 156 | shell: bash 157 | 158 | - name: Show R CMD config --all 159 | run: | 160 | R CMD config --all 161 | shell: bash 162 | -------------------------------------------------------------------------------- /.github/workflows/lock.yaml: -------------------------------------------------------------------------------- 1 | name: "Lock threads" 2 | permissions: 3 | issues: write 4 | pull-requests: write 5 | discussions: write 6 | on: 7 | workflow_dispatch: 8 | schedule: 9 | - cron: "37 2 * * *" 10 | 11 | jobs: 12 | lock: 13 | runs-on: ubuntu-24.04 14 | steps: 15 | - uses: krlmlr/lock-threads@patch-1 16 | with: 17 | github-token: ${{ github.token }} 18 | issue-inactive-days: "365" 19 | issue-lock-reason: "" 20 | issue-comment: > 21 | This old thread has been automatically locked. If you think you have 22 | found something related to this, please open a new issue and link to this 23 | old issue if necessary. 24 | -------------------------------------------------------------------------------- /.github/workflows/matrix-check/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to check a matrix with all R and OS versions, computed with the versions-matrix action" 2 | inputs: 3 | matrix: 4 | description: "Generated matrix" 5 | required: true 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - name: Install json2yaml 11 | run: | 12 | sudo npm install -g json2yaml 13 | shell: bash 14 | 15 | - run: | 16 | matrix='${{ inputs.matrix }}' 17 | if [ -n "${matrix}" ]; then 18 | echo $matrix | jq . 19 | echo $matrix | json2yaml 20 | else 21 | echo "No matrix found" 22 | fi 23 | shell: bash 24 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown-build/action.yml: -------------------------------------------------------------------------------- 1 | name: "Action to build a pkgdown website" 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Build site 7 | run: | 8 | pkgdown::build_site() 9 | shell: Rscript {0} 10 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown-deploy/action.yml: -------------------------------------------------------------------------------- 1 | name: "Action to deploy a pkgdown website" 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Deploy site 7 | uses: nick-fields/retry@v3 8 | with: 9 | timeout_minutes: 15 10 | max_attempts: 10 11 | command: | 12 | R -q -e 'pkgdown::deploy_to_branch(new_process = FALSE)' 13 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Also included in R-CMD-check.yaml, this workflow only listens to pushes to branches 3 | # that start with "docs*" or "cran-*" and does not need to act on pushes to the main branch. 4 | on: 5 | push: 6 | branches: 7 | - "docs*" 8 | - "cran-*" 9 | # The main branch is excluded here, it is handled by the R-CMD-check workflow. 10 | # This workflow is only for handling pushes to designated branches. 11 | workflow_dispatch: 12 | 13 | name: pkgdown 14 | 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.sha }}-${{ github.base_ref || '' }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | pkgdown: 21 | runs-on: ubuntu-24.04 22 | 23 | name: "pkgdown" 24 | 25 | # Begin custom: services 26 | # End custom: services 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | 31 | - uses: ./.github/workflows/rate-limit 32 | with: 33 | token: ${{ secrets.GITHUB_TOKEN }} 34 | 35 | - uses: ./.github/workflows/git-identity 36 | if: github.event_name == 'push' 37 | 38 | - uses: ./.github/workflows/custom/before-install 39 | if: hashFiles('.github/workflows/custom/before-install/action.yml') != '' 40 | 41 | - uses: ./.github/workflows/install 42 | with: 43 | token: ${{ secrets.GITHUB_TOKEN }} 44 | cache-version: pkgdown-2 45 | needs: website 46 | extra-packages: r-lib/pkgdown local::. 47 | 48 | - uses: ./.github/workflows/custom/after-install 49 | if: hashFiles('.github/workflows/custom/after-install/action.yml') != '' 50 | 51 | - uses: ./.github/workflows/pkgdown-build 52 | if: github.event_name != 'push' 53 | 54 | - uses: ./.github/workflows/pkgdown-deploy 55 | if: github.event_name == 'push' 56 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | issue_comment: 3 | types: [created] 4 | name: Commands 5 | jobs: 6 | document: 7 | if: startsWith(github.event.comment.body, '/document') 8 | name: document 9 | # macos is actually better here due to native binary packages 10 | runs-on: macos-latest 11 | env: 12 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: r-lib/actions/pr-fetch@v2 16 | with: 17 | repo-token: ${{ secrets.GITHUB_TOKEN }} 18 | - uses: r-lib/actions/setup-r@v2 19 | - name: Configure Git identity 20 | run: | 21 | env | sort 22 | git config --local user.name "$GITHUB_ACTOR" 23 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 24 | shell: bash 25 | - name: Install dependencies 26 | run: | 27 | install.packages(c("remotes", "roxygen2"), type = "binary") 28 | remotes::install_deps(dependencies = TRUE) 29 | shell: Rscript {0} 30 | - name: Document 31 | run: | 32 | roxygen2::roxygenise() 33 | shell: Rscript {0} 34 | - name: commit 35 | run: | 36 | if [ -n "$(git status --porcelain man/ NAMESPACE)" ]; then 37 | git add man/ NAMESPACE 38 | git commit -m 'Document' 39 | fi 40 | - uses: r-lib/actions/pr-push@v2 41 | with: 42 | repo-token: ${{ secrets.GITHUB_TOKEN }} 43 | style: 44 | if: startsWith(github.event.comment.body, '/style') 45 | name: style 46 | # macos is actually better here due to native binary packages 47 | runs-on: macos-latest 48 | env: 49 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 50 | steps: 51 | - uses: actions/checkout@v4 52 | - uses: r-lib/actions/pr-fetch@v2 53 | with: 54 | repo-token: ${{ secrets.GITHUB_TOKEN }} 55 | - uses: r-lib/actions/setup-r@v2 56 | - name: Configure Git identity 57 | run: | 58 | env | sort 59 | git config --local user.name "$GITHUB_ACTOR" 60 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 61 | shell: bash 62 | - name: Install dependencies 63 | run: | 64 | install.packages(c("styler", "roxygen2"), type = "binary") 65 | shell: Rscript {0} 66 | - name: Style 67 | run: | 68 | styler::style_pkg(strict = FALSE) 69 | shell: Rscript {0} 70 | - name: commit 71 | run: | 72 | if [ -n "$(git status --porcelain '*.R' '*.Rmd')" ]; then 73 | git add '*.R' '*.Rmd' 74 | git commit -m 'Style' 75 | fi 76 | - uses: r-lib/actions/pr-push@v2 77 | with: 78 | repo-token: ${{ secrets.GITHUB_TOKEN }} 79 | merge: 80 | if: startsWith(github.event.comment.body, '/merge') 81 | name: merge 82 | runs-on: ubuntu-22.04 83 | steps: 84 | - name: Create and merge pull request 85 | run: | 86 | set -exo pipefail 87 | PR_DETAILS=$( curl -s --header "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.issue.number }} ) 88 | echo "$PR_DETAILS" | jq . 89 | PR_BASE=$(echo "$PR_DETAILS" | jq -r .base.ref) 90 | PR_HEAD=$(echo "$PR_DETAILS" | jq -r .head.ref) 91 | PR_URL=$(curl -s -X POST --header "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" --data '{ "head": "'$PR_BASE'", "base": "'$PR_HEAD'", "title": "Merge back PR target branch", "body": "Target: #${{ github.event.issue.number }}" }' https://api.github.com/repos/${{ github.repository }}/pulls | jq -r .url ) 92 | echo $PR_URL 93 | # Merging here won't run CI/CD 94 | # curl -s -X PUT --header "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" $PR_URL/merge 95 | # A mock job just to ensure we have a successful build status 96 | finish: 97 | runs-on: ubuntu-22.04 98 | steps: 99 | - run: true 100 | -------------------------------------------------------------------------------- /.github/workflows/rate-limit/action.yml: -------------------------------------------------------------------------------- 1 | name: "Check GitHub rate limits" 2 | inputs: 3 | token: # id of input 4 | description: GitHub token, pass secrets.GITHUB_TOKEN 5 | required: true 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - name: Check rate limits 11 | run: | 12 | curl -s --header "authorization: Bearer ${{ inputs.token }}" https://api.github.com/rate_limit 13 | shell: bash 14 | -------------------------------------------------------------------------------- /.github/workflows/revdep.yaml: -------------------------------------------------------------------------------- 1 | # This workflow creates many jobs, run only when a branch is created 2 | on: 3 | push: 4 | branches: 5 | - "revdep*" # never run automatically on main branch 6 | 7 | name: revdep 8 | 9 | jobs: 10 | matrix: 11 | runs-on: ubuntu-22.04 12 | outputs: 13 | matrix: ${{ steps.set-matrix.outputs.matrix }} 14 | 15 | name: Collect revdeps 16 | 17 | env: 18 | R_REMOTES_NO_ERRORS_FROM_WARNINGS: true 19 | RSPM: https://packagemanager.rstudio.com/cran/__linux__/bionic/latest 20 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 21 | # prevent rgl issues because no X11 display is available 22 | RGL_USE_NULL: true 23 | # Begin custom: env vars 24 | # End custom: env vars 25 | 26 | steps: 27 | - name: Check rate limits 28 | run: | 29 | curl -s --header "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/rate_limit 30 | shell: bash 31 | 32 | - uses: actions/checkout@v4 33 | 34 | # FIXME: Avoid reissuing succesful jobs 35 | # https://docs.github.com/en/free-pro-team@latest/rest/reference/actions#list-jobs-for-a-workflow-run 36 | # https://docs.github.com/en/free-pro-team@latest/rest/reference/actions#workflow-runs 37 | - id: set-matrix 38 | run: | 39 | package <- read.dcf("DESCRIPTION")[, "Package"][[1]] 40 | deps <- tools:::package_dependencies(package, reverse = TRUE, which = c("Depends", "Imports", "LinkingTo", "Suggests"))[[1]] 41 | json <- paste0( 42 | '{"package":[', 43 | paste0('"', deps, '"', collapse = ","), 44 | ']}' 45 | ) 46 | writeLines(json) 47 | writeLines(paste0("matrix=", json), Sys.getenv("GITHUB_OUTPUT")) 48 | shell: Rscript {0} 49 | 50 | check-matrix: 51 | runs-on: ubuntu-22.04 52 | needs: matrix 53 | steps: 54 | - name: Install json2yaml 55 | run: | 56 | sudo npm install -g json2yaml 57 | 58 | - name: Check matrix definition 59 | run: | 60 | matrix='${{ needs.matrix.outputs.matrix }}' 61 | echo $matrix 62 | echo $matrix | jq . 63 | echo $matrix | json2yaml 64 | 65 | R-CMD-check: 66 | needs: matrix 67 | 68 | runs-on: ubuntu-22.04 69 | 70 | name: 'revdep: ${{ matrix.package }}' 71 | 72 | # Begin custom: services 73 | # End custom: services 74 | 75 | strategy: 76 | fail-fast: false 77 | matrix: ${{fromJson(needs.matrix.outputs.matrix)}} 78 | 79 | env: 80 | R_REMOTES_NO_ERRORS_FROM_WARNINGS: true 81 | RSPM: https://packagemanager.rstudio.com/cran/__linux__/bionic/latest 82 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 83 | # prevent rgl issues because no X11 display is available 84 | RGL_USE_NULL: true 85 | # Begin custom: env vars 86 | # End custom: env vars 87 | 88 | steps: 89 | - name: Check rate limits 90 | run: | 91 | curl -s --header "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/rate_limit 92 | shell: bash 93 | 94 | - uses: actions/checkout@v4 95 | 96 | # Begin custom: before install 97 | # End custom: before install 98 | 99 | - name: Use RSPM 100 | run: | 101 | mkdir -p /home/runner/work/_temp/Library 102 | echo 'local({release <- system2("lsb_release", "-sc", stdout = TRUE); options(repos=c(CRAN = paste0("https://packagemanager.rstudio.com/all/__linux__/", release, "/latest")), HTTPUserAgent = sprintf("R/%s R (%s)", getRversion(), paste(getRversion(), R.version$platform, R.version$arch, R.version$os)))}); .libPaths("/home/runner/work/_temp/Library")' | sudo tee /etc/R/Rprofile.site 103 | 104 | - name: Install remotes 105 | run: | 106 | if (!requireNamespace("curl", quietly = TRUE)) install.packages("curl") 107 | if (!requireNamespace("remotes", quietly = TRUE)) install.packages("remotes") 108 | shell: Rscript {0} 109 | 110 | - uses: r-lib/actions/setup-pandoc@v2 111 | 112 | - name: Install system dependencies 113 | if: runner.os == 'Linux' 114 | run: | 115 | sudo apt-get update -y 116 | Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "22.04")); package <- "${{ matrix.package }}"; deps <- tools::package_dependencies(package, which = "Suggests")[[1]]; lapply(c(package, deps), function(x) { writeLines(remotes::system_requirements("ubuntu", "22.04", package = x)) })' | sort | uniq > .github/deps.sh 117 | cat .github/deps.sh 118 | sudo sh < .github/deps.sh 119 | 120 | - name: Install package 121 | run: | 122 | package <- "${{ matrix.package }}" 123 | install.packages(package, dependencies = TRUE) 124 | remotes::install_cran("rcmdcheck") 125 | shell: Rscript {0} 126 | 127 | - name: Session info old 128 | run: | 129 | options(width = 100) 130 | if (!requireNamespace("sessioninfo", quietly = TRUE)) install.packages("sessioninfo") 131 | pkgs <- installed.packages()[, "Package"] 132 | sessioninfo::session_info(pkgs, include_base = TRUE) 133 | shell: Rscript {0} 134 | 135 | # Begin custom: after install 136 | # End custom: after install 137 | 138 | - name: Check old 139 | env: 140 | _R_CHECK_CRAN_INCOMING_: false 141 | _R_CHECK_SYSTEM_CLOCK_: false 142 | _R_CHECK_FUTURE_FILE_TIMESTAMPS_: false 143 | # Avoid downloading binary package from RSPM 144 | run: | 145 | package <- "${{ matrix.package }}" 146 | options(HTTPUserAgent = "gha") 147 | path <- download.packages(package, destdir = ".github")[, 2] 148 | print(path) 149 | 150 | dir <- file.path("revdep", package) 151 | dir.create(dir, showWarnings = FALSE, recursive = TRUE) 152 | check <- rcmdcheck::rcmdcheck(path, args = c("--no-manual", "--as-cran"), error_on = "never", check_dir = file.path(dir, "check")) 153 | file.rename(file.path(dir, "check"), file.path(dir, "old")) 154 | saveRDS(check, file.path(dir, "old.rds")) 155 | shell: Rscript {0} 156 | 157 | - name: Install local package 158 | run: | 159 | remotes::install_local(".", force = TRUE) 160 | shell: Rscript {0} 161 | 162 | - name: Session info new 163 | run: | 164 | options(width = 100) 165 | pkgs <- installed.packages()[, "Package"] 166 | sessioninfo::session_info(pkgs, include_base = TRUE) 167 | shell: Rscript {0} 168 | 169 | - name: Check new 170 | env: 171 | _R_CHECK_CRAN_INCOMING_: false 172 | _R_CHECK_SYSTEM_CLOCK_: false 173 | _R_CHECK_FUTURE_FILE_TIMESTAMPS_: false 174 | run: | 175 | package <- "${{ matrix.package }}" 176 | path <- dir(".github", pattern = paste0("^", package), full.names = TRUE)[[1]] 177 | print(path) 178 | 179 | dir <- file.path("revdep", package) 180 | check <- rcmdcheck::rcmdcheck(path, args = c("--no-manual", "--as-cran"), error_on = "never", check_dir = file.path(dir, "check")) 181 | file.rename(file.path(dir, "check"), file.path(dir, "new")) 182 | saveRDS(check, file.path(dir, "new.rds")) 183 | shell: Rscript {0} 184 | 185 | - name: Compare 186 | run: | 187 | package <- "${{ matrix.package }}" 188 | dir <- file.path("revdep", package) 189 | old <- readRDS(file.path(dir, "old.rds")) 190 | new <- readRDS(file.path(dir, "new.rds")) 191 | compare <- rcmdcheck::compare_checks(old, new) 192 | compare 193 | cmp <- compare$cmp 194 | if (!identical(cmp[cmp$which == "old", "output"], cmp[cmp$which == "new", "output"])) { 195 | if (!requireNamespace("waldo", quietly = TRUE)) install.packages("waldo") 196 | print(waldo::compare(old, new)) 197 | 198 | stop("Check output differs.") 199 | } 200 | shell: Rscript {0} 201 | 202 | - name: Upload check results 203 | if: failure() 204 | uses: actions/upload-artifact@main 205 | with: 206 | name: ${{ matrix.package }}-results 207 | path: revdep/${{ matrix.package }} 208 | 209 | - name: Check rate limits 210 | if: always() 211 | run: | 212 | curl -s --header "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/rate_limit 213 | shell: bash 214 | -------------------------------------------------------------------------------- /.github/workflows/roxygenize/action.yml: -------------------------------------------------------------------------------- 1 | name: "Action to create documentation with roxygen2" 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Roxygenize 7 | run: | 8 | try(roxygen2::roxygenize()) 9 | shell: Rscript {0} 10 | -------------------------------------------------------------------------------- /.github/workflows/style/action.yml: -------------------------------------------------------------------------------- 1 | name: "Action to auto-style a package" 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Check styler options 7 | id: check 8 | run: | 9 | set -x 10 | scope=$( ( grep Config/autostyle/scope DESCRIPTION || true ) | cut -d " " -f 2) 11 | strict=$( ( grep Config/autostyle/strict DESCRIPTION || true ) | cut -d " " -f 2) 12 | rmd=$( ( grep Config/autostyle/rmd DESCRIPTION || true ) | cut -d " " -f 2) 13 | echo scope=$scope >> $GITHUB_OUTPUT 14 | echo strict=$strict >> $GITHUB_OUTPUT 15 | echo rmd=$rmd >> $GITHUB_OUTPUT 16 | shell: bash 17 | 18 | - uses: actions/cache@v4 19 | if: ${{ steps.check.outputs.scope }} 20 | with: 21 | path: | 22 | ~/.cache/R/R.cache 23 | key: ${{ runner.os }}-2-${{ github.run_id }}- 24 | restore-keys: | 25 | ${{ runner.os }}-2- 26 | 27 | - name: Imprint run ID 28 | if: ${{ steps.check.outputs.scope }} 29 | run: | 30 | mkdir -p ~/.cache/R/R.cache/styler 31 | touch ~/.cache/R/R.cache/${{ github.run_id }} 32 | shell: bash 33 | 34 | - name: Show cache 35 | if: ${{ steps.check.outputs.scope }} 36 | run: | 37 | ls -l ~/.cache/R/R.cache 38 | ls -l ~/.cache/R/R.cache/styler 39 | shell: bash 40 | 41 | - name: Enable styler cache 42 | if: ${{ steps.check.outputs.scope }} 43 | run: | 44 | styler::cache_activate(verbose = TRUE) 45 | shell: Rscript {0} 46 | 47 | - name: Run styler 48 | if: ${{ steps.check.outputs.scope }} 49 | run: | 50 | strict <- as.logical("${{ steps.check.outputs.strict }}") 51 | if (is.na(strict)) { 52 | strict <- FALSE 53 | } 54 | rmd <- as.logical("${{ steps.check.outputs.rmd }}") 55 | if (is.na(rmd)) { 56 | rmd <- TRUE 57 | } 58 | styler::style_pkg( 59 | scope = "${{ steps.check.outputs.scope }}", 60 | strict = strict, 61 | filetype = c("R", "Rprofile", if (rmd) c("Rmd", "Rmarkdown", "Rnw", "Qmd")) 62 | ) 63 | shell: Rscript {0} 64 | 65 | - name: Show cache again 66 | if: ${{ steps.check.outputs.scope }} 67 | run: | 68 | ls -l ~/.cache/R/R.cache 69 | ls -l ~/.cache/R/R.cache/styler 70 | gdu -s --inodes ~/.cache/R/R.cache/styler/* || du -s --inodes ~/.cache/R/R.cache/styler/* 71 | shell: bash 72 | -------------------------------------------------------------------------------- /.github/workflows/update-snapshots/action.yml: -------------------------------------------------------------------------------- 1 | name: "Action to create pull requests for updated testthat snapshots" 2 | description: > 3 | This action will run `testthat::test_local()` for tests that seem to use snapshots, 4 | this is determined by reading and grepping the test files. 5 | If the tests are failing, snapshots are updated, and a pull request is opened. 6 | inputs: 7 | base: 8 | description: "The base branch to create the pull request against." 9 | required: false 10 | default: "main" 11 | 12 | runs: 13 | using: "composite" 14 | steps: 15 | - name: Run tests on test files that use snapshots 16 | id: run-tests 17 | run: | 18 | ## -- Run tests on test files that use snapshots -- 19 | rx <- "^test-(.*)[.][rR]$" 20 | files <- dir("tests/testthat", pattern = rx) 21 | has_snapshot <- vapply(files, function(.x) any(grepl("snapshot", readLines(file.path("tests/testthat", .x)), fixed = TRUE)), logical(1)) 22 | if (any(has_snapshot)) { 23 | patterns <- gsub(rx, "^\\1$", files[has_snapshot]) 24 | pattern <- paste0(patterns, collapse = "|") 25 | tryCatch( 26 | { 27 | result <- as.data.frame(testthat::test_local(pattern = pattern, reporter = "silent", stop_on_failure = FALSE)) 28 | print(result) 29 | failures <- result[result$failed + result$warning > 0, ] 30 | if (nrow(failures) > 0) { 31 | writeLines("Snapshot tests failed/warned.") 32 | print(failures[names(failures) != "result"]) 33 | print(failures$result) 34 | testthat::snapshot_accept() 35 | writeLines("changed=true", Sys.getenv("GITHUB_OUTPUT")) 36 | } else { 37 | writeLines("Snapshot tests ran successfully.") 38 | } 39 | }, 40 | error = print 41 | ) 42 | } else { 43 | writeLines("No snapshots found.") 44 | } 45 | shell: Rscript {0} 46 | 47 | - name: Add snapshots to Git 48 | if: ${{ steps.run-tests.outputs.changed }} 49 | run: | 50 | ## -- Add snapshots to Git -- 51 | mkdir -p tests/testthat/_snaps 52 | git add -- tests/testthat/_snaps 53 | shell: bash 54 | 55 | - name: Check changed files 56 | if: ${{ steps.run-tests.outputs.changed }} 57 | id: check-changed 58 | run: | 59 | echo "changed=$(git status --porcelain -- tests/testthat/_snaps | head -n 1)" >> $GITHUB_OUTPUT 60 | shell: bash 61 | 62 | - name: Derive branch name 63 | if: ${{ steps.check-changed.outputs.changed }} 64 | id: matrix-desc 65 | run: | 66 | config=$(echo '${{ toJSON(matrix) }}' | jq -c .) 67 | echo "text=$(echo ${config})" >> $GITHUB_OUTPUT 68 | echo "branch=$(echo ${config} | sed -r 's/[^0-9a-zA-Z]+/-/g;s/^-//;s/-$//')" >> $GITHUB_OUTPUT 69 | shell: bash 70 | 71 | - name: Create pull request 72 | if: ${{ steps.check-changed.outputs.changed }} 73 | id: cpr 74 | uses: peter-evans/create-pull-request@v6 75 | with: 76 | base: ${{ inputs.base }} 77 | branch: snapshot-${{ inputs.base }}-${{ github.job }}-${{ steps.matrix-desc.outputs.branch }} 78 | delete-branch: true 79 | title: "test: Snapshot updates for ${{ github.job }} (${{ steps.matrix-desc.outputs.text }})" 80 | body: "Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action${{ github.event.number && format(' for #{0}', github.event.number) || '' }}." 81 | add-paths: | 82 | tests/testthat/_snaps 83 | 84 | - name: Fail if pull request created 85 | if: ${{ steps.cpr.outputs.pull-request-number }} 86 | run: | 87 | false 88 | shell: bash 89 | -------------------------------------------------------------------------------- /.github/workflows/versions-matrix/action.R: -------------------------------------------------------------------------------- 1 | # Determine active versions of R to test against 2 | tags <- xml2::read_html("https://svn.r-project.org/R/tags/") 3 | 4 | bullets <- 5 | tags |> 6 | xml2::xml_find_all("//li") |> 7 | xml2::xml_text() 8 | 9 | version_bullets <- grep("^R-([0-9]+-[0-9]+-[0-9]+)/$", bullets, value = TRUE) 10 | versions <- unique(gsub("^R-([0-9]+)-([0-9]+)-[0-9]+/$", "\\1.\\2", version_bullets)) 11 | 12 | r_release <- head(sort(as.package_version(versions), decreasing = TRUE), 5) 13 | 14 | deps <- desc::desc_get_deps() 15 | r_crit <- deps$version[deps$package == "R"] 16 | if (length(r_crit) == 1) { 17 | min_r <- as.package_version(gsub("^>= ([0-9]+[.][0-9]+)(?:.*)$", "\\1", r_crit)) 18 | r_release <- r_release[r_release >= min_r] 19 | } 20 | 21 | r_versions <- c("devel", as.character(r_release)) 22 | 23 | macos <- data.frame(os = "macos-latest", r = r_versions[2:3]) 24 | windows <- data.frame(os = "windows-latest", r = r_versions[1:3]) 25 | linux_devel <- data.frame(os = "ubuntu-22.04", r = r_versions[1], `http-user-agent` = "release", check.names = FALSE) 26 | linux <- data.frame(os = "ubuntu-22.04", r = r_versions[-1]) 27 | covr <- data.frame(os = "ubuntu-22.04", r = r_versions[2], covr = "true", desc = "with covr") 28 | 29 | include_list <- list(macos, windows, linux_devel, linux, covr) 30 | 31 | if (file.exists(".github/versions-matrix.R")) { 32 | custom <- source(".github/versions-matrix.R")$value 33 | if (is.data.frame(custom)) { 34 | custom <- list(custom) 35 | } 36 | include_list <- c(include_list, custom) 37 | } 38 | 39 | print(include_list) 40 | 41 | filter <- read.dcf("DESCRIPTION")[1, ]["Config/gha/filter"] 42 | if (!is.na(filter)) { 43 | filter_expr <- parse(text = filter)[[1]] 44 | subset_fun_expr <- bquote(function(x) subset(x, .(filter_expr))) 45 | subset_fun <- eval(subset_fun_expr) 46 | include_list <- lapply(include_list, subset_fun) 47 | print(include_list) 48 | } 49 | 50 | to_json <- function(x) { 51 | if (nrow(x) == 0) return(character()) 52 | parallel <- vector("list", length(x)) 53 | for (i in seq_along(x)) { 54 | parallel[[i]] <- paste0('"', names(x)[[i]], '":"', x[[i]], '"') 55 | } 56 | paste0("{", do.call(paste, c(parallel, sep = ",")), "}") 57 | } 58 | 59 | configs <- unlist(lapply(include_list, to_json)) 60 | json <- paste0('{"include":[', paste(configs, collapse = ","), "]}") 61 | 62 | if (Sys.getenv("GITHUB_OUTPUT") != "") { 63 | writeLines(paste0("matrix=", json), Sys.getenv("GITHUB_OUTPUT")) 64 | } 65 | writeLines(json) 66 | -------------------------------------------------------------------------------- /.github/workflows/versions-matrix/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to compute a matrix with all R and OS versions" 2 | 3 | outputs: 4 | matrix: 5 | description: "Generated matrix" 6 | value: ${{ steps.set-matrix.outputs.matrix }} 7 | 8 | runs: 9 | using: "composite" 10 | steps: 11 | - name: Install json2yaml 12 | run: | 13 | sudo npm install -g json2yaml 14 | shell: bash 15 | 16 | - id: set-matrix 17 | run: | 18 | Rscript ./.github/workflows/versions-matrix/action.R 19 | shell: bash 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | inst/rmarkdown/templates/cynkra_report/skeleton/*.log 3 | inst/rmarkdown/templates/cynkra_report/skeleton/*.pdf 4 | inst/rmarkdown/templates/cynkra_report/skeleton/skeleton.tex 5 | inst/rmarkdown/templates/cynkra_report/skeleton/skeleton2.tex 6 | .Rproj.user 7 | inst/doc 8 | docs 9 | README.html 10 | /mydown 11 | -------------------------------------------------------------------------------- /.lintr: -------------------------------------------------------------------------------- 1 | linters: with_defaults( 2 | line_length_linter(100), 3 | commented_code_linter = NULL, 4 | object_usage_linter = NULL, 5 | spaces_left_parentheses_linter = NULL 6 | ) 7 | exclude: "# nolint all" 8 | exclude_start: "# nolint start" 9 | exclude_end: "# nolint end" 10 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Type: Package 2 | Package: indiedown 3 | Title: Individual R Markdown Templates 4 | Version: 0.1.1.9037 5 | Authors@R: 6 | c(person(given = "Christoph", 7 | family = "Sax", 8 | role = c("aut", "cre"), 9 | email = "christoph.sax@gmail.com", 10 | comment = c(ORCID = "0000-0002-7192-7044")), 11 | person(given = "Kirill", 12 | family = "Müller", 13 | role = "aut", 14 | comment = c(ORCID = "0000-0002-1416-3412")), 15 | person(given = "Angelica", 16 | family = "Becerra", 17 | role = "aut"), 18 | person(given = "Frederik", 19 | family = "Aust", 20 | role = "aut"), 21 | person(given = "cynkra GmbH", 22 | role = c("fnd", "cph"), 23 | email = "mail@cynkra.com")) 24 | Description: Simplifies the generation of customized R Markdown PDF 25 | templates. A template may include an individual logo, typography, 26 | geometry or color scheme. The package provides a skeleton with 27 | detailed instructions for customizations. The skeleton can be modified 28 | by changing defaults in the 'YAML' header, by adding additional 29 | 'LaTeX' commands or by applying dynamic adjustments in R. Individual 30 | corporate design elements, such as a title page, can be added as R 31 | functions that produce 'LaTeX' code. 32 | License: MIT + file LICENSE 33 | URL: https://indiedown.cynkra.com/, 34 | https://github.com/cynkra/indiedown 35 | BugReports: https://github.com/cynkra/indiedown/issues 36 | Depends: 37 | R (>= 4.0.0) 38 | Imports: 39 | cli, 40 | fs, 41 | gfonts, 42 | rlang, 43 | withr 44 | Suggests: 45 | callr, 46 | knitr (>= 1.12), 47 | rcmdcheck, 48 | rmarkdown, 49 | rstudioapi, 50 | spelling, 51 | testthat (>= 3.0.0), 52 | tinytex 53 | VignetteBuilder: 54 | knitr 55 | Config/testthat/edition: 3 56 | Encoding: UTF-8 57 | Language: en-US 58 | Roxygen: list(markdown = TRUE) 59 | RoxygenNote: 7.3.2.9000 60 | Config/Needs/website: cynkra/cynkratemplate 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2021 2 | COPYRIGHT HOLDER: 2021 cynkra GmbH 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2021 cynkra GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(create_indiedown_package) 4 | export(dr_down) 5 | export(use_indiedown_gfonts) 6 | import(cli) 7 | importFrom(stats,setNames) 8 | importFrom(utils,installed.packages) 9 | importFrom(utils,packageVersion) 10 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # indiedown 0.1.1.9037 4 | 5 | ## Continuous integration 6 | 7 | - Enhance permissions for workflow (#74). 8 | 9 | 10 | # indiedown 0.1.1.9036 11 | 12 | ## Continuous integration 13 | 14 | - Permissions, better tests for missing suggests, lints (#73). 15 | 16 | 17 | # indiedown 0.1.1.9035 18 | 19 | ## Continuous integration 20 | 21 | - Only fail covr builds if token is given (#72). 22 | 23 | - Always use `_R_CHECK_FORCE_SUGGESTS_=false` (#71). 24 | 25 | 26 | # indiedown 0.1.1.9034 27 | 28 | ## Continuous integration 29 | 30 | - Correct installation of xml2 (#70). 31 | 32 | 33 | # indiedown 0.1.1.9033 34 | 35 | ## Chore 36 | 37 | - Auto-update from GitHub Actions. 38 | 39 | Run: https://github.com/cynkra/indiedown/actions/runs/14636209573 40 | 41 | ## Continuous integration 42 | 43 | - Explain (#69). 44 | 45 | - Add xml2 for covr, print testthat results (#68). 46 | 47 | - Fix (#67). 48 | 49 | - Sync (#66). 50 | 51 | 52 | # indiedown 0.1.1.9032 53 | 54 | ## Continuous integration 55 | 56 | - Avoid failure in fledge workflow if no changes (#62). 57 | 58 | 59 | # indiedown 0.1.1.9031 60 | 61 | ## Continuous integration 62 | 63 | - Fetch tags for fledge workflow to avoid unnecessary NEWS entries (#61). 64 | 65 | 66 | # indiedown 0.1.1.9030 67 | 68 | ## Continuous integration 69 | 70 | - Use larger retry count for lock-threads workflow (#60). 71 | 72 | 73 | # indiedown 0.1.1.9029 74 | 75 | ## Continuous integration 76 | 77 | - Ignore errors when removing pkg-config on macOS (#59). 78 | 79 | 80 | # indiedown 0.1.1.9028 81 | 82 | ## Continuous integration 83 | 84 | - Explicit permissions (#58). 85 | 86 | 87 | # indiedown 0.1.1.9027 88 | 89 | ## Continuous integration 90 | 91 | - Use styler from main branch (#57). 92 | 93 | 94 | # indiedown 0.1.1.9026 95 | 96 | ## Continuous integration 97 | 98 | - Need to install R on Ubuntu 24.04 (#56). 99 | 100 | - Use Ubuntu 24.04 and styler PR (#54). 101 | 102 | ## Uncategorized 103 | 104 | - PLACEHOLDER https://github.com/cynkra/indiedown/pull/16 (#16). 105 | 106 | 107 | # indiedown 0.1.1.9025 108 | 109 | ## Continuous integration 110 | 111 | - Correctly detect branch protection (#53). 112 | 113 | 114 | # indiedown 0.1.1.9024 115 | 116 | ## Continuous integration 117 | 118 | - Use stable pak (#52). 119 | 120 | 121 | # indiedown 0.1.1.9023 122 | 123 | ## Continuous integration 124 | 125 | - Trigger run (#51). 126 | 127 | - ci: Trigger run 128 | 129 | - ci: Latest changes 130 | 131 | 132 | # indiedown 0.1.1.9022 133 | 134 | ## Continuous integration 135 | 136 | - Use pkgdown branch (#50). 137 | 138 | - ci: Use pkgdown branch 139 | 140 | - ci: Updates from duckdb 141 | 142 | - ci: Trigger run 143 | 144 | 145 | # indiedown 0.1.1.9021 146 | 147 | ## Continuous integration 148 | 149 | - Install via R CMD INSTALL ., not pak (#49). 150 | 151 | - ci: Install via R CMD INSTALL ., not pak 152 | 153 | - ci: Bump version of upload-artifact action 154 | 155 | 156 | # indiedown 0.1.1.9020 157 | 158 | ## Continuous integration 159 | 160 | - Install local package for pkgdown builds. 161 | 162 | - Improve support for protected branches with fledge. 163 | 164 | - Improve support for protected branches, without fledge. 165 | 166 | 167 | # indiedown 0.1.1.9019 168 | 169 | ## Chore 170 | 171 | - Auto-update from GitHub Actions. 172 | 173 | Run: https://github.com/cynkra/indiedown/actions/runs/10425483323 174 | 175 | ## Continuous integration 176 | 177 | - Sync with latest developments. 178 | 179 | 180 | # indiedown 0.1.1.9018 181 | 182 | ## Continuous integration 183 | 184 | - Use v2 instead of master. 185 | 186 | 187 | # indiedown 0.1.1.9017 188 | 189 | ## Continuous integration 190 | 191 | - Inline action. 192 | 193 | 194 | # indiedown 0.1.1.9016 195 | 196 | ## Chore 197 | 198 | - Auto-update from GitHub Actions. 199 | 200 | Run: https://github.com/cynkra/indiedown/actions/runs/10207825578 201 | 202 | ## Continuous integration 203 | 204 | - Use dev roxygen2 and decor. 205 | 206 | 207 | # indiedown 0.1.1.9015 208 | 209 | ## Continuous integration 210 | 211 | - Fix on Windows, tweak lock workflow. 212 | 213 | 214 | # indiedown 0.1.1.9014 215 | 216 | ## Chore 217 | 218 | - Auto-update from GitHub Actions. 219 | 220 | Run: https://github.com/cynkra/indiedown/actions/runs/9727973619 221 | 222 | 223 | # indiedown 0.1.1.9013 224 | 225 | ## Chore 226 | 227 | - Auto-update from GitHub Actions. 228 | 229 | Run: https://github.com/cynkra/indiedown/actions/runs/9692290881 230 | 231 | ## Continuous integration 232 | 233 | - Avoid checking bashisms on Windows. 234 | 235 | - Better commit message. 236 | 237 | - Bump versions, better default, consume custom matrix. 238 | 239 | - Recent updates. 240 | 241 | 242 | # indiedown 0.1.1.9012 243 | 244 | ## Documentation 245 | 246 | - Fix url (#28). 247 | 248 | 249 | # indiedown 0.1.1.9011 250 | 251 | ## Chore 252 | 253 | ### deps 254 | 255 | - Update peter-evans/create-pull-request action to v6. 256 | 257 | 258 | # indiedown 0.1.1.9010 259 | 260 | ## Chore 261 | 262 | ### deps 263 | 264 | - Update nick-fields/retry action to v3. 265 | 266 | 267 | # indiedown 0.1.1.9009 268 | 269 | ## Chore 270 | 271 | ### deps 272 | 273 | - Update dessant/lock-threads action to v5. 274 | 275 | ### deps 276 | 277 | - Update actions/cache action to v4. 278 | 279 | ## Documentation 280 | 281 | - Set BS version explicitly for now. 282 | 283 | https://github.com/cynkra/cynkratemplate/issues/53 284 | 285 | 286 | # indiedown 0.1.1.9008 287 | 288 | - Internal changes only. 289 | 290 | 291 | # indiedown 0.1.1.9007 292 | 293 | - Internal changes only. 294 | 295 | 296 | # indiedown 0.1.1.9006 297 | 298 | - Merge pull request #39 from cynkra/snapshot-main-rcc-smoke-null. 299 | 300 | 301 | # indiedown 0.1.1.9005 302 | 303 | ## Chore 304 | 305 | - Build-ignore `renovate.json`. 306 | 307 | ## Uncategorized 308 | 309 | - Merge pull request #30 from cynkra/renovate/configure. 310 | 311 | Configure Renovate 312 | 313 | 314 | # indiedown 0.1.1.9004 315 | 316 | - Internal changes only. 317 | 318 | 319 | # indiedown 0.1.1.9003 320 | 321 | - Internal changes only. 322 | 323 | 324 | # indiedown 0.1.1.9002 325 | 326 | ## Chore 327 | 328 | - Avoid tests on R \< 4.0 on GHA. 329 | 330 | 331 | # indiedown 0.1.1.9001 332 | 333 | - Internal changes only. 334 | 335 | 336 | # indiedown 0.1.1.9000 337 | 338 | ## simplify 339 | 340 | - Substitute <<>> in the template. 341 | 342 | ## Uncategorized 343 | 344 | - Harmonize yaml formatting. 345 | 346 | - Revert changes to matrix section. 347 | 348 | - Merge pull request #27 from cynkra/f-backto-cleveref. 349 | 350 | Back to cleveref 351 | 352 | - Merge pull request #26 from cynkra/b-cleveref. 353 | 354 | Comment out cleveref since not used actively 355 | 356 | - Merge pull request #25 from cynkra/tidyout. 357 | 358 | removed tidyverse library call 359 | 360 | - Merge pull request #17 from cynkra/b-workflow-2. 361 | 362 | 363 | 364 | - Merge pull request #15 from cynkra/b-workflows. 365 | 366 | 367 | 368 | - Reduce parallelism. 369 | 370 | - Also check dev on cran-* branches. 371 | 372 | - Update hash key for dev. 373 | 374 | - Remove R 3.3. 375 | 376 | - Merge pull request #14 from cynkra/dr_down. 377 | 378 | Dr down 379 | 380 | - Merge pull request #1 from christophsax/more-pandoc. 381 | 382 | More pandoc 383 | 384 | 385 | # indiedown 0.1.1 386 | 387 | - CRAN feedback: better DESCRIPTION, extended documentation, examples. 388 | 389 | # indiedown 0.1.0 390 | 391 | Initial release. 392 | 393 | - `create_indiedown_package()` creates a ready-to-customize R Markdown template package. 394 | - `use_indiedown_gfonts()` adds Google fonts to a template package. 395 | -------------------------------------------------------------------------------- /R/create_indiedown_package.R: -------------------------------------------------------------------------------- 1 | #' Skeleton for a Customized R Markdown Template 2 | #' 3 | #' Set up the packages structure for an indiedown-based customized R Markdown 4 | #' template. See `vignette("indiedown")` for a more detailed usage example. 5 | #' 6 | #' @param path Package path 7 | #' @param overwrite Should existing assets be overwritten? 8 | #' 9 | #' @return This function is called for its side effects and returns `NULL`, invisibly. 10 | #' 11 | #' @export 12 | #' @examples 13 | #' path <- file.path(tempdir(), "mydown") 14 | #' 15 | #' # set up empty R Package 'mydown' 16 | #' create_indiedown_package(path, overwrite = TRUE) 17 | create_indiedown_package <- function(path, overwrite = FALSE) { 18 | if (fs::dir_exists(path)) { 19 | if (!overwrite) { 20 | stop("Path exists, use `overwrite = TRUE` to overwrite.", call. = FALSE) 21 | } 22 | } else if (fs::file_exists(path)) { 23 | stop("Path exists and is a file or a link, remove before proceeding.", call. = FALSE) 24 | } 25 | 26 | path_skeleton <- system.file("mypackage", package = "indiedown") 27 | 28 | fs::dir_copy( 29 | path_skeleton, 30 | path, 31 | overwrite = TRUE 32 | ) 33 | 34 | pkg_name <- basename(path) 35 | 36 | withr::local_dir(path) 37 | 38 | files <- c( 39 | "inst/rmarkdown/templates/report/skeleton/skeleton.Rmd", 40 | "R/indiedown_pdf_document.R", 41 | "man/mypackage.Rd", 42 | "DESCRIPTION", 43 | "NAMESPACE" 44 | ) 45 | 46 | gsub_in_file( 47 | pattern = "mypackage", 48 | replacement = pkg_name, 49 | file = files 50 | ) 51 | 52 | file.rename( 53 | "man/mypackage.Rd", 54 | file.path("man", paste0(pkg_name, ".Rd")) 55 | ) 56 | 57 | # Can't use .here as part of the template due to the leading dot 58 | writeLines(character(), ".here") 59 | 60 | cli_alert_success("indiedown skeleton set up at {.file {path}}") 61 | cli_alert_info('See {.code vignette("indiedown")} for how to customize the {pkg_name} package') 62 | 63 | if (!rlang::is_installed("rmarkdown")) { 64 | cli_alert_warning("Install the {.pkg rmarkdown} package to create documents from this template.") 65 | } 66 | 67 | invisible() 68 | } 69 | -------------------------------------------------------------------------------- /R/dr_down.R: -------------------------------------------------------------------------------- 1 | #' Hardware report (experimental) 2 | #' 3 | #' `dr_down` analyzes the R markdown capabilities of the hardware. 4 | #' 5 | #' @export 6 | #' @examples 7 | #' \dontrun{ 8 | #' indiedown::dr_down() 9 | #' } 10 | dr_down <- function() { 11 | 12 | # Check the version 13 | pandoc_v <- as.character(rmarkdown::pandoc_version()) 14 | r_v <- paste(version$major, version$minor, sep = ".") 15 | 16 | if (Sys.getenv("RSTUDIO") == "1" && requireNamespace("rstudioapi", quietly = TRUE)) { 17 | rstudio_v <- as.character(rstudioapi::versionInfo()$version) 18 | } else { 19 | rstudio_v <- NA_character_ 20 | } 21 | 22 | if(requireNamespace("tinytex", quietly = TRUE)){ 23 | tinytex_v <- as.character(packageVersion("tinytex")) 24 | is_tinytex <- tinytex::is_tinytex() 25 | } else{ 26 | tinytex_v <- NA_character_ 27 | is_tinytex <- FALSE 28 | } 29 | 30 | version_info <- c( 31 | r = r_v, 32 | rstudio = rstudio_v, 33 | tinytex = tinytex_v, 34 | pandoc = pandoc_v 35 | ) 36 | 37 | # test different latex engine to compile a basic document 38 | latex_engines <- c( 39 | "lualatex", 40 | "xelatex", 41 | "pdflatex" 42 | ) 43 | 44 | tdir <- tempfile("dr_down") 45 | fs::dir_create(tdir) 46 | a <- fs::file_copy( 47 | fs::path_package("indiedown", "dr_down", "test.Rmd"), 48 | tdir 49 | ) 50 | 51 | try_with_engine <- function(latex_engines) { 52 | tryCatch( 53 | suppressWarnings( 54 | rmarkdown::render( 55 | fs::path(tdir, "test.Rmd"), 56 | rmarkdown::pdf_document(latex_engine = latex_engines), 57 | quiet = TRUE 58 | ) 59 | ), 60 | error = identity 61 | ) 62 | } 63 | 64 | ans <- lapply(latex_engines, try_with_engine) 65 | success <- setNames(!sapply(ans, inherits, "error"), latex_engines) 66 | 67 | # clean up 68 | fs::dir_delete(tdir) 69 | 70 | 71 | cli_h1("System Information") 72 | 73 | cli_alert_info("R-Version: {version_info['r']}") 74 | 75 | cli_alert_info("Operating System: {R.Version()$os}") 76 | 77 | if (is_tinytex) { 78 | cli_alert_success("tinytex Version: {version_info['tinytex']}") 79 | } else { 80 | cli_alert_warning("tinytex not installed") 81 | } 82 | 83 | if (rmarkdown::pandoc_available()) { 84 | cli_alert_success("pandoc Version: {version_info['pandoc']}") 85 | } else { 86 | cli_alert_warning("pandoc not available") 87 | } 88 | 89 | cli_alert_info("indiedown Version: {packageVersion('indiedown')}") 90 | 91 | if (!is.na(version_info['rstudio'])) { 92 | cli_alert_success("RStudio: {version_info['rstudio']}") 93 | } else { 94 | cli_alert_info("Running outside of RStudio") 95 | } 96 | 97 | 98 | cli_h1("Test Runs") 99 | text <- paste("Running", names(success), ifelse(success, "successfully", "unsuccessfully")) 100 | for (i in seq_along(text)) { 101 | if (success[i]) { 102 | cli_alert_success(text[i]) 103 | } else { 104 | cli_alert_danger(text[i]) 105 | } 106 | } 107 | 108 | ans <- list( 109 | version_info = version_info, 110 | success = success, 111 | is_tinytex = is_tinytex 112 | ) 113 | invisible(ans) 114 | } 115 | -------------------------------------------------------------------------------- /R/import.R: -------------------------------------------------------------------------------- 1 | #' @import cli 2 | #' @importFrom stats setNames 3 | #' @importFrom utils installed.packages packageVersion 4 | NULL 5 | -------------------------------------------------------------------------------- /R/use_indiedown_fonts.R: -------------------------------------------------------------------------------- 1 | #' Download and Use Google Fonts 2 | #' 3 | #' Download and use Google fonts, using the *gfonts* package. 4 | #' 5 | #' @inheritParams create_indiedown_package 6 | #' @inheritParams gfonts::download_font 7 | #' @return This function is called for its side effects and returns `NULL`, invisibly. 8 | #' @export 9 | #' @examples 10 | #' \donttest{ 11 | #' path <- file.path(tempdir(), "mydown") 12 | #' create_indiedown_package(path, overwrite = TRUE) 13 | #' # Use Lora, instead of default Roboto 14 | #' use_indiedown_gfonts( 15 | #' path = path, 16 | #' id = "lora", 17 | #' variants = c("regular", "italic", "700", "700italic") 18 | #' ) 19 | #' } 20 | use_indiedown_gfonts <- function(path = ".", 21 | id = "roboto", 22 | variants = c("regular", "300italic", "700", "700italic")) { 23 | path_fonts <- fs::path_norm(fs::path(path, "inst", "indiedown", "fonts")) 24 | 25 | fs::dir_create(path_fonts) 26 | 27 | # FIXME can we avoid downloading the non ttfs? 28 | gfonts::download_font(id = id, output_dir = path_fonts, variants = variants) 29 | file.remove(file.path( 30 | path_fonts, 31 | grep("\\.ttf", list.files(path_fonts), value = TRUE, invert = TRUE) 32 | )) 33 | 34 | font_path <- function(var) { 35 | grep(paste0("-", var, ".ttf$"), list.files(path_fonts), value = TRUE) 36 | } 37 | 38 | file_variants <- vapply(variants, font_path, "") 39 | 40 | default_names <- c("regular", "italic", "bold", "bolditalic") 41 | 42 | fs::file_move( 43 | file.path(path_fonts, file_variants), 44 | file.path(path_fonts, paste0(default_names, ".ttf")) 45 | ) 46 | 47 | cli_alert_success("Added {id} fonts") 48 | 49 | invisible() 50 | } 51 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | gsub_in_file <- function(pattern, replacement, file, fixed = TRUE) { 2 | if (length(file) > 1) { 3 | lapply(file, function(e) gsub_in_file(pattern, replacement, file = e, fixed = fixed)) 4 | return(invisible(TRUE)) 5 | } 6 | 7 | con <- file(file) 8 | on.exit(close(con)) 9 | txt <- readLines(con = con) 10 | txt <- gsub(pattern, replacement, txt, fixed = fixed) 11 | writeLines(txt, con = con) 12 | 13 | invisible(TRUE) 14 | } 15 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: downlit::readme_document 3 | --- 4 | 5 | 6 | # indiedown 7 | 8 | Individual R Markdown PDF Templates. 9 | 10 | 11 | [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental) 12 | [![R build status](https://github.com/cynkra/indiedown/workflows/rcc/badge.svg)](https://github.com/cynkra/indiedown/actions) 13 | [![Coverage status](https://codecov.io/gh/cynkra/indiedown/branch/main/graph/badge.svg)](https://codecov.io/github/cynkra/indiedown?branch=master) 14 | ![CRAN status](https://www.r-pkg.org/badges/version/indiedown) 15 | 16 | 17 | indiedown allows you to generate a customized R Markdown PDF template in a few basic steps. 18 | 19 | Start by installing indiedown: 20 | 21 | ```r 22 | install.packages("indiedown") 23 | ``` 24 | 25 | You can also install the development version from GitHub: 26 | 27 | ```r 28 | # install.packages("devtools") 29 | devtools::install_github("cynkra/indiedown") 30 | ``` 31 | 32 | To create your own customized R Markdown template, start by creating an indiedown template package, called `mydown` in this example. 33 | Navigate to the directory where you want to create the package, then: 34 | 35 | ```r 36 | indiedown::create_indiedown_package("mydown") 37 | ``` 38 | 39 | This creates a package skeleton in the new `mydown` directory in the current working directory. 40 | You can build *mydown*, using "Build and Reload" in the RStudio or via the command line, as follows: 41 | 42 | ```r 43 | devtools::install("mydown") 44 | ``` 45 | 46 | With *mydown* built and installed, our new template is available in RStudio (after a restart). 47 | 48 | - Read more at `vignette("indiedown")`. 49 | - See `vignette("walkthrough")` for a step by step guide to customization. 50 | - See `vignette("customize")` for advanced customization. 51 | 52 | --- 53 | 54 | ## Code of Conduct 55 | 56 | Please note that the indiedown project is released with a [Contributor Code of Conduct](https://cynkra.github.io/indiedown/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms. 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # indiedown 4 | 5 | Individual R Markdown PDF Templates. 6 | 7 | 8 | 9 | [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental) [![R build status](https://github.com/cynkra/indiedown/workflows/rcc/badge.svg)](https://github.com/cynkra/indiedown/actions) [![Coverage status](https://codecov.io/gh/cynkra/indiedown/branch/main/graph/badge.svg)](https://codecov.io/github/cynkra/indiedown?branch=master) ![CRAN status](https://www.r-pkg.org/badges/version/indiedown) 10 | 11 | 12 | 13 | indiedown allows you to generate a customized R Markdown PDF template in a few basic steps. 14 | 15 | Start by installing indiedown: 16 | 17 |
18 | install.packages("indiedown")
19 | 20 | You can also install the development version from GitHub: 21 | 22 |
23 | # install.packages("devtools")
24 | devtools::install_github("cynkra/indiedown")
25 | 26 | To create your own customized R Markdown template, start by creating an indiedown template package, called `mydown` in this example. Navigate to the directory where you want to create the package, then: 27 | 28 |
29 | indiedown::create_indiedown_package("mydown")
30 | 31 | This creates a package skeleton in the new `mydown` directory in the current working directory. You can build *mydown*, using “Build and Reload” in the RStudio or via the command line, as follows: 32 | 33 |
34 | devtools::install("mydown")
35 | 36 | With *mydown* built and installed, our new template is available in RStudio (after a restart). 37 | 38 | - Read more at `vignette("indiedown")`. 39 | - See `vignette("walkthrough")` for a step by step guide to customization. 40 | - See `vignette("customize")` for advanced customization. 41 | 42 | ------------------------------------------------------------------------ 43 | 44 | ## Code of Conduct 45 | 46 | Please note that the indiedown project is released with a [Contributor Code of Conduct](https://cynkra.github.io/indiedown/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms. 47 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://indiedown.cynkra.com/ 2 | 3 | template: 4 | package: cynkratemplate 5 | bootstrap: 5 6 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## CRAN comments after intitial submission 2 | 3 | > Please rather write "R Markdown" instead of "RMarkdown" in title and description. 4 | 5 | Done. 6 | 7 | > Please add more details about the package functionality and implemented methods in your Description text. 8 | 9 | Done. 10 | 11 | > Please add \value to .Rd files regarding exported methods and explain the functions results in the documentation. Please write about the structure of the output (class) and also what the output means. (If a function does not return a value, please document that too, e.g. \value{No return value, called for side effects} or similar) 12 | Missing Rd-tags: 13 | cd_format_date.Rd: \value 14 | cd_knit_chunk_opts.Rd: \value 15 | cd_page_title.Rd: \value 16 | default.Rd: \value 17 | indiedown_glue.Rd: \value 18 | indiedown_path.Rd: \value 19 | mypackage.Rd: \value 20 | 21 | Done. 22 | 23 | > \dontrun{} should only be used if the example really cannot be executed (e.g. because of missing additional software, missing API keys, ...) by the user. That's why wrapping examples in \dontrun{} adds the comment ('# Not run:') as a warning for the user. 24 | Is this necessary? 25 | 26 | > If not, please unwrap the examples if they are executable in < 5 sec, or replace \dontrun{} with \donttest{}. 27 | 28 | \dontrun{} -> \donttest{} 29 | 30 | > If possible: Please add some more small executable examples in your Rd-files to illustrate the use of the exported function but also enable automatic testing. 31 | 32 | Done. Added examples to all functions 33 | -------------------------------------------------------------------------------- /indiedown.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | ProjectId: 53e0e1b2-d87d-400d-9abd-7cb90a2fb129 3 | 4 | RestoreWorkspace: No 5 | SaveWorkspace: No 6 | AlwaysSaveHistory: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: Yes 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: Sweave 14 | LaTeX: pdfLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | Lato 2 | Lifecycle 3 | Pandoc 4 | RStudio 5 | Roboto 6 | Walkthrough 7 | YAML 8 | customizations 9 | gfonts 10 | kableExtra 11 | lato 12 | mydown 13 | pandoc 14 | tex 15 | unescaped 16 | unsplash 17 | -------------------------------------------------------------------------------- /inst/dr_down/test.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Test" 3 | output: pdf_document 4 | --- 5 | 6 | # Title 7 | 8 | Text 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /inst/dr_down/test.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/indiedown/690052729dc3a83bf08afca91c307c868311eeb7/inst/dr_down/test.pdf -------------------------------------------------------------------------------- /inst/mypackage/.gitignore: -------------------------------------------------------------------------------- 1 | /check 2 | -------------------------------------------------------------------------------- /inst/mypackage/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: mypackage 2 | Title: Acme Inc. Corporate Design R Markdown Template 3 | Version: 0.1 4 | Authors@R: 5 | person(given = "First", 6 | family = "Last", 7 | role = c("aut", "cre"), 8 | email = "first.last@example.com") 9 | Description: Provides an R Markdown template conforming to the 10 | corporate design guidelines at Acme Inc. 11 | License: file LICENSE 12 | Depends: 13 | R (>= 4.0.0) 14 | Imports: 15 | glue, 16 | knitr (>= 1.12), 17 | rmarkdown, 18 | utils, 19 | withr, 20 | yaml 21 | Encoding: UTF-8 22 | LazyData: true 23 | Roxygen: list(markdown = TRUE) 24 | RoxygenNote: 7.1.1 25 | -------------------------------------------------------------------------------- /inst/mypackage/LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/indiedown/690052729dc3a83bf08afca91c307c868311eeb7/inst/mypackage/LICENSE -------------------------------------------------------------------------------- /inst/mypackage/NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(cd_format_date) 4 | export(cd_knit_chunk_opts) 5 | export(cd_page_title) 6 | export(default) 7 | export(indiedown_glue) 8 | export(indiedown_path) 9 | export(indiedown_path_tex) 10 | export(mypackage) 11 | -------------------------------------------------------------------------------- /inst/mypackage/R/cd_format_date.R: -------------------------------------------------------------------------------- 1 | #' Corporate Design: Format Date 2 | #' 3 | #' Format date in local language format 4 | #' 5 | #' @param date Current date 6 | #' @param lang Language, either `"de-DE"`, `"de-CH"` or `"en_US"` 7 | #' @return Character string, date in local format 8 | #' @examples 9 | #' cd_format_date("2012-01-01", "de-DE") 10 | #' cd_format_date("2012-01-01", "en-US") 11 | #' @export 12 | cd_format_date <- function(date, lang = default(rmarkdown::metadata$lang, "en-US")) { 13 | date <- as.Date(date) 14 | if (lang %in% c("german", "de-DE", "de-CH")) { 15 | withr::with_locale(c("LC_TIME" = "de_DE"), format(date, "%e. %B %Y")) 16 | } else { 17 | withr::with_locale(c("LC_TIME" = "en_US"), format(date, "%B %e, %Y")) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /inst/mypackage/R/cd_knit_chunk_opts.R: -------------------------------------------------------------------------------- 1 | #' Corporate Design: knitr Chunk Options 2 | #' 3 | #' Set knitr chunk options in accordance with corporate design. 4 | #' 5 | #' @param twocolumn Use two-column mode? This will affect `fig.width` and `fig.height`. 6 | #' @param fig.width Figure width. If `NULL`, automatically chosen based on `twocolumn`. 7 | #' @param fig.height Figure height If `NULL`, automatically chosen based on `twocolumn`. 8 | #' @param fig.pos Floating modifier for figures 9 | #' @param message Should messages be shown? 10 | #' @param echo Should echo be shown? 11 | #' @return This function is called for its side effects and returns `NULL`, invisibly. 12 | #' @examples 13 | #' cd_knit_chunk_opts() 14 | #' 15 | #' @export 16 | cd_knit_chunk_opts <- function(twocolumn = default(rmarkdown::metadata$twocolumn, FALSE), 17 | fig.width = NULL, 18 | fig.height = NULL, 19 | fig.pos = "h", 20 | message = FALSE, 21 | echo = FALSE) { 22 | 23 | if (twocolumn) { 24 | fig.width <- default(fig.width, 4) 25 | fig.height <- default(fig.height, 3.5) 26 | } else { 27 | if (isTRUE(rmarkdown::metadata$wide)) { 28 | fig.width <- default(fig.width, 8.5) 29 | fig.height <- default(fig.height, 3.38) 30 | } else { 31 | fig.width <- default(fig.width, 7.25) 32 | fig.height <- default(fig.height, 3.5) 33 | } 34 | } 35 | knitr::opts_chunk$set( 36 | fig.width = fig.width, 37 | fig.height = fig.height, 38 | fig.pos = fig.pos, 39 | message = message, 40 | echo = echo 41 | ) 42 | 43 | options(knitr.table.format = "latex") 44 | invisible() 45 | } 46 | -------------------------------------------------------------------------------- /inst/mypackage/R/cd_page_title.R: -------------------------------------------------------------------------------- 1 | #' Corporate Design: Title Page 2 | #' 3 | #' Example function to generate a title page 4 | #' 5 | #' @param title Document title 6 | #' @param subtitle Document subtitle 7 | #' @param date Document creation date 8 | #' @return Object of class `"knit_asis"` (so that knitr will treat it as is). LaTeX code for title page. 9 | #' @export 10 | #' @examples 11 | #' cd_page_title( 12 | #' title = "My Title", 13 | #' subtitle = "My Subtitle" 14 | #' ) 15 | cd_page_title <- function(title = default(rmarkdown::metadata$title, "Title"), 16 | subtitle = default(rmarkdown::metadata$subtitle, "Subtitle"), 17 | date = default(rmarkdown::metadata$date, cd_format_date(Sys.Date()))) { 18 | 19 | logo_path <- indiedown_path_tex("res/logo.png") 20 | 21 | indiedown_glue( 22 | # R >=4, raw strings allow to write LaTeX without escaping \ etc 23 | r"( 24 | \vspace*{-1cm} 25 | \begin{center} 26 | \makebox[\textwidth]{\includegraphics[width=0.1\paperwidth]{<>}} 27 | \end{center} 28 | 29 | 30 | \vspace*{2cm} 31 | 32 | \noindent \Huge <> 33 | 34 | \noindent \huge <<subtitle>> 35 | 36 | \vspace*{2cm} 37 | 38 | 39 | \normalsize 40 | 41 | \noindent <<date>> 42 | 43 | \clearpage 44 | )" 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /inst/mypackage/R/indiedown.R: -------------------------------------------------------------------------------- 1 | # Indiedown functionality, copied from indiedown 2 | # 3 | # do not customize this file! 4 | 5 | indiedown_pdf_document_with_asset <- function(includes = NULL, ...) { 6 | 7 | # file preamble 8 | if (file.exists(file.path(indiedown_path(), "preamble.tex"))) { 9 | 10 | file_preamble <- tempfile(fileext = ".tex") 11 | txt <- readLines(file.path(indiedown_path(), "preamble.tex"), encoding = "UTF-8") 12 | txt <- gsub("<<indiedown_path>>", indiedown_path_tex(), txt, fixed = TRUE) 13 | writeLines(txt, file_preamble) 14 | 15 | if (is.null(includes)) { 16 | includes <- rmarkdown::includes(in_header = file_preamble) 17 | } else { 18 | if ("in_header" %in% names(includes)) { 19 | warning("The use of 'includes, in_header' overwrites preamble.tex from asset and may mess up the layout.") 20 | } else { 21 | includes$in_header <- file_preamble 22 | } 23 | } 24 | } 25 | 26 | ans <- rmarkdown::pdf_document(includes = includes, ...) 27 | 28 | # use pre_processor from asset package or pre_processor_basic, if not present 29 | file_pre_processor <- file.path(indiedown_path(), "pre_processor.R") 30 | if (file.exists(file_pre_processor)) { 31 | env <- environment() 32 | source(file.path(file_pre_processor), env) 33 | stopifnot(exists("pre_processor", envir = env)) 34 | } else { 35 | pre_processor <- pre_processor_basic 36 | } 37 | 38 | ans$pre_processor <- pre_processor 39 | ans 40 | } 41 | 42 | pre_processor_basic <- function(metadata, input_file, runtime, knit_meta, files_dir, output_dir) { 43 | args <- apply_default_yaml(metadata = metadata) 44 | } 45 | 46 | apply_default_yaml <- function(metadata) { 47 | defaults_txt <- readLines(file.path(indiedown_path(), "default.yaml"), encoding = "UTF-8") 48 | 49 | # replace <<indiedown_path>> by package path 50 | defaults_txt <- gsub("<<indiedown_path>>", indiedown_path_tex(), defaults_txt, fixed = TRUE) 51 | 52 | defaults <- yaml::yaml.load(defaults_txt) 53 | 54 | # do not apply asset defaults if variable set in document 55 | defaults_applied <- defaults[setdiff(names(defaults), names(metadata))] 56 | 57 | args <- list_to_pandoc_args(defaults_applied) 58 | 59 | # pandoc ignores 'header-includes' if includes$in_header is specified 60 | # https://github.com/jgm/pandoc/issues/3139 61 | # we can still use `header-includes` by passing it as a command line option to pandoc 62 | if (!is.null(rmarkdown::metadata$`header-includes`)) { 63 | args <- c(args, "--variable", paste0("header-includes:", paste(rmarkdown::metadata$`header-includes`, collapse = "\n"))) 64 | } 65 | 66 | args 67 | } 68 | 69 | # list <- list( 70 | # indent = TRUE, 71 | # `compact-title` = TRUE, 72 | # classoption = c("twocolumn", "landscape") 73 | # ) 74 | # list_to_pandoc_args(list) 75 | list_to_pandoc_args <- function(list) { 76 | is_logical <- vapply(list, is.logical, TRUE) 77 | is_character <- vapply(list, is.character, TRUE) 78 | 79 | # prepare logicals (TRUE FALSE to yes no) 80 | list[is_logical] <- lapply(list[is_logical], function(x) ifelse(x, "yes", "no")) 81 | # prepare character (comma separate multiple values) 82 | list[is_character] <- lapply(list[is_character], paste, collapse = ",") 83 | 84 | vars <- paste0(names(list), ":", unlist(list)) 85 | 86 | zip <- function(x, y) { 87 | x <- rep_len(x, length(y)) 88 | m <- matrix(c(x, y), nrow = 2, byrow = TRUE) 89 | as.vector(m) 90 | } 91 | zip("--variable", vars) 92 | } 93 | 94 | #' Path helpers 95 | #' 96 | #' `indiedown_path()` creates a path to this package's indiedown assets, 97 | #' located at `inst/indiedown`. 98 | #' 99 | #' @param ... Path components, passed on to `system.file()`. 100 | #' @return Character string, path to this package's indiedown assets 101 | #' @examples 102 | #' indiedown_path() 103 | #' @export 104 | indiedown_path <- function(...) { 105 | system.file("indiedown", ..., package = utils::packageName()) 106 | } 107 | 108 | #' Path to indiedown assets, usable in LaTeX 109 | #' 110 | #' `indiedown_path_tex()` creates a path that is usable in LaTeX, 111 | #' with all special characters escaped. 112 | #' 113 | #' @rdname indiedown_path 114 | #' @export 115 | indiedown_path_tex <- function(...) { 116 | sanitize_tex(indiedown_path(...)) 117 | } 118 | 119 | 120 | sanitize_tex <- function(path) { 121 | # on win, tex does not like \\ 122 | path <- gsub("\\", "/", fixed = TRUE, path) 123 | path <- gsub("~", "\\string~", path, fixed = TRUE) 124 | path 125 | } 126 | 127 | #' Glue LaTeX Code for Use in R Markdown 128 | #' 129 | #' Use the function to wrap literal LaTeX code in R. Use a raw string input 130 | #' (`r"()"`) to automatically escape quotes and backslashes, as they are common 131 | #' in LaTeX. Expressions enclosed by `<<` and `>>` will be evaluated as R code. 132 | #' 133 | #' `indiedown_glue()` uses a different default than the underlying 134 | #' `glue::glue()` (`<<` and `>>`), because braces are so common in LaTeX. 135 | #' Doubling the full delimiter (`<<<<` and `>>>>`) escapes it. 136 | #' 137 | #' @param x Character, often as a raw string (`r"()"`) 138 | #' @param .open,.close The opening and closing delimiter. 139 | #' @return Object of class `"knit_asis"` (so that knitr will treat it as is). Usually LaTeX code. 140 | #' @export 141 | #' @examples 142 | #' x <- "something" 143 | #' indiedown_glue(r"(\LaTeX\ code with <<x>> substituted )") 144 | #' 145 | indiedown_glue <- function(x, .open = "<<", .close = ">>") { 146 | ans <- glue::glue(x, .envir = sys.frame(-1), .open = .open, .close = .close) 147 | knitr::asis_output(ans) 148 | } 149 | 150 | 151 | # compatible with older versions. We can use raw strings from 4.0 on. 152 | read_tex <- function(file) { 153 | paste(readLines(indiedown_path("tex", file), encoding = "UTF-8"), collapse = "\n") 154 | } 155 | 156 | #' Set Default Value 157 | #' 158 | #' Set default value of an argument. If `x` is `NULL`, `default` is used. 159 | #' 160 | #' @param x Character string, or `NULL` 161 | #' @param default If `x` is `NULL`, `default` is used. 162 | #' 163 | #' @return Character string 164 | #' @examples 165 | #' default(NULL) 166 | #' default(NULL, "my default") 167 | #' default("a string", "my default") 168 | #' @export 169 | default <- function(x, default = "") { 170 | ans <- if (is.null(x)) default else x 171 | force(ans) 172 | } 173 | -------------------------------------------------------------------------------- /inst/mypackage/R/indiedown_pdf_document.R: -------------------------------------------------------------------------------- 1 | # indiedown: customize options to rmarkdown::pdf_document() here 2 | 3 | #' Customized R Markdown Document 4 | #' 5 | #' `mypackage()` is the main function of the mypackage package. Use as 6 | #' `output: mypackage::mypackage` in the R Markdown YAML header. 7 | #' 8 | #' @param ... Passed on to [rmarkdown::pdf_document()]. 9 | #' @return R Markdown output format to pass to [rmarkdown::render()]. 10 | #' @export 11 | mypackage <- function(...) { 12 | indiedown_pdf_document_with_asset( 13 | highlight = "pygments", 14 | latex_engine = "lualatex", 15 | ... 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /inst/mypackage/inst/indiedown/default.yaml: -------------------------------------------------------------------------------- 1 | # indiedown: customize defaults in YAML header here 2 | 3 | indent: false 4 | compact-title: true 5 | subparagraph: true # needed for titlesec 6 | papersize: a4 7 | documentclass: article 8 | secnumdepth: 2 9 | fontsize: "11pt" 10 | 11 | mainfont: regular.ttf 12 | mainfontoptions: 13 | - Path=<<indiedown_path>>/fonts/ 14 | - BoldFont=bold.ttf 15 | - ItalicFont=italic.ttf 16 | - BoldItalicFont=bolditalic.ttf 17 | - Scale=0.92 18 | 19 | linestretch: 1.05 20 | 21 | geometry: 22 | - top=2cm 23 | - bottom=2cm 24 | - left=2cm 25 | - right=2cm 26 | 27 | -------------------------------------------------------------------------------- /inst/mypackage/inst/indiedown/fonts/bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/indiedown/690052729dc3a83bf08afca91c307c868311eeb7/inst/mypackage/inst/indiedown/fonts/bold.ttf -------------------------------------------------------------------------------- /inst/mypackage/inst/indiedown/fonts/bolditalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/indiedown/690052729dc3a83bf08afca91c307c868311eeb7/inst/mypackage/inst/indiedown/fonts/bolditalic.ttf -------------------------------------------------------------------------------- /inst/mypackage/inst/indiedown/fonts/italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/indiedown/690052729dc3a83bf08afca91c307c868311eeb7/inst/mypackage/inst/indiedown/fonts/italic.ttf -------------------------------------------------------------------------------- /inst/mypackage/inst/indiedown/fonts/regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/indiedown/690052729dc3a83bf08afca91c307c868311eeb7/inst/mypackage/inst/indiedown/fonts/regular.ttf -------------------------------------------------------------------------------- /inst/mypackage/inst/indiedown/pre_processor.R: -------------------------------------------------------------------------------- 1 | # indiedown: customize conditional settings here 2 | 3 | pre_processor <- function(metadata, input_file, runtime, knit_meta, files_dir, output_dir) { 4 | 5 | # apply default.yaml (do not remove) 6 | args <- apply_default_yaml(metadata = metadata) 7 | 8 | # set the margin based on twocolumn and wide 9 | if (is.null(metadata$geometry)) { 10 | if (isTRUE(metadata$twocolumn) || isTRUE(metadata$wide)) { 11 | args <- c(args, "--variable", "geometry:top=1cm,bottom=1.75cm,left=2cm,right=2cm,includehead,includefoot") 12 | } else { 13 | args <- c(args, "--variable", "geometry:top=1cm,bottom=1.75cm,left=2.5cm,right=2.5cm,includehead,includefoot") 14 | } 15 | } 16 | 17 | args 18 | } 19 | -------------------------------------------------------------------------------- /inst/mypackage/inst/indiedown/preamble.tex: -------------------------------------------------------------------------------- 1 | % indiedown: customize additional LaTeX settings here 2 | 3 | % order is important here to avoid conflicts: 4 | % see section 14.1 of cleveref package: https://www.ctan.org/pkg/cleveref 5 | \usepackage{hyperref} 6 | \usepackage{cleveref} 7 | 8 | % word style tabs 9 | \usepackage{tabto} 10 | 11 | % no indentation, space between paragraphs 12 | \usepackage{parskip} 13 | 14 | % provides the H float modifier 15 | \usepackage{float} 16 | 17 | % full width table (with kable) 18 | \usepackage{tabu} 19 | \tabulinesep=1.1mm 20 | 21 | % Support for kableExtra package 22 | \usepackage{booktabs} 23 | \usepackage{longtable} 24 | \usepackage{multirow} 25 | \usepackage{pdflscape} 26 | \usepackage{threeparttable} 27 | \usepackage{threeparttablex} 28 | \usepackage[normalem]{ulem} 29 | \usepackage{makecell} 30 | \usepackage{colortbl} 31 | 32 | % extended implementation of the array and tabular environments 33 | \usepackage{array} 34 | 35 | % embedd graphics 36 | \usepackage{graphicx} 37 | \setkeys{Gin}{width=\linewidth,totalheight=\textheight,keepaspectratio} 38 | 39 | % verbatim with URL-sensitive line breaks 40 | \usepackage{url} 41 | 42 | % units. E.g., \unitfrac{m}{s} 43 | \usepackage{units} 44 | 45 | % cross-references to according to the type of cross-reference 46 | % comment out since not used (no \cref and company are found) and avoid conflicts with hyperref. 47 | % see section 14.1 of cleveref package: https://www.ctan.org/pkg/cleveref 48 | % \usepackage{cleveref} 49 | 50 | % Increase the number of simultaneous LaTeX floats 51 | \usepackage{morefloats} 52 | 53 | % Simple argument parsing 54 | \usepackage{xparse} 55 | 56 | % smaller section headings (also needed for titleformat below) 57 | \usepackage[small]{titlesec} 58 | 59 | % change space between letters 60 | \usepackage{fontspec} 61 | 62 | % for protrusion (even margins) and expansion (fewer/better hyphenations) 63 | \hyphenpenalty=7500 64 | % https://tex.stackexchange.com/a/335088/8057 65 | \pretolerance=5000 66 | \tolerance=9000 67 | % Do not set \lefthyphenmin, \righthyphenmin, or \<lang>hyphenmins 68 | % (https://tex.stackexchange.com/a/235279/8057): 69 | % These values add too much space between words. 70 | 71 | % omit default title page 72 | \renewcommand\maketitle{} 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /inst/mypackage/inst/indiedown/res/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/indiedown/690052729dc3a83bf08afca91c307c868311eeb7/inst/mypackage/inst/indiedown/res/logo.png -------------------------------------------------------------------------------- /inst/mypackage/inst/rmarkdown/templates/report/skeleton/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.pdf 3 | -------------------------------------------------------------------------------- /inst/mypackage/inst/rmarkdown/templates/report/skeleton/skeleton.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title : "Report-Title" 3 | subtitle : "Subtitle" 4 | author : ["My-Name", "Our-Team"] 5 | 6 | documentclass : article # article (default) report 7 | fontsize : 11pt # 10pt 11pt (default) 12pt 8 | numbersections : true 9 | 10 | output : mypackage::mypackage 11 | --- 12 | 13 | 14 | ```{r, include = FALSE} 15 | library(knitr) 16 | library(kableExtra) 17 | library(mypackage) 18 | cd_knit_chunk_opts() 19 | ``` 20 | 21 | ```{r} 22 | cd_page_title() 23 | ``` 24 | 25 | # Customized R Markdown Template 26 | 27 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 28 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 29 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 30 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 31 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 32 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 33 | -------------------------------------------------------------------------------- /inst/mypackage/inst/rmarkdown/templates/report/template.yaml: -------------------------------------------------------------------------------- 1 | name: Customized R Markdown Report 2 | description: > 3 | Customized R Markdown template, generated by indiedown 4 | create_dir: FALSE 5 | -------------------------------------------------------------------------------- /inst/mypackage/man/cd_format_date.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cd_format_date.R 3 | \name{cd_format_date} 4 | \alias{cd_format_date} 5 | \title{Corporate Design: Format Date} 6 | \usage{ 7 | cd_format_date(date, lang = default(rmarkdown::metadata$lang, "en-US")) 8 | } 9 | \arguments{ 10 | \item{date}{Current date} 11 | 12 | \item{lang}{Language, either \code{"de-DE"}, \code{"de-CH"} or \code{"en_US"}} 13 | } 14 | \value{ 15 | Character string, date in local format 16 | } 17 | \description{ 18 | Format date in local language format 19 | } 20 | \examples{ 21 | cd_format_date("2012-01-01", "de-DE") 22 | cd_format_date("2012-01-01", "en-US") 23 | } 24 | -------------------------------------------------------------------------------- /inst/mypackage/man/cd_knit_chunk_opts.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cd_knit_chunk_opts.R 3 | \name{cd_knit_chunk_opts} 4 | \alias{cd_knit_chunk_opts} 5 | \title{Corporate Design: knitr Chunk Options} 6 | \usage{ 7 | cd_knit_chunk_opts( 8 | twocolumn = default(rmarkdown::metadata$twocolumn, FALSE), 9 | fig.width = NULL, 10 | fig.height = NULL, 11 | fig.pos = "h", 12 | message = FALSE, 13 | echo = FALSE 14 | ) 15 | } 16 | \arguments{ 17 | \item{twocolumn}{Use two-column mode? This will affect \code{fig.width} and \code{fig.height}.} 18 | 19 | \item{fig.width}{Figure width. If \code{NULL}, automatically chosen based on \code{twocolumn}.} 20 | 21 | \item{fig.height}{Figure height If \code{NULL}, automatically chosen based on \code{twocolumn}.} 22 | 23 | \item{fig.pos}{Floating modifier for figures} 24 | 25 | \item{message}{Should messages be shown?} 26 | 27 | \item{echo}{Should echo be shown?} 28 | } 29 | \value{ 30 | This function is called for its side effects and returns \code{NULL}, invisibly. 31 | } 32 | \description{ 33 | Set knitr chunk options in accordance with corporate design. 34 | } 35 | \examples{ 36 | cd_knit_chunk_opts() 37 | 38 | } 39 | -------------------------------------------------------------------------------- /inst/mypackage/man/cd_page_title.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cd_page_title.R 3 | \name{cd_page_title} 4 | \alias{cd_page_title} 5 | \title{Corporate Design: Title Page} 6 | \usage{ 7 | cd_page_title( 8 | title = default(rmarkdown::metadata$title, "Title"), 9 | subtitle = default(rmarkdown::metadata$subtitle, "Subtitle"), 10 | date = default(rmarkdown::metadata$date, cd_format_date(Sys.Date())) 11 | ) 12 | } 13 | \arguments{ 14 | \item{title}{Document title} 15 | 16 | \item{subtitle}{Document subtitle} 17 | 18 | \item{date}{Document creation date} 19 | } 20 | \value{ 21 | Object of class \code{"knit_asis"} (so that knitr will treat it as is). LaTeX code for title page. 22 | } 23 | \description{ 24 | Example function to generate a title page 25 | } 26 | \examples{ 27 | cd_page_title( 28 | title = "My Title", 29 | subtitle = "My Subtitle" 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /inst/mypackage/man/default.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/indiedown.R 3 | \name{default} 4 | \alias{default} 5 | \title{Set Default Value} 6 | \usage{ 7 | default(x, default = "") 8 | } 9 | \arguments{ 10 | \item{x}{Character string, or \code{NULL}} 11 | 12 | \item{default}{If \code{x} is \code{NULL}, \code{default} is used.} 13 | } 14 | \value{ 15 | Character string 16 | } 17 | \description{ 18 | Set default value of an argument. If \code{x} is \code{NULL}, \code{default} is used. 19 | } 20 | \examples{ 21 | default(NULL) 22 | default(NULL, "my default") 23 | default("a string", "my default") 24 | } 25 | -------------------------------------------------------------------------------- /inst/mypackage/man/indiedown_glue.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/indiedown.R 3 | \name{indiedown_glue} 4 | \alias{indiedown_glue} 5 | \title{Glue LaTeX Code for Use in R Markdown} 6 | \usage{ 7 | indiedown_glue(x, .open = "<<", .close = ">>") 8 | } 9 | \arguments{ 10 | \item{x}{Character, often as a raw string (\code{r"()"})} 11 | 12 | \item{.open, .close}{The opening and closing delimiter.} 13 | } 14 | \value{ 15 | Object of class \code{"knit_asis"} (so that knitr will treat it as is). Usually LaTeX code. 16 | } 17 | \description{ 18 | Use the function to wrap literal LaTeX code in R. Use a raw string input 19 | (\code{r"()"}) to automatically escape quotes and backslashes, as they are common 20 | in LaTeX. Expressions enclosed by \verb{<<} and \verb{>>} will be evaluated as R code. 21 | } 22 | \details{ 23 | \code{indiedown_glue()} uses a different default than the underlying 24 | \code{glue::glue()} (\verb{<<} and \verb{>>}), because braces are so common in LaTeX. 25 | Doubling the full delimiter (\verb{<<<<} and \verb{>>>>}) escapes it. 26 | } 27 | \examples{ 28 | x <- "something" 29 | indiedown_glue(r"(\LaTeX\ code with <<x>> substituted )") 30 | 31 | } 32 | -------------------------------------------------------------------------------- /inst/mypackage/man/indiedown_path.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/indiedown.R 3 | \name{indiedown_path} 4 | \alias{indiedown_path} 5 | \alias{indiedown_path_tex} 6 | \title{Path helpers} 7 | \usage{ 8 | indiedown_path(...) 9 | 10 | indiedown_path_tex(...) 11 | } 12 | \arguments{ 13 | \item{...}{Path components, passed on to \code{system.file()}.} 14 | } 15 | \value{ 16 | Character string, path to this package's indiedown assets 17 | } 18 | \description{ 19 | \code{indiedown_path()} creates a path to this package's indiedown assets, 20 | located at \code{inst/indiedown}. 21 | 22 | \code{indiedown_path_tex()} creates a path that is usable in LaTeX, 23 | with all special characters escaped. 24 | } 25 | \examples{ 26 | indiedown_path() 27 | } 28 | -------------------------------------------------------------------------------- /inst/mypackage/man/mypackage.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/indiedown_pdf_document.R 3 | \name{mypackage} 4 | \alias{mypackage} 5 | \title{Customized R Markdown Document} 6 | \usage{ 7 | mypackage(...) 8 | } 9 | \arguments{ 10 | \item{...}{Passed on to \code{\link[rmarkdown:pdf_document]{rmarkdown::pdf_document()}}.} 11 | } 12 | \value{ 13 | R Markdown output format to pass to \code{\link[rmarkdown:render]{rmarkdown::render()}}. 14 | } 15 | \description{ 16 | \code{mypackage()} is the main function of the mypackage package. Use as 17 | \code{output: mypackage::mypackage} in the R Markdown YAML header. 18 | } 19 | -------------------------------------------------------------------------------- /man/create_indiedown_package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/create_indiedown_package.R 3 | \name{create_indiedown_package} 4 | \alias{create_indiedown_package} 5 | \title{Skeleton for a Customized R Markdown Template} 6 | \usage{ 7 | create_indiedown_package(path, overwrite = FALSE) 8 | } 9 | \arguments{ 10 | \item{path}{Package path} 11 | 12 | \item{overwrite}{Should existing assets be overwritten?} 13 | } 14 | \value{ 15 | This function is called for its side effects and returns \code{NULL}, invisibly. 16 | } 17 | \description{ 18 | Set up the packages structure for an indiedown-based customized R Markdown 19 | template. See \code{vignette("indiedown")} for a more detailed usage example. 20 | } 21 | \examples{ 22 | path <- file.path(tempdir(), "mydown") 23 | 24 | # set up empty R Package 'mydown' 25 | create_indiedown_package(path, overwrite = TRUE) 26 | } 27 | -------------------------------------------------------------------------------- /man/dr_down.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dr_down.R 3 | \name{dr_down} 4 | \alias{dr_down} 5 | \title{Hardware report (experimental)} 6 | \usage{ 7 | dr_down() 8 | } 9 | \description{ 10 | \code{dr_down} analyzes the R markdown capabilities of the hardware. 11 | } 12 | \examples{ 13 | \dontrun{ 14 | indiedown::dr_down() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /man/use_indiedown_gfonts.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/use_indiedown_fonts.R 3 | \name{use_indiedown_gfonts} 4 | \alias{use_indiedown_gfonts} 5 | \title{Download and Use Google Fonts} 6 | \usage{ 7 | use_indiedown_gfonts( 8 | path = ".", 9 | id = "roboto", 10 | variants = c("regular", "300italic", "700", "700italic") 11 | ) 12 | } 13 | \arguments{ 14 | \item{path}{Package path} 15 | 16 | \item{id}{Id of the font, correspond to column \code{id} from \code{\link[gfonts]{get_all_fonts}}.} 17 | 18 | \item{variants}{Variant(s) to download, default is to includes all available ones.} 19 | } 20 | \value{ 21 | This function is called for its side effects and returns \code{NULL}, invisibly. 22 | } 23 | \description{ 24 | Download and use Google fonts, using the \emph{gfonts} package. 25 | } 26 | \examples{ 27 | \donttest{ 28 | path <- file.path(tempdir(), "mydown") 29 | create_indiedown_package(path, overwrite = TRUE) 30 | # Use Lora, instead of default Roboto 31 | use_indiedown_gfonts( 32 | path = path, 33 | id = "lora", 34 | variants = c("regular", "italic", "700", "700italic") 35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>cynkra/renovate-config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /tests/spelling.R: -------------------------------------------------------------------------------- 1 | if (requireNamespace("spelling", quietly = TRUE)) { 2 | spelling::spell_check_test( 3 | vignettes = TRUE, error = FALSE, 4 | skip_on_cran = TRUE 5 | ) 6 | } 7 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(indiedown) 3 | 4 | test_check("indiedown") 5 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/create_indiedown_package.md: -------------------------------------------------------------------------------- 1 | # create_indiedown_package() snapshot test 2 | 3 | Code 4 | create_indiedown_package("mydown") 5 | Message 6 | v indiedown skeleton set up at 'mydown' 7 | i See `vignette("indiedown")` for how to customize the mydown package 8 | Code 9 | withr::with_collate("C", sort(fs::dir_ls("mydown", recurse = TRUE))) 10 | Output 11 | mydown/DESCRIPTION 12 | mydown/LICENSE 13 | mydown/NAMESPACE 14 | mydown/R 15 | mydown/R/cd_format_date.R 16 | mydown/R/cd_knit_chunk_opts.R 17 | mydown/R/cd_page_title.R 18 | mydown/R/indiedown.R 19 | mydown/R/indiedown_pdf_document.R 20 | mydown/inst 21 | mydown/inst/indiedown 22 | mydown/inst/indiedown/default.yaml 23 | mydown/inst/indiedown/fonts 24 | mydown/inst/indiedown/fonts/bold.ttf 25 | mydown/inst/indiedown/fonts/bolditalic.ttf 26 | mydown/inst/indiedown/fonts/italic.ttf 27 | mydown/inst/indiedown/fonts/regular.ttf 28 | mydown/inst/indiedown/pre_processor.R 29 | mydown/inst/indiedown/preamble.tex 30 | mydown/inst/indiedown/res 31 | mydown/inst/indiedown/res/logo.png 32 | mydown/inst/rmarkdown 33 | mydown/inst/rmarkdown/templates 34 | mydown/inst/rmarkdown/templates/report 35 | mydown/inst/rmarkdown/templates/report/skeleton 36 | mydown/inst/rmarkdown/templates/report/skeleton/skeleton.Rmd 37 | mydown/inst/rmarkdown/templates/report/template.yaml 38 | mydown/man 39 | mydown/man/cd_format_date.Rd 40 | mydown/man/cd_knit_chunk_opts.Rd 41 | mydown/man/cd_page_title.Rd 42 | mydown/man/default.Rd 43 | mydown/man/indiedown_glue.Rd 44 | mydown/man/indiedown_path.Rd 45 | mydown/man/mydown.Rd 46 | Code 47 | unlink("mydown/R/cd_page_title.R") 48 | create_indiedown_package("mydown", overwrite = TRUE) 49 | Message 50 | v indiedown skeleton set up at 'mydown' 51 | i See `vignette("indiedown")` for how to customize the mydown package 52 | 53 | -------------------------------------------------------------------------------- /tests/testthat/test-create_indiedown_package.R: -------------------------------------------------------------------------------- 1 | test_that("create_indiedown_package() works", { 2 | root <- tempfile("indiedown") 3 | dir.create(root) 4 | 5 | withr::local_dir(root) 6 | 7 | expect_message(expect_message(create_indiedown_package("mydown"))) 8 | withr::with_collate("C", sort(fs::dir_ls("mydown", recurse = TRUE))) 9 | 10 | unlink("mydown/R/cd_page_title.R") 11 | expect_message(expect_message(create_indiedown_package("mydown", overwrite = TRUE))) 12 | 13 | expect_true(file.exists("mydown/R/cd_page_title.R")) 14 | }) 15 | 16 | test_that("create_indiedown_package() snapshot test", { 17 | skip_if(identical(Sys.getenv("R_COVR"), "true")) 18 | 19 | root <- tempfile("indiedown") 20 | dir.create(root) 21 | 22 | withr::local_dir(root) 23 | 24 | expect_snapshot({ 25 | create_indiedown_package("mydown") 26 | withr::with_collate("C", sort(fs::dir_ls("mydown", recurse = TRUE))) 27 | 28 | unlink("mydown/R/cd_page_title.R") 29 | create_indiedown_package("mydown", overwrite = TRUE) 30 | }) 31 | 32 | expect_true(file.exists("mydown/R/cd_page_title.R")) 33 | }) 34 | 35 | test_that("create_indiedown_package() fails with meaningful error message", { 36 | root <- tempfile("indiedown") 37 | dir.create(root) 38 | 39 | withr::local_dir(root) 40 | 41 | fs::dir_create("mydown") 42 | fs::file_create("mydown2") 43 | withr::defer(fs::file_delete("mydown2")) 44 | withr::defer(fs::dir_delete("mydown")) 45 | 46 | expect_error(create_indiedown_package("mydown"), "overwrite") 47 | expect_error(create_indiedown_package("mydown2"), "file") 48 | }) 49 | 50 | test_that("create_indiedown_package() fails for links with meaningful error message", { 51 | skip("Ignore for now") 52 | 53 | root <- tempfile("indiedown") 54 | dir.create(root) 55 | 56 | withr::local_dir(root) 57 | 58 | skip_on_os("windows") 59 | 60 | fs::dir_create("mydown") 61 | fs::link_create("mydown", "mydown3") 62 | fs::link_create("mydown-bogus", "mydown4") 63 | withr::defer(fs::link_delete("mydown-bogus")) 64 | withr::defer(fs::link_delete("mydown3")) 65 | withr::defer(fs::dir_delete("mydown")) 66 | 67 | expect_error(create_indiedown_package("mydown3"), "overwrite") 68 | expect_error(create_indiedown_package("mydown4"), "link") 69 | }) 70 | -------------------------------------------------------------------------------- /tests/testthat/test-dr_down.R: -------------------------------------------------------------------------------- 1 | test_that("dr_down speaks about System Information", { 2 | skip_on_cran() 3 | expect_message(indiedown::dr_down(), "System Information") 4 | }) 5 | -------------------------------------------------------------------------------- /tests/testthat/test-install.R: -------------------------------------------------------------------------------- 1 | test_that("create_indiedown_package() can be installed", { 2 | root <- tempfile("indiedown") 3 | dir.create(root) 4 | 5 | withr::local_dir(root) 6 | 7 | suppressMessages(create_indiedown_package("mydown")) 8 | 9 | withr::local_temp_libpaths() 10 | 11 | # R CMD check is checked on GitHub Actions 12 | expect_error(callr::rcmd("INSTALL", "mydown", fail_on_status = TRUE), NA) 13 | }) 14 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/customize.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Customize Individual R Markdown Templates" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Customize Individual R Markdown Templates} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>" 14 | ) 15 | ``` 16 | 17 | ```{r setup} 18 | library(indiedown) 19 | ``` 20 | 21 | ## Assets 22 | 23 | **Location:** `inst/indiedown` (folder) 24 | 25 | Template assets are stored in a folder `inst/indiedown`. 26 | This folder contains all the necessary information to customize your markdown template. 27 | It can contain basic pandoc settings, additional LaTeX settings and dynamic pandoc settings. 28 | 29 | ### Basic Settings 30 | 31 | **Location:** `default.yaml` (required) 32 | 33 | `default.yaml` is the only required element of an indiedown template. 34 | It specifies the default pandoc options used by your template. 35 | To experiment with pandoc options, simply add them to the YAML header of a standard R Markdown document. 36 | 37 | Most relevant LaTeX options can be set as pandoc options. For example, pandoc options allow you to set a customized font or a user defined geometry. If possible always prefer Pandoc options instead of LaTeX (covered below), as this will minimize unwanted interactions with other options. 38 | 39 | For a detailed description of all pandoc options, see the 'Variables for LaTeX' section in the [pandoc Manual](https://pandoc.org/MANUAL.html#). 40 | 41 | `default.yaml`, as well as `preamble.tex` supports substitution of the variable `<<indiedown_path>`, which is explained below. 42 | 43 | 44 | ### Additional LaTeX Settings 45 | 46 | **Location:** `preamble.tex` (optional, advanced) 47 | 48 | Sometimes, pandoc options are just not enough. While pandoc allows you to directly apply single LaTeX commands in the YAML header (using `headers-include`), `preamble.tex` offers a more convenient way to include LaTeX commands at the beginning of your document. 49 | 50 | To include commands, simply edit `preamble.tex`. 51 | Common options set in `preamble.tex` are the modification of section headings (e.g., `\usepackage[small]{titlesec}`), the provision of the `H` float modifier 52 | (`\usepackage{float}`), or the packages used by [kableExtra](https://CRAN.R-project.org/package=kableExtra). 53 | 54 | For most customizations, it is recommended to turn off the default LaTeX title page, using 55 | `\renewcommand\maketitle{}`. Creating your own title page via LaTeX commands in the R Markdown script or by a customized R function is a much easier and more flexible way to create a customized title page. 56 | 57 | 58 | ### Dynamic Settings 59 | 60 | **Location:** `pre_processor.R` (optional, advanced) 61 | 62 | In a few cases, pandoc options should be set dynamically. 63 | E.g., the geometry of a document should depend on whether it uses two columns or just one. 64 | `pre_processor.R` allows you to dynamically modify pandoc options, using plain R. 65 | See the example script for how to use it. 66 | 67 | 68 | ### Variable Substitution 69 | 70 | `default.yaml`, as well as `preamble.tex` supports substitution of the variable `<<indiedown_path>`. 71 | Whenever you mention `<<indiedown_path>` in one of your files, it will be substituted by the path to the `indiedown` folder. 72 | 73 | 74 | ### Including Additional Assets 75 | 76 | Most customized styles will include some additional assets, such as a logo or a special font. Assets can be put into arbitrarily named folders. To access the assets from `default.yaml` or `preamble.tex`, simply use `<<indiedown_path>/res` and the path will be correctly resolved by indiedown. 77 | 78 | ## LaTeX Generators 79 | 80 | Indiedown packages use R functions to generate LaTeX code for customization. 81 | 82 | For most customizations, it is recommended to turn off the default LaTeX title page, and create your own title page via LaTeX commands. The basic repertory are the LaTeX commands `\large`, `\Large`, `\LARGE`, `\huge`, etc, along with `vspace{1ex}`, but all LaTeX trickery can be applied. The LaTeX helper functions should be put in the `R` folder of your package. 83 | 84 | 85 | ## Templates 86 | 87 | R Markdown Templates demonstrate the use of a customized style. R Markdown requires you to put them in `inst/rmarkdown/templates/mytemplate`. 88 | RStudio will recognize them and show them when you click 'New R Markdown Document' and 'from Template'. 89 | You can also use `usethis::use_rmarkdown_template()` to add a new template. 90 | -------------------------------------------------------------------------------- /vignettes/fig/rstudio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/indiedown/690052729dc3a83bf08afca91c307c868311eeb7/vignettes/fig/rstudio.png -------------------------------------------------------------------------------- /vignettes/fig/status-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/indiedown/690052729dc3a83bf08afca91c307c868311eeb7/vignettes/fig/status-1.png -------------------------------------------------------------------------------- /vignettes/fig/status-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/indiedown/690052729dc3a83bf08afca91c307c868311eeb7/vignettes/fig/status-2.png -------------------------------------------------------------------------------- /vignettes/indiedown.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction to Indiedown" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Introduction to Indiedown} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>" 14 | ) 15 | ``` 16 | 17 | indiedown makes it easy to generate a customized R Markdown PDF template that follow the principles of your corporate design. 18 | 19 | Start by installing indiedown: 20 | 21 | ```{r eval = FALSE} 22 | install.packages("indiedown") 23 | ``` 24 | 25 | You can also install the development version from GitHub: 26 | 27 | ```{r eval = FALSE} 28 | # install.packages("remotes") 29 | remotes::install_github("cynkra/indiedown") 30 | ``` 31 | 32 | ## Create an customized R Markdown template 33 | 34 | ```{r eval = FALSE} 35 | # install.packages("devtools") 36 | devtools::install_github("cynkra/indiedown") 37 | ``` 38 | 39 | To create your own customized R Markdown template, start by creating an indiedown template package, called `mydown` in this example. 40 | Navigate to the directory where you want to create the package, then: 41 | 42 | ```{r eval = FALSE} 43 | indiedown::create_indiedown_package("mydown") 44 | ``` 45 | 46 | This creates a package skeleton in the new `mydown` directory in the current working directory. 47 | You can build *mydown*, using "Build and Reload" in the RStudio or via the command line, as follows: 48 | 49 | ```{r eval = FALSE} 50 | devtools::install("mydown") 51 | ``` 52 | 53 | With *mydown* built and installed, our new template is available in RStudio (after a restart): 54 | 55 | ![](fig/rstudio.png){ width=50% } 56 | 57 | 58 | 59 | ## Customization points 60 | 61 | indiedown does not modify the default `.tex` template of Pandoc. Instead, all modifications are applied on top of it. 62 | This should make it compliant with future releases of Pandoc and R Markdown. 63 | 64 | There are three possible customization points to customize an indiedown skeleton: 65 | 66 | - Set defaults (such as fonts or geometry) in the YAML header at `inst/indiedown/default.yaml` 67 | 68 | - Tweak LaTeX settings at `inst/indiedown/preamble.tex` 69 | 70 | - Apply dynamic adjustments in `pre_processor.R` (advanced) 71 | 72 | See the `vignette("customize")` for details. 73 | 74 | 75 | ## Corporate Design Elements 76 | 77 | Indiedown packages use R functions to generate LaTeX code for customization. 78 | 79 | For most customizations, it is recommended to turn off the default LaTeX title page and create your own title page via LaTeX commands. The basic repertory are the LaTeX commands `\large`, `\Large`, `\LARGE`, `\huge`, etc, along with `vspace{1ex}`, but all LaTeX trickery can be applied. The LaTeX helper functions should be put in the `R` folder of your package. 80 | 81 | The function `cd_page_title()` creates an example title page that can be adjusted to your needs. Note that the example uses raw strings, which are only available in R >= 4. They make it easy to write LaTeX code directly in R. See details in `?indiedown_glue` for methods that work well with R < 4. 82 | 83 | 84 | ## Principles of Indiedown 85 | 86 | ### Extend Pandoc .tex, do not replace 87 | 88 | indiedown does not modify the default `.tex` template of Pandoc. Instead, all modifications are applied on top of it. 89 | This should make it compliant with future releases of Pandoc and R Markdown. 90 | 91 | ### Use R to customize 92 | 93 | Keep all corporate design elements as R code. This keeps the LaTeX code simple and allows flexible extensions. 94 | -------------------------------------------------------------------------------- /vignettes/walkthrough.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Walkthrough: Build your own R Markdown Template" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Walkthrough: Build your own R Markdown Template} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>" 14 | ) 15 | ``` 16 | 17 | `vignette("intro")` has shown how to set up a package skeleton with indiedown. This article teaches you how to apply customizations to produce a template that fits your needs. 18 | 19 | 20 | ## Customization points 21 | 22 | indiedown does not modify the default `.tex` template of Pandoc. Instead, all modifications are applied on top of it. 23 | This should make it compliant with future releases of Pandoc and R Markdown. 24 | 25 | There are three possible customization points to customize an indiedown skeleton: 26 | 27 | - Set defaults (such as fonts or geometry) in the YAML header at `inst/indiedown/default.yaml` 28 | 29 | - Tweak LaTeX settings at `inst/indiedown/preamble.tex` 30 | 31 | - Apply dynamic adjustments in `pre_processor.R` (advanced) 32 | 33 | For this tutorial, we will focus on the first two points only. 34 | 35 | ## `default.yaml` 36 | 37 | Let's have a look at `inst/indedown/default.yaml`: 38 | 39 | ```yml 40 | # indiedown: customize defaults in YAML header here 41 | 42 | indent: false 43 | compact-title: true 44 | subparagraph: true # needed for titlesec 45 | papersize: a4 46 | documentclass: article 47 | secnumdepth: 2 48 | fontsize: "11pt" 49 | 50 | mainfont: regular.ttf 51 | mainfontoptions: 52 | - Path=<<indiedown_path>>/fonts/ 53 | - BoldFont=bold.ttf 54 | - ItalicFont=italic.ttf 55 | - BoldItalicFont=bolditalic.ttf 56 | - Scale=0.92 57 | 58 | linestretch: 1.05 59 | 60 | geometry: 61 | - top=2cm 62 | - bottom=2cm 63 | - left=2cm 64 | - right=2cm 65 | ``` 66 | 67 | These are simply defaults for the custom template. So rather than adding these options to the YAML header of the `.Rmd` each time, they can be stored centrally. 68 | You can overwrite these values in the `.Rmd` file. 69 | 70 | It is recommended to set as many customizations as possible as Pandoc options, and fall back to LaTeX if this is not possible. 71 | A full list of options is given in the [LaTeX section of the Pandoc manual](https://pandoc.org/MANUAL.html#variables-for-latex). 72 | 73 | In our case, we want to use slightly wider page margins, so we change the geometry to: 74 | 75 | ```yml 76 | geometry: 77 | - top=2.5cm 78 | - bottom=2.5cm 79 | - left=3cm 80 | - right=3cm 81 | ``` 82 | 83 | ## Changing Fonts 84 | 85 | Changing the font to fit your corporate style is usually the first step in customization. 86 | 87 | Note that `<<indiedown_path>>` will be automatically substituted with the actual location of `inst/indiedown`. That is why we can include *assets* that are picked up by the template. The default template includes the free Roboto font, which is located at `inst/indiedown`. 88 | 89 | We could replace that with a proprietary font to match the corporate design. 90 | Instead we use a helper function to get one of [Google's free fonts](https://fonts.google.com). 91 | 92 | use_indiedown_gfonts(id = "lato") 93 | 94 | The function relies on the [gfonts](https://CRAN.R-project.org/package=gfonts) package to download and install the *Lato* font. 95 | If you don't know the ID of a font, use `gfonts::get_all_fonts()` to get a list of all available fonts. 96 | 97 | Let's preview what we have by now: 98 | 99 | ![](fig/status-1.png){ width=70% } 100 | 101 | 102 | ## `preamble.tex` 103 | 104 | Let's turn to the second customization point, `preamble.tex`. This contains LaTeX code that is run at the beginning. Add everything here that cannot be changed in the `default.yaml`. We want to use slightly bigger headers, and change the `titlesec` line to: 105 | 106 | ```tex 107 | \usepackage[medium]{titlesec} 108 | ``` 109 | 110 | As for `default.yaml`, it supports substitution of `<<indiedown_path>>`, so you could link to images here as well. Generally, you do not want do too many thing here. 111 | Note that the default LaTeX title page is disabled in the default template, for good reason: 112 | 113 | ```tex 114 | \renewcommand\maketitle{} 115 | ``` 116 | 117 | Instead, we will build our own title page in R now. 118 | 119 | ## A customized title page. 120 | 121 | One of the core principles of indiedown is to keep corporate design elements as R code. That way, elements can be arranged flexibly and adjusted to particular needs. 122 | 123 | As a final step, we build a customized title page by modifying the function definition of `R/cd_page_title`. The function writes LaTeX code that will be added to the `.Rmd` when `cd_page_title()` is called. 124 | 125 | To make writing of LaTeX code in R easy, we rely on the raw string feature that was added in R 4.0.0. It allows you to write pure LaTeX code in R, without having to escape every `\` by a second `\\`. 126 | 127 | If you have to use a version of R that is older, the `read_tex()` function offers an alternative way to write unescaped LaTeX code. See `?read_tex`. 128 | 129 | The `indiedown_glue()` helper function uses the [glue](https://CRAN.R-project.org/package=glue) package to substitute variables in the string. That way, variables such as `title` or `subtitle` will be substituted in the LaTeX code. 130 | 131 | With `indiedown_path_tex()`, you can also refer to the path of `inst/indiedown`, and use assets from the package. In the example, we use this to include a logo. Now, we don't want to use a logo but rather a photograph of a beautiful parrot. We download the image from [unsplash.com](https://unsplash.com/photos/gGC63oug3iY) in medium quality and store it in `res/mikhail-vasilyev-gGC63oug3iY-unsplash.jpg`. Then, we change the function definition as follows: 132 | 133 | ```r 134 | cd_page_title <- function(title = default(rmarkdown::metadata$title, "Title"), 135 | subtitle = default(rmarkdown::metadata$subtitle, "Subtitle"), 136 | date = default(rmarkdown::metadata$date, cd_format_date(Sys.Date()))) { 137 | 138 | img_path <- indiedown_path_tex("res/mikhail-vasilyev-gGC63oug3iY-unsplash.jpg") 139 | 140 | indiedown_glue( 141 | # R >=4, raw strings allow to write LaTeX without escaping \ etc 142 | r"( 143 | 144 | \vspace*{-3cm} 145 | 146 | \begin{center} 147 | \makebox[\textwidth]{\includegraphics[width=\paperwidth]{<<img_path>>}} 148 | \end{center} 149 | 150 | \vspace*{2cm} 151 | 152 | \noindent \Huge <<title>> 153 | 154 | \noindent \huge <<subtitle>> 155 | 156 | \vspace*{2cm} 157 | 158 | \normalsize 159 | 160 | \noindent <<date>> 161 | 162 | \clearpage 163 | )" 164 | ) 165 | } 166 | ``` 167 | 168 | Of course, we could also pass an image path as an argument, so we can choose a different title page in each report. 169 | 170 | Building the package again, and running the template `.Rmd`, we get: 171 | 172 | ![](fig/status-2.png){ width=30% } 173 | 174 | ## Where to go from here 175 | 176 | indiedown provides a framework to easily generate customized R Markdown templates. Templates can be customized by the full power of Pandoc and LaTeX through various points of customization. 177 | 178 | This walk-through introduced two of them. 179 | For more details and a description of the third one, see `vignette("customize")`. 180 | 181 | 182 | 183 | 184 | 185 | 186 | --------------------------------------------------------------------------------