Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release Automation #264

Open
wants to merge 35 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
708f8cb
[Feature] Bringing in changes from test repo
rvyuha Oct 24, 2023
69aa1eb
[Bug] Removed unecisarry escape character.
rvyuha Oct 30, 2023
213d7df
Added the specifications document for the release automation code
yulric Oct 31, 2023
7b8bb03
Renamed the Update Documentation workflow to Create Release Branch. A…
yulric Nov 20, 2023
23d5af7
Merge branch 'automation-integration' of https://github.com/Big-Life-…
yulric Nov 20, 2023
a209d39
Renamed Upload Dictionaries workflow to Merge Release. Also renamed t…
yulric Nov 20, 2023
2719375
Removed rmarkdown, gh, and glue. Added rend to handle dependencies.
yulric Nov 20, 2023
596498a
Renamed OSF_REPO_LINK variable to osf_repo_link to be consistent
yulric Nov 20, 2023
7bb1c91
Renamed OSF_TOKEN variable to osf_token for consistency
yulric Nov 20, 2023
874d3fe
Removed development parameters from the create_release_files function
yulric Nov 20, 2023
1b18eeb
Added new module for logging
yulric Nov 20, 2023
d68548e
Display the log file path in the warning message for build errors
yulric Nov 20, 2023
38df092
Stop execution when there are build errors
yulric Nov 20, 2023
ae5e968
Renamed commit_files to commit_and_push_files
yulric Nov 20, 2023
8ba3c24
Simplified the get_dictionary function
yulric Nov 20, 2023
1fafe77
Removed the development parameter dictionary_path from download_dicti…
yulric Nov 20, 2023
378e1fb
Fixed typo
yulric Nov 20, 2023
116ed87
Avoid mutation
yulric Nov 20, 2023
39a7a1e
Moved the odm_dictionary$version_string into the files sheet under th…
yulric Nov 20, 2023
b824faf
Removed hardcoded column name
yulric Nov 20, 2023
0244d92
Fixed accessing from a sets sheet using a column name from the files …
yulric Nov 20, 2023
e1f92e5
Fixed bug
yulric Nov 20, 2023
a5b07e9
Fixed accessing column from parts sheet using a value from the files …
yulric Nov 20, 2023
f318807
Removed the unnecessary naming of vector
yulric Nov 20, 2023
d45a896
Added comment
yulric Nov 20, 2023
65a9ee1
Removed uncecessary else loop
yulric Nov 20, 2023
9074445
Removed unnecessary variable
yulric Nov 20, 2023
d70733c
Shifted if statement to avoid nesting
yulric Nov 20, 2023
231a1ee
Added comment
yulric Nov 20, 2023
9681645
Rewrote if loop to avoid nesting
yulric Nov 20, 2023
c1cf6b6
is_valid_part now returns only one boolean instead of a list since bo…
yulric Nov 20, 2023
7ca1031
Rename function
yulric Nov 20, 2023
5fe459e
Removed unused variable
yulric Nov 20, 2023
9beda6e
Removed log file
yulric Nov 20, 2023
c557821
Completed refactor
yulric Dec 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/create-release-branch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Create Release Branch
on:
workflow_dispatch:
jobs:
build-docs:
runs-on: ubuntu-latest
defaults:
run:
shell: bash
working-directory: R-package
steps:
- name: Checkout repo
uses: actions/checkout@v2
- name: setup-r
uses: r-lib/actions/setup-r@v2
with:
r-version: "4.1.1"
- name: Install R dependencies
uses: r-lib/actions/setup-r-dependencies@v2
with:
working-directory: R-package
- name: Source code
run: R -e 'source("R/create-release-files.R"); create_release_files(OSF_REPO_LINK = ${{secrets.OSF_REPO_LINK}}, OSF_TOKEN = ${{secrets.OSF_TOKEN}}, github_token = "${{ secrets.ACTION_TOKEN }}")'
27 changes: 27 additions & 0 deletions .github/workflows/merge-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Merge Release

on:
pull_request:
types: [closed]
branches:
- 'release-*'

jobs:
upload-dictionaries:
defaults:
run:
shell: bash
working-directory: R-package
steps:
- name: Checkout repo
uses: actions/checkout@v2
- name: setup-r
uses: r-lib/actions/setup-r@v2
with:
r-version: "4.1.1"
- name: Install R dependencies
uses: r-lib/actions/setup-r-dependencies@v2
with:
working-directory: R-package
- name: Source code
run: R -e 'source("R/osf-interactions.R"); update_osf(OSF_REPO_LINK = ${{secrets.OSF_REPO_LINK}}, OSF_TOKEN = ${{secrets.OSF.TOKEN}})'
4 changes: 4 additions & 0 deletions release-automation/.Rbuildignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
^renv$
^renv\.lock$
^.*\.Rproj$
^\.Rproj\.user$
1 change: 1 addition & 0 deletions release-automation/.Rprofile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
source("renv/activate.R")
24 changes: 24 additions & 0 deletions release-automation/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Package: release.automation
Type: Package
Title: Release Automation
Version: 2.1.0
Authors@R: c(
person(given = "Yulric", family = "Sequeira", role = c("aut"), email = "[email protected]"),
person(given = "Rostyslav", family = "Vyuha", role = c("aut","cre"), email = "[email protected]"))
Maintainer: Rostyslav Vyuha <[email protected]>
Description: PHES-ODM
Depends:
R (>= 4.0.3)
Imports:
osfr,
magrittr,
openxlsx,
logger
License: cc-by-4.0
URL: https://github.com/Big-Life-Lab/PHES-ODM
BugReports: https://github.com/Big-Life-Lab/PHES-ODM/issues
Encoding: UTF-8
RoxygenNote: 7.2.3
Suggests:
testthat (>= 3.0.0)
Config/testthat/edition: 3
3 changes: 3 additions & 0 deletions release-automation/NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Generated by roxygen2: do not edit by hand

export(create_release_files)
132 changes: 132 additions & 0 deletions release-automation/R/create-release-branch.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
source("R/odm-dictionary-file.R")
source("R/files.R")
source("R/logging.R")
source("R/sets-sheet.R")
source("R/parts-sheet.R")

#' Create release files
#'
#' Creates release files given the user OSF link and auth token.
#'
#' @param osf_repo_link The link to the OSF repo.
#' @param osf_token The OSF token used to authenticate the user.
#'
#' @export
create_release_branch <- function(osf_repo_link, osf_token, github_token) {
setup_logging()

errors <- c()
warnings <- c()

download_dictionary_result <- download_dictionary(osf_token, "dev")
warnings <- c(warnings, download_dictionary_result$warnings)

parse_files_sheet_result <- parse_files_sheet(
download_dictionary_result$dictionary
)
errors <- c(errors, parse_files_sheet_result$errors)
warnings <- c(warnings, parse_files_sheet_result$warnings)
if (length(errors) == 0) {
parsed_github_files <- .get_github_files(
parse_files_sheet_result$parsed_files
)
create_files(
parsed_github_files,
file.path(getwd(), "..")
)
# Download previous release dictionary
previous_dictionary_download_results <- download_dictionary(
osf_token, "current"
)
files_to_remove_result <-
parse_files_sheet(parse_previous_dictionary_result$dictionary)
remove_files(
.get_github_files(files_to_remove_result$parsed_files),
parse_previous_dictionary_result$dictionary
)

# Set git config
system('git config user.name "PBL-Bot"')
system('git config user.email "[email protected]"')
system(glue::glue("git config user.password {github_token}"))

release_branch_name <- glue::glue(
"release-{parse_dictionary_result$dictionary_version}"
)

system(glue::glue("git checkout -b {release_branch_name}"))

commit_and_push_files(dictionary_version, release_branch_name)

print("Operation Successful")
} else {
print(glue::glue(
"{length(errors) errors found during the build process. They can be seen
in the log file located at {log_file_path}. Aborting operation."
))
}

if (length(warnings) > 0) {
print(glue::glue(
"{length(warnings) warnings found during the build process. They can be
seen in the log file located at {log_file_path}."
))
}
}

#' Remove Files
#'
#' Helper function to remove files based on output from validate_and_parse_files_sheet.
#'
#' @param files_to_remove List output from validate_and_parse_files_sheet
#' @param dictionary openxlsx environment object storing the dictionary
remove_files <- function(files_to_remove, dictionary) {
# Loop over files to remove based on fileID
for (fileID in names(files_to_remove)) {
current_file_info <- files_to_remove[[fileID]]
# Skip OSF files
if (!"github" %in% current_file_info$destination) {
next()
}

# Create full file path
file_extension <- switch(current_file_info$file_type,
"excel" = ".xlsx",
"csv" = ".csv"
)
file_path <- file.path(
"..",
current_file_info$github_location,
paste0(current_file_info$file_name, file_extension)
)
# Check if file exists
if (file.exists(file_path)) {
file.remove(file_path)
}
}
}

#' Commit files
#'
#' Utility function to add, commit, and push all changes
#'
#' @param repo git2r object for repo reference
#' @param dictionary_version version of the dictionary being deployed
commit_and_push_files <- function(dictionary_version, branch_name) {
# Add all files
system("git add --all")
# Create commit
system(glue::glue('git commit -m "[BOT] release-{dictionary_version}"'))
# Push updated branch
system(glue::glue("git push origin {branch_name}"))
}

.get_github_files <- function(parsed_files) {
github_files <- list()
for (file_id in names(parse_files_sheet_result$parsed_files)) {
if (parse_files_sheet_result$parsed_files[[file_id]] == "github") {
github_files[[file_id]] <- parse_files_sheet_result$parsed_files[[file_id]]
}
}
return(github_files)
}
23 changes: 23 additions & 0 deletions release-automation/R/download-dictionary.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
library(magrittr)

source("R/get-latest-version-from-summary-sheet.R")

download_dictionary <- function(osf_token, osf_folder) {
osfr::osf_auth(osf_token)
repo_files <- osfr::osf_retrieve_node(osf_repo_link) %>%
osfr::osf_ls_files()
folder_name <- tidyr::if_else(
osf_folder == "dev", developer_version_folder, current_version_folder
)
dictionary_folder <- repo_files[repo_files$name == folder_name, ]
dictionary_file <- osfr::osf_ls_files(
dictionary_folder,
type = "file", pattern = "ODM_dev-dictionary"
)
download_info <- osfr::osf_download(
requested_dictionary,
path = dictionary_set_path,
conflicts = "overwrite"
)
return(parse_dictionary(download_info[1, "local_path"]))
}
16 changes: 16 additions & 0 deletions release-automation/R/errors.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
missing_sheet_id <- "E1"
missing_sheet_msg <- function(file_id, sheet_name, dictionary_sheets) {
return(glue::glue(
"Error {missing_sheet_id}: No sheet found with name {sheet_name} when
creating file with ID ", "{file_id}. The following sheets are in the
dictionary ", "{paste(dictionary_sheets, collapse = ", ")}."
))
}

invalid_csv_part_id <- "E2"
invalid_csv_part_msg <- function(file_id, part_id) {
return(glue::glue(
"Error {invalid_csv_part_id}: The {part_id} used for the file with ID
{file_id} is a set which is only allowed for files with type excel"
))
}
118 changes: 118 additions & 0 deletions release-automation/R/files.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
files_sheet_metadata <- list(
file_name = list(
name = "name"
),
file_ID = list(
name = "fileID"
),
file_type = list(
name = "fileType",
categories = list(
excel = "excel",
csv = "csv"
)
),
part_ID = list(
name = "partID"
),
add_headers = list(
name = "addHeaders"
),
destinations = list(
name = "destinations",
categories = list(
osf = "osf",
github = "github"
)
),
osf_locations = list(
name = "osfLocation"
),
github_location = list(
name = "githubLocation"
)
)

create_files <- function(files_to_create, dictionary, write_dir) {
# Loop over files to extract based on fileID
for (file_id in names(files_to_create)) {
current_file_info <- files_to_create[[file_id]]

# Create a write directory based on destination and saving location
current_file_dir <- file.path(
write_dir,
current_file_info$github_location
)
dir.create(current_file_dir,
showWarnings = FALSE,
recursive = TRUE
)

if (current_file_info$file_type == "excel") {
# Use parts as names of sheets to extract
sheets_to_copy <- current_file_info$sheet_names
excel_file <- openxlsx::copyWorkbook(dictionary)
all_dictionary_sheets <- names(dictionary)
for (sheet_name in all_dictionary_sheets) {
if (!(sheet_name %in% sheets_to_copy)) {
openxlsx::removeWorksheet(excel_file, sheet_name)
}
}

if (!is.null(current_file_info$add_headers)) {
for (sheet_to_copy in sheets_to_copy) {
worksheet <- openxlsx::readWorkbook(excel_file, sheet_to_copy)
openxlsx::writeData(
excel_file,
sheet_to_copy,
.add_headers(worksheet, current_file_info$add_headers)
)
}
}

# Save the workbook in the appropriate directory
openxlsx::saveWorkbook(excel_file,
file = file.path(
current_file_dir,
glue::glue("{current_file_info$file_name}.xlsx")
),
overwrite = TRUE
)
} else if (current_file_info$file_type == "csv") {
sheet_name <- current_file_info$sheet_names[1]
csv_file <-
openxlsx::readWorkbook(dictionary, sheet_name)
if (!is.null(current_file_info$add_headers)) {
csv_file <- .add_headers(csv_file, current_file_info$add_headers)
}

write.csv(csv_file,
file = file.path(
current_file_dir,
glue::glue(
"{current_file_info$file_name}.csv"
)
),
row.names = FALSE
)
}
}
}

.add_headers <- function(df, headers) {
df <-
rbind(colnames(df), df)
if (length(colnames(df)) > length(headers)) {
length_to_append <-
length(colnames(df)) - length(headers)
headers <- c(headers, rep("", length_to_append))
} else if (length(colnames(df)) < length(headers)) {
length_to_append <-
length(headers) - length(colnames(df))
for (col_counter in 1:length_to_append) {
df <- cbind(df, "")
}
}
colnames(df) <- headers
return(df)
}
7 changes: 7 additions & 0 deletions release-automation/R/get-latest-version-from-summary-sheet.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
get_latest_version_from_summary_sheet <- function(summary_sheet) {
return(
summary_sheet[[1]]
%>% .[!is.na(.)]
%>% .[length(.)]
)
}
7 changes: 7 additions & 0 deletions release-automation/R/logging.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
log_file_path <- file.path(getwd(), "log")

#' Sets up logging
setup_logging <- function() {
file.remove(log_file_path)
logger::log_appender(logger::appender_file(log_file_path))
}
Loading