deployed stargate to gnosis #287
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
# This git action combines version control and audit checks | |
# Version Control: | |
# - will check all modified or new contracts in src/* | |
# - makes sure that all contracts that have changes in audit-relevant code require a contract version update | |
# - relevant changes => anything except for changes of license identifier, solidity pragma or pure comment changes | |
# - fails if a version was not updated despite relevant changes | |
# - fails if a new contract was added without a ///custom:version tag | |
# - will update the PR title to contain contract names with version changes (incl. their new version) | |
# Audit Checker: | |
# - will only run if version-control job passed successfully | |
# - will remove the (protected) "AuditCompleted" label in the beginning to prevent erroneous states | |
# - reuses the list of relevant contracts identified by the version-control job | |
# - will assign "AuditRequired" label if relevant contracts were identified, otherwise it wil assign "AuditNotRequired" | |
# - requires an audit for each of the relevant contracts | |
# - checks if the logged audit information is coherent | |
# - checks include: | |
# - ensuring the audit log contains an entry for all added/modified contracts in their latest version | |
# - ensuring that an audit report has been added at the logged path | |
# - ensuring that the commit hash that was audited is actually part of this PR | |
# - assigns label "AuditCompleted" if the audit for each relevant modified contract passed all checks | |
# - KNOWN LIMITATIONS | |
# - will only check the last 100 commits for any matches with audit commit hashes | |
# - only one audit can be registered per contract (in a specific version) | |
name: VersionControlAndAuditVerification | |
on: | |
pull_request: | |
types: [opened, edited, synchronize, review_requested, ready_for_review] | |
pull_request_review: | |
types: [submitted] | |
jobs: | |
version-control: | |
if: ${{ github.event.pull_request.draft == false }} | |
runs-on: ubuntu-latest | |
concurrency: | |
group: sc-core-dev-approval-${{ github.event.pull_request.number }} | |
cancel-in-progress: true | |
env: | |
CONTINUE: false # makes sure that variable is correctly initialized in all cases | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: Get list of modified files by this PR | |
id: modified_files | |
run: | | |
BASE_REF="${{ github.event.pull_request.base.ref }}" | |
##### initialize empty file so that artifact upload step cannot fail | |
echo "" > contracts_for_audit.txt | |
##### get all files modified by this PR | |
FILES="$(git diff --name-only "origin/${BASE_REF}" HEAD)" | |
##### make sure that there are modified files | |
if [[ -z "$FILES" ]]; then | |
echo -e "\033[31mNo files found. This should not happen. Please check the code of the Github action\033[0m" | |
exit 1 | |
fi | |
##### Initialize empty variables | |
CONTRACTS="" | |
##### go through all file paths and identify all files in src/ folder (version control is only active in this folder) | |
while IFS= read -r FILE; do | |
if echo "${FILE}" | grep -E '^src/.*\.sol$'; then | |
CONTRACTS="${CONTRACTS}${FILE}"$'\n' | |
fi | |
done <<< "${FILES}" | |
##### if none found, exit here as there is nothing to do | |
if [[ -z "${CONTRACTS}" ]]; then | |
echo -e "\033[32mNo version-controlled contracts found in files modified/added by this PR.\033[0m" | |
echo -e "\033[32mNo further checks are required.\033[0m" | |
# set action output to false | |
echo "CONTINUE=false" >> $GITHUB_ENV | |
exit 0 | |
else | |
# set action output to true | |
echo "CONTINUE=true" >> $GITHUB_ENV | |
fi | |
##### Write filenames to temporary files (using variables here was causing issues due to the file names) | |
echo -e "$CONTRACTS" > modified_contracts.txt | |
- name: Verify version updates on modified contracts | |
id: verify_version_changes | |
if: env.CONTINUE == 'true' | |
run: | | |
##### Read tmp file into variable | |
CONTRACTS=$(cat modified_contracts.txt) | |
##### Initialize variables | |
MISSING_VERSION_TAG=() | |
MISSING_VERSION_UPDATE=() | |
CONTRACTS_FOR_TITLE=() | |
CONTRACTS_FOR_AUDIT=() | |
echo "--------------------" | |
##### Process each file separately | |
while IFS= read -r FILE_PATH; do | |
echo "Now checking contract: $FILE_PATH" | |
VERSION_TAG=$(grep -E '^/// @custom:version' "$FILE_PATH" || true) | |
VERSION=$(echo "$VERSION_TAG" | sed -E 's/^\/\/\/ @custom:version ([0-9]+\.[0-9]+\.[0-9]+).*$/\1/' || true) | |
##### Extract the filename without extension | |
FILENAME=$(basename "$FILE_PATH" .sol) | |
##### Check if a version tag exists in the contract file | |
if [[ -z "$VERSION_TAG" ]]; then | |
echo -e "\033[31mFile does not contain a version tag\033[0m" | |
MISSING_VERSION_TAG+=("$FILE_PATH") | |
else | |
echo -e "\033[32mFile contains a custom:version tag\033[0m" | |
##### get all changes of the current file/contract | |
DIFF_OUTPUT=$(git diff origin/${{ github.event.pull_request.base.ref }} HEAD "$FILE_PATH") | |
##### Check if the version was updated in this PR | |
if echo "$DIFF_OUTPUT" | grep -qE '^\+/// @custom:version'; then | |
echo -e "\033[32mFile version was updated in this PR to version $VERSION\033[0m" | |
NEW_VERSION=$(echo "$VERSION_TAG" | awk '{print $NF}') | |
CONTRACTS_FOR_AUDIT+=("${FILE_PATH}") | |
CONTRACTS_FOR_TITLE+=("${FILENAME} v${NEW_VERSION}") | |
else | |
##### Check if changes are relevant (ignore comments, formatting, pragma, license changes) | |
if echo "$DIFF_OUTPUT" | grep -qE '^\+\/\/\/|^\+pragma|^\+// SPDX-License-Identifier:'; then | |
echo -e "\033[32mChange is non-relevant (comments/formatting/pragma/license). No version update required.\033[0m" | |
else | |
##### add to files with missing version updates | |
echo -e "\033[31mThe file changed but the file version was not updated\033[0m" | |
MISSING_VERSION_UPDATE+=("$FILE_PATH") | |
fi | |
fi | |
fi | |
echo "--------------------" | |
done <<< "$CONTRACTS" | |
##### If any contract files are missing a version tag, this must be corrected before continuing | |
if [[ ${#MISSING_VERSION_TAG[@]} -ne 0 ]]; then | |
echo "--------------------" | |
echo ">>>>>>" | |
echo -e "\033[31mThe following files are missing a custom:version tag in their code:\033[0m" | |
echo "${MISSING_VERSION_TAG[*]}" | |
echo -e "\033[31mEvery version-controlled contract needs to have a custom:version tag in its code.\033[0m" | |
echo -e "\033[31mThis Github action cannot complete until these issues are solved.\033[0m" | |
exit 1 | |
fi | |
##### if the version was not updated in any of the changed contracts, store the list of affected files in a tmp file | |
if [[ ${#MISSING_VERSION_UPDATE[@]} -ne 0 ]]; then | |
echo "--------------------" | |
echo ">>>>>>" | |
echo -e "\033[31mThe following contract(s) have been modified but their version tags were not updated:\033[0m" | |
echo "${MISSING_VERSION_UPDATE[*]}" | |
echo -e "\033[31mPlease make sure to update a contract's version whenever there are changes in the file.\033[0m" | |
echo -e "\033[31mThis Github action cannot complete until these issues are solved.\033[0m" | |
echo "" | |
exit 1 | |
fi | |
##### store any contracts that were correctly updated in a tmp file so we can check the PR title after for each of those | |
if [[ ${#CONTRACTS_FOR_AUDIT[@]} -ne 0 ]]; then | |
##### create a string from the array with all contracts | |
CONTRACTS_FOR_AUDIT_STR=$(IFS=,; echo "${CONTRACTS_FOR_AUDIT[*]}") | |
CONTRACTS_FOR_TITLE_STR=$(IFS=,; echo "${CONTRACTS_FOR_TITLE[*]}") | |
echo -e "${CONTRACTS_FOR_TITLE_STR[*]}" > contracts_for_title.txt | |
else | |
echo -e "\033[32mDid not find any contracts for which version control is activated.\033[0m" | |
echo -e "\033[32mNo further checks are required.\033[0m" | |
echo "CONTINUE=false" >> $GITHUB_ENV | |
exit 0 | |
fi | |
##### Upload this file in any case to prevent error in following job when trying to download this file | |
echo -e "${CONTRACTS_FOR_AUDIT_STR[*]}" > contracts_for_audit.txt | |
echo "CONTRACTS MARKED FOR AUDIT: ${CONTRACTS_FOR_AUDIT_STR[*]}" | |
echo "##############file written" | |
- name: Compose updated PR title | |
env: | |
PR_TITLE: ${{ github.event.pull_request.title }} | |
run: | | |
##### Read tmp files into variables | |
if [ -f contracts_for_title.txt ]; then | |
UPDATED_CONTRACTS=$(cat contracts_for_title.txt) | |
else | |
UPDATED_CONTRACTS="" | |
fi | |
echo "UPDATED CONTRACTS: $UPDATED_CONTRACTS" | |
##### Step 1: Remove everything in and including brackets from the current title | |
BASE_TITLE=$(echo "$PR_TITLE" | sed 's/\s*\[.*$//') | |
echo "BASE_TITLE: $BASE_TITLE" | |
##### Step 2: Trim whitespace from the base title | |
BASE_TITLE="$(echo -e "${BASE_TITLE}" | sed 's/[[:space:]]*$//')" | |
##### Step 3: Construct the new title if there are updated contracts | |
if [[ -n "$UPDATED_CONTRACTS" ]]; then | |
# Create new title with updated contracts | |
PR_TITLE_UPDATED="${BASE_TITLE} [${UPDATED_CONTRACTS}]" | |
else | |
# If no updated contracts, keep the title as the base title | |
PR_TITLE_UPDATED="$BASE_TITLE" | |
fi | |
##### Step 4: Trim whitespace from the updated title | |
PR_TITLE_UPDATED="$(echo -e "${PR_TITLE_UPDATED}" | sed 's/[[:space:]]*$//')" | |
##### Step 5: Log current and new titles and check if an update is needed | |
echo "Current PR Title: '$PR_TITLE'" | |
echo "New PR Title: '$PR_TITLE_UPDATED'" | |
if [[ "$PR_TITLE" != "$PR_TITLE_UPDATED" ]]; then | |
echo "Updating PR title from '$PR_TITLE' to '$PR_TITLE_UPDATED'." | |
echo "PR_TITLE_UPDATED=$PR_TITLE_UPDATED" >> $GITHUB_ENV | |
echo "CONTINUE=true" >> $GITHUB_ENV | |
else | |
echo -e "\033[32mNo PR title updates are required. This Github action will end here.\033[0m" | |
echo "CONTINUE=false" >> $GITHUB_ENV | |
exit 0 | |
fi | |
- name: Update the PR title on GitHub | |
if: env.CONTINUE == 'true' | |
env: | |
GH_PAT: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
PR_TITLE_UPDATED: ${{ env.PR_TITLE_UPDATED }} | |
run: | | |
##### unset the default git token (does not have sufficient rights to perform the update) | |
unset GITHUB_TOKEN | |
##### use the Personal Access Token to log into git CLI | |
if ! gh auth login --with-token <<< "$GH_PAT"; then | |
echo -e "\033[31mFailed to authenticate with GitHub. Git action cannot continue\033[0m" | |
exit 1 | |
fi | |
##### update the PR title | |
if ! gh pr edit ${{ github.event.pull_request.number }} --title "${{ env.PR_TITLE_UPDATED }}"; then | |
echo "::error::Failed to update PR title" | |
echo -e "\033[31mFailed to update PR title. Git action cannot continue\033[0m" | |
exit 1 | |
fi | |
- name: Save updated contracts for downstream jobs | |
uses: actions/upload-artifact@v4.6.0 | |
with: | |
name: contracts_for_audit | |
path: contracts_for_audit.txt | |
# ------------------------ end of version control checker ------------------------ | |
audit-verification: | |
if: ${{ github.event.pull_request.draft == false }} | |
needs: version-control | |
runs-on: ubuntu-latest | |
env: | |
GITHUB_TOKEN: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} | |
AUDIT_LOG_PATH: 'audit/auditLog.json' | |
PR_NUMBER: ${{ github.event.pull_request.number }} | |
AUDIT_REQUIRED: false | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: Download list of audit-relevant contracts from previous version control step | |
uses: actions/download-artifact@v4.1.8 | |
with: | |
name: contracts_for_audit | |
# makes sure that the "AuditCompleted" label is always removed in the beginning to make sure that this label is never assigned in (case of) error | |
- name: Remove label "AuditCompleted", if currently assigned | |
uses: actions-ecosystem/action-remove-labels@v1 | |
continue-on-error: true | |
with: | |
github_token: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} # we use the token of the lifi-action-bot so the label protection check will pass | |
labels: 'AuditCompleted' | |
number: ${{ env.PR_NUMBER }} | |
fail_on_error: false | |
- name: Check if audit is required for modified contracts | |
id: check_contract_for_audit_required | |
run: | | |
MODIFIED_CONTRACTS=$(cat contracts_for_audit.txt) | |
echo "MODIFIED_CONTRACTS: $MODIFIED_CONTRACTS" | |
##### Make sure that there are modified files | |
if [ -z "$MODIFIED_CONTRACTS" ]; then | |
echo -e "\033[32mNo protected contracts found in this PR.\033[0m" | |
echo "AUDIT_REQUIRED=false" >> "$GITHUB_ENV" | |
else | |
echo -e "\033[31mProtected contracts have audit-relevant changes in this PR.\033[0m" | |
echo "AUDIT_REQUIRED=true" >> "$GITHUB_ENV" | |
fi | |
- name: Assign, update, and verify labels based on check outcome | |
uses: actions/github-script@v7 | |
env: | |
AUDIT_REQUIRED: ${{ env.AUDIT_REQUIRED }} | |
with: | |
script: | | |
const { execSync } = require('child_process'); | |
// ANSI escape codes for colors (used for colored output in Git action console) | |
const colors = { | |
reset: "\033[0m", | |
red: "\033[31m", | |
green: "\033[32m", | |
}; | |
// Fetch currently assigned labels from GitHub using GitHub CLI | |
let assignedLabels = []; | |
try { | |
// Fetch the labels directly from the pull request | |
const labelsOutput = execSync(`gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name'`).toString(); | |
// Split the labels output into an array and trim each label | |
assignedLabels = labelsOutput.split('\n').map(label => label.trim()).filter(Boolean); | |
} catch (error) { | |
console.error(`${colors.red}Error fetching assigned labels: ${error.message}${colors.reset}`); | |
process.exit(1); | |
} | |
// check if audit is required (determined by previous step) | |
const auditRequired = process.env.AUDIT_REQUIRED === 'true'; | |
// determine which label should be assigned and which should be removed | |
const labelToAssign = auditRequired ? 'AuditRequired' : 'AuditNotRequired'; | |
const oppositeLabel = auditRequired ? 'AuditNotRequired' : 'AuditRequired'; | |
console.log(`Currently assigned labels: ${JSON.stringify(assignedLabels)}`); | |
console.log(`Label '${labelToAssign}' has to be assigned to this PR`); | |
console.log(`Label '${oppositeLabel}' will be removed, if currently present`); | |
// Assign the required label if not already present | |
if (!assignedLabels.includes(labelToAssign)) { | |
console.log(`Now assigning label: ${labelToAssign}`); | |
execSync(`gh pr edit ${{ github.event.pull_request.number }} --add-label "${labelToAssign}"`, { stdio: 'inherit' }); | |
} else { | |
console.log(`${colors.green}Label "${labelToAssign}" is already assigned. No action needed.${colors.reset}`); | |
} | |
// Remove the opposite label if it is present | |
if (assignedLabels.includes(oppositeLabel)) { | |
console.log(`Now removing opposite label: ${oppositeLabel}`); | |
execSync(`gh pr edit ${{ github.event.pull_request.number }} --remove-label "${oppositeLabel}"`, { stdio: 'inherit' }); | |
} else { | |
console.log(`${colors.green}Opposite label "${oppositeLabel}" is not assigned. No action needed.${colors.reset}`); | |
} | |
// fetch all currently assigned labels again | |
assignedLabels = [] | |
try { | |
// Fetch the labels directly from the pull request | |
const labelsOutput = execSync(`gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name'`).toString(); | |
// Split the labels output into an array and trim each label | |
assignedLabels = labelsOutput.split('\n').map(label => label.trim()).filter(Boolean); | |
} catch (error) { | |
console.error(`${colors.red}Error fetching assigned labels: ${error.message}${colors.reset}`); | |
process.exit(1); | |
} | |
// Verify that exactly one of the two labels is assigned | |
const totalLabelsAssigned = assignedLabels.filter(label => ['AuditRequired', 'AuditNotRequired'].includes(label)).length; | |
if (totalLabelsAssigned !== 1) { | |
console.error(`${colors.red}Error: Exactly one of the two protected labels should be assigned but found ${totalLabelsAssigned} assigned labels.${colors.reset}`); | |
process.exit(1); | |
} else { | |
console.log(`${colors.green}Verified that exactly one label is assigned. Check passed :)${colors.reset}`); | |
} | |
console.log(`Currently assigned labels: ${JSON.stringify(assignedLabels)}`); | |
- name: Check Audit Log | |
id: check-audit-log | |
if: ${{ always() && env.AUDIT_REQUIRED == 'true' }} # always() ensures that validation is always executed, even if env variable is not set | |
run: | | |
echo "This step will make sure that an audit is logged for each contract modified/added by this PR." | |
echo "It will also make sure that no information is missing in the audit log and that the information is meaningful." | |
# load list of protected contracts | |
PROTECTED_CONTRACTS=$(cat contracts_for_audit.txt) | |
echo "PROTECTED_CONTRACTS: $PROTECTED_CONTRACTS" | |
##### make sure that there are any protected contracts | |
if [[ -z $PROTECTED_CONTRACTS ]]; then | |
echo -e "\033[31mNo protected contracts found. This should not happen (action should stop earlier). Please check the code of the Github action. Aborting now.\033[0m" | |
exit 1 | |
fi | |
# iterate through all contracts | |
while IFS= read -r FILE; do | |
echo "-----------" | |
echo "now checking file $FILE" | |
##### load contract version | |
VERSION=$(sed -nE 's/^\/\/\/ @custom:version ([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' "$FILE") | |
##### make sure that contract version was extracted successfully | |
if [[ -z $VERSION ]]; then | |
echo -e "\033[31mCould not find version of contract $FILE. This should not happen. Please check the Github action code. Aborting now.\033[0m" | |
exit 1 | |
fi | |
##### see if audit log contains an entry with those values | |
FILENAME=$(basename "$FILE" .sol) | |
##### Check if the contract and version exist in the JSON and get the audit IDs | |
AUDIT_IDS=$(jq -e -r --arg filename "$FILENAME" --arg version "$VERSION" \ | |
'if .auditedContracts[$filename][$version] != null then .auditedContracts[$filename][$version][] else empty end' "$AUDIT_LOG_PATH") || { | |
echo "::error::Failed to parse audit log JSON for $FILENAME v$VERSION" | |
exit 1 | |
} | |
##### Count the number of audits found in the log for this contract/version | |
if [[ -z "$AUDIT_IDS" ]]; then | |
AUDIT_COUNT=0 | |
else | |
AUDIT_COUNT=$(echo "$AUDIT_IDS" | wc -l) | |
fi | |
##### Ensure exactly one audit is logged; handle errors if not | |
if [[ $AUDIT_COUNT -ne 1 ]]; then | |
if [[ $AUDIT_COUNT -gt 1 ]]; then | |
echo -e "\033[31mError: Multiple audits found for contract $FILENAME in version $VERSION.\033[0m" | |
echo -e "\033[31mOnly one audit should be logged per contract version.\033[0m" | |
echo -e "\033[31mPlease fix the audit log and try again.\033[0m" | |
else | |
echo -e "\033[31mError: Could not find a logged audit for contract $FILENAME in version $VERSION.\033[0m" | |
echo -e "\033[31mThis check will not pass until the audit log contains a completed audit for this file.\033[0m" | |
fi | |
exit 1 | |
fi | |
##### Extract the single audit ID | |
AUDIT_ID=$(echo "$AUDIT_IDS" | head -n 1) | |
##### Extract audit entry details for the single audit ID | |
AUDIT_ENTRY=$(jq -r --arg audit_id "$AUDIT_ID" '.audits[$audit_id]' "$AUDIT_LOG_PATH") | |
##### Check if AUDIT_ENTRY is valid JSON | |
if [[ -z "$AUDIT_ENTRY" || "$AUDIT_ENTRY" == "null" ]]; then | |
echo -e "\033[31mError: The logged audit ID ($AUDIT_ID) for contract $FILE seems to be invalid.\033[0m" | |
exit 1 | |
fi | |
echo "File $FILE was audited in $AUDIT_ID" | |
echo "Now checking if all required information is logged for this audit..." | |
##### Extract log entry values into variables | |
AUDIT_COMPLETED_ON=$(echo "$AUDIT_ENTRY" | jq -r '.auditCompletedOn') | |
AUDITED_BY=$(echo "$AUDIT_ENTRY" | jq -r '.auditedBy') | |
AUDITOR_GIT_HANDLE=$(echo "$AUDIT_ENTRY" | jq -r '.auditorGitHandle') | |
AUDIT_REPORT_PATH=$(echo "$AUDIT_ENTRY" | jq -r '.auditReportPath') | |
AUDIT_COMMIT_HASH=$(echo "$AUDIT_ENTRY" | jq -r '.auditCommitHash') | |
##### make sure that audit log entry contains date | |
if [ -z "$AUDIT_COMPLETED_ON" ]; then | |
echo -e "\033[31mThe audit log entry for file $FILE contains an invalid or no 'auditCompletedOn' date.\033[0m" | |
echo -e "\033[31mThis github action cannot complete before the audit log is complete.\033[0m" | |
echo -e "\033[31mAborting now.\033[0m" | |
exit 1 | |
else | |
echo "The audit log contains a date for $AUDIT_ID: $AUDIT_COMPLETED_ON" | |
fi | |
##### make sure that audit log entry contains auditor's (company) name | |
if [ -z "$AUDITED_BY" ]; then | |
echo -e "\033[31mThe audit log entry for file $FILE contains invalid or no 'auditedBy' information.\033[0m" | |
echo -e "\033[31mThis github action cannot complete before the audit log is complete.\033[0m" | |
echo -e "\033[31mAborting now.\033[0m" | |
exit 1 | |
else | |
echo "The audit log contains the auditor's name for $AUDIT_ID: $AUDITED_BY" | |
fi | |
##### make sure that audit log entry contains auditor's git handle | |
if [ -z "$AUDITOR_GIT_HANDLE" ]; then | |
echo -e "\033[31mThe audit log entry for file $FILE contains invalid or no 'auditorGitHandle' information.\033[0m" | |
echo -e "\033[31mThis github action cannot complete before the audit log is complete.\033[0m" | |
echo -e "\033[31mAborting now.\033[0m" | |
exit 1 | |
else | |
echo "The audit log contains the auditor's github handle for $AUDIT_ID: $AUDITOR_GIT_HANDLE" | |
fi | |
##### make sure that a file exists at the audit report path | |
if [ ! -f "$AUDIT_REPORT_PATH" ]; then | |
echo -e "\033[31mCould not find an audit report in path $AUDIT_REPORT_PATH for contract "$FILENAME".\033[0m" | |
echo -e "\033[31mThis github action cannot complete before the audit report is uploaded to 'audit/reports/'.\033[0m" | |
echo -e "\033[31mAborting now.\033[0m" | |
exit 1 | |
else | |
echo "The audit report for $AUDIT_ID was found in path $AUDIT_REPORT_PATH" | |
fi | |
##### make sure that audit log entry contains audit commit hash | |
if [ -z "$AUDIT_COMMIT_HASH" ]; then | |
echo -e "\033[31mThe audit log entry for file $FILE contains invalid or no 'auditCommitHash' information.\033[0m" | |
echo -e "\033[31mThis github action cannot complete before the audit log is complete.\033[0m" | |
echo -e "\033[31mAborting now.\033[0m" | |
exit 1 | |
else | |
echo "The audit log contains the commit hash that was audited in $AUDIT_ID: $AUDIT_COMMIT_HASH" | |
fi | |
echo -e "\033[32mThe audit log contains all required information for contract $FILE\033[0m" | |
echo "now checking if audit commit hash $AUDIT_COMMIT_HASH is associated with PR $PR_NUMBER" | |
##### Fetch the list of commits associated with the PR | |
COMMIT_LIST=$(gh pr view "$PR_NUMBER" --json commits --jq '.commits[].oid') | |
##### Check if the target commit is in the list | |
if echo "$COMMIT_LIST" | grep -q "$AUDIT_COMMIT_HASH"; then | |
echo -e "\033[32mCommit $AUDIT_COMMIT_HASH is associated with PR #$PR_NUMBER.\033[0m" | |
else | |
echo -e "\033[31mCommit $AUDIT_COMMIT_HASH is NOT associated with PR #$PR_NUMBER.\033[0m" | |
exit 1 | |
fi | |
##### Check if the auditor git handle exists on github | |
echo "now checking if the auditor git handle ($AUDITOR_GIT_HANDLE) actually exists" | |
if gh api users/$AUDITOR_GIT_HANDLE > /dev/null 2>&1; then | |
echo -e "\033[32mA user with handle '$AUDITOR_GIT_HANDLE' exists on GitHub.\033[0m" | |
else | |
echo -e "\033[31mA user with handle '$AUDITOR_GIT_HANDLE' does not exist on GitHub.\033[0m" | |
echo -e "\033[31mPlease fix the audit log before continuing.\033[0m" | |
echo -e "\033[31mCheck failed.\033[0m" | |
exit 1 | |
fi | |
# ##### ----------------------------------------------------------------------------- | |
# ##### DISABLED FOR NOW (NEED TO CHECK IF THIS IS COMPATIBLE WITH OUR FLOW) | |
# ##### Fetch PR reviews using the GitHub API via gh cli | |
# echo "now checking if the auditor ($AUDITOR_GIT_HANDLE) approved this PR ($PR_NUMBER)" | |
# REVIEWS=$(gh api repos/lifinance/contracts/pulls/$PR_NUMBER/reviews --jq '.[] | select(.state == "APPROVED") | @json') | |
# ##### Check if the output is empty or not valid JSON | |
# if [[ -z "$REVIEWS" ]]; then | |
# echo "ERROR: No reviews found or failed to fetch reviews for PR #$PR_NUMBER" | |
# exit 1 | |
# fi | |
# ##### Flag to track if the review by the specified person is found | |
# FOUND_REVIEW=false | |
# ##### Check if the desired reviewer is present among the reviews | |
# echo "$REVIEWS" | jq -c '.' | while read -r REVIEW; do | |
# AUTHOR=$(echo "$REVIEW" | jq -r '.user.login // empty') | |
# STATE=$(echo "$REVIEW" | jq -r '.state // empty') | |
# echo "found review by $AUTHOR with state $STATE" | |
# ##### Check if the reviewer is the person we're looking for | |
# if [ "$AUTHOR" == "$REVIEWER" ]; then | |
# echo "Approving review found by $REVIEWER" | |
# FOUND_REVIEW=true | |
# exit 0 | |
# fi | |
# done | |
# ##### If no matching review was found, exit with an error | |
# if [ "$FOUND_REVIEW" == true ]; then | |
# echo -e "\033[32mPR $PR_NUMBER has an approving review by $AUDITOR_GIT_HANDLE\033[0m" | |
# echo -e "\033[32mCheck passed\033[0m" | |
# exit 0 | |
# else | |
# echo -e "\033[31mERROR: No review found by git user '$AUDITOR_GIT_HANDLE' (= the auditor)\033[0m" | |
# echo -e "\033[31mCheck failed\033[0m" | |
# exit 1 | |
# fi | |
# ##### ----------------------------------------------------------------------------- | |
done <<< "$PROTECTED_CONTRACTS" | |
echo -e "\033[32mSuccessfully verified that all contracts in this PRs are audited.\033[0m" | |
echo -e "\033[32mCheck passed.\033[0m" | |
echo "Assigning label 'AuditCompleted' next" | |
- name: Assign label "AuditCompleted" if all checks passed | |
if: ${{ env.AUDIT_REQUIRED == 'true' }} | |
uses: actions-ecosystem/action-add-labels@v1 | |
id: assign_label | |
with: | |
github_token: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} # we use the token of the lifi-action-bot so the label protection check will pass | |
labels: 'AuditCompleted' | |
number: ${{ env.PR_NUMBER }} |