Skip to content

[TEAM] TEAM-devops-5 #116

[TEAM] TEAM-devops-5

[TEAM] TEAM-devops-5 #116

name: Route Issue to Repo or Team PR
on:
issues:
types: [opened]
permissions:
contents: write
pull-requests: write
jobs:
process_repo_or_team_request:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Determine Request Type
id: classify
run: |
TITLE="${{ github.event.issue.title }}"
echo "Issue Title: $TITLE"
if [[ "$TITLE" == *"[Repo Request]"* ]]; then
echo "type=repo" >> $GITHUB_OUTPUT
elif [[ "$TITLE" == *"[TEAM]"* ]]; then
echo "type=team" >> $GITHUB_OUTPUT
else
echo "Unsupported issue type, skipping."
echo "type=skip" >> $GITHUB_OUTPUT
fi
- name: Stop if unsupported issue
if: steps.classify.outputs.type == 'skip'
run: echo "No supported issue type found. Exiting."
- name: Process Repo Request
if: steps.classify.outputs.type == 'repo'
run: |
ISSUE_BODY=$(jq -r '.issue.body' "$GITHUB_EVENT_PATH")
# Print the full issue body to debug formatting issues
echo "Full ISSUE_BODY content:"
echo "$ISSUE_BODY"
- name: Parse Issue Details
if: steps.classify.outputs.type == 'repo'
id: parse
run: |
ISSUE_BODY=$(jq -r '.issue.body' "$GITHUB_EVENT_PATH")
# Extract values by looking for the header and getting the next non-empty line
REPO_NAME=$(echo "$ISSUE_BODY" | awk -F'\n' '/^### Repository Name$/{getline; getline; print $0}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
VISIBILITY=$(echo "$ISSUE_BODY" | awk -F'\n' '/^### Visibility$/{getline; getline; print $0}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
BRANCH=$(echo "$ISSUE_BODY" | awk -F'\n' '/^### Default Branch$/{getline; getline; print $0}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
INCLUDE_README=$(echo "$ISSUE_BODY" | awk -F'\n' '/^### Include README$/{getline; getline; print $0}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
# Debugging Output
echo "Extracted Repository Name: '$REPO_NAME'"
echo "Extracted Visibility: '$VISIBILITY'"
echo "Extracted Branch: '$BRANCH'"
echo "Extracted Include README: '$INCLUDE_README'"
# Ensure default values if missing
if [ -z "$REPO_NAME" ]; then echo "Error: Repository Name is required"; exit 1; fi
if [ -z "$BRANCH" ]; then BRANCH="main"; fi
echo "REPO_NAME=$REPO_NAME" >> $GITHUB_ENV
echo "VISIBILITY=$VISIBILITY" >> $GITHUB_ENV
echo "BRANCH=$BRANCH" >> $GITHUB_ENV
echo "INCLUDE_README=$INCLUDE_README" >> $GITHUB_ENV
- name: Set Git Identity
if: steps.classify.outputs.type == 'repo'
run: |
git config --global user.name "github-actions"
git config --global user.email "github-actions@github.com"
- name: Create Repository Structure
if: steps.classify.outputs.type == 'repo'
run: |
mkdir -p repos/${{ env.REPO_NAME }}
cd repos/${{ env.REPO_NAME }}
git init -b ${{ env.BRANCH }}
# Ensure at least one file exists before committing
if [[ "${{ env.INCLUDE_README }}" == "Yes" ]]; then
echo "# ${{ env.REPO_NAME }}" > README.md
else
touch .gitkeep # Create a placeholder file if README is not included
fi
git add .
git commit -m "Initial commit for ${{ env.REPO_NAME }}"
- name: Commit Changes
if: steps.classify.outputs.type == 'repo'
run: |
cd repos/${{ env.REPO_NAME }}
git add .
git commit -m "Add repository structure for ${{ env.REPO_NAME }}" || echo "No changes to commit"
- name: Create Pull Request for Repository
if: steps.classify.outputs.type == 'repo'
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GH_PAT }}
title: "Create new repository: ${{ env.REPO_NAME }}"
body: |
Repository Details:
- **Name**: ${{ env.REPO_NAME }}
- **Visibility**: ${{ env.VISIBILITY }}
- **Default Branch**: ${{ env.BRANCH }}
- **Include README**: ${{ env.INCLUDE_README }}
branch: "create-${{ env.REPO_NAME }}"
- name: Process Team Request
if: steps.classify.outputs.type == 'team'
run: |
# Extract issue body from GitHub event
ISSUE_BODY="${{ github.event.issue.body }}"
# Debugging: Print the issue body
echo "Issue Body:"
echo "$ISSUE_BODY"
TEAM_NAME=$(echo "$ISSUE_BODY" | awk '/^### Team Name$/{getline; getline; print}' | sed 's/[`]//g' | xargs)
PARENT_TEAM=$(echo "$ISSUE_BODY" | awk '/^### Parent Team \(optional\)$/{getline; getline; print}' | sed 's/[`]//g' | xargs)
VISIBILITY=$(echo "$ISSUE_BODY" | awk '/^### Visibility$/{getline; getline; print}' | sed 's/[`]//g' | xargs)
PURPOSE=$(echo "$ISSUE_BODY" | awk '/^### Purpose$/{getline; getline; print}' | sed 's/[`]//g' | xargs)
MAINTAINERS=$(echo "$ISSUE_BODY" | awk '/^### Maintainers \(comma-separated\)$/{getline; getline; print}' | sed 's/[`]//g' | xargs)
MAINTAINER_FULL_NAMES=$(echo "$ISSUE_BODY" | awk '/^### Maintainer Full Names \(comma-separated\)$/{getline; getline; print}' | sed 's/[`]//g' | xargs)
TEAM_MEMBERS=$(echo "$ISSUE_BODY" | awk '/^### Team Members \(comma-separated\)$/{getline; getline; print}' | sed 's/[`]//g' | xargs)
MEMBER_FULL_NAMES=$(echo "$ISSUE_BODY" | awk '/^### Team Member Full Names \(comma-separated\)$/{getline; getline; print}' | sed 's/[`]//g' | xargs)
echo "Extracted Values:"
echo "Team Name: '$TEAM_NAME'"
echo "Visibility: '$VISIBILITY'"
echo "Purpose: '$PURPOSE'"
echo "Maintainers: '$MAINTAINERS'"
echo "Maintainer Full Names: '$MAINTAINER_FULL_NAMES'"
echo "Team Members: '$TEAM_MEMBERS'"
echo "Member Full Names: '$MEMBER_FULL_NAMES'"
echo "parent_team=$PARENT_TEAM" >> "$GITHUB_ENV"
if [[ ! "$TEAM_NAME" =~ ^TEAM-[a-zA-Z0-9_-]{1,20}$ ]]; then
echo "::error::Invalid team name format: '$TEAM_NAME'"
exit 1
fi
VISIBILITY=${VISIBILITY:-Visible}
PURPOSE=${PURPOSE:-Team created via automation}
MAINTAINERS=${MAINTAINERS:-none}
TEAM_MEMBERS=${TEAM_MEMBERS:-none}
MAINTAINER_FULL_NAMES=${MAINTAINER_FULL_NAMES:-none}
MEMBER_FULL_NAMES=${MEMBER_FULL_NAMES:-none}
PARENT_TEAM=${PARENT_TEAM:-none}
echo "$TEAM_NAME" > team-name.txt
echo "team_name=$TEAM_NAME" >> "$GITHUB_ENV"
echo "visibility=$VISIBILITY" >> "$GITHUB_ENV"
echo "purpose=$PURPOSE" >> "$GITHUB_ENV"
echo "maintainers=$MAINTAINERS" >> "$GITHUB_ENV"
echo "maintainer_full_names=$MAINTAINER_FULL_NAMES" >> "$GITHUB_ENV"
echo "team_members=$TEAM_MEMBERS" >> "$GITHUB_ENV"
echo "member_full_names=$MEMBER_FULL_NAMES" >> "$GITHUB_ENV"
echo "parent_team=$PARENT_TEAM" >> "$GITHUB_ENV"
- name: Debug team info
run: |
echo "Maintainers to validate: $MAINTAINERS"
echo "Team members to validate: $TEAM_MEMBERS"
echo "Maintainer full names: $MAINTAINER_FULL_NAMES"
echo "Member full names: $MEMBER_FULL_NAMES"
env:
MAINTAINERS: ${{ env.maintainers }}
TEAM_MEMBERS: ${{ env.team_members }}
MAINTAINER_FULL_NAMES: ${{ env.maintainer_full_names }}
MEMBER_FULL_NAMES: ${{ env.member_full_names }}
- name: Validate GitHub usernames and full names
if: env.maintainers != '_No response_' || env.team_members != '_No response_'
shell: bash
run: |
validate_username_format() {
local username=$1
local username_regex='^[a-zA-Z0-9][a-zA-Z0-9-]{0,38}$'
if [[ ! "$username" =~ $username_regex ]]; then
echo "::error::Invalid GitHub username format: '$username'"
exit 1
fi
}
validate_username_existence() {
local username=$1
response=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/users/$username")
if echo "$response" | grep -q '"login":'; then
echo "✅ GitHub user exists: $username"
else
echo "::error::GitHub user does not exist: $username"
echo "Response: $response"
exit 1
fi
}
validate_full_name_with_api() {
local username=$1
local expected_full_name=$2
user_info=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/users/$username")
actual_name=$(echo "$user_info" | jq -r '.name')
if [[ "$actual_name" == "null" || -z "$actual_name" ]]; then
echo "::warning::No public full name for '$username'. Skipping name match."
return
fi
expected_full_name=$(echo "$expected_full_name" | xargs)
actual_name=$(echo "$actual_name" | xargs)
if [[ "${expected_full_name,,}" != "${actual_name,,}" ]]; then
echo "::error::Full name mismatch for '$username': expected '$expected_full_name', found '$actual_name'"
exit 1
fi
echo "✅ Full name matches for '$username'"
}
validate_user_block() {
local user_type=$1
local usernames=$2
local full_names=$3
if [[ "$usernames" == "_No response_" || "$usernames" == "none" || -z "$usernames" ]]; then
echo "No $user_type to validate."
return
fi
if [[ "$full_names" == "_No response_" || "$full_names" == "none" || -z "$full_names" ]]; then
echo "::error::Full names required for $user_type but missing."
exit 1
fi
IFS=',' read -ra USERNAME_ARRAY <<< "$usernames"
IFS=',' read -ra FULLNAME_ARRAY <<< "$full_names"
if [[ ${#USERNAME_ARRAY[@]} -ne ${#FULLNAME_ARRAY[@]} ]]; then
echo "::error::Mismatch in number of $user_type usernames and full names."
exit 1
fi
for i in "${!USERNAME_ARRAY[@]}"; do
username=$(echo "${USERNAME_ARRAY[$i]}" | xargs)
full_name=$(echo "${FULLNAME_ARRAY[$i]}" | xargs)
echo "🔍 Validating $user_type: '$username' -> '$full_name'"
validate_username_format "$username"
validate_username_existence "$username"
validate_full_name_with_api "$username" "$full_name"
done
}
validate_user_block "maintainers" "$MAINTAINERS" "$MAINTAINER_FULL_NAMES"
validate_user_block "team members" "$TEAM_MEMBERS" "$MEMBER_FULL_NAMES"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MAINTAINERS: ${{ env.maintainers }}
TEAM_MEMBERS: ${{ env.team_members }}
MAINTAINER_FULL_NAMES: ${{ env.maintainer_full_names }}
MEMBER_FULL_NAMES: ${{ env.member_full_names }}
- name: Create Pull Request for Team
if: steps.classify.outputs.type == 'team'
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.GH_PAT }}
commit-message: "Add team: ${{ env.team_name }}"
title: "Create team: ${{ env.team_name }}"
body: |
### Team Creation Request
**Team Name:** ${{ env.team_name }}
**Parent Team:** ${{ env.parent_team }}
**Visibility:** ${{ env.visibility }}
**Purpose:** ${{ env.purpose }}
**Maintainers:** ${{ env.maintainers }}
**Maintainer Full Names:** ${{ env.maintainer_full_names }}
**Team Members:** ${{ env.team_members }}
**Team Member Full Names:** ${{ env.member_full_names }}
Generated from issue #${{ github.event.issue.number }}
branch: "team-request/${{ github.event.issue.number }}"
labels: "team-creation"
paths: |
team-name.txt
maintainers_mapping.txt
team_members_mapping.txt
- name: Post a message in Slack
uses: slackapi/slack-github-action@v2.0.0
with:
webhook: ${{ secrets.SLACK_TOKEN }}
webhook-type: incoming-webhook
payload: |
text: "*New Team PR Created: `${{ env.team_name }}`*"
blocks:
- type: section
text:
type: mrkdwn
text: ":tada: A new PR has been created for team *`${{ env.team_name }}`*!\n*Purpose:* ${{ env.purpose }}\n*Maintainers:* ${{ env.maintainers }}\n*Parent:* `${{ env.parent_team }}`\n<https://github.com/${{ github.repository }}/pull/${{ steps.create_pr.outputs.pull-request-number }}|View Pull Request>"