Skip to content

Closes #143 and modifies #139 for removal of admiral.test and adding … #466

Closes #143 and modifies #139 for removal of admiral.test and adding …

Closes #143 and modifies #139 for removal of admiral.test and adding … #466

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"]
});
}