├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ ├── R │ └── conditions.R │ ├── pkgcheck.yaml │ ├── pkgdown.yaml │ ├── test-coverage.yaml │ ├── test-octolog.yaml │ └── update-citation-cff.yaml ├── .gitignore ├── .lintr ├── .pre-commit-config.yaml ├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── commands.R ├── fs.R ├── messages.R ├── octolog-package.R └── util.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── codecov.yml ├── codemeta.json ├── inst ├── CITATION └── WORDLIST ├── man ├── enable_github_colors.Rd ├── encode_string.Rd ├── figures │ ├── annotations.png │ ├── error.png │ ├── masking.png │ └── summary.png ├── octo_abort.Rd ├── octo_add_path.Rd ├── octo_echo_on.Rd ├── octo_mask_value.Rd ├── octo_set_envvar.Rd ├── octo_set_output.Rd ├── octo_start_group.Rd ├── octo_stop_commands.Rd ├── octocat.Rd ├── octolog-package.Rd └── on_github.Rd ├── tests ├── testthat.R └── testthat │ ├── _snaps │ ├── commands.md │ ├── commands │ │ ├── gh_env1 │ │ ├── gh_env2 │ │ └── gh_path1 │ ├── messages.md │ └── util.md │ ├── test-commands.R │ ├── test-messages.R │ └── test-util.R └── vignettes ├── .gitignore └── octolog.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^LICENSE\.md$ 2 | ^README\.Rmd$ 3 | ^\.lintr$ 4 | ^\.github$ 5 | ^_pkgdown\.yml$ 6 | ^docs$ 7 | ^pkgdown$ 8 | ^codecov\.yml$ 9 | ^CODE_OF_CONDUCT\.md$ 10 | ^CONTRIBUTING\.md$ 11 | ^codemeta\.json$ 12 | ^\.pre-commit-config\.yaml$ 13 | ^inst/action$ 14 | ^doc$ 15 | ^Meta$ 16 | ^CITATION\.cff$ 17 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/master/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.head_ref }} 11 | cancel-in-progress: true 12 | 13 | name: R-CMD-check 14 | 15 | jobs: 16 | R-CMD-check: 17 | runs-on: ${{ matrix.config.os }} 18 | 19 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | config: 25 | - {os: macOS-latest, r: 'release'} 26 | - {os: windows-latest, r: 'release'} 27 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 28 | - {os: ubuntu-latest, r: 'release'} 29 | - {os: ubuntu-latest, r: 'oldrel-1'} 30 | 31 | env: 32 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 33 | R_KEEP_PKG_SOURCE: yes 34 | 35 | steps: 36 | - uses: actions/checkout@v2 37 | 38 | - uses: r-lib/actions/setup-pandoc@v2 39 | 40 | - uses: r-lib/actions/setup-r@v2 41 | with: 42 | r-version: ${{ matrix.config.r }} 43 | http-user-agent: ${{ matrix.config.http-user-agent }} 44 | use-public-rspm: true 45 | 46 | - uses: r-lib/actions/setup-r-dependencies@v2 47 | with: 48 | extra-packages: any::rcmdcheck 49 | 50 | - uses: r-lib/actions/check-r-package@v2 51 | -------------------------------------------------------------------------------- /.github/workflows/R/conditions.R: -------------------------------------------------------------------------------- 1 | octo_start_group("Conditions") 2 | 3 | octo_debug("Tracking the error") 4 | octo_inform(c("A notice", i = "And some information")) 5 | octo_warn("A warning") 6 | octo_warn(c("A warning", v = "Without a trace"), trace = NULL) 7 | octo_abort(c("An error that does not kill R", i = "And how to fix it."), .fail_fast = FALSE) 8 | 9 | octo_end_group() -------------------------------------------------------------------------------- /.github/workflows/pkgcheck.yaml: -------------------------------------------------------------------------------- 1 | name: pkgcheck 2 | 3 | # This will cancel running jobs once a new run is triggered 4 | concurrency: 5 | group: ${{ github.workflow }}-${{ github.head_ref }} 6 | cancel-in-progress: true 7 | 8 | on: 9 | # Manually trigger the Action under Actions/pkgcheck 10 | workflow_dispatch: 11 | # Run on every push to main 12 | push: 13 | branches: 14 | - main 15 | 16 | jobs: 17 | check: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: ropensci-review-tools/pkgcheck-action@main 21 | with: 22 | summary-only: false 23 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/master/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | release: 7 | types: [published] 8 | workflow_dispatch: 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.head_ref }} 12 | cancel-in-progress: true 13 | 14 | name: pkgdown 15 | 16 | jobs: 17 | pkgdown: 18 | runs-on: ubuntu-latest 19 | env: 20 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 21 | steps: 22 | - uses: actions/checkout@v2 23 | 24 | - uses: r-lib/actions/setup-pandoc@v2 25 | 26 | - uses: r-lib/actions/setup-r@v2 27 | with: 28 | use-public-rspm: true 29 | 30 | - uses: r-lib/actions/setup-r-dependencies@v2 31 | with: 32 | extra-packages: | 33 | any::pkgdown 34 | local::. 35 | - name: Deploy package 36 | run: | 37 | git config --local user.name "$GITHUB_ACTOR" 38 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 39 | Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)' 40 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/master/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | 8 | name: test-coverage 9 | 10 | jobs: 11 | test-coverage: 12 | runs-on: ubuntu-latest 13 | env: 14 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - uses: r-lib/actions/setup-r@v2 20 | with: 21 | use-public-rspm: true 22 | 23 | - uses: r-lib/actions/setup-r-dependencies@v2 24 | with: 25 | extra-packages: any::covr, any::xml2 26 | 27 | - name: Test coverage 28 | run: | 29 | cov <- covr::package_coverage() 30 | covr::to_cobertura(cov, "cov.xml") 31 | shell: Rscript {0} 32 | 33 | - name: Coveralls GitHub Action 34 | uses: coverallsapp/github-action@v2 35 | with: 36 | format: 'cobertura' 37 | file: 'cov.xml' 38 | -------------------------------------------------------------------------------- /.github/workflows/test-octolog.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/master/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.head_ref }} 11 | cancel-in-progress: true 12 | 13 | name: Octolog example workflow 14 | 15 | jobs: 16 | test: 17 | runs-on: ubuntu-latest 18 | env: 19 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - uses: r-lib/actions/setup-r@v2 24 | with: 25 | use-public-rspm: true 26 | 27 | - uses: r-lib/actions/setup-r-dependencies@v2 28 | with: 29 | extra-packages: local::. 30 | 31 | - name: Test octolog 32 | id: step1 33 | run: | 34 | library(octolog) 35 | library(magrittr) 36 | options(keep.source = TRUE) 37 | enable_github_colors() 38 | 39 | source(".github/workflows/R/conditions.R") 40 | 41 | 42 | octo_inform("Setting envvars for the next step") 43 | octo_set_envvar(c("A multiline", "string"), "MULTI_LINE") 44 | octo_set_envvar("A multiline\nstring", "MULTI_LINE2") 45 | 46 | octo_echo_on() 47 | octo_inform("Masking some secret stuff!") 48 | Sys.setenv("SECRET_NUMBER" = "007") 49 | octo_mask_envvar("SECRET_NUMBER") 50 | octo_warn("`octo_mask_envvar` will only take effect in the NEXT step!") 51 | print(Sys.getenv("SECRET_NUMBER")) 52 | octo_mask_value("secret_gh_token") 53 | octo_warn("`octo_mask_value` will take effect immediately!") 54 | print("I accidentally logged my: secret_gh_token") 55 | octo_echo_off() 56 | 57 | str <- encode_string(readLines("README.md"), join = TRUE) 58 | octo_set_output(str, "desc") 59 | 60 | t <- octo_stop_commands() 61 | octo_inform("This will not be parsed") 62 | octo_start_commands(t) 63 | octo_inform("Now all commands are parsed again!") 64 | 65 | octo_warn("The added dir will only be on the path in the next step.") 66 | octo_add_path("bin_dir/", check = FALSE) 67 | system("echo $PATH") 68 | shell: Rscript {0} 69 | - name: Next step 70 | run: | 71 | library(octolog) 72 | 73 | octo_warn("The added dir is now available!") 74 | system("echo $PATH") 75 | print("I accidentally logged my: secret_gh_token") 76 | print("secret_gh_token") 77 | octo_inform("Now in the next step the envvar mask has taken effect:") 78 | print(Sys.getenv("SECRET_NUMBER")) 79 | system("echo $SECRET_NUMBER") 80 | system("echo 'Oh no:secret_gh_token'") 81 | 82 | octo_inform("The envvar set in the last step can be used now:") 83 | print(Sys.getenv("MULTI_LINE")) 84 | print(Sys.getenv("MULTI_LINE2")) 85 | octo_inform("We can use outputs from previous steps in other steps using the step id.") 86 | octo_inform("Multiline strings will be automatically converted from their escaped form.") 87 | 88 | cat('${{steps.step1.outputs.desc}}') 89 | shell: Rscript {0} 90 | 91 | -------------------------------------------------------------------------------- /.github/workflows/update-citation-cff.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/master/examples 2 | # The action runs when: 3 | # - A new release is published 4 | # - The DESCRIPTION or inst/CITATION are modified 5 | # - Can be run manually 6 | # For customizing the triggers, visit https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows 7 | on: 8 | release: 9 | types: [published] 10 | push: 11 | branches: [master, main] 12 | paths: 13 | - DESCRIPTION 14 | - inst/CITATION 15 | workflow_dispatch: 16 | 17 | name: Update Meta 18 | 19 | jobs: 20 | update-meta: 21 | runs-on: ubuntu-latest 22 | env: 23 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 24 | steps: 25 | - uses: actions/checkout@v3 26 | - uses: r-lib/actions/setup-r@v2 27 | - uses: r-lib/actions/setup-r-dependencies@v2 28 | with: 29 | use-public-rspm: true 30 | extra-packages: | 31 | any::codemetar 32 | any::cffr 33 | any::V8 34 | 35 | - name: Update CITATION.cff 36 | shell: Rscript {0} 37 | run: | 38 | cffr::cff_write() 39 | codemetar::write_codemeta() 40 | 41 | - name: Commit results 42 | run: | 43 | git config --local user.name "$GITHUB_ACTOR" 44 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 45 | git add CITATION.cff 46 | git add codemeta.json 47 | git commit -m 'Update CITATION.cff' || echo "No changes to commit" 48 | git push origin || echo "No changes to commit" 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | docs 2 | inst/doc 3 | /doc/ 4 | /Meta/ 5 | -------------------------------------------------------------------------------- /.lintr: -------------------------------------------------------------------------------- 1 | linters: with_defaults(object_usage_linter = NULL) 2 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # All available hooks: https://pre-commit.com/hooks.html 2 | # R specific hooks: https://github.com/lorenzwalthert/precommit 3 | repos: 4 | - repo: https://github.com/lorenzwalthert/precommit 5 | rev: v0.2.2.9009 6 | hooks: 7 | - id: style-files 8 | args: [--style_pkg=styler, --style_fun=tidyverse_style] 9 | - id: roxygenize 10 | # roxygen requires loading pkg -> add dependencies from DESCRIPTION 11 | additional_dependencies: 12 | - cli 13 | - fs 14 | - glue 15 | - magrittr 16 | - rlang 17 | - utils 18 | - withr 19 | # codemeta must be above use-tidy-description when both are used 20 | - id: codemeta-description-updated 21 | - id: use-tidy-description 22 | # - id: spell-check 23 | # exclude: > 24 | # (?x)^( 25 | # .*\.[rR]| 26 | # .*\.feather| 27 | # .*\.jpeg| 28 | # .*\.pdf| 29 | # .*\.png| 30 | # .*\.py| 31 | # .*\.RData| 32 | # .*\.rds| 33 | # .*\.Rds| 34 | # .*\.Rproj| 35 | # .*\.sh| 36 | # (.*/|)\.gitignore| 37 | # (.*/|)\.gitlab-ci\.yml| 38 | # (.*/|)\.lintr| 39 | # (.*/|)\.pre-commit-.*| 40 | # (.*/|)\.Rbuildignore| 41 | # (.*/|)\.Renviron| 42 | # (.*/|)\.Rprofile| 43 | # (.*/|)\.travis\.yml| 44 | # (.*/|)appveyor\.yml| 45 | # (.*/|)NAMESPACE| 46 | # (.*/|)renv/settings\.dcf| 47 | # (.*/|)renv\.lock| 48 | # (.*/|)WORDLIST| 49 | # \.github/workflows/.*| 50 | # data/.*| 51 | # )$ 52 | #- id: lintr 53 | - id: readme-rmd-rendered 54 | - id: parsable-R 55 | - id: no-browser-statement 56 | - id: deps-in-desc 57 | - repo: https://github.com/pre-commit/pre-commit-hooks 58 | rev: v4.1.0 59 | hooks: 60 | - id: check-added-large-files 61 | args: ['--maxkb=200'] 62 | - id: end-of-file-fixer 63 | exclude: '\.Rd|_snaps/' 64 | - repo: local 65 | hooks: 66 | - id: forbid-to-commit 67 | name: Don't commit common R artifacts 68 | entry: Cannot commit .Rhistory, .RData, .Rds or .rds. 69 | language: fail 70 | files: '\.Rhistory|\.RData|\.Rds|\.rds$' 71 | # `exclude: ` to allow committing specific files 72 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------- 2 | # CITATION file created with {cffr} R package, v0.2.2 3 | # See also: https://docs.ropensci.org/cffr/ 4 | # ----------------------------------------------------------- 5 | 6 | cff-version: 1.2.0 7 | message: 'To cite package "octolog" in publications use:' 8 | type: software 9 | license: MIT 10 | title: 'octolog: Better ''GitHub Actions'' Logging' 11 | version: 0.1.1.9000 12 | doi: 10.5281/zenodo.5948954 13 | abstract: Provides an API for 'GitHub' workflow commands and 14 | makes it possible to signal conditions from within R that will create annotations 15 | when used within 'GitHub Actions' but raise 16 | normal R conditions when used interactively. 17 | authors: 18 | - family-names: Wujciak-Jens 19 | given-names: Jacob 20 | email: jacob@wujciak.de 21 | orcid: https://orcid.org/0000-0002-7281-3989 22 | preferred-citation: 23 | type: manual 24 | title: 'octolog: Better ''GitHub Actions'' Logging' 25 | authors: 26 | - family-names: Wujciak-Jens 27 | given-names: Jacob 28 | email: jacob@wujciak.de 29 | orcid: https://orcid.org/0000-0002-7281-3989 30 | notes: https://github.com/assignUser/octolog, https://assignuser.github.io/octolog/ 31 | year: '2022' 32 | url: https://doi.org/10.5281/zenodo.5948954 33 | doi: 10.5281/zenodo.5948954 34 | repository-code: https://github.com/assignUser/octolog 35 | url: https://jacob.wujciak.de/octolog/ 36 | contact: 37 | - family-names: Wujciak-Jens 38 | given-names: Jacob 39 | email: jacob@wujciak.de 40 | orcid: https://orcid.org/0000-0002-7281-3989 41 | keywords: 42 | - actions 43 | - ci 44 | - r 45 | - rstats 46 | - workflows 47 | references: 48 | - type: software 49 | title: cli 50 | abstract: 'cli: Helpers for Developing Command Line Interfaces' 51 | notes: Imports 52 | authors: 53 | - family-names: Csárdi 54 | given-names: Gábor 55 | email: csardi.gabor@gmail.com 56 | year: '2022' 57 | url: https://CRAN.R-project.org/package=cli 58 | version: '>= 3.0.0' 59 | - type: software 60 | title: glue 61 | abstract: 'glue: Interpreted String Literals' 62 | notes: Imports 63 | authors: 64 | - family-names: Hester 65 | given-names: Jim 66 | orcid: https://orcid.org/0000-0002-2739-7082 67 | - family-names: Bryan 68 | given-names: Jennifer 69 | email: jenny@rstudio.com 70 | orcid: https://orcid.org/0000-0002-6983-2759 71 | year: '2022' 72 | url: https://CRAN.R-project.org/package=glue 73 | - type: software 74 | title: rlang 75 | abstract: 'rlang: Functions for Base Types and Core R and ''Tidyverse'' Features' 76 | notes: Imports 77 | authors: 78 | - family-names: Henry 79 | given-names: Lionel 80 | email: lionel@rstudio.com 81 | - family-names: Wickham 82 | given-names: Hadley 83 | email: hadley@rstudio.com 84 | year: '2022' 85 | url: https://CRAN.R-project.org/package=rlang 86 | version: '>= 0.4.10' 87 | - type: software 88 | title: utils 89 | abstract: 'R: A Language and Environment for Statistical Computing' 90 | notes: Imports 91 | authors: 92 | - name: R Core Team 93 | location: 94 | name: Vienna, Austria 95 | year: '2022' 96 | url: https://www.R-project.org/ 97 | institution: 98 | name: R Foundation for Statistical Computing 99 | - type: software 100 | title: withr 101 | abstract: 'withr: Run Code ''With'' Temporarily Modified Global State' 102 | notes: Imports 103 | authors: 104 | - family-names: Hester 105 | given-names: Jim 106 | - family-names: Henry 107 | given-names: Lionel 108 | email: lionel@rstudio.com 109 | - family-names: Müller 110 | given-names: Kirill 111 | email: krlmlr+r@mailbox.org 112 | - family-names: Ushey 113 | given-names: Kevin 114 | email: kevinushey@gmail.com 115 | - family-names: Wickham 116 | given-names: Hadley 117 | email: hadley@rstudio.com 118 | - family-names: Chang 119 | given-names: Winston 120 | year: '2022' 121 | url: https://CRAN.R-project.org/package=withr 122 | - type: software 123 | title: knitr 124 | abstract: 'knitr: A General-Purpose Package for Dynamic Report Generation in R' 125 | notes: Suggests 126 | authors: 127 | - family-names: Xie 128 | given-names: Yihui 129 | email: xie@yihui.name 130 | orcid: https://orcid.org/0000-0003-0645-5666 131 | year: '2022' 132 | url: https://CRAN.R-project.org/package=knitr 133 | - type: software 134 | title: covr 135 | abstract: 'covr: Test Coverage for Packages' 136 | notes: Suggests 137 | authors: 138 | - family-names: Hester 139 | given-names: Jim 140 | email: james.f.hester@gmail.com 141 | year: '2022' 142 | url: https://CRAN.R-project.org/package=covr 143 | - type: software 144 | title: mockery 145 | abstract: 'mockery: Mocking Library for R' 146 | notes: Suggests 147 | authors: 148 | - family-names: Finkelstein 149 | given-names: Noam 150 | - family-names: Bartnik 151 | given-names: Lukasz 152 | - family-names: Hester 153 | given-names: Jim 154 | - family-names: Wickham 155 | given-names: Hadley 156 | email: hadley@rstudio.com 157 | year: '2022' 158 | url: https://CRAN.R-project.org/package=mockery 159 | version: '>= 0.4.2.9000' 160 | - type: software 161 | title: openssl 162 | abstract: 'openssl: Toolkit for Encryption, Signatures and Certificates Based on 163 | OpenSSL' 164 | notes: Suggests 165 | authors: 166 | - family-names: Ooms 167 | given-names: Jeroen 168 | email: jeroen@berkeley.edu 169 | orcid: https://orcid.org/0000-0002-4035-0289 170 | year: '2022' 171 | url: https://CRAN.R-project.org/package=openssl 172 | - type: software 173 | title: testthat 174 | abstract: 'testthat: Unit Testing for R' 175 | notes: Suggests 176 | authors: 177 | - family-names: Wickham 178 | given-names: Hadley 179 | email: hadley@rstudio.com 180 | year: '2022' 181 | url: https://CRAN.R-project.org/package=testthat 182 | version: '>= 3.0.0' 183 | - type: software 184 | title: rmarkdown 185 | abstract: 'rmarkdown: Dynamic Documents for R' 186 | notes: Suggests 187 | authors: 188 | - family-names: Allaire 189 | given-names: JJ 190 | email: jj@rstudio.com 191 | - family-names: Xie 192 | given-names: Yihui 193 | email: xie@yihui.name 194 | orcid: https://orcid.org/0000-0003-0645-5666 195 | - family-names: McPherson 196 | given-names: Jonathan 197 | email: jonathan@rstudio.com 198 | - family-names: Luraschi 199 | given-names: Javier 200 | email: javier@rstudio.com 201 | - family-names: Ushey 202 | given-names: Kevin 203 | email: kevin@rstudio.com 204 | - family-names: Atkins 205 | given-names: Aron 206 | email: aron@rstudio.com 207 | - family-names: Wickham 208 | given-names: Hadley 209 | email: hadley@rstudio.com 210 | - family-names: Cheng 211 | given-names: Joe 212 | email: joe@rstudio.com 213 | - family-names: Chang 214 | given-names: Winston 215 | email: winston@rstudio.com 216 | - family-names: Iannone 217 | given-names: Richard 218 | email: rich@rstudio.com 219 | orcid: https://orcid.org/0000-0003-3925-190X 220 | year: '2022' 221 | url: https://CRAN.R-project.org/package=rmarkdown 222 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards 42 | of acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies 54 | when an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail 56 | address, posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at jacob@wujciak.de. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, 118 | available at . 119 | 120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 121 | enforcement ladder](https://github.com/mozilla/diversity). 122 | 123 | [homepage]: https://www.contributor-covenant.org 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | . Translations are available at . 127 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to octolog 2 | 3 | ## Opening issues 4 | 5 | The easiest way to note any behavioural curiosities or to request any new 6 | features is by opening a [github 7 | issue](https://github.com/assignUser/octolog/issues). 8 | 9 | 10 | ## Development guidelines 11 | 12 | If you'd like to contribute changes to `octolog`, we use [the GitHub 13 | flow](https://docs.github.com/en/get-started/quickstart/github-flow) for proposing, 14 | submitting, reviewing, and accepting changes. If you haven't done this before, 15 | there's a nice [overview of git](https://r-pkgs.org/git.html), as well 16 | as [best practices for submitting pull requests](http://r-pkgs.org/git.html#pr-make) 17 | in the R packages book by Hadley Wickham and Jenny Bryan. 18 | 19 | `octolog` uses the [the commonly used tidyverse style 20 | guide](https://style.tidyverse.org/syntax.html#spacing). Tools like [`styler`](https://styler.r-lib.org/) and [`lintr`](https://github.com/r-lib/lintr) can make it easier to adhere to this style. 21 | 22 | All(75%+) new code should be covered by unit test using [`testthat`](https://testthat.r-lib.org/index.html) 3rd edition. 23 | 24 | ## Code of Conduct 25 | 26 | We want to encourage a warm, welcoming, and safe environment for contributing to 27 | this project. See the [code of 28 | conduct](https://github.com/assignUser/octolog/blob/main/CODE_OF_CONDUCT.md) for 29 | more information. 30 | 31 | ## Credits 32 | This guide is too a large extent copied from the contribution guide of [`pkgcheck`](https://github.com/ropensci-review-tools/pkgcheck/blob/main/CONTRIBUTING.md). -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: octolog 2 | Title: Better 'GitHub Actions' Logging 3 | Version: 0.1.1.9000 4 | Authors@R: 5 | person("Jacob", "Wujciak-Jens", , "jacob@wujciak.de", role = c("aut", "cre"), 6 | comment = c(ORCID = "0000-0002-7281-3989")) 7 | Description: Provides an API for 'GitHub' workflow 8 | commands and makes it possible to signal conditions from within R that 9 | will create annotations when used within 'GitHub Actions' 10 | but raise normal R conditions 11 | when used interactively. 12 | License: MIT + file LICENSE 13 | URL: https://github.com/assignUser/octolog, 14 | https://jacob.wujciak.de/octolog/ 15 | BugReports: https://github.com/assignUser/octolog/issues 16 | Imports: 17 | cli (>= 3.0.0), 18 | glue, 19 | rlang (>= 0.4.10), 20 | utils, 21 | withr 22 | Suggests: 23 | knitr, 24 | covr, 25 | mockery (>= 0.4.2.9000), 26 | openssl, 27 | testthat (>= 3.0.0), 28 | rmarkdown 29 | Config/testthat/edition: 3 30 | Config/testthat/parallel: true 31 | Encoding: UTF-8 32 | Roxygen: list(markdown = TRUE) 33 | RoxygenNote: 7.2.0 34 | VignetteBuilder: knitr 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2022 2 | COPYRIGHT HOLDER: octolog authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2022 octolog authors 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(disable_github_colors) 4 | export(enable_github_colors) 5 | export(encode_string) 6 | export(octo_abort) 7 | export(octo_add_path) 8 | export(octo_debug) 9 | export(octo_echo_off) 10 | export(octo_echo_on) 11 | export(octo_end_group) 12 | export(octo_inform) 13 | export(octo_mask_envvar) 14 | export(octo_mask_value) 15 | export(octo_set_envvar) 16 | export(octo_set_output) 17 | export(octo_start_commands) 18 | export(octo_start_group) 19 | export(octo_stop_commands) 20 | export(octo_warn) 21 | export(octocat) 22 | export(on_github) 23 | importFrom(glue,glue) 24 | importFrom(rlang,"%||%") 25 | importFrom(rlang,`%|%`) 26 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # octolog (development version) 2 | 3 | * octolog is now compatible with rlang >= 0.4.10 4 | * openssl is now optional as it is only used in `octo_stop_commands()`. 5 | * An unsafe fallback is used to generate the token outside of GitHub Actions. 6 | 7 | # octolog 0.1.1 8 | 9 | * add CITATION 10 | 11 | # octolog 0.1.0 12 | 13 | * This is the initial release! 14 | -------------------------------------------------------------------------------- /R/commands.R: -------------------------------------------------------------------------------- 1 | #' Grouping log lines 2 | #' 3 | #' These functions make it possible to group lines in the GitHub Actions log. 4 | #' Groups can not be nested at this point, see this [issue](https://github.com/actions/runner/issues/802). 5 | #' @param name Name of the group, single line. 6 | #' @examples 7 | #' Sys.setenv(GITHUB_ACTIONS = "TRUE") 8 | #' octo_start_group("Print stuff") 9 | #' print("Log other output") 10 | #' octo_end_group() 11 | #' @export 12 | #' @seealso [GitHub Docs](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#grouping-log-lines) 13 | octo_start_group <- function(name) { 14 | if (length(name) != 1) { 15 | octo_abort("Group {.arg name} must be length 1!") 16 | } 17 | 18 | octocat(glue("::group::{name}")) 19 | } 20 | 21 | #' @rdname octo_start_group 22 | #' @export 23 | octo_end_group <- function() { 24 | octocat("::endgroup::") 25 | } 26 | 27 | #' Masking a value or envvar in the GHA log. 28 | #' 29 | #' This will mask either a `value` or an envvar and prevent them (or their 30 | #' content) from showing up in the GitHub Actions log. 31 | #' \cr ***ATTENTION***: Currently the masking of envvar values will only take 32 | #' effect in the ***NEXT*** step of the workflow. Values that are masked 33 | #' directly are masked immediately. This is not very clear in the GitHub Docs 34 | #' but very important. 35 | #' @details The masking is not restricted to R output, rather it will work for 36 | #' any logged output. For a practical demonstration please see the 37 | #' [{octolog} example workflow](https://github.com/assignUser/octolog/actions/workflows/test-octolog.yaml) 38 | #' 39 | #' Additionally some values and envvars will be masked automatically by github, 40 | #' though this behavior is poorly documented. It looks like anything with 41 | #' "TOKEN" will be masked. Related Issues 42 | #' [here](https://github.com/actions/runner/issues/643#issuecomment-823537871) 43 | #' and 44 | #' [here](https://github.com/actions/runner/issues/475#issuecomment-742271143). 45 | #' @param value A single value to mask, coercible to string. 46 | #' @param name Name of the envvar to mask. 47 | #' @seealso [GitHub Docs](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#masking-a-value-in-log) 48 | #' @examples 49 | #' octo_mask_value("secret_token123") 50 | #' # The mask takes effect in the NEXT step 51 | #' print("Current token: secret_token123") 52 | #' # Will log as 53 | #' # "Current token:***" 54 | #' 55 | #' Sys.setenv("SECRET_TOKEN" = "007") 56 | #' octo_mask_envvar("SECRET_TOKEN") 57 | #' # The mask takes effect in the NEXT step 58 | #' print(Sys.getenv("SECRET_TOKEN")) 59 | #' # Will log as 60 | #' # "***" 61 | #' @export 62 | octo_mask_value <- function(value) { 63 | if (length(value) != 1) { 64 | octo_abort(c("You can only mask one value at a time.")) 65 | } 66 | 67 | octocat(glue("::add-mask::{value}")) 68 | } 69 | 70 | #' @rdname octo_mask_value 71 | #' @export 72 | octo_mask_envvar <- function(name) { 73 | if (length(name) != 1) { 74 | octo_abort(c("You can only mask one envvar at a time.")) 75 | } 76 | if (is.na(Sys.getenv(name, NA_character_))) { 77 | octo_abort("The envvar {.envvar {name}} does not exists!") 78 | } 79 | 80 | octocat(glue("::add-mask::${name}")) 81 | } 82 | 83 | #' Set an output parameter 84 | #' 85 | #' Set an actions output parameter. These can be accessed in later steps. 86 | #' @param name A character vector length 1. 87 | #' @param value A single line string (or coercible to character). Use 88 | #' [encode_string()] to encode a numeric 89 | #' or multiline string. 90 | #' @seealso [GitHub Docs](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter) 91 | #' @examples 92 | #' Sys.setenv(GITHUB_ACTION = "true") 93 | #' string <- "A multi-line \n String." 94 | #' value <- encode_string(string) 95 | #' octo_set_output("important-value", value) 96 | #' @export 97 | octo_set_output <- function(value, name) { 98 | if (length(name) != 1) { 99 | octo_abort(c("The output {.arg name} must be length 1.")) 100 | } 101 | if (length(value) != 1) { 102 | octo_abort(c("The output {.arg value} must be length 1.")) 103 | } 104 | 105 | octocat(glue("::set-output name={name}::{value}")) 106 | } 107 | 108 | #' Stop workflow commands 109 | #' 110 | #' This will stop github from processing any workflow commands until 111 | #' [octo_start_commands()] is called with the correct `token`. This can be used 112 | #' if untrusted output (e.g. issue titles, bodies or commit messages) needs to 113 | #' be logged this can be used to stop this output from running possibly 114 | #' malicious workflow commands. Requires the suggested package 115 | #' [openssl::openssl] to be installed, when used within GitHub Actions. 116 | #' @param token A unique token used to restart workflow command parsing. 117 | #' @return The `token` needed to reactivate the workflow command parsing. When 118 | #' not on GitHub Actions returns a `token` not generated by `openssl` to 119 | #' avoid the dependency. 120 | #' 121 | #' @examples 122 | #' Sys.setenv(GITHUB_ACTIONS = "true") 123 | #' tk <- octo_stop_commands() 124 | #' # Commands will not be parsed by GitHub Actions 125 | #' octo_start_commands(tk) 126 | #' @seealso The [example workflow](https://github.com/assignUser/octolog/actions/workflows/test-octolog.yaml) 127 | #' and the [GitHub Blog](https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/) 128 | #' @export 129 | octo_stop_commands <- function() { 130 | if (on_github() && !rlang::is_installed("openssl")) { 131 | octo_abort( 132 | paste0( 133 | "The package {.pkg openssl} must be installed to use", 134 | " this function safely!" 135 | ) 136 | ) 137 | } 138 | 139 | if (on_github()) { 140 | token <- paste0(openssl::rand_bytes(12), collapse = "") 141 | } else { 142 | token <- basename(tempfile("")) 143 | } 144 | octocat(glue("::stop-commands::{token}")) 145 | 146 | token 147 | } 148 | 149 | #' @rdname octo_stop_commands 150 | #' @export 151 | octo_start_commands <- function(token) { 152 | octocat(glue("::{token}::")) 153 | } 154 | 155 | #' Echo workflow commands 156 | #' 157 | #' Enable or disable echoing of workflow commands in the log. This can be useful 158 | #' for debugging. Some commands are always echoed and will not be effected by 159 | #' these functions, this includes [octo_debug()] (if debugging is turned on), 160 | #' [octo_warn()] and [octo_abort()]. 161 | #' @export 162 | #' @examples 163 | #' Sys.setenv(GITHUB_ACTIONS = "true") 164 | #' octo_echo_on() 165 | #' # workflow commands will be printed in their unparsed state in addition to 166 | #' # their normal effects 167 | #' octo_echo_off() 168 | #' @seealso [GitHub Docs](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#echoing-command-outputs) 169 | octo_echo_on <- function() { 170 | octocat("::echo::on") 171 | } 172 | 173 | #' @rdname octo_echo_on 174 | #' @export 175 | octo_echo_off <- function() { 176 | octocat("::echo::off") 177 | } 178 | 179 | 180 | #' Set environment variables 181 | #' 182 | #' This will set an environment variable in a way that makes it available to the 183 | #' following steps, compared to using [Sys.setenv()], which would only make an 184 | #' envvar available in the current step. 185 | #' @param value Value of the envvar, coercible to string. Can be a multiline 186 | #' string or character vector of `length > 1`, each element will be 187 | #' interpreted as one line. 188 | #' @param name Name of the envvar. 189 | #' @param set Should the envvar also be set in this step? 190 | #' @param delim Delimiter used for multiline strings. No need to change this 191 | #' unless your string contains 'EOF'. 192 | #' @return `name` invisibly. 193 | #' @examples 194 | #' \dontrun{ 195 | #' val <- c("Some content", "that spans", "multiple lines") 196 | #' octo_set_envvar(val, "multi") 197 | #' octo_set_envvar(2342, "pid") 198 | #' } 199 | #' @export 200 | #' @seealso [octo_mask_envvar()]and the 201 | #' [GitHub docs](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable) 202 | octo_set_envvar <- function(value, name, set = TRUE, delim = "EOF") { 203 | if (length(name) != 1) { 204 | octo_abort("{.arg name} must be length 1.") 205 | } 206 | 207 | if (set) { 208 | rlang::exec( 209 | Sys.setenv, "{name}" := as.character( # nolint 210 | glue("{ paste0(value, collapse ='\n') }") 211 | ) 212 | ) 213 | } 214 | 215 | if (on_github()) { 216 | head <- glue("{name}<<{delim}") 217 | write(c(head, value, delim), Sys.getenv("GITHUB_ENV"), append = TRUE) 218 | } 219 | 220 | invisible(name) 221 | } 222 | utils::globalVariables(":=", "octolog") 223 | 224 | #' Add a system path 225 | #' 226 | #' Prepends a directory so the runners `PATH` envvar in a way that make it 227 | #' available in the following steps of the action. The `PATH` will not update 228 | #' during this step. 229 | #' 230 | #' @param dir A directory. If relative will turned absolute using 231 | #' [base::getwd()]. 232 | #' @param check Should be checked that `dir` is an existing dir. 233 | #' @return `dir` invisibly. 234 | #' @seealso The [{octolog} example workflow](https://github.com/assignUser/octolog/actions/workflows/test-octolog.yaml) 235 | #' and the [GitHub Docs](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path). 236 | #' @examples 237 | #' \dontrun{ 238 | #' octo_add_path("/.local/bin") 239 | #' } 240 | #' @export 241 | octo_add_path <- function(dir, check = TRUE) { 242 | if (length(dir) != 1) { 243 | octo_abort("{.arg dir} must be length 1.") 244 | } 245 | 246 | if (!is.character(dir)) { 247 | octo_abort("{.arg dir} must be a string.") 248 | } 249 | 250 | if (!dir.exists(dir) && check) { 251 | octo_abort( 252 | c("The path {.path {dir}} could not be found.") 253 | ) 254 | } 255 | 256 | if (!is_absolute_path(dir)) { 257 | dir <- file.path(getwd(), dir) 258 | } 259 | 260 | if (on_github()) { 261 | write(dir, Sys.getenv("GITHUB_PATH"), append = TRUE) 262 | } 263 | 264 | invisible(dir) 265 | } 266 | -------------------------------------------------------------------------------- /R/fs.R: -------------------------------------------------------------------------------- 1 | # This file contains code derived from 2 | # 3 | # https://github.com/r-lib/fs 4 | # 5 | # and has the following copyright notice: 6 | # MIT License 7 | 8 | # Copyright (c) 2020 fs authors 9 | 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | 17 | # The above copyright notice and this permission notice shall be included in all 18 | # copies or substantial portions of the Software. 19 | 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | # SOFTWARE. 27 | 28 | is_absolute_path <- function(path) { 29 | grepl("^~", path) | grepl("^(/+|[A-Za-z]:)", path) 30 | } 31 | -------------------------------------------------------------------------------- /R/messages.R: -------------------------------------------------------------------------------- 1 | #' @describeIn octo_abort A debug message which is only visible if the 2 | #' secret `ACTIONS_STEP_DEBUG` is set. For local use set option 3 | #' `octolog.debug = TRUE`. 4 | #' @export 5 | octo_debug <- function(message, .envir = parent.frame()) { 6 | if (on_github()) { 7 | signal_github_condition("::debug", message, .envir = .envir) 8 | } else if (getOption("octolog.debug", FALSE)) { 9 | cli::cli_bullets(c("!" = "This is a debug message", message)) 10 | } 11 | 12 | invisible(message) 13 | } 14 | 15 | #' @rdname octo_abort 16 | #' @export 17 | octo_inform <- function(message, 18 | ..., 19 | trace = rlang::trace_back(), 20 | title = NULL, 21 | .envir = parent.frame()) { 22 | if (on_github()) { 23 | signal_github_condition("::notice ", message, trace, title, .envir) 24 | } else { 25 | cli::cli_inform(message, ..., .envir = .envir) 26 | } 27 | 28 | invisible(message) 29 | } 30 | 31 | #' @rdname octo_abort 32 | #' @export 33 | octo_warn <- function(message, 34 | ..., 35 | trace = rlang::trace_back(), 36 | title = NULL, 37 | .envir = parent.frame()) { 38 | if (on_github()) { 39 | signal_github_condition("::warning ", message, trace, title, .envir) 40 | } else { 41 | cli::cli_warn(message, ..., .envir = .envir) 42 | } 43 | 44 | invisible(message) 45 | } 46 | 47 | 48 | 49 | #' Signal conditions that create GitHub annotations. 50 | #' 51 | #' These functions are a drop-in replacements for [cli::cli_warn()] and friends. 52 | #' If used while R is running within a GitHub Action, the conditions will be 53 | #' signaled in such a way that they will create annotations on the files 54 | #' affected. Even if [enable_github_colors()] was used the conditions will not 55 | #' have colors in the log as the color codes break the annotation. 56 | #' 57 | #' Annotations will only have file and line references if the option 58 | #' `keep.source = TRUE` is set. It defaults to `FALSE` when in non-interactive 59 | #' use. 60 | #' 61 | #' The file path for the annotations will be relative to the R working 62 | #' directory, if you want to change that set the envvar `OCTOLOG_REPO_DIR` to 63 | #' the dir the path should be relative to. This will be necessary if the current 64 | #' working directory is not the repository root (e.g. you checked out into a 65 | #' separate dir), as annotations require the file paths to be relative to the 66 | #' repository root. 67 | #' @inheritParams cli::cli_abort 68 | #' @param title A custom title for the GitHub annotation. 69 | #' @param trace An [rlang::trace_back()] will only be passed to [rlang::abort()] 70 | #' if not [missing()]. 71 | #' @param .fail_fast An error on GitHub will not kill the R process if this is 72 | #' set to `FALSE`. Use the option `octolog.fail_fast` to set the value 73 | #' globally.` 74 | #' @examples 75 | #' Sys.setenv(GITHUB_ACTIONS = "") 76 | #' octo_warn(c("A warning message", i = "Try something else!"), 77 | #' title = "Custom Title" 78 | #' ) 79 | #' Sys.setenv(GITHUB_ACTIONS = "TRUE") 80 | #' octo_warn(c("A warning message", i = "Try something else!"), 81 | #' title = "Custom Title" 82 | #' ) 83 | #' @seealso [cli::cli_abort()] 84 | #' @export 85 | octo_abort <- function(message, 86 | ..., 87 | trace = rlang::trace_back(), 88 | title = NULL, 89 | .envir = parent.frame(), 90 | .fail_fast = getOption("octolog.fail_fast") %||% TRUE) { 91 | if (on_github()) { 92 | signal_github_condition("::error ", message, trace, title, .envir) 93 | } 94 | 95 | if (!on_github() || .fail_fast) { 96 | if (missing(trace)) trace <- NULL 97 | 98 | if (utils::packageVersion("rlang") >= "1.0.0") { 99 | cli::cli_abort(message, 100 | ..., 101 | trace = trace, 102 | .envir = .envir, 103 | call = rlang::caller_env() 104 | ) 105 | } else { 106 | cli::cli_abort(message, 107 | ..., 108 | trace = trace, 109 | .envir = .envir 110 | ) 111 | } 112 | } 113 | 114 | invisible(message) 115 | } 116 | 117 | #' Print a github condition 118 | #' 119 | #' @param prefix The condition prefix. 120 | #' @param message The message string which was [prepare_string()]'ed. 121 | #' @param trace An [rlang::trace_back()]. 122 | #' @param title An optional title for the condition. 123 | #' @param .envir Environment the message is [glue::glue()]ed in. 124 | #' @noRd 125 | signal_github_condition <- function(prefix = c( 126 | "::error ", 127 | "::warning ", 128 | "::notice ", 129 | "::debug" 130 | ), 131 | message, 132 | trace = NULL, 133 | title = NULL, 134 | .envir = parent.frame()) { 135 | if (is.null(title)) { 136 | title <- "" 137 | } else { 138 | title <- glue(",title={title}") 139 | } 140 | 141 | if (is.null(trace)) { 142 | loc_str <- "" 143 | } else { 144 | loc_str <- get_location_string(trace) 145 | } 146 | 147 | # Colors work in the log but break the annotations 148 | disable_github_colors(quiet = TRUE) 149 | message <- prepare_string(message, .envir = .envir) 150 | octocat(glue("{prefix}{loc_str}{title}::{message}")) 151 | } 152 | -------------------------------------------------------------------------------- /R/octolog-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | 4 | ## usethis namespace: start 5 | #' @importFrom glue glue 6 | #' @importFrom rlang %||% 7 | ## usethis namespace: end 8 | NULL 9 | -------------------------------------------------------------------------------- /R/util.R: -------------------------------------------------------------------------------- 1 | #' Detect if R is running within a GitHub Action. 2 | #' 3 | #' @return `TRUE` if on GitHub. `FALSE` otherwise. 4 | #' @details Uses `GITHUB_ACTIONS` envvar`. 5 | #' @examples 6 | #' on_github() 7 | #' @export 8 | on_github <- function() { 9 | tolower(Sys.getenv("GITHUB_ACTIONS")) == "true" 10 | } 11 | 12 | 13 | #' "cat" a string if on GitHub 14 | #' 15 | #' Prints a `string` if on GitHub, detected via env var `GITHUB_ACTIONS == 16 | #' 'true'`. 17 | #' 18 | #' @param string A character vector of length 1. This string will be terminated 19 | #' with `last` and printed with a new line. 20 | #' @return `string` invisibly 21 | #' @examples 22 | #' Sys.setenv(GITHUB_ACTIONS = "true") 23 | #' octocat("::error ::Something is wrong") 24 | #' Sys.unsetenv("GITHUB_ACTIONS") 25 | #' octocat("::error ::Something is wrong") 26 | #' 27 | #' @export 28 | octocat <- function(string) { 29 | if (on_github()) { 30 | stopifnot(is.character(string)) 31 | stopifnot(length(string) == 1) 32 | cat(string, "\n", sep = "") 33 | } 34 | 35 | invisible(string) 36 | } 37 | 38 | #' Prepare a String for GitHub 39 | #' 40 | #' This will format the string with [cli::format_message()] and encode it to be 41 | #' in one line for GitHub Action output. 42 | #' @inheritParams encode_string 43 | #' @noRd 44 | prepare_string <- function(string, .envir = parent.frame()) { 45 | encode_string( 46 | cli::format_message(string, .envir = .envir) 47 | ) 48 | } 49 | 50 | #' Encode String for GitHub Actions 51 | #' 52 | #' Encodes a multiline string into one line for GitHub Action output. 53 | #' 54 | #' This will only encode '%', '\\n', '\\r' as these will be automatically 55 | #' decoded by GitHub when using the output via `${{ 56 | #' steps..outputs. }}`. You can use [utils::URLencode()] instead 57 | #' of this function to also escape everything else problematic like (double) 58 | #' quotes etc. but you will have to manually use [utils::URLdecode()] to revert 59 | #' this. 60 | #' @param string A character vector. 61 | #' @param join Join vector into single string using encoded newline. 62 | #' @return A character vector (of length 1 if `join`). 63 | #' @examples 64 | #' chrs <- c("100% This is some output with \n", "a new line") 65 | #' encode_string(chrs) 66 | #' 67 | #' # encode some md (e.g. to post as comment) 68 | #' md <- c("# Important PR Comment", " ", "This commit is great!") 69 | #' encode_string(md, TRUE) 70 | #' @export 71 | encode_string <- function(string, join = FALSE) { 72 | string <- gsub("%", "%25", string) 73 | string <- gsub("\n", "%0A", string) 74 | encoded <- gsub("\r", "%0D", string) 75 | 76 | if (join) { 77 | encoded <- paste0(encoded, collapse = "%0A") 78 | } 79 | 80 | encoded 81 | } 82 | 83 | 84 | #' Enable/disable Colors on GitHub Actions 85 | #' 86 | #' This will set the envvar `R_CLI_NUM_COLORS` to `n_colors` within the scope of 87 | #' `.local_envir`. To avoid side effects through overriding 88 | #' [crayon::has_color()], this function only works [on_github()]. 89 | #' @param n_colors An integer giving the number of colors. Default 24bit. 90 | #' @param quiet Should messages be printed? 91 | #' @inheritParams withr::local_envvar 92 | #' @return Invisibly returns `TRUE` if enabling/disabling was successful, 93 | #' `FALSE` otherwise. 94 | #' @examples 95 | #' Sys.setenv(GITHUB_ACTIONS = "true") 96 | #' enable_github_colors() 97 | #' Sys.setenv(GITHUB_ACTIONS = "false") 98 | #' enable_github_colors() 99 | #' @export 100 | enable_github_colors <- function(n_colors = as.integer(256^3), 101 | .local_envir = parent.frame(), quiet = FALSE) { 102 | if (on_github()) { 103 | ct <- Sys.getenv("R_CLI_NUM_COLORS", unset = NA_character_) 104 | if (is.na(ct)) { 105 | withr::local_envvar( 106 | "R_CLI_NUM_COLORS" = n_colors, 107 | .local_envir = .local_envir 108 | ) 109 | 110 | if (!quiet) cli::cli_alert_success("Enabled colors!") 111 | } else { 112 | if (!quiet) cli::cli_alert_info("{.envvar R_CLI_NUM_COLORS} already set.") 113 | } 114 | return(invisible(TRUE)) 115 | } 116 | 117 | invisible(FALSE) 118 | } 119 | 120 | #' @rdname enable_github_colors 121 | #' @export 122 | disable_github_colors <- function(.local_envir = parent.frame(), 123 | quiet = FALSE) { 124 | if (on_github()) { 125 | withr::local_options(cli.num_colors = 1, .local_envir = .local_envir) 126 | if (!quiet) { 127 | cli::cli_alert_danger("Disabeled colors!") 128 | } 129 | 130 | return(invisible(TRUE)) 131 | } 132 | 133 | invisible(FALSE) 134 | } 135 | 136 | #' Extract file path and position from trace_back. 137 | #' 138 | #' @param trace An [rlang::trace_back()] object. 139 | #' @return A string formated for use in GitHub Action workflow commands. 140 | #' @importFrom rlang `%|%` 141 | #' @noRd 142 | get_location_string <- function(trace) { 143 | if (is.null(trace)) { 144 | return("") 145 | } 146 | 147 | src <- integer(0) 148 | 149 | # This would work with $call via partial matching but better 150 | # to be explicit 151 | if (utils::packageVersion("rlang") >= "1.0.0") { 152 | calls <- trace$call 153 | } else { 154 | calls <- trace$calls 155 | } 156 | 157 | for (call in calls) { 158 | if (!is.null(attributes(call))) { 159 | src <- attr(call, "srcref") 160 | break 161 | } 162 | } 163 | 164 | if (length(src) == 0) { 165 | return("") 166 | } 167 | 168 | path <- utils::getSrcFilename(src, full.names = TRUE) 169 | 170 | if (!is_absolute_path(path)) { 171 | path <- file.path(getwd(), path) 172 | } else { 173 | path <- path.expand(path) 174 | } 175 | 176 | # For annotations to work the path has to be relative 177 | # to the repository root 178 | base_path <- ( 179 | Sys.getenv("OCTOLOG_REPO_DIR", unset = NA_character_) %|% getwd() 180 | ) 181 | path <- path.expand(path) 182 | path <- sub(glue("{base_path}/"), "", path) 183 | 184 | glue::glue( 185 | paste0( 186 | "file={path},", 187 | "line={src[[1]]},endLine={src[[3]]},", 188 | "col={src[[5]]},endCol={src[[6]]}" 189 | ) 190 | ) 191 | } 192 | 193 | on_windows <- function() { 194 | tolower(Sys.info()[["sysname"]]) == "windows" 195 | } 196 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: 3 | github_document: default 4 | --- 5 | 6 | 7 | 8 | # octolog 9 | 10 | 11 | [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) 12 | [![R-CMD-check](https://github.com/assignUser/octolog/workflows/R-CMD-check/badge.svg)](https://github.com/assignUser/octolog/actions) 13 | [![pkgcheck](https://github.com/assignUser/octolog/actions/workflows/pkgcheck.yaml/badge.svg)](https://github.com/assignUser/octolog/actions/workflows/pkgcheck.yaml) 14 | [![Coverage Status](https://coveralls.io/repos/github/assignUser/octolog/badge.svg?branch=main)](https://coveralls.io/github/assignUser/octolog?branch=main) 15 | [![DOI](https://zenodo.org/badge/451156961.svg)](https://zenodo.org/badge/latestdoi/451156961) 16 | 17 | 18 | Octolog provides a complete[^1] API for GitHub [workflow commands](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions) that makes it easy to create powerful GitHub Actions that create readable logs. 19 | 20 | Additionally it is possible to signal conditions with octolog from R that will create annotations when used within GitHub Actions but raise normal R conditions 21 | when used interactively. 22 | ![Annotations on files of a PR](man/figures/error.png) 23 | 24 | [^1]: The only exception is [`save-state`](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#sending-values-to-the-pre-and-post-actions) which can only be used in JavaScript actions. 25 | 26 | ## Installation 27 | 28 | You can install the development version of octolog with either of these options: 29 | 30 | ``` r 31 | pak::pak("assignUser/octolog") 32 | devtools::install_github("assignUser/octolog") 33 | ``` 34 | 35 | ## What are *GitHub Actions*? 36 | [GitHub Actions](https://github.com/features/actions) is a powerful,free[^2] [CI](https://devguide.ropensci.org/ci.html) service integrated into every repository on GitHub. You can use actions created by other users (e.g. the very popular [r-lib/actions](https://github.com/r-lib/actions) collection of R related actions) or create your own customized workflow that fits your needs. 37 | 38 | [^2]: For public repositories. 39 | 40 | ## Example 41 | 42 | The best way to see how {octolog} works, is in an actual [workflow](https://github.com/assignUser/octolog/actions/workflows/test-octolog.yaml). For more information please see the [website](assignUser.github.io/octolog) and the [introductory vignette](https://jacob.wujciak.de/octolog/articles/octolog.html). 43 | 44 | ## Code of Conduct 45 | 46 | Please note that the octolog project is released with a [Contributor Code of Conduct](https://jacob.wujciak.de/octolog/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms. 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # octolog 5 | 6 | 7 | 8 | [![Project Status: Active – The project has reached a stable, usable 9 | state and is being actively 10 | developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) 11 | [![R-CMD-check](https://github.com/assignUser/octolog/workflows/R-CMD-check/badge.svg)](https://github.com/assignUser/octolog/actions) 12 | [![pkgcheck](https://github.com/assignUser/octolog/actions/workflows/pkgcheck.yaml/badge.svg)](https://github.com/assignUser/octolog/actions/workflows/pkgcheck.yaml) 13 | [![Coverage 14 | Status](https://coveralls.io/repos/github/assignUser/octolog/badge.svg?branch=main)](https://coveralls.io/github/assignUser/octolog?branch=main) 15 | [![DOI](https://zenodo.org/badge/451156961.svg)](https://zenodo.org/badge/latestdoi/451156961) 16 | 17 | 18 | Octolog provides a complete[^1] API for GitHub [workflow 19 | commands](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions) 20 | that makes it easy to create powerful GitHub Actions that create 21 | readable logs. 22 | 23 | Additionally it is possible to signal conditions with octolog from R 24 | that will create annotations when used within GitHub Actions but raise 25 | normal R conditions when used interactively. ![Annotations on files of a 26 | PR](man/figures/error.png) 27 | 28 | ## Installation 29 | 30 | You can install the development version of octolog with either of these 31 | options: 32 | 33 | ``` r 34 | pak::pak("assignUser/octolog") 35 | devtools::install_github("assignUser/octolog") 36 | ``` 37 | 38 | ## What are *GitHub Actions*? 39 | 40 | [GitHub Actions](https://github.com/features/actions) is a 41 | powerful,free[^2] [CI](https://devguide.ropensci.org/ci.html) service 42 | integrated into every repository on GitHub. You can use actions created 43 | by other users (e.g. the very popular 44 | [r-lib/actions](https://github.com/r-lib/actions) collection of R 45 | related actions) or create your own customized workflow that fits your 46 | needs. 47 | 48 | ## Example 49 | 50 | The best way to see how {octolog} works, is in an actual 51 | [workflow](https://github.com/assignUser/octolog/actions/workflows/test-octolog.yaml). 52 | For more information please see the 53 | [website](assignUser.github.io/octolog) and the [introductory 54 | vignette](https://jacob.wujciak.de/octolog/articles/octolog.html). 55 | 56 | ## Code of Conduct 57 | 58 | Please note that the octolog project is released with a [Contributor 59 | Code of Conduct](https://jacob.wujciak.de/octolog/CODE_OF_CONDUCT.html). 60 | By contributing to this project, you agree to abide by its terms. 61 | 62 | [^1]: The only exception is 63 | [`save-state`](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#sending-values-to-the-pre-and-post-actions) 64 | which can only be used in JavaScript actions. 65 | 66 | [^2]: For public repositories. 67 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://assignuser.github.io/octolog 2 | development: 3 | mode: auto 4 | authors: 5 | Jacob Wujciak-Jens: 6 | href: https://jacob.wujciak.de 7 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: auto 6 | threshold: 1% 7 | informational: true 8 | patch: 9 | default: 10 | target: auto 11 | threshold: 1% 12 | informational: true 13 | -------------------------------------------------------------------------------- /codemeta.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://doi.org/10.5063/schema/codemeta-2.0", 3 | "@type": "SoftwareSourceCode", 4 | "identifier": "octolog", 5 | "description": "Provides an API for 'GitHub' workflow commands and makes it possible to signal conditions from within R that will create annotations when used within 'GitHub Actions' but raise normal R conditions when used interactively.", 6 | "name": "octolog: Better 'GitHub Actions' Logging", 7 | "relatedLink": "https://jacob.wujciak.de/octolog/", 8 | "codeRepository": "https://github.com/assignUser/octolog", 9 | "issueTracker": "https://github.com/assignUser/octolog/issues", 10 | "license": "https://spdx.org/licenses/MIT", 11 | "version": "0.1.1.9000", 12 | "programmingLanguage": { 13 | "@type": "ComputerLanguage", 14 | "name": "R", 15 | "url": "https://r-project.org" 16 | }, 17 | "runtimePlatform": "R version 4.2.0 (2022-04-22)", 18 | "author": [ 19 | { 20 | "@type": "Person", 21 | "givenName": "Jacob", 22 | "familyName": "Wujciak-Jens", 23 | "email": "jacob@wujciak.de", 24 | "@id": "https://orcid.org/0000-0002-7281-3989" 25 | } 26 | ], 27 | "maintainer": [ 28 | { 29 | "@type": "Person", 30 | "givenName": "Jacob", 31 | "familyName": "Wujciak-Jens", 32 | "email": "jacob@wujciak.de", 33 | "@id": "https://orcid.org/0000-0002-7281-3989" 34 | } 35 | ], 36 | "softwareSuggestions": [ 37 | { 38 | "@type": "SoftwareApplication", 39 | "identifier": "knitr", 40 | "name": "knitr", 41 | "provider": { 42 | "@id": "https://cran.r-project.org", 43 | "@type": "Organization", 44 | "name": "Comprehensive R Archive Network (CRAN)", 45 | "url": "https://cran.r-project.org" 46 | }, 47 | "sameAs": "https://CRAN.R-project.org/package=knitr" 48 | }, 49 | { 50 | "@type": "SoftwareApplication", 51 | "identifier": "covr", 52 | "name": "covr", 53 | "provider": { 54 | "@id": "https://cran.r-project.org", 55 | "@type": "Organization", 56 | "name": "Comprehensive R Archive Network (CRAN)", 57 | "url": "https://cran.r-project.org" 58 | }, 59 | "sameAs": "https://CRAN.R-project.org/package=covr" 60 | }, 61 | { 62 | "@type": "SoftwareApplication", 63 | "identifier": "mockery", 64 | "name": "mockery", 65 | "version": ">= 0.4.2.9000", 66 | "provider": { 67 | "@id": "https://cran.r-project.org", 68 | "@type": "Organization", 69 | "name": "Comprehensive R Archive Network (CRAN)", 70 | "url": "https://cran.r-project.org" 71 | }, 72 | "sameAs": "https://CRAN.R-project.org/package=mockery" 73 | }, 74 | { 75 | "@type": "SoftwareApplication", 76 | "identifier": "openssl", 77 | "name": "openssl", 78 | "provider": { 79 | "@id": "https://cran.r-project.org", 80 | "@type": "Organization", 81 | "name": "Comprehensive R Archive Network (CRAN)", 82 | "url": "https://cran.r-project.org" 83 | }, 84 | "sameAs": "https://CRAN.R-project.org/package=openssl" 85 | }, 86 | { 87 | "@type": "SoftwareApplication", 88 | "identifier": "testthat", 89 | "name": "testthat", 90 | "version": ">= 3.0.0", 91 | "provider": { 92 | "@id": "https://cran.r-project.org", 93 | "@type": "Organization", 94 | "name": "Comprehensive R Archive Network (CRAN)", 95 | "url": "https://cran.r-project.org" 96 | }, 97 | "sameAs": "https://CRAN.R-project.org/package=testthat" 98 | }, 99 | { 100 | "@type": "SoftwareApplication", 101 | "identifier": "rmarkdown", 102 | "name": "rmarkdown", 103 | "provider": { 104 | "@id": "https://cran.r-project.org", 105 | "@type": "Organization", 106 | "name": "Comprehensive R Archive Network (CRAN)", 107 | "url": "https://cran.r-project.org" 108 | }, 109 | "sameAs": "https://CRAN.R-project.org/package=rmarkdown" 110 | } 111 | ], 112 | "softwareRequirements": { 113 | "1": { 114 | "@type": "SoftwareApplication", 115 | "identifier": "cli", 116 | "name": "cli", 117 | "version": ">= 3.0.0", 118 | "provider": { 119 | "@id": "https://cran.r-project.org", 120 | "@type": "Organization", 121 | "name": "Comprehensive R Archive Network (CRAN)", 122 | "url": "https://cran.r-project.org" 123 | }, 124 | "sameAs": "https://CRAN.R-project.org/package=cli" 125 | }, 126 | "2": { 127 | "@type": "SoftwareApplication", 128 | "identifier": "glue", 129 | "name": "glue", 130 | "provider": { 131 | "@id": "https://cran.r-project.org", 132 | "@type": "Organization", 133 | "name": "Comprehensive R Archive Network (CRAN)", 134 | "url": "https://cran.r-project.org" 135 | }, 136 | "sameAs": "https://CRAN.R-project.org/package=glue" 137 | }, 138 | "3": { 139 | "@type": "SoftwareApplication", 140 | "identifier": "rlang", 141 | "name": "rlang", 142 | "version": ">= 0.4.10", 143 | "provider": { 144 | "@id": "https://cran.r-project.org", 145 | "@type": "Organization", 146 | "name": "Comprehensive R Archive Network (CRAN)", 147 | "url": "https://cran.r-project.org" 148 | }, 149 | "sameAs": "https://CRAN.R-project.org/package=rlang" 150 | }, 151 | "4": { 152 | "@type": "SoftwareApplication", 153 | "identifier": "utils", 154 | "name": "utils" 155 | }, 156 | "5": { 157 | "@type": "SoftwareApplication", 158 | "identifier": "withr", 159 | "name": "withr", 160 | "provider": { 161 | "@id": "https://cran.r-project.org", 162 | "@type": "Organization", 163 | "name": "Comprehensive R Archive Network (CRAN)", 164 | "url": "https://cran.r-project.org" 165 | }, 166 | "sameAs": "https://CRAN.R-project.org/package=withr" 167 | }, 168 | "SystemRequirements": null 169 | }, 170 | "fileSize": "213.022KB", 171 | "citation": [ 172 | { 173 | "@type": "SoftwareSourceCode", 174 | "datePublished": "2022", 175 | "author": [ 176 | { 177 | "@type": "Person", 178 | "givenName": "Jacob", 179 | "familyName": "Wujciak-Jens" 180 | } 181 | ], 182 | "name": "octolog: Better 'GitHub Actions' Logging", 183 | "identifier": "10.5281/zenodo.5948954", 184 | "url": "https://doi.org/10.5281/zenodo.5948954", 185 | "description": "https://github.com/assignUser/octolog, https://assignuser.github.io/octolog/", 186 | "@id": "https://doi.org/10.5281/zenodo.5948954", 187 | "sameAs": "https://doi.org/10.5281/zenodo.5948954" 188 | } 189 | ], 190 | "releaseNotes": "https://github.com/assignUser/octolog/blob/master/NEWS.md", 191 | "readme": "https://github.com/assignUser/octolog/blob/main/README.md", 192 | "contIntegration": ["https://github.com/assignUser/octolog/actions", "https://github.com/assignUser/octolog/actions/workflows/pkgcheck.yaml", "https://app.codecov.io/gh/assignUser/octolog?branch=main"], 193 | "developmentStatus": "https://www.repostatus.org/#active", 194 | "keywords": ["rstats", "actions", "workflows", "r", "ci"] 195 | } 196 | -------------------------------------------------------------------------------- /inst/CITATION: -------------------------------------------------------------------------------- 1 | citHeader("To cite octolog in publications use:") 2 | 3 | citEntry( 4 | entry = "Manual", 5 | title = "octolog: Better 'GitHub Actions' Logging", 6 | author = "Jacob Wujciak-Jens", 7 | note = "https://github.com/assignUser/octolog, https://assignuser.github.io/octolog/", 8 | year = 2022, 9 | url = "https://doi.org/10.5281/zenodo.5948954", 10 | doi = "10.5281/zenodo.5948954", 11 | textVersion = paste( 12 | "Jacob Wujciak-Jens (2022).", 13 | "octolog: Better 'GitHub Action' Logging.", 14 | "doi: 10.5281/zenodo.5948955", 15 | "https://assignuser.github.io/octolog" 16 | ) 17 | ) 18 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | assignuser 2 | assignUser 3 | aut 4 | BugReports 5 | cli 6 | CMD 7 | codecov 8 | Codecov 9 | codemeta 10 | codeRepository 11 | coercible 12 | ComputerLanguage 13 | Config 14 | contIntegration 15 | covr 16 | cran 17 | cre 18 | de 19 | developmentStatus 20 | dir 21 | doi 22 | envvar 23 | EOF 24 | familyName 25 | fileSize 26 | fs 27 | gh 28 | github 29 | GitHub 30 | givenName 31 | https 32 | io 33 | issueTracker 34 | jacob 35 | Jens 36 | lifecycle 37 | Lifecycle 38 | magrittr 39 | md 40 | octolog 41 | Octolog 42 | orcid 43 | programmingLanguage 44 | readme 45 | README 46 | relatedLink 47 | rlang 48 | Roxygen 49 | RoxygenNote 50 | rstats 51 | runtimePlatform 52 | sameAs 53 | SoftwareApplication 54 | softwareRequirements 55 | SoftwareSourceCode 56 | softwareSuggestions 57 | spdx 58 | SystemRequirements 59 | testthat 60 | withr 61 | wujciak 62 | Wujciak 63 | -------------------------------------------------------------------------------- /man/enable_github_colors.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/util.R 3 | \name{enable_github_colors} 4 | \alias{enable_github_colors} 5 | \alias{disable_github_colors} 6 | \title{Enable/disable Colors on GitHub Actions} 7 | \usage{ 8 | enable_github_colors( 9 | n_colors = as.integer(256^3), 10 | .local_envir = parent.frame(), 11 | quiet = FALSE 12 | ) 13 | 14 | disable_github_colors(.local_envir = parent.frame(), quiet = FALSE) 15 | } 16 | \arguments{ 17 | \item{n_colors}{An integer giving the number of colors. Default 24bit.} 18 | 19 | \item{.local_envir}{\verb{[environment]}\cr The environment to use for scoping.} 20 | 21 | \item{quiet}{Should messages be printed?} 22 | } 23 | \value{ 24 | Invisibly returns \code{TRUE} if enabling/disabling was successful, 25 | \code{FALSE} otherwise. 26 | } 27 | \description{ 28 | This will set the envvar \code{R_CLI_NUM_COLORS} to \code{n_colors} within the scope of 29 | \code{.local_envir}. To avoid side effects through overriding 30 | \code{\link[crayon:has_color]{crayon::has_color()}}, this function only works \code{\link[=on_github]{on_github()}}. 31 | } 32 | \examples{ 33 | Sys.setenv(GITHUB_ACTIONS = "true") 34 | enable_github_colors() 35 | Sys.setenv(GITHUB_ACTIONS = "false") 36 | enable_github_colors() 37 | } 38 | -------------------------------------------------------------------------------- /man/encode_string.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/util.R 3 | \name{encode_string} 4 | \alias{encode_string} 5 | \title{Encode String for GitHub Actions} 6 | \usage{ 7 | encode_string(string, join = FALSE) 8 | } 9 | \arguments{ 10 | \item{string}{A character vector.} 11 | 12 | \item{join}{Join vector into single string using encoded newline.} 13 | } 14 | \value{ 15 | A character vector (of length 1 if \code{join}). 16 | } 17 | \description{ 18 | Encodes a multiline string into one line for GitHub Action output. 19 | } 20 | \details{ 21 | This will only encode '\%', '\\n', '\\r' as these will be automatically 22 | decoded by GitHub when using the output via \verb{$\{\{ steps..outputs. \}\}}. You can use \code{\link[utils:URLencode]{utils::URLencode()}} instead 23 | of this function to also escape everything else problematic like (double) 24 | quotes etc. but you will have to manually use \code{\link[utils:URLencode]{utils::URLdecode()}} to revert 25 | this. 26 | } 27 | \examples{ 28 | chrs <- c("100\% This is some output with \n", "a new line") 29 | encode_string(chrs) 30 | 31 | # encode some md (e.g. to post as comment) 32 | md <- c("# Important PR Comment", " ", "This commit is great!") 33 | encode_string(md, TRUE) 34 | } 35 | -------------------------------------------------------------------------------- /man/figures/annotations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assignUser/octolog/f17ff3a6aa549ffc37767060aca25984a40cd08d/man/figures/annotations.png -------------------------------------------------------------------------------- /man/figures/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assignUser/octolog/f17ff3a6aa549ffc37767060aca25984a40cd08d/man/figures/error.png -------------------------------------------------------------------------------- /man/figures/masking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assignUser/octolog/f17ff3a6aa549ffc37767060aca25984a40cd08d/man/figures/masking.png -------------------------------------------------------------------------------- /man/figures/summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assignUser/octolog/f17ff3a6aa549ffc37767060aca25984a40cd08d/man/figures/summary.png -------------------------------------------------------------------------------- /man/octo_abort.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/messages.R 3 | \name{octo_debug} 4 | \alias{octo_debug} 5 | \alias{octo_inform} 6 | \alias{octo_warn} 7 | \alias{octo_abort} 8 | \title{Signal conditions that create GitHub annotations.} 9 | \usage{ 10 | octo_debug(message, .envir = parent.frame()) 11 | 12 | octo_inform( 13 | message, 14 | ..., 15 | trace = rlang::trace_back(), 16 | title = NULL, 17 | .envir = parent.frame() 18 | ) 19 | 20 | octo_warn( 21 | message, 22 | ..., 23 | trace = rlang::trace_back(), 24 | title = NULL, 25 | .envir = parent.frame() 26 | ) 27 | 28 | octo_abort( 29 | message, 30 | ..., 31 | trace = rlang::trace_back(), 32 | title = NULL, 33 | .envir = parent.frame(), 34 | .fail_fast = getOption("octolog.fail_fast") \%||\% TRUE 35 | ) 36 | } 37 | \arguments{ 38 | \item{message}{It is formatted via a call to \code{\link[cli:cli_bullets]{cli_bullets()}}.} 39 | 40 | \item{.envir}{Environment to evaluate the glue expressions in.} 41 | 42 | \item{...}{Passed to \code{\link[rlang:abort]{rlang::abort()}}, \code{\link[rlang:abort]{rlang::warn()}} or 43 | \code{\link[rlang:abort]{rlang::inform()}}.} 44 | 45 | \item{trace}{An \code{\link[rlang:trace_back]{rlang::trace_back()}} will only be passed to \code{\link[rlang:abort]{rlang::abort()}} 46 | if not \code{\link[=missing]{missing()}}.} 47 | 48 | \item{title}{A custom title for the GitHub annotation.} 49 | 50 | \item{.fail_fast}{An error on GitHub will not kill the R process if this is 51 | set to \code{FALSE}. Use the option \code{octolog.fail_fast} to set the value 52 | globally.`} 53 | } 54 | \description{ 55 | These functions are a drop-in replacements for \code{\link[cli:cli_abort]{cli::cli_warn()}} and friends. 56 | If used while R is running within a GitHub Action, the conditions will be 57 | signaled in such a way that they will create annotations on the files 58 | affected. Even if \code{\link[=enable_github_colors]{enable_github_colors()}} was used the conditions will not 59 | have colors in the log as the color codes break the annotation. 60 | } 61 | \details{ 62 | Annotations will only have file and line references if the option 63 | \code{keep.source = TRUE} is set. It defaults to \code{FALSE} when in non-interactive 64 | use. 65 | 66 | The file path for the annotations will be relative to the R working 67 | directory, if you want to change that set the envvar \code{OCTOLOG_REPO_DIR} to 68 | the dir the path should be relative to. This will be necessary if the current 69 | working directory is not the repository root (e.g. you checked out into a 70 | separate dir), as annotations require the file paths to be relative to the 71 | repository root. 72 | } 73 | \section{Functions}{ 74 | \itemize{ 75 | \item \code{octo_debug}: A debug message which is only visible if the 76 | secret \code{ACTIONS_STEP_DEBUG} is set. For local use set option 77 | \code{octolog.debug = TRUE}. 78 | }} 79 | 80 | \examples{ 81 | Sys.setenv(GITHUB_ACTIONS = "") 82 | octo_warn(c("A warning message", i = "Try something else!"), 83 | title = "Custom Title" 84 | ) 85 | Sys.setenv(GITHUB_ACTIONS = "TRUE") 86 | octo_warn(c("A warning message", i = "Try something else!"), 87 | title = "Custom Title" 88 | ) 89 | } 90 | \seealso{ 91 | \code{\link[cli:cli_abort]{cli::cli_abort()}} 92 | } 93 | -------------------------------------------------------------------------------- /man/octo_add_path.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/commands.R 3 | \name{octo_add_path} 4 | \alias{octo_add_path} 5 | \title{Add a system path} 6 | \usage{ 7 | octo_add_path(dir, check = TRUE) 8 | } 9 | \arguments{ 10 | \item{dir}{A directory. If relative will turned absolute using 11 | \code{\link[base:getwd]{base::getwd()}}.} 12 | 13 | \item{check}{Should be checked that \code{dir} is an existing dir.} 14 | } 15 | \value{ 16 | \code{dir} invisibly. 17 | } 18 | \description{ 19 | Prepends a directory so the runners \code{PATH} envvar in a way that make it 20 | available in the following steps of the action. The \code{PATH} will not update 21 | during this step. 22 | } 23 | \examples{ 24 | \dontrun{ 25 | octo_add_path("/.local/bin") 26 | } 27 | } 28 | \seealso{ 29 | The \href{https://github.com/assignUser/octolog/actions/workflows/test-octolog.yaml}{{octolog} example workflow} 30 | and the \href{https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path}{GitHub Docs}. 31 | } 32 | -------------------------------------------------------------------------------- /man/octo_echo_on.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/commands.R 3 | \name{octo_echo_on} 4 | \alias{octo_echo_on} 5 | \alias{octo_echo_off} 6 | \title{Echo workflow commands} 7 | \usage{ 8 | octo_echo_on() 9 | 10 | octo_echo_off() 11 | } 12 | \description{ 13 | Enable or disable echoing of workflow commands in the log. This can be useful 14 | for debugging. Some commands are always echoed and will not be effected by 15 | these functions, this includes \code{\link[=octo_debug]{octo_debug()}} (if debugging is turned on), 16 | \code{\link[=octo_warn]{octo_warn()}} and \code{\link[=octo_abort]{octo_abort()}}. 17 | } 18 | \examples{ 19 | Sys.setenv(GITHUB_ACTIONS = "true") 20 | octo_echo_on() 21 | # workflow commands will be printed in their unparsed state in addition to 22 | # their normal effects 23 | octo_echo_off() 24 | } 25 | \seealso{ 26 | \href{https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#echoing-command-outputs}{GitHub Docs} 27 | } 28 | -------------------------------------------------------------------------------- /man/octo_mask_value.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/commands.R 3 | \name{octo_mask_value} 4 | \alias{octo_mask_value} 5 | \alias{octo_mask_envvar} 6 | \title{Masking a value or envvar in the GHA log.} 7 | \usage{ 8 | octo_mask_value(value) 9 | 10 | octo_mask_envvar(name) 11 | } 12 | \arguments{ 13 | \item{value}{A single value to mask, coercible to string.} 14 | 15 | \item{name}{Name of the envvar to mask.} 16 | } 17 | \description{ 18 | This will mask either a \code{value} or an envvar and prevent them (or their 19 | content) from showing up in the GitHub Actions log. 20 | \cr \emph{\strong{ATTENTION}}: Currently the masking of envvar values will only take 21 | effect in the \emph{\strong{NEXT}} step of the workflow. Values that are masked 22 | directly are masked immediately. This is not very clear in the GitHub Docs 23 | but very important. 24 | } 25 | \details{ 26 | The masking is not restricted to R output, rather it will work for 27 | any logged output. For a practical demonstration please see the 28 | \href{https://github.com/assignUser/octolog/actions/workflows/test-octolog.yaml}{{octolog} example workflow} 29 | 30 | Additionally some values and envvars will be masked automatically by github, 31 | though this behavior is poorly documented. It looks like anything with 32 | "TOKEN" will be masked. Related Issues 33 | \href{https://github.com/actions/runner/issues/643#issuecomment-823537871}{here} 34 | and 35 | \href{https://github.com/actions/runner/issues/475#issuecomment-742271143}{here}. 36 | } 37 | \examples{ 38 | octo_mask_value("secret_token123") 39 | # The mask takes effect in the NEXT step 40 | print("Current token: secret_token123") 41 | # Will log as 42 | # "Current token:***" 43 | 44 | Sys.setenv("SECRET_TOKEN" = "007") 45 | octo_mask_envvar("SECRET_TOKEN") 46 | # The mask takes effect in the NEXT step 47 | print(Sys.getenv("SECRET_TOKEN")) 48 | # Will log as 49 | # "***" 50 | } 51 | \seealso{ 52 | \href{https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#masking-a-value-in-log}{GitHub Docs} 53 | } 54 | -------------------------------------------------------------------------------- /man/octo_set_envvar.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/commands.R 3 | \name{octo_set_envvar} 4 | \alias{octo_set_envvar} 5 | \title{Set environment variables} 6 | \usage{ 7 | octo_set_envvar(value, name, set = TRUE, delim = "EOF") 8 | } 9 | \arguments{ 10 | \item{value}{Value of the envvar, coercible to string. Can be a multiline 11 | string or character vector of \code{length > 1}, each element will be 12 | interpreted as one line.} 13 | 14 | \item{name}{Name of the envvar.} 15 | 16 | \item{set}{Should the envvar also be set in this step?} 17 | 18 | \item{delim}{Delimiter used for multiline strings. No need to change this 19 | unless your string contains 'EOF'.} 20 | } 21 | \value{ 22 | \code{name} invisibly. 23 | } 24 | \description{ 25 | This will set an environment variable in a way that makes it available to the 26 | following steps, compared to using \code{\link[=Sys.setenv]{Sys.setenv()}}, which would only make an 27 | envvar available in the current step. 28 | } 29 | \examples{ 30 | \dontrun{ 31 | val <- c("Some content", "that spans", "multiple lines") 32 | octo_set_envvar(val, "multi") 33 | octo_set_envvar(2342, "pid") 34 | } 35 | } 36 | \seealso{ 37 | \code{\link[=octo_mask_envvar]{octo_mask_envvar()}}and the 38 | \href{https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable}{GitHub docs} 39 | } 40 | -------------------------------------------------------------------------------- /man/octo_set_output.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/commands.R 3 | \name{octo_set_output} 4 | \alias{octo_set_output} 5 | \title{Set an output parameter} 6 | \usage{ 7 | octo_set_output(value, name) 8 | } 9 | \arguments{ 10 | \item{value}{A single line string (or coercible to character). Use 11 | \code{\link[=encode_string]{encode_string()}} to encode a numeric 12 | or multiline string.} 13 | 14 | \item{name}{A character vector length 1.} 15 | } 16 | \description{ 17 | Set an actions output parameter. These can be accessed in later steps. 18 | } 19 | \examples{ 20 | Sys.setenv(GITHUB_ACTION = "true") 21 | string <- "A multi-line \n String." 22 | value <- encode_string(string) 23 | octo_set_output("important-value", value) 24 | } 25 | \seealso{ 26 | \href{https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter}{GitHub Docs} 27 | } 28 | -------------------------------------------------------------------------------- /man/octo_start_group.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/commands.R 3 | \name{octo_start_group} 4 | \alias{octo_start_group} 5 | \alias{octo_end_group} 6 | \title{Grouping log lines} 7 | \usage{ 8 | octo_start_group(name) 9 | 10 | octo_end_group() 11 | } 12 | \arguments{ 13 | \item{name}{Name of the group, single line.} 14 | } 15 | \description{ 16 | These functions make it possible to group lines in the GitHub Actions log. 17 | Groups can not be nested at this point, see this \href{https://github.com/actions/runner/issues/802}{issue}. 18 | } 19 | \examples{ 20 | Sys.setenv(GITHUB_ACTIONS = "TRUE") 21 | octo_start_group("Print stuff") 22 | print("Log other output") 23 | octo_end_group() 24 | } 25 | \seealso{ 26 | \href{https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#grouping-log-lines}{GitHub Docs} 27 | } 28 | -------------------------------------------------------------------------------- /man/octo_stop_commands.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/commands.R 3 | \name{octo_stop_commands} 4 | \alias{octo_stop_commands} 5 | \alias{octo_start_commands} 6 | \title{Stop workflow commands} 7 | \usage{ 8 | octo_stop_commands() 9 | 10 | octo_start_commands(token) 11 | } 12 | \arguments{ 13 | \item{token}{A unique token used to restart workflow command parsing.} 14 | } 15 | \value{ 16 | The \code{token} needed to reactivate the workflow command parsing. When 17 | not on GitHub Actions returns a \code{token} not generated by \code{openssl} to 18 | avoid the dependency. 19 | } 20 | \description{ 21 | This will stop github from processing any workflow commands until 22 | \code{\link[=octo_start_commands]{octo_start_commands()}} is called with the correct \code{token}. This can be used 23 | if untrusted output (e.g. issue titles, bodies or commit messages) needs to 24 | be logged this can be used to stop this output from running possibly 25 | malicious workflow commands. Requires the suggested package 26 | \link[openssl:openssl]{openssl::openssl} to be installed, when used within GitHub Actions. 27 | } 28 | \examples{ 29 | Sys.setenv(GITHUB_ACTIONS = "true") 30 | tk <- octo_stop_commands() 31 | # Commands will not be parsed by GitHub Actions 32 | octo_start_commands(tk) 33 | } 34 | \seealso{ 35 | The \href{https://github.com/assignUser/octolog/actions/workflows/test-octolog.yaml}{example workflow} 36 | and the \href{https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/}{GitHub Blog} 37 | } 38 | -------------------------------------------------------------------------------- /man/octocat.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/util.R 3 | \name{octocat} 4 | \alias{octocat} 5 | \title{"cat" a string if on GitHub} 6 | \usage{ 7 | octocat(string) 8 | } 9 | \arguments{ 10 | \item{string}{A character vector of length 1. This string will be terminated 11 | with \code{last} and printed with a new line.} 12 | } 13 | \value{ 14 | \code{string} invisibly 15 | } 16 | \description{ 17 | Prints a \code{string} if on GitHub, detected via env var \code{GITHUB_ACTIONS == 'true'}. 18 | } 19 | \examples{ 20 | Sys.setenv(GITHUB_ACTIONS = "true") 21 | octocat("::error ::Something is wrong") 22 | Sys.unsetenv("GITHUB_ACTIONS") 23 | octocat("::error ::Something is wrong") 24 | 25 | } 26 | -------------------------------------------------------------------------------- /man/octolog-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/octolog-package.R 3 | \docType{package} 4 | \name{octolog-package} 5 | \alias{octolog} 6 | \alias{octolog-package} 7 | \title{octolog: Better 'GitHub Actions' Logging} 8 | \description{ 9 | Provides an API for 'GitHub' \url{https://github.com} workflow commands and makes it possible to signal conditions from within R that will create annotations when used within 'GitHub Actions' \url{https://github.com/features/actions} but raise normal R conditions when used interactively. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://github.com/assignUser/octolog} 15 | \item \url{https://jacob.wujciak.de/octolog/} 16 | \item Report bugs at \url{https://github.com/assignUser/octolog/issues} 17 | } 18 | 19 | } 20 | \author{ 21 | \strong{Maintainer}: Jacob Wujciak-Jens \email{jacob@wujciak.de} (\href{https://orcid.org/0000-0002-7281-3989}{ORCID}) 22 | 23 | } 24 | \keyword{internal} 25 | -------------------------------------------------------------------------------- /man/on_github.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/util.R 3 | \name{on_github} 4 | \alias{on_github} 5 | \title{Detect if R is running within a GitHub Action.} 6 | \usage{ 7 | on_github() 8 | } 9 | \value{ 10 | \code{TRUE} if on GitHub. \code{FALSE} otherwise. 11 | } 12 | \description{ 13 | Detect if R is running within a GitHub Action. 14 | } 15 | \details{ 16 | Uses \code{GITHUB_ACTIONS} envvar`. 17 | } 18 | \examples{ 19 | on_github() 20 | } 21 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | 2 | options(keep.source = TRUE) 3 | library(testthat) 4 | library(octolog) 5 | 6 | enable_github_colors() 7 | test_check("octolog") 8 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/commands.md: -------------------------------------------------------------------------------- 1 | # groups [plain] 2 | 3 | Code 4 | octo_start_group("testthat") 5 | Output 6 | ::group::testthat 7 | 8 | --- 9 | 10 | Code 11 | octo_end_group() 12 | Output 13 | ::endgroup:: 14 | 15 | --- 16 | 17 | Code 18 | octo_start_group("testthat") 19 | 20 | --- 21 | 22 | Code 23 | octo_end_group() 24 | 25 | # groups [ansi] 26 | 27 | Code 28 | octo_start_group("testthat") 29 | Output 30 | ::group::testthat 31 | 32 | --- 33 | 34 | Code 35 | octo_end_group() 36 | Output 37 | ::endgroup:: 38 | 39 | --- 40 | 41 | Code 42 | octo_start_group("testthat") 43 | 44 | --- 45 | 46 | Code 47 | octo_end_group() 48 | 49 | # groups [unicode] 50 | 51 | Code 52 | octo_start_group("testthat") 53 | Output 54 | ::group::testthat 55 | 56 | --- 57 | 58 | Code 59 | octo_end_group() 60 | Output 61 | ::endgroup:: 62 | 63 | --- 64 | 65 | Code 66 | octo_start_group("testthat") 67 | 68 | --- 69 | 70 | Code 71 | octo_end_group() 72 | 73 | # groups [fancy] 74 | 75 | Code 76 | octo_start_group("testthat") 77 | Output 78 | ::group::testthat 79 | 80 | --- 81 | 82 | Code 83 | octo_end_group() 84 | Output 85 | ::endgroup:: 86 | 87 | --- 88 | 89 | Code 90 | octo_start_group("testthat") 91 | 92 | --- 93 | 94 | Code 95 | octo_end_group() 96 | 97 | # group errors [plain] 98 | 99 | Code 100 | octo_start_group(c("error", "too much")) 101 | Output 102 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::Group `name` must be length 1! 103 | Condition 104 | Error in `octo_start_group()`: 105 | ! Group `name` must be length 1! 106 | 107 | --- 108 | 109 | Group `name` must be length 1! 110 | 111 | # group errors [ansi] 112 | 113 | Code 114 | octo_start_group(c("error", "too much")) 115 | Output 116 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::Group `name` must be length 1! 117 | Condition 118 | Error in `octo_start_group()`: 119 | ! Group `name` must be length 1! 120 | 121 | --- 122 | 123 | Group `name` must be length 1! 124 | 125 | # group errors [unicode] 126 | 127 | Code 128 | octo_start_group(c("error", "too much")) 129 | Output 130 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::Group `name` must be length 1! 131 | Condition 132 | Error in `octo_start_group()`: 133 | ! Group `name` must be length 1! 134 | 135 | --- 136 | 137 | Group `name` must be length 1! 138 | 139 | # group errors [fancy] 140 | 141 | Code 142 | octo_start_group(c("error", "too much")) 143 | Output 144 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::Group `name` must be length 1! 145 | Condition 146 | Error in `octo_start_group()`: 147 | ! Group `name` must be length 1! 148 | 149 | --- 150 | 151 | Group `name` must be length 1! 152 | 153 | # masking [plain] 154 | 155 | Code 156 | octo_mask_value("token2123123") 157 | Output 158 | ::add-mask::token2123123 159 | 160 | --- 161 | 162 | Code 163 | octo_mask_envvar("SECRET_VAR1") 164 | Output 165 | ::add-mask::$SECRET_VAR1 166 | 167 | --- 168 | 169 | Code 170 | octo_mask_value("token2123123") 171 | 172 | --- 173 | 174 | Code 175 | octo_mask_envvar("SECRET_VAR1") 176 | 177 | # masking [ansi] 178 | 179 | Code 180 | octo_mask_value("token2123123") 181 | Output 182 | ::add-mask::token2123123 183 | 184 | --- 185 | 186 | Code 187 | octo_mask_envvar("SECRET_VAR1") 188 | Output 189 | ::add-mask::$SECRET_VAR1 190 | 191 | --- 192 | 193 | Code 194 | octo_mask_value("token2123123") 195 | 196 | --- 197 | 198 | Code 199 | octo_mask_envvar("SECRET_VAR1") 200 | 201 | # masking [unicode] 202 | 203 | Code 204 | octo_mask_value("token2123123") 205 | Output 206 | ::add-mask::token2123123 207 | 208 | --- 209 | 210 | Code 211 | octo_mask_envvar("SECRET_VAR1") 212 | Output 213 | ::add-mask::$SECRET_VAR1 214 | 215 | --- 216 | 217 | Code 218 | octo_mask_value("token2123123") 219 | 220 | --- 221 | 222 | Code 223 | octo_mask_envvar("SECRET_VAR1") 224 | 225 | # masking [fancy] 226 | 227 | Code 228 | octo_mask_value("token2123123") 229 | Output 230 | ::add-mask::token2123123 231 | 232 | --- 233 | 234 | Code 235 | octo_mask_envvar("SECRET_VAR1") 236 | Output 237 | ::add-mask::$SECRET_VAR1 238 | 239 | --- 240 | 241 | Code 242 | octo_mask_value("token2123123") 243 | 244 | --- 245 | 246 | Code 247 | octo_mask_envvar("SECRET_VAR1") 248 | 249 | # masking errors [plain] 250 | 251 | Code 252 | octo_mask_value(c(1, 2)) 253 | Output 254 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::You can only mask one value at a time. 255 | Condition 256 | Error in `octo_mask_value()`: 257 | ! You can only mask one value at a time. 258 | 259 | --- 260 | 261 | Code 262 | octo_mask_envvar(c("VAR1", "VAR2")) 263 | Output 264 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::You can only mask one envvar at a time. 265 | Condition 266 | Error in `octo_mask_envvar()`: 267 | ! You can only mask one envvar at a time. 268 | 269 | --- 270 | 271 | Code 272 | octo_mask_envvar("SECRET_VAR") 273 | Output 274 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::The envvar `SECRET_VAR` does not exists! 275 | Condition 276 | Error in `octo_mask_envvar()`: 277 | ! The envvar `SECRET_VAR` does not exists! 278 | 279 | --- 280 | 281 | You can only mask one value at a time. 282 | 283 | --- 284 | 285 | You can only mask one envvar at a time. 286 | 287 | --- 288 | 289 | The envvar `SECRET_VAR` does not exists! 290 | 291 | # masking errors [ansi] 292 | 293 | Code 294 | octo_mask_value(c(1, 2)) 295 | Output 296 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::You can only mask one value at a time. 297 | Condition 298 | Error in `octo_mask_value()`: 299 | ! You can only mask one value at a time. 300 | 301 | --- 302 | 303 | Code 304 | octo_mask_envvar(c("VAR1", "VAR2")) 305 | Output 306 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::You can only mask one envvar at a time. 307 | Condition 308 | Error in `octo_mask_envvar()`: 309 | ! You can only mask one envvar at a time. 310 | 311 | --- 312 | 313 | Code 314 | octo_mask_envvar("SECRET_VAR") 315 | Output 316 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::The envvar `SECRET_VAR` does not exists! 317 | Condition 318 | Error in `octo_mask_envvar()`: 319 | ! The envvar `SECRET_VAR` does not exists! 320 | 321 | --- 322 | 323 | You can only mask one value at a time. 324 | 325 | --- 326 | 327 | You can only mask one envvar at a time. 328 | 329 | --- 330 | 331 | The envvar `SECRET_VAR` does not exists! 332 | 333 | # masking errors [unicode] 334 | 335 | Code 336 | octo_mask_value(c(1, 2)) 337 | Output 338 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::You can only mask one value at a time. 339 | Condition 340 | Error in `octo_mask_value()`: 341 | ! You can only mask one value at a time. 342 | 343 | --- 344 | 345 | Code 346 | octo_mask_envvar(c("VAR1", "VAR2")) 347 | Output 348 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::You can only mask one envvar at a time. 349 | Condition 350 | Error in `octo_mask_envvar()`: 351 | ! You can only mask one envvar at a time. 352 | 353 | --- 354 | 355 | Code 356 | octo_mask_envvar("SECRET_VAR") 357 | Output 358 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::The envvar `SECRET_VAR` does not exists! 359 | Condition 360 | Error in `octo_mask_envvar()`: 361 | ! The envvar `SECRET_VAR` does not exists! 362 | 363 | --- 364 | 365 | You can only mask one value at a time. 366 | 367 | --- 368 | 369 | You can only mask one envvar at a time. 370 | 371 | --- 372 | 373 | The envvar `SECRET_VAR` does not exists! 374 | 375 | # masking errors [fancy] 376 | 377 | Code 378 | octo_mask_value(c(1, 2)) 379 | Output 380 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::You can only mask one value at a time. 381 | Condition 382 | Error in `octo_mask_value()`: 383 | ! You can only mask one value at a time. 384 | 385 | --- 386 | 387 | Code 388 | octo_mask_envvar(c("VAR1", "VAR2")) 389 | Output 390 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::You can only mask one envvar at a time. 391 | Condition 392 | Error in `octo_mask_envvar()`: 393 | ! You can only mask one envvar at a time. 394 | 395 | --- 396 | 397 | Code 398 | octo_mask_envvar("SECRET_VAR") 399 | Output 400 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::The envvar `SECRET_VAR` does not exists! 401 | Condition 402 | Error in `octo_mask_envvar()`: 403 | ! The envvar `SECRET_VAR` does not exists! 404 | 405 | --- 406 | 407 | You can only mask one value at a time. 408 | 409 | --- 410 | 411 | You can only mask one envvar at a time. 412 | 413 | --- 414 | 415 | The envvar `SECRET_VAR` does not exists! 416 | 417 | # set_output [plain] 418 | 419 | Code 420 | octo_set_output(5, c("some", "output")) 421 | Output 422 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::The output `name` must be length 1. 423 | Condition 424 | Error in `octo_set_output()`: 425 | ! The output `name` must be length 1. 426 | 427 | --- 428 | 429 | Code 430 | octo_set_output(c(2, 3), "output") 431 | Output 432 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::The output `value` must be length 1. 433 | Condition 434 | Error in `octo_set_output()`: 435 | ! The output `value` must be length 1. 436 | 437 | --- 438 | 439 | Code 440 | octo_set_output("Some Important text to pass on", "pkg-info") 441 | Output 442 | ::set-output name=pkg-info::Some Important text to pass on 443 | 444 | --- 445 | 446 | The output `name` must be length 1. 447 | 448 | --- 449 | 450 | The output `value` must be length 1. 451 | 452 | --- 453 | 454 | Code 455 | octo_set_output("Some Important text to pass on", "pkg-info") 456 | 457 | # set_output [ansi] 458 | 459 | Code 460 | octo_set_output(5, c("some", "output")) 461 | Output 462 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::The output `name` must be length 1. 463 | Condition 464 | Error in `octo_set_output()`: 465 | ! The output `name` must be length 1. 466 | 467 | --- 468 | 469 | Code 470 | octo_set_output(c(2, 3), "output") 471 | Output 472 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::The output `value` must be length 1. 473 | Condition 474 | Error in `octo_set_output()`: 475 | ! The output `value` must be length 1. 476 | 477 | --- 478 | 479 | Code 480 | octo_set_output("Some Important text to pass on", "pkg-info") 481 | Output 482 | ::set-output name=pkg-info::Some Important text to pass on 483 | 484 | --- 485 | 486 | The output `name` must be length 1. 487 | 488 | --- 489 | 490 | The output `value` must be length 1. 491 | 492 | --- 493 | 494 | Code 495 | octo_set_output("Some Important text to pass on", "pkg-info") 496 | 497 | # set_output [unicode] 498 | 499 | Code 500 | octo_set_output(5, c("some", "output")) 501 | Output 502 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::The output `name` must be length 1. 503 | Condition 504 | Error in `octo_set_output()`: 505 | ! The output `name` must be length 1. 506 | 507 | --- 508 | 509 | Code 510 | octo_set_output(c(2, 3), "output") 511 | Output 512 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::The output `value` must be length 1. 513 | Condition 514 | Error in `octo_set_output()`: 515 | ! The output `value` must be length 1. 516 | 517 | --- 518 | 519 | Code 520 | octo_set_output("Some Important text to pass on", "pkg-info") 521 | Output 522 | ::set-output name=pkg-info::Some Important text to pass on 523 | 524 | --- 525 | 526 | The output `name` must be length 1. 527 | 528 | --- 529 | 530 | The output `value` must be length 1. 531 | 532 | --- 533 | 534 | Code 535 | octo_set_output("Some Important text to pass on", "pkg-info") 536 | 537 | # set_output [fancy] 538 | 539 | Code 540 | octo_set_output(5, c("some", "output")) 541 | Output 542 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::The output `name` must be length 1. 543 | Condition 544 | Error in `octo_set_output()`: 545 | ! The output `name` must be length 1. 546 | 547 | --- 548 | 549 | Code 550 | octo_set_output(c(2, 3), "output") 551 | Output 552 | ::error file=universe.R,line=23,endLine=42,col=3,endCol=27::The output `value` must be length 1. 553 | Condition 554 | Error in `octo_set_output()`: 555 | ! The output `value` must be length 1. 556 | 557 | --- 558 | 559 | Code 560 | octo_set_output("Some Important text to pass on", "pkg-info") 561 | Output 562 | ::set-output name=pkg-info::Some Important text to pass on 563 | 564 | --- 565 | 566 | The output `name` must be length 1. 567 | 568 | --- 569 | 570 | The output `value` must be length 1. 571 | 572 | --- 573 | 574 | Code 575 | octo_set_output("Some Important text to pass on", "pkg-info") 576 | 577 | # stop_commands [plain] 578 | 579 | Code 580 | octo_stop_commands() 581 | Output 582 | ::stop-commands::token123 583 | [1] "token123" 584 | 585 | --- 586 | 587 | Code 588 | octo_start_commands("token123") 589 | Output 590 | ::token123:: 591 | 592 | --- 593 | 594 | Code 595 | octo_stop_commands() 596 | Output 597 | [1] "token123" 598 | 599 | --- 600 | 601 | Code 602 | octo_start_commands("token123") 603 | 604 | # stop_commands [ansi] 605 | 606 | Code 607 | octo_stop_commands() 608 | Output 609 | ::stop-commands::token123 610 | [1] "token123" 611 | 612 | --- 613 | 614 | Code 615 | octo_start_commands("token123") 616 | Output 617 | ::token123:: 618 | 619 | --- 620 | 621 | Code 622 | octo_stop_commands() 623 | Output 624 | [1] "token123" 625 | 626 | --- 627 | 628 | Code 629 | octo_start_commands("token123") 630 | 631 | # stop_commands [unicode] 632 | 633 | Code 634 | octo_stop_commands() 635 | Output 636 | ::stop-commands::token123 637 | [1] "token123" 638 | 639 | --- 640 | 641 | Code 642 | octo_start_commands("token123") 643 | Output 644 | ::token123:: 645 | 646 | --- 647 | 648 | Code 649 | octo_stop_commands() 650 | Output 651 | [1] "token123" 652 | 653 | --- 654 | 655 | Code 656 | octo_start_commands("token123") 657 | 658 | # stop_commands [fancy] 659 | 660 | Code 661 | octo_stop_commands() 662 | Output 663 | ::stop-commands::token123 664 | [1] "token123" 665 | 666 | --- 667 | 668 | Code 669 | octo_start_commands("token123") 670 | Output 671 | ::token123:: 672 | 673 | --- 674 | 675 | Code 676 | octo_stop_commands() 677 | Output 678 | [1] "token123" 679 | 680 | --- 681 | 682 | Code 683 | octo_start_commands("token123") 684 | 685 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/commands/gh_env1: -------------------------------------------------------------------------------- 1 | OCTO_VAR1< 5 | %\VignetteIndexEntry{Introduction to octolog} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | Getting started with octolog is easy, just replace the *{cli}* conditions functions with their octolog counterpart: 11 | 12 | * `cli::cli_inform` $\rightarrow$ `octolog::octo_inform` 13 | * `cli::cli_warn` $\rightarrow$ `octolog::octo_warn` 14 | * `cli::cli_abort` $\rightarrow$ `octolog::octo_abort` 15 | 16 | In an interactive session you will not see a difference, as the calls are passed through to {cli} but if your code throws conditions within a GitHub Action (detected via the environment variable `GITHUB_ACTIONS = "true"`) the behaviour will change. The conditions will be signaled in a way that will make them stand out in the log, appear in the action summary and create annotations[^1] on affected files. 17 | 18 | ![Annotations on files of a PR.](../man/figures/annotations.png) 19 | 20 | You can throw *errors* that do not kill the R session when using `octo_abort()` by setting the optional argument `.fail_fast = FALSE`. This can be useful if you want a workflow to fail (e.g. to block a PR) by throwing an error but do not want to stop the R process. 21 | 22 | ![Annotations in the workflow summary. ](../man/figures/summary.png) 23 | 24 | [^1]: Annotations only work on pull requests. 25 | 26 | ## Workflow commands 27 | 28 | In addition to signaling conditions, GitHub Actions provide a number of [workflow commands](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions) to interact with the runner and following actions steps. Octolog provides a complete API[^2] for the workflow commands, please see the [function reference](https://jacob.wujciak.de/octolog/reference/index.html) and the official documentation for an overview. To see the workflow commands in practice you can check out the [example workflow](https://github.com/assignUser/octolog/actions/workflows/test-octolog.yaml) or the real world use case of the [pkgcheck action](https://github.com/ropensci-review-tools/pkgcheck-action). 29 | 30 | [^2]: The only exception is [`save-state`](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#sending-values-to-the-pre-and-post-actions) which can only be used in JavaScript actions. 31 | 32 | ## Security 33 | 34 | **In doubt always refer to the [official documentation](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions) and [security hardening guide](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions) and test the behaviour to make sure the functions behave as you expect!** 35 | 36 | There are commands that can have an impact on the security of your repository if they are not or wrongly used. As their official documentation is a bit lacking in parts this section will add some additional remarks. 37 | 38 | ### Masking 39 | 40 | Masking values directly (`octo_mask_value`) and masking environment variables and their values (`octo_mask_envvar`) differs in unexpected and undocumented ways. 41 | 42 | ![Difference in masking values and environment variables. ](../man/figures/masking.png) 43 | 44 | Values masked with `octo_mask_value` will be replaced with "\*\*\*"" immediately but environment variables will only be masked in the ***next*** `step:` of the action! 45 | 46 | ### Stopping command parsing 47 | 48 | If you have to log untrusted user input (like commit messages, issue titles ...) you can prevent the execution of workflow commands using `octo_stop_commands`.Internally the function creates a token based on cryptographically secure random bytes from {openssl}, this makes it impossible for a third party to guess the token before submitting their changes (e.g. to a PR) to enable the execution of their untrusted commands. This is not intended to protect against other R code. Due to R's metaprogramming capabilities it is not possible to protect the token within the same R session. 49 | 50 | ***NEVER*** run untrusted code using an action trigger that gives write access to your repository like "pull_request_target". See this article about [*pwn requests*](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/) for more in-depth information. 51 | 52 | --------------------------------------------------------------------------------