Closes #143 and modifies #139 for removal of admiral.test and adding … #466
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
on: | |
workflow_dispatch: | |
workflow_call: | |
inputs: | |
r-version: | |
description: 'The version of R to use' | |
default: '4.1' | |
required: false | |
type: string | |
push-templates-data: | |
description: 'Push generated templates data to pharmaverseadam repo' | |
default: false | |
required: false | |
type: boolean | |
exclude-templates-data: | |
description: 'List of data generated by templates to exclude (templates script will run, but results will not be push to pharmaverseadam repo) - comma seperated list' | |
default: '' | |
required: false | |
type: string | |
push: | |
branches: | |
- main | |
- devel | |
- pre-release | |
pull_request: | |
branches: | |
- main | |
- devel | |
- pre-release | |
env: | |
# branch name for PR | |
source-branch: update_templates_data | |
repo: pharmaverseadam | |
target-branch: main | |
reviewers: > | |
{ | |
"admiralonco": { | |
"reviewers": ["bundfussr", "manciniedoardo"] | |
}, | |
"admiralophtha": { | |
"reviewers": ["manciniedoardo"] | |
}, | |
"admiral": { | |
"reviewers": ["manciniedoardo", "bms63"] | |
}, | |
"admiralvaccine": { | |
"reviewers": ["ahasoplakus", "arjoon-r", "manciniedoardo"] | |
} | |
} | |
# data folder name for templates | |
templates_data_folder: ./data/ | |
name: Check Templates | |
concurrency: | |
group: templates-${{ github.event.pull_request.number || github.ref }} | |
cancel-in-progress: true | |
jobs: | |
get_r_version: | |
name: Get R version | |
runs-on: ubuntu-latest | |
if: > | |
!contains(github.event.commits[0].message, '[skip check_templates]') | |
outputs: | |
r-version: ${{ steps.normalizer.outputs.R_VERSION }} | |
steps: | |
- name: Normalize inputs | |
id: normalizer | |
run: | | |
R_VERSION="${{ inputs.r-version }}" | |
if [ "$R_VERSION" == "" ] | |
then { | |
R_VERSION="4.1" | |
} | |
fi | |
echo "R_VERSION=$R_VERSION" >> $GITHUB_OUTPUT | |
shell: bash | |
check_templates: | |
name: Verify | |
if: > | |
!contains(github.event.commits[0].message, '[skip check_templates]') | |
runs-on: ubuntu-latest | |
container: | |
image: "ghcr.io/pharmaverse/admiralci-${{needs.get_r_version.outputs.r-version}}:latest" | |
needs: get_r_version | |
env: | |
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} | |
steps: | |
##################### BEGIN boilerplate steps ##################### | |
- name: Get branch names | |
id: branch-name | |
uses: tj-actions/[email protected] | |
- name: Checkout repo (PR) 🛎 | |
uses: actions/checkout@v3 | |
if: github.event_name == 'pull_request' | |
with: | |
ref: ${{ steps.branch-name.outputs.head_ref_branch }} | |
repository: ${{ github.event.pull_request.head.repo.full_name }} | |
- name: Checkout repository | |
uses: actions/checkout@v3 | |
if: github.event_name != 'pull_request' | |
with: | |
ref: ${{ steps.branch-name.outputs.head_ref_branch }} | |
fetch-depth: 0 | |
- name: Add safe directory | |
run: | | |
git config --global --add safe.directory "${GITHUB_WORKSPACE}" # open issue: git dubious ownership running in docker container context: https://github.com/actions/runner/issues/2033 | |
shell: bash | |
- name: Normalize inputs | |
id: normalizer | |
run: | | |
function remove_substring() { | |
echo "${1//$2}" | |
} | |
BRANCH=${{ steps.branch-name.outputs.head_ref_branch }} | |
if [ "${BRANCH}" == "" ] | |
then { | |
BRANCH=$(git name-rev HEAD | awk '{print $NF}') | |
} | |
fi | |
if [[ $BRANCH == remotes/pull/* ]] | |
then { | |
PR=$(remove_substring "$BRANCH" "/merge") | |
PR=$(remove_substring "$PR" "remotes/") | |
PR=$(echo $PR | sed 's/pull/pulls/') | |
echo "Accessing repos/${GITHUB_REPOSITORY}/${PR}" | |
BRANCH=$(gh api -X GET repos/${GITHUB_REPOSITORY}/${PR} | jq -r '.head.ref') | |
} | |
fi | |
echo "BRANCH is $BRANCH" | |
R_VERSION="${{ inputs.r-version }}" | |
if [ "$R_VERSION" == "" ] | |
then { | |
R_VERSION="4.1" | |
} | |
fi | |
echo "R_VERSION=$R_VERSION" >> $GITHUB_OUTPUT | |
echo "SD_GIT_REF=$BRANCH" >> $GITHUB_OUTPUT | |
shell: bash | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
- name: Restore cache | |
uses: actions/cache@v3 | |
with: | |
path: | | |
~/.staged.dependencies | |
key: staged-deps | |
restore-keys: staged-deps | |
- name: Run Staged dependencies | |
uses: insightsengineering/staged-dependencies-action@v1 | |
with: | |
run-system-dependencies: false | |
enable-check: false | |
git-ref: ${{ steps.normalizer.outputs.SD_GIT_REF }} | |
renv-restore: false | |
env: | |
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} | |
##################### END boilerplate steps ##################### | |
- name: Install package | |
run: | | |
R CMD build --no-build-vignettes --no-manual . | |
R CMD INSTALL *.tar.gz | |
- name: Run Template Scripts | |
run: | | |
desc <- read.dcf("DESCRIPTION") | |
package_name <- as.character(desc[, "Package"]) | |
templates <- list.files( | |
system.file("templates", package = package_name), | |
pattern = "\\.R$", | |
ignore.case = TRUE, | |
full.names = TRUE | |
) | |
if (length(templates) > 0) { | |
exit_codes <- purrr::map_chr(templates, function(file) { | |
cmd <- sprintf("Rscript --vanilla %s", file) | |
system(cmd) | |
}) | |
if (any(exit_codes == 1L)) { | |
failed_scripts <- basename(templates)[which(exit_codes == 1L)] | |
err_msg <- sprintf( | |
"Executing the following template scripts failed:\n%s", | |
paste("-", failed_scripts, collapse = "\n") | |
) | |
stop(err_msg, call. = FALSE) | |
} | |
} | |
shell: Rscript {0} | |
- name: Add other data formats and suffixes, exclude data | |
if: inputs.push-templates-data | |
run: | | |
library(readxl) | |
library(zip) | |
loadRData <- function(fileName){ | |
load(fileName) | |
get(ls()[ls() != "fileName"]) | |
} | |
print(file.path("${{ github.workspace }}", "tmp")) | |
print(file.path(Sys.getenv("GITHUB_WORKSPACE"), "tmp")) | |
folder_path <- file.path(Sys.getenv("GITHUB_WORKSPACE"), "tmp") | |
rda_files <- list.files(path = folder_path, pattern = "\\.rda$") | |
ex_files <- unlist(strsplit("${{ inputs.exclude-templates-data }}", ",")) # files to exclude | |
for (rda_file in rda_files) { | |
# delete file if part of inputs.exclude-templates-data | |
if (gsub("\\.rda$", "", rda_file) %in% ex_files) { | |
file.remove(file.path(folder_path, rda_file)) | |
cat("Deleted:", rda_file, "\n") | |
} | |
else { | |
print(sprintf("converting file %s", rda_file)) | |
data <- loadRData(file.path(folder_path, rda_file)) | |
suffix <- gsub("pharmaverse/admiral", "", "${{ github.repository }}") | |
if (nchar(suffix) > 0) { | |
rda_file_renamed <- gsub("\\.rda$", sprintf("_%s.rda", suffix), rda_file) | |
csv_file <- gsub("\\.rda$", sprintf("_%s.csv", suffix), rda_file) | |
file.rename(file.path(folder_path, rda_file), file.path(folder_path, rda_file_renamed)) | |
} | |
else { | |
csv_file <- gsub("\\.rda$", ".csv", rda_file) | |
rda_file_renamed <- rda_file | |
} | |
write.csv(data, file = file.path(folder_path, csv_file), row.names = FALSE) | |
# rename content of rda file | |
rda_var <- gsub("\\.rda$", "", rda_file_renamed) | |
print(rda_var) | |
assign(rda_var, data) | |
do.call(save, list(rda_var, file = file.path(folder_path, sprintf("%s.rda", rda_var)), compress = "bzip2")) | |
# create associated documentation inside R folder | |
dataset_name <- gsub("\\.rda$", "", rda_file_renamed) | |
doc_string <- paste( | |
sprintf("#' Dataset %s", dataset_name), | |
"#'", | |
sprintf("#' %s dataset", dataset_name), | |
"#'", | |
sprintf("#' @name %s", dataset_name), | |
"#' @docType data", | |
sprintf("#' @format A data frame with %s columns:", ncol(data)), | |
"#' \\describe{", | |
paste(sapply(names(data), function(col_name) { | |
paste(sprintf("#' \\item{ %s }{%s}", col_name, col_name)) | |
}, USE.NAMES = FALSE), collapse="\n"), | |
"#' }", | |
"#'", sprintf("#' @source Generated from ${{ github.repository }}."), | |
"#' @references None", | |
"#'", sprintf("#' @examples\n#' data(\"%s\")", dataset_name), | |
sep = '\n', | |
sprintf("\"%s\"", dataset_name) | |
) | |
doc_dir <- "datasets_doc" | |
if (!file.exists(doc_dir)) { | |
dir.create(doc_dir, recursive = TRUE) | |
} | |
writeLines(doc_string, con = file.path(doc_dir, paste0(dataset_name, ".R"))) | |
} | |
} | |
shell: Rscript {0} | |
# zip templates data | |
- name: zip artifacts data | |
if: inputs.push-templates-data | |
run: | | |
find "$GITHUB_WORKSPACE/tmp" -type f \( -name "*.rda" -o -name "*.csv" \) -exec zip -j "$GITHUB_WORKSPACE/data.zip" {} \; | |
find "$GITHUB_WORKSPACE/datasets_doc" -type f \( -name "*.R" \) -exec zip -j "$GITHUB_WORKSPACE/doc.zip" {} \; | |
# store templates data as artifacts | |
- name: Archive templates data | |
if: inputs.push-templates-data | |
uses: actions/upload-artifact@v3 | |
with: | |
name: data_templates | |
path: ${{ github.workspace }}/data.zip | |
# store templates data as artifacts | |
- name: Archive doc | |
if: inputs.push-templates-data | |
uses: actions/upload-artifact@v3 | |
with: | |
name: doc_templates | |
path: ${{ github.workspace }}/doc.zip | |
- name: Checkout repo (PR) 🛎 | |
if: inputs.push-templates-data | |
uses: actions/checkout@v3 | |
with: | |
ref: main | |
repository: "${{ github.repository_owner }}/${{ env.repo }}" | |
fetch-depth: 2 | |
token: ${{ secrets.PHARMAVERSE_BOT }} | |
- name: Add safe directory | |
if: inputs.push-templates-data | |
run: | | |
git config --global --add safe.directory "${GITHUB_WORKSPACE}" | |
shell: bash | |
- name: Download data artifacts | |
if: inputs.push-templates-data | |
uses: actions/download-artifact@v3 | |
with: | |
name: data_templates | |
- name: Download doc artifacts | |
if: inputs.push-templates-data | |
uses: actions/download-artifact@v3 | |
with: | |
name: doc_templates | |
# detect diffs om templates data | |
- name: Check templates changes | |
if: inputs.push-templates-data | |
id: changes | |
run: | | |
head_data_folder=$(git ls-tree -r HEAD data) # check if data folder exists on remote main branch | |
unzip -o "${GITHUB_WORKSPACE}/data.zip" -d "${GITHUB_WORKSPACE}/inst/extdata/" | |
unzip -o "${GITHUB_WORKSPACE}/doc.zip" -d "${GITHUB_WORKSPACE}/R" | |
# move every rda files inside data folder | |
mkdir -p data | |
for rda_file in inst/extdata/*.rda; do | |
filename=$(basename "$rda_file") | |
mv "$rda_file" "data/$filename" | |
done | |
if [ -z "${head_data_folder}" ] | |
then { | |
echo "data folder not existing on main branch yet - init mode (all templates data files will be pushed)" | |
diff_files=$(ls "${GITHUB_WORKSPACE}/data") | |
} else { | |
git add data R | |
diff_files_data=$(git status data | grep 'data/.*\.rda$') | |
diff_files_doc=$(git status R | grep 'R/.*\.R$') | |
diff_files="${diff_files_data}${diff_files_doc}" | |
} | |
fi | |
if [ -z "$diff_files" ] | |
then { | |
echo "No changes detected after running templates. Stop here" | |
echo "diff=false" >> $GITHUB_OUTPUT | |
} else { | |
echo "Changes detected, new PR will be created" | |
echo "list of files with new updates: $diff_files" | |
echo "diff=true" >> $GITHUB_OUTPUT | |
} | |
fi | |
shell: bash {0} | |
- name: Generate man pages | |
if: inputs.push-templates-data | |
run: | | |
Rscript -e "roxygen2::roxygenize('.', roclets = c('rd', 'collate', 'namespace'))" | |
git add man | |
shell: bash {0} | |
- name: Update spellchecks list | |
if: inputs.push-templates-data | |
run: | | |
spelling::update_wordlist(pkg=".", confirm=FALSE) | |
shell: Rscript {0} | |
- name: set source-branch name | |
if: inputs.push-templates-data | |
id: branch | |
run: | | |
repo_name=$(basename "${{ github.repository }}") | |
echo "source-branch=${{ env.source-branch }}_${repo_name}@devel" >> $GITHUB_OUTPUT | |
shell: bash {0} | |
# if diff detected, push changes | |
- name: Commit and push changes in {{ env.repo }} | |
if: ${{ inputs.push-templates-data && steps.changes.outputs.diff == 'true' }} | |
uses: stefanzweifel/git-auto-commit-action@v4 | |
with: | |
commit_message: templates data updates from ${{ github.repository }} | |
file_pattern: 'inst/extdata/* data/* R/* inst/WORDLIST' | |
commit_user_name: pharmaverse-bot | |
commit_user_email: [email protected] | |
branch: "${{ steps.branch.outputs.source-branch }}" | |
create_branch: true | |
- name: Create Pull Request | |
if: ${{ inputs.push-templates-data && steps.changes.outputs.diff == 'true' }} | |
uses: actions/github-script@v6 | |
with: | |
github-token: ${{ secrets.PHARMAVERSE_BOT }} | |
script: | | |
// Look for any open PRs | |
const repo_owner = "${{ github.repository_owner }}"; | |
const target_repo_name = "${{ env.repo }}"; | |
const result = await github.rest.pulls.list({ | |
owner: repo_owner, | |
repo: target_repo_name, | |
state: "open" | |
}) | |
let create_new_pr = true; | |
for (const pr of result.data) { | |
// Look for distinct PR branch name | |
if (pr.head.ref === "${{ steps.branch.outputs.source-branch }}") { | |
console.log("PR with head ref " + pr.head.ref + " already exists"); | |
create_new_pr = false; | |
break; | |
} | |
} | |
// If no PR with distinguished branch name has been found | |
// create a new PR to track changes to renv.lock. | |
if (create_new_pr) { | |
console.log("Creating a new PR"); | |
const result2 = await github.rest.pulls.create({ | |
title: 'Templates data updates from ${{ github.repository }}', | |
owner: repo_owner, | |
repo: target_repo_name, | |
head: '${{ steps.branch.outputs.source-branch }}', | |
base: '${{ env.target-branch }}', | |
body: [ | |
'This PR has been automatically generated by ', | |
'check-templates workflow from ${{ github.repository }}.', | |
'\n\nPlease review the changes.' | |
].join('') | |
}); | |
// Assign reviewers to the PR | |
let rev = process.env.reviewers; | |
let reviewers_dict = JSON.parse(rev); | |
let current_repo_name_full = "${{ github.repository }}"; | |
let current_repo_name = current_repo_name_full.split('/').pop(); | |
const result3 = await github.rest.pulls.requestReviewers({ | |
owner: repo_owner, | |
repo: target_repo_name, | |
pull_number: result2.data.number, | |
reviewers: reviewers_dict[current_repo_name]["reviewers"] | |
}); | |
} |