diff --git a/.github/workflows/create-ready-for-testing-message.yml b/.github/workflows/create-ready-for-testing-message.yml index 2974f480..f7454585 100644 --- a/.github/workflows/create-ready-for-testing-message.yml +++ b/.github/workflows/create-ready-for-testing-message.yml @@ -1,27 +1,47 @@ -name: Create discord thread and comment on GitHub issue when script is ready for testing +name: Discord testing-thread lifecycle (ready / defer / close) on: issues: types: - labeled + - unlabeled + - closed permissions: issues: write actions: write jobs: - post_to_discord: + # --------------------------------------------------------------------------- + # Ready For Testing added -> create (or re-open) the Discord thread + comment + # --------------------------------------------------------------------------- + ready: runs-on: ubuntu-latest - if: contains(github.event.issue.labels.*.name, 'Ready For Testing') && github.repository == 'community-scripts/ProxmoxVED' + if: | + github.repository == 'community-scripts/ProxmoxVED' + && github.event.action == 'labeled' + && github.event.label.name == 'Ready For Testing' steps: - - name: Extract Issue Title and Script Type + - name: Extract Script Name and Type id: extract_info env: ISSUE_BODY: ${{ github.event.issue.body }} + ISSUE_TITLE: ${{ github.event.issue.title }} run: | - # Extract title (lowercase, spaces to dashes) - TITLE=$(echo '${{ github.event.issue.title }}' | tr '[:upper:]' '[:lower:]' | sed 's/ /-/g') - echo "TITLE=$TITLE" >> $GITHUB_ENV + # DISPLAY name: original issue-title capitalization (prose + Discord thread name) + echo "DISPLAY=$ISSUE_TITLE" >> $GITHUB_ENV + + # SLUG: from the "Name of the Script" field in the issue body, normalized to the + # repo's lowercase + spaces->dashes convention (e.g. "Alpine Cinny" -> "alpine-cinny"). + NAME_RAW=$(printf '%s\n' "$ISSUE_BODY" \ + | sed -n '/^###[[:space:]]*Name of the Script/,/^###/p' \ + | sed '1d;/^###/d;/^[[:space:]]*$/d' | head -n1) + if [ -z "$NAME_RAW" ]; then + # Fallback for issues not created from the template: use the title. + NAME_RAW="$ISSUE_TITLE" + fi + SLUG=$(echo "$NAME_RAW" | tr -d '\r' | tr '[:upper:]' '[:lower:]' | sed 's/[[:space:]]\+/-/g') + echo "SLUG=$SLUG" >> $GITHUB_ENV # Extract script type from issue body (using env var to handle special chars) if echo "$ISSUE_BODY" | grep -qi "CT (LXC Container)"; then @@ -34,7 +54,7 @@ jobs: SCRIPT_TYPE="pve" else # Fallback: detect by filename pattern or default to ct - if [[ "$TITLE" == *"-vm"* ]]; then + if [[ "$SLUG" == *"-vm"* ]]; then SCRIPT_TYPE="vm" else SCRIPT_TYPE="ct" @@ -42,7 +62,7 @@ jobs: fi echo "SCRIPT_TYPE=$SCRIPT_TYPE" >> $GITHUB_ENV - echo "Detected script type: $SCRIPT_TYPE for title: $TITLE" + echo "Detected script type: $SCRIPT_TYPE for slug: $SLUG (display: $DISPLAY)" - name: Check if Files Exist in community-scripts/ProxmoxVED id: check_files @@ -51,33 +71,33 @@ jobs: run: | REPO="community-scripts/ProxmoxVED" API_URL="https://api.github.com/repos/$REPO/contents" - TITLE="${{ env.TITLE }}" + SLUG="${{ env.SLUG }}" SCRIPT_TYPE="${{ env.SCRIPT_TYPE }}" # Define files based on script type case "$SCRIPT_TYPE" in ct) FILES=( - "ct/${TITLE}.sh" - "install/${TITLE}-install.sh" - "json/${TITLE}.json" + "ct/${SLUG}.sh" + "install/${SLUG}-install.sh" + "json/${SLUG}.json" ) ;; vm) FILES=( - "vm/${TITLE}.sh" - "json/${TITLE}.json" + "vm/${SLUG}.sh" + "json/${SLUG}.json" ) ;; addon) FILES=( - "tools/addon/${TITLE}.sh" - "json/${TITLE}.json" + "tools/addon/${SLUG}.sh" + "json/${SLUG}.json" ) ;; pve) FILES=( - "tools/pve/${TITLE}.sh" + "tools/pve/${SLUG}.sh" ) ;; esac @@ -98,24 +118,25 @@ jobs: - name: Create message to send id: create_message run: | - TITLE="${{ env.TITLE }}" + SLUG="${{ env.SLUG }}" + DISPLAY="${{ env.DISPLAY }}" SCRIPT_TYPE="${{ env.SCRIPT_TYPE }}" - VAR="The ${TITLE} script is ready for testing:\n" + VAR="The ${DISPLAY} script is ready for testing:\n" # Generate correct command based on script type case "$SCRIPT_TYPE" in ct) - VAR+="\`\`\`bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${TITLE}.sh)\"\`\`\`\n" + VAR+="\`\`\`bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${SLUG}.sh)\"\`\`\`\n" ;; vm) - VAR+="\`\`\`bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/vm/${TITLE}.sh)\"\`\`\`\n" + VAR+="\`\`\`bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/vm/${SLUG}.sh)\"\`\`\`\n" ;; addon) - VAR+="\`\`\`bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/tools/addon/${TITLE}.sh)\"\`\`\`\n" + VAR+="\`\`\`bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/tools/addon/${SLUG}.sh)\"\`\`\`\n" ;; pve) - VAR+="\`\`\`bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/tools/pve/${TITLE}.sh)\"\`\`\`\n" + VAR+="\`\`\`bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/tools/pve/${SLUG}.sh)\"\`\`\`\n" ;; esac @@ -123,7 +144,7 @@ jobs: JSON_FILE="" case "$SCRIPT_TYPE" in ct|vm|addon) - JSON_FILE="json/${TITLE}.json" + JSON_FILE="json/${SLUG}.json" ;; esac @@ -236,4 +257,124 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh workflow run push_json_to_pocketbase.yml --repo ${{ github.repository }} -f script_slug=${{ env.TITLE }} + gh workflow run push_json_to_pocketbase.yml --repo ${{ github.repository }} -f script_slug=${{ env.SLUG }} + + # --------------------------------------------------------------------------- + # deferred added OR Ready For Testing removed -> lock the Discord thread. + # On the 'deferred' label only: also post a GitHub note and lock the issue. + # --------------------------------------------------------------------------- + lock: + runs-on: ubuntu-latest + if: | + github.repository == 'community-scripts/ProxmoxVED' && ( + (github.event.action == 'labeled' && github.event.label.name == 'deferred') || + (github.event.action == 'unlabeled' && github.event.label.name == 'Ready For Testing') + ) + steps: + - name: Find Discord thread + id: find_thread + run: | + ISSUE_TITLE="${{ github.event.issue.title }}" + + THREAD_ID=$(curl -s "https://discord.com/api/v10/guilds/${{ secrets.DISCORD_GUILD_ID }}/threads/active" \ + -H "Authorization: Bot ${{ secrets.DISCORD_BOT_TOKEN }}" \ + -H "Content-Type: application/json" | \ + jq -r --arg TITLE "$ISSUE_TITLE" --arg PARENT_ID "${{ secrets.DISCORD_CHANNEL_ID }}" \ + '.threads[] | select(.parent_id == $PARENT_ID and .name == ("Wanted Tester for " + $TITLE)) | .id') + + if [ -n "$THREAD_ID" ]; then + echo "Found thread: $THREAD_ID" + echo "thread_id=$THREAD_ID" >> "$GITHUB_OUTPUT" + else + echo "No Discord thread found for: $ISSUE_TITLE" + fi + + - name: Get last GitHub issue comment + if: steps.find_thread.outputs.thread_id != '' + id: last_comment + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + LAST=$(gh issue view ${{ github.event.issue.number }} \ + --repo ${{ github.repository }} \ + --comments \ + --json comments \ + --jq '.comments[-1].body // empty') + + if [[ -z "$LAST" ]]; then + LAST=$(gh issue view ${{ github.event.issue.number }} \ + --repo ${{ github.repository }} \ + --json body \ + --jq '.body') + fi + + { + echo "last_comment<> "$GITHUB_OUTPUT" + + - name: Post deferral notice to Discord thread + if: steps.find_thread.outputs.thread_id != '' + env: + DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }} + THREAD_ID: ${{ steps.find_thread.outputs.thread_id }} + LAST_COMMENT: ${{ steps.last_comment.outputs.last_comment }} + ISSUE_URL: ${{ github.event.issue.html_url }} + run: | + MESSAGE="$(printf '⏸️ **This script has been deferred.**\n\nLast update from the issue:\n\n%s\n\n🔗 %s' "$LAST_COMMENT" "$ISSUE_URL")" + JSON_PAYLOAD=$(jq -n --arg content "$MESSAGE" '{content: $content}') + curl -s -X POST "https://discord.com/api/v10/channels/$THREAD_ID/messages" \ + -H "Authorization: Bot $DISCORD_BOT_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$JSON_PAYLOAD" + + - name: Lock Discord thread + if: steps.find_thread.outputs.thread_id != '' + run: | + curl -s -X PATCH "https://discord.com/api/v10/channels/${{ steps.find_thread.outputs.thread_id }}" \ + -H "Authorization: Bot ${{ secrets.DISCORD_BOT_TOKEN }}" \ + -H "Content-Type: application/json" \ + -d '{"locked": true}' + + - name: Add deferral note to GitHub issue + if: github.event.action == 'labeled' && github.event.label.name == 'deferred' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh issue comment ${{ github.event.issue.number }} \ + --repo ${{ github.repository }} \ + --body "⏸️ This script has been **deferred**. The Discord testing thread has been locked. Re-add the \`Ready For Testing\` label to resume testing." + + - name: Lock GitHub issue + if: github.event.action == 'labeled' && github.event.label.name == 'deferred' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api --method PUT /repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/lock \ + -f lock_reason=resolved + + # --------------------------------------------------------------------------- + # Issue closed -> delete the Discord thread + # --------------------------------------------------------------------------- + close: + runs-on: ubuntu-latest + if: github.repository == 'community-scripts/ProxmoxVED' && github.event.action == 'closed' + env: + ISSUE_TITLE: ${{ github.event.issue.title }} + steps: + - name: Get thread-ID and delete thread + run: | + THREAD_ID=$(curl -s -X GET "https://discord.com/api/v10/guilds/${{ secrets.DISCORD_GUILD_ID }}/threads/active" \ + -H "Authorization: Bot ${{ secrets.DISCORD_BOT_TOKEN }}" \ + -H "Content-Type: application/json" | \ + jq -r --arg TITLE "$ISSUE_TITLE" --arg PARENT_ID "${{ secrets.DISCORD_CHANNEL_ID }}" \ + '.threads[] | select(.parent_id == $PARENT_ID and .name == ("Wanted Tester for " + $TITLE)) | .id') + + if [ -n "$THREAD_ID" ]; then + echo "Thread found: $THREAD_ID. Deleting..." + curl -X DELETE "https://discord.com/api/v10/channels/$THREAD_ID" \ + -H "Authorization: Bot ${{ secrets.DISCORD_BOT_TOKEN }}" + else + echo "No thread found for issue: $ISSUE_TITLE" + fi diff --git a/.github/workflows/defer-discord-thread.yml b/.github/workflows/defer-discord-thread.yml deleted file mode 100644 index 7e37b5d0..00000000 --- a/.github/workflows/defer-discord-thread.yml +++ /dev/null @@ -1,92 +0,0 @@ -name: Lock Discord Thread and GitHub Issue on Deferral - -on: - issues: - types: - - labeled - - unlabeled - -permissions: - issues: write - -jobs: - defer_discord_thread: - runs-on: ubuntu-latest - if: | - github.repository == 'community-scripts/ProxmoxVED' && ( - (github.event.action == 'labeled' && github.event.label.name == 'deferred') || - (github.event.action == 'unlabeled' && github.event.label.name == 'Ready For Testing') - ) - steps: - - name: Find Discord thread - id: find_thread - run: | - ISSUE_TITLE="${{ github.event.issue.title }}" - - THREAD_ID=$(curl -s "https://discord.com/api/v10/guilds/${{ secrets.DISCORD_GUILD_ID }}/threads/active" \ - -H "Authorization: Bot ${{ secrets.DISCORD_BOT_TOKEN }}" \ - -H "Content-Type: application/json" | \ - jq -r --arg TITLE "$ISSUE_TITLE" --arg PARENT_ID "${{ secrets.DISCORD_CHANNEL_ID }}" \ - '.threads[] | select(.parent_id == $PARENT_ID and .name == ("Wanted Tester for " + $TITLE)) | .id') - - if [ -n "$THREAD_ID" ]; then - echo "Found thread: $THREAD_ID" - echo "thread_id=$THREAD_ID" >> "$GITHUB_OUTPUT" - else - echo "No Discord thread found for: $ISSUE_TITLE" - fi - - - name: Get last GitHub issue comment - if: steps.find_thread.outputs.thread_id != '' - id: last_comment - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - LAST=$(gh issue view ${{ github.event.issue.number }} \ - --repo ${{ github.repository }} \ - --comments \ - --json comments \ - --jq '.comments[-1].body // empty') - - if [[ -z "$LAST" ]]; then - LAST=$(gh issue view ${{ github.event.issue.number }} \ - --repo ${{ github.repository }} \ - --json body \ - --jq '.body') - fi - - { - echo "last_comment<> "$GITHUB_OUTPUT" - - - name: Post deferral notice to Discord thread - if: steps.find_thread.outputs.thread_id != '' - env: - DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }} - THREAD_ID: ${{ steps.find_thread.outputs.thread_id }} - LAST_COMMENT: ${{ steps.last_comment.outputs.last_comment }} - ISSUE_URL: ${{ github.event.issue.html_url }} - run: | - MESSAGE="$(printf '⏸️ **This script has been deferred.**\n\nLast update from the issue:\n\n%s\n\n🔗 %s' "$LAST_COMMENT" "$ISSUE_URL")" - JSON_PAYLOAD=$(jq -n --arg content "$MESSAGE" '{content: $content}') - curl -s -X POST "https://discord.com/api/v10/channels/$THREAD_ID/messages" \ - -H "Authorization: Bot $DISCORD_BOT_TOKEN" \ - -H "Content-Type: application/json" \ - -d "$JSON_PAYLOAD" - - - name: Lock Discord thread - if: steps.find_thread.outputs.thread_id != '' - run: | - curl -s -X PATCH "https://discord.com/api/v10/channels/${{ steps.find_thread.outputs.thread_id }}" \ - -H "Authorization: Bot ${{ secrets.DISCORD_BOT_TOKEN }}" \ - -H "Content-Type: application/json" \ - -d '{"locked": true}' - - - name: Lock GitHub issue - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh api --method PUT /repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/lock \ - -f lock_reason=resolved diff --git a/.github/workflows/delete-discord-thread.yml b/.github/workflows/delete-discord-thread.yml deleted file mode 100644 index 1c36059e..00000000 --- a/.github/workflows/delete-discord-thread.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Delete Discord Thread on GitHub Issue Closure - -on: - issues: - types: - - closed - -jobs: - close_discord_thread: - if: github.repository == 'community-scripts/ProxmoxVED' - runs-on: ubuntu-latest - env: - ISSUE_TITLE: ${{ github.event.issue.title }} - - steps: - - name: Get thread-ID op and close thread - run: | - THREAD_ID=$(curl -s -X GET "https://discord.com/api/v10/guilds/${{ secrets.DISCORD_GUILD_ID }}/threads/active" \ - -H "Authorization: Bot ${{ secrets.DISCORD_BOT_TOKEN }}" \ - -H "Content-Type: application/json" | \ - jq -r --arg TITLE "$ISSUE_TITLE" '.threads[] | select(.parent_id == "${{ secrets.DISCORD_CHANNEL_ID }}" and .name == ("Wanted Tester for " + $TITLE)) | .id') - - if [ -n "$THREAD_ID" ]; then - echo "Thread found: $THREAD_ID. Archiving..." - curl -X DELETE "https://discord.com/api/v10/channels/$THREAD_ID" \ - -H "Authorization: Bot ${{ secrets.DISCORD_BOT_TOKEN }}" - else - echo "No thread found for issue: $ISSUE_TITLE" - fi diff --git a/.github/workflows/delete_new_script.yaml b/.github/workflows/delete_new_script.yaml index 678e4e75..c217833d 100644 --- a/.github/workflows/delete_new_script.yaml +++ b/.github/workflows/delete_new_script.yaml @@ -25,9 +25,23 @@ jobs: - name: Extract Issue Info and Script Type id: extract_info + env: + ISSUE_BODY: ${{ github.event.issue.body }} + ISSUE_TITLE: ${{ github.event.issue.title }} run: | - TITLE=$(echo '${{ github.event.issue.title }}' | tr '[:upper:]' '[:lower:]' | tr -d ' ') - BODY='${{ github.event.issue.body }}' + BODY="$ISSUE_BODY" + + # TITLE (slug): from the "Name of the Script" field in the issue body, normalized to + # the repo's lowercase + spaces->dashes convention (e.g. "Alpine Cinny" -> "alpine-cinny"). + # This must match the slug move-to-main-repo.yaml used to create the files, otherwise the + # delete branch would not find them. Falls back to the title if the field is absent. + NAME_RAW=$(printf '%s\n' "$BODY" \ + | sed -n '/^###[[:space:]]*Name of the Script/,/^###/p' \ + | sed '1d;/^###/d;/^[[:space:]]*$/d' | head -n1) + if [ -z "$NAME_RAW" ]; then + NAME_RAW="$ISSUE_TITLE" + fi + TITLE=$(echo "$NAME_RAW" | tr -d '\r' | tr '[:upper:]' '[:lower:]' | sed 's/[[:space:]]\+/-/g') echo "TITLE=$TITLE" >> $GITHUB_ENV diff --git a/.github/workflows/move-to-main-repo.yaml b/.github/workflows/move-to-main-repo.yaml index a4ae86f0..64eb1f38 100644 --- a/.github/workflows/move-to-main-repo.yaml +++ b/.github/workflows/move-to-main-repo.yaml @@ -60,11 +60,23 @@ jobs: exit 1 fi - script_name=$(echo "$filtered_issue" | jq -r '.title' | tr '[:upper:]' '[:lower:]' | tr -d ' ') + issue_title=$(echo "$filtered_issue" | jq -r '.title') issue_nr=$(echo "$filtered_issue" | jq -r '.number') issue_body=$(echo "$filtered_issue" | jq -r '.body') + # script_name (slug): from the "Name of the Script" field in the issue body, + # normalized to the repo's lowercase + spaces->dashes convention + # (e.g. "Alpine Cinny" -> "alpine-cinny"). Falls back to the title if absent. + name_raw=$(printf '%s\n' "$issue_body" \ + | sed -n '/^###[[:space:]]*Name of the Script/,/^###/p' \ + | sed '1d;/^###/d;/^[[:space:]]*$/d' | head -n1) + if [ -z "$name_raw" ]; then + name_raw="$issue_title" + fi + script_name=$(echo "$name_raw" | tr -d '\r' | tr '[:upper:]' '[:lower:]' | sed 's/[[:space:]]\+/-/g') + echo "Script Name: $script_name" + echo "Issue Title: $issue_title" echo "Issue Number: $issue_nr" # Detect script type from issue body @@ -90,6 +102,12 @@ jobs: echo "script_name=$script_name" >> $GITHUB_OUTPUT echo "issue_nr=$issue_nr" >> $GITHUB_OUTPUT echo "script_type=$script_type" >> $GITHUB_OUTPUT + echo "issue_title=$issue_title" >> $GITHUB_OUTPUT + { + echo "issue_body<> $GITHUB_OUTPUT - name: Check if script files exist id: check_files @@ -254,15 +272,24 @@ jobs: id: create_pull_request env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + ISSUE_TITLE: ${{ steps.list_issues.outputs.issue_title }} + ISSUE_BODY: ${{ steps.list_issues.outputs.issue_body }} + ISSUE_NR: ${{ steps.list_issues.outputs.issue_nr }} run: | - script_name="${{ steps.list_issues.outputs.script_name }}" - script_type="${{ steps.list_issues.outputs.script_type }}" + # PR body = original issue content + automated-migration footer + printf '%s\n' "$ISSUE_BODY" > pr_body.md + { + echo "" + echo "---" + echo "Automated migration from ProxmoxVED (community-scripts/ProxmoxVED#${ISSUE_NR})." + } >> pr_body.md + gh pr create \ --repo community-scripts/ProxmoxVE \ --head "$branch_name" \ --base main \ - --title "${script_name}" \ - --body "Automated migration of **${script_name}** (type: ${script_type}) from ProxmoxVED to ProxmoxVE." + --title "$ISSUE_TITLE" \ + --body-file pr_body.md PR_NUMBER=$(gh pr list --repo community-scripts/ProxmoxVE --head "$branch_name" --json number --jq '.[].number') echo "PR_NUMBER=$PR_NUMBER" echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT