diff --git a/.github/workflows/check-repositories.yml b/.github/workflows/check-repositories.yml new file mode 100644 index 00000000..72893d17 --- /dev/null +++ b/.github/workflows/check-repositories.yml @@ -0,0 +1,46 @@ +name: DARMA repositories check + +# Runs at 00:00 UTC on day 1 of every month +on: + # schedule: + # - cron: '0 0 1 * *' + push: + branches: + - 1-validate-required-workflows-usage-across-repositories + +concurrency: + group: ${{ github.event.repository.name }}-${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: True + +jobs: + list_repositories: + name: List repositories + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v4 + + - name: List repositories + id: list-repositories + run: | + REPOSITORIES=$(bash ci/list_repositories.sh) + echo "repositories=$(echo $REPOSITORIES)" >> $GITHUB_OUTPUT + outputs: + repositories: ${{ steps.list-repositories.outputs.repositories }} + + check_repository: + name: Check repository (${{ matrix.repository.name }}) + runs-on: ubuntu-latest + needs: list_repositories + env: + GH_TOKEN: ${{ secrets.ISSUE_CREATION_1}} + strategy: + fail-fast: false + matrix: + repository: ${{ fromJson(needs.list_repositories.outputs.repositories ) }} + steps: + - uses: actions/checkout@v4 + - name: Check repositories + run: | + bash ./ci/check_repository.sh ${{ matrix.repository.name }} diff --git a/.github/workflows/matchers/shell.json b/.github/workflows/matchers/shell.json new file mode 100644 index 00000000..c52c79e0 --- /dev/null +++ b/.github/workflows/matchers/shell.json @@ -0,0 +1,31 @@ +{ + "problemMatcher": [ + { + "owner": "shell-error", + "severity": "error", + "pattern": [ + { + "regexp": "^[error]:\\s(.+)$" + } + ] + }, + { + "owner": "shell-warning", + "severity": "warning", + "pattern": [ + { + "regexp": "^[warning]:\\s(.+)$" + } + ] + }, + { + "owner": "shell-notice", + "severity": "notice", + "pattern": [ + { + "regexp": "^[notice]:\\s(.+)$" + } + ] + } + ] + } \ No newline at end of file diff --git a/.gitignore b/.gitignore index ed8ebf58..c2e544e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -__pycache__ \ No newline at end of file +__pycache__ +ci/repositories.json diff --git a/ci/check_repository.sh b/ci/check_repository.sh new file mode 100644 index 00000000..646864df --- /dev/null +++ b/ci/check_repository.sh @@ -0,0 +1,101 @@ +#!/bin/bash + +# Check that a repository is compliant: +# - all expected workflows are present + +CURRENT_DIR="$(dirname -- "$(realpath -- "$0")")" # Current directory +PARENT_DIR="$(dirname "$CURRENT_DIR")" +WORKING_DIR="$PARENT_DIR/output" +ORG=DARMA-tasking +REPOSITORY=$1 +EXPECTED_WORKFLOWS=( \ +find-unsigned-commits \ +check-commit-format \ +find-trailing-whitespace \ +check-pr-fixes-issue \ +action-git-diff-check \ +) + +# Clean +rm -rf $WORKING_DIR +mkdir -p $WORKING_DIR + +# Initialize +N_ERRORS=0 +TSSTART=$(date +%s) +echo "$ORG/$REPOSITORY > Cloning repository..."; +git clone https://github.com/$ORG/$REPOSITORY $WORKING_DIR/$REPOSITORY >/dev/null 2>&1 + +# Directory containing workflow files +WORKFLOWS_DIR="$WORKING_DIR/$REPOSITORY/.github/workflows" +FOUND_WORKFLOWS=() + +# Check workflows +if [ ! -d "$WORKFLOWS_DIR" ]; then + echo "[error] Workflow directory '$WORKFLOWS_DIR' does not exist." + exit 1 +fi + +for file in "$WORKFLOWS_DIR"/*.yml; do + if [ ! -f "$file" ]; then + continue + fi + + # Check each file for the current workflow + for w in "${EXPECTED_WORKFLOWS[@]}"; do + if grep -qE "uses: .*/$w" "$file"; then + if [[ ! " ${FOUND_WORKFLOWS[@]} " =~ " $w " ]]; then + FOUND_WORKFLOWS+=("$w") + echo "[ok] Found workflow '$w' in file '${file#$WORKING_DIR/}'" + fi + fi + done + + # Exit if all workflows are found + if [ ${#FOUND_WORKFLOWS[@]} -eq ${#EXPECTED_WORKFLOWS[@]} ]; then + break + fi +done + +# Find any missing workflows +MISSING_WORKFLOWS=() +if [ ${#FOUND_WORKFLOWS[@]} -ne ${#EXPECTED_WORKFLOWS[@]} ]; then + echo "[error] Missing workflows:" + for w in "${EXPECTED_WORKFLOWS[@]}"; do + if [[ ! " ${FOUND_WORKFLOWS[@]} " =~ " $w " ]]; then + echo " - $w" + MISSING_WORKFLOWS+=("$w") + ((N_ERRORS++)) + fi + done +else + echo "[ok] All expected workflows are present." +fi + +# Finalize +TSEND=$(date +%s) +TSDURATION=$(( $TSEND - $TSSTART )) +if [[ $N_ERRORS -gt 0 ]]; then + echo "Creating an issue in $REPOSITORY to add missing workflows..." + + if [ ${#MISSING_WORKFLOWS[@]} -gt 0 ]; then + ISSUE_TITLE="[workflows] Missing Actions" + ISSUE_BODY="The following actions are missing from the repository:" + for w in "${MISSING_WORKFLOWS[@]}"; do + ISSUE_BODY+=$'\n- '"$w" + done + + gh issue create \ + --repo "$ORG/$REPOSITORY" \ + --title "$ISSUE_TITLE" \ + --body "$ISSUE_BODY" + fi +else + echo "[success] repository checks OK." +fi +echo "$WORKING_DIR/$REPOSITORY has been processed in $TSDURATION seconds." +echo "--------------------------------------------------"; + +if [[ $N_ERRORS -gt 0 ]]; then + exit 1 +fi diff --git a/ci/list_repositories.sh b/ci/list_repositories.sh new file mode 100644 index 00000000..ae33654d --- /dev/null +++ b/ci/list_repositories.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# List the repositories in the DARMA-tasking organization - in JSON format - +# that need to be checked + +ORG=DARMA-tasking +EXCLUDE='[ + "DARMA-tasking.github.io", + "find-unsigned-commits", + "check-commit-format", + "find-trailing-whitespace", + "check-pr-fixes-issue", + "vt-sample-project", + "parallel-for-transformer", + "detector", + "workflows" +]' +JQ="$EXCLUDE as \$blacklist | .[] | select(.isFork | not) | select(.name as \$in | \$blacklist | index(\$in) | not)" +gh repo list $ORG --json name,defaultBranchRef,isFork --jq "$JQ" | jq -s 'sort_by(.name)'