├── .github └── workflows │ ├── workflow-as-kubernetes-init.yaml │ ├── workflow-as-kubernetes.yaml │ └── workflow-golang-ci.yaml ├── .gitignore ├── LICENSE ├── OWNERS ├── README.md └── examples ├── golang-workflow.yaml ├── kube-workflow-init.yaml └── kube-workflow.yaml /.github/workflows/workflow-as-kubernetes-init.yaml: -------------------------------------------------------------------------------- 1 | name: Workflow As Kubernetes Initialization 2 | 3 | on: 4 | workflow_call: 5 | secrets: 6 | AGENT_TOKEN: 7 | required: true 8 | 9 | env: 10 | GH_TOKEN: ${{ secrets.AGENT_TOKEN }} 11 | 12 | jobs: 13 | initialize: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v2 19 | 20 | - name: Register required labels 21 | run: | 22 | # All needs-xxx should use #EDEDED. 23 | 24 | gh label create "enhancement" --color 99CCFF --description "Categorizes issue or PR as related to a new feature with API changes." --force 25 | gh label create "feature" --color 99CCFF --description "Categorizes issue or PR as related to a new feature." --force 26 | gh label create "cleanup" --color C5DEF5 --description "Categorizes issue or PR as related to cleaning up code, process, or technical debt." --force 27 | gh label create "bug" --color B60205 --description "Categorizes issue or PR as related to a bug." --force 28 | gh label create "documentation" --color C5DEF5 --description "Categorizes issue or PR as related to documentation." --force 29 | gh label create "failing-test" --color B60205 --description "Categorizes issue or PR as related to a consistently or frequently failing test." --force 30 | gh label create "support" --color d876e3 --description "Categorizes issue or PR as related to support." --force 31 | 32 | gh label create "lgtm" --color 0E8A16 --description "Looks good to me, indicates that a PR is ready to be merged." --force 33 | gh label create "approved" --color 0E8A16 --description "Indicates a PR has been approved by an approver from all required OWNERS files." --force 34 | 35 | gh label create "do-not-merge/hold" --color D22628 --description "Indicates that a PR should not merge because someone has issued a /hold command." --force 36 | gh label create "do-not-merge/needs-kind" --color D22628 --description "Indicates a PR lacks a `kind/foo` label and requires one." --force 37 | gh label create "do-not-merge/work-in-progress" --color D22628 --description "Indicates that a PR should not merge because it is a work in progress." --force 38 | 39 | gh label create "needs-priority" --color EDEDED --description "Indicates a PR lacks a `priority/foo` label and requires one." --force 40 | gh label create "important-critical-urgent" --color ff7500 --description "Highest priority. Must be actively worked on as someone's top priority right now." --force 41 | gh label create "important-soon" --color ff8936 --description "Must be staffed and worked on either currently, or very soon, ideally in time for the next release." --force 42 | gh label create "important-longterm" --color ffa400 --description "Important over the long term, but may not be staffed and/or may need multiple releases to complete." --force 43 | gh label create "backlog" --color E9CE8E --description "Higher priority than priority/awaiting-more-evidence." --force 44 | gh label create "awaiting-more-evidence" --color FEF2C0 --description "Lowest priority. Possibly useful, but not yet enough support to actually get it done." --force 45 | 46 | gh label create "needs-triage" --color EDEDED --description "Indicates an issue or PR lacks a `triage/foo` label and requires one." --force 47 | gh label create "needs-kind" --color EDEDED --description "Indicates a PR lacks a `kind/foo` label and requires one." --force 48 | gh label create "needs-priority" --color EDEDED --description "Indicates a PR lacks a `priority/foo` label and requires one." --force 49 | gh label create "triage/needs-information" --color 9D108A --description "Indicates an issue needs more information in order to work on it." --force 50 | gh label create "triage/accepted" --color C2E0C6 --description "Indicates an issue or PR is ready to be actively worked on." --force 51 | 52 | gh label create "api-change" --color E99695 --description "Indicates PR includes api change." --force 53 | -------------------------------------------------------------------------------- /.github/workflows/workflow-as-kubernetes.yaml: -------------------------------------------------------------------------------- 1 | name: Workflow As Kubernetes 2 | 3 | on: 4 | workflow_call: 5 | secrets: 6 | AGENT_TOKEN: 7 | required: true 8 | 9 | env: 10 | GH_TOKEN: ${{ secrets.AGENT_TOKEN }} 11 | GH_DEBUG: api 12 | 13 | jobs: 14 | on-new-push: 15 | if: github.event_name == 'pull_request_target' && github.event.action == 'synchronize' 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@v4 21 | 22 | - name: Remove lgtm label on new push 23 | run: | 24 | PR_NUMBER=${{ github.event.pull_request.number }} 25 | 26 | if [ -z "$PR_NUMBER" ]; then 27 | echo "No PR number found in push event context." 28 | exit 0 29 | fi 30 | 31 | if gh pr view $PR_NUMBER --json labels --jq '.labels[].name' | grep -q "lgtm"; then 32 | gh pr edit $PR_NUMBER --remove-label lgtm 33 | echo "Removed 'lgtm' label due to new push." 34 | fi 35 | 36 | on-issue-opened: 37 | if: github.event_name == 'issues' && github.event.action == 'opened' 38 | runs-on: ubuntu-latest 39 | 40 | steps: 41 | - name: Checkout code 42 | uses: actions/checkout@v4 43 | 44 | - name: Issue information 45 | run: | 46 | PR_NUMBER=$(jq -r '.issue.number' $GITHUB_EVENT_PATH) 47 | gh issue edit $PR_NUMBER --add-label "needs-triage,needs-priority" 48 | 49 | on-pr-opened: 50 | if: github.event_name == 'pull_request_target' && github.event.action == 'opened' 51 | runs-on: ubuntu-latest 52 | 53 | steps: 54 | - name: Checkout code 55 | uses: actions/checkout@v4 56 | 57 | - name: Install yq 58 | run: pip install yq 59 | 60 | - name: Issue information 61 | run: | 62 | PR_NUMBER=${{ github.event.pull_request.number }} 63 | AUTHOR=${{ github.event.pull_request.user.login }} 64 | 65 | gh pr edit $PR_NUMBER --add-label "needs-triage,needs-priority,do-not-merge/needs-kind" 66 | 67 | if yq ".approvers[] | select(. == \"$AUTHOR\") " OWNERS | grep -q "$AUTHOR"; then 68 | gh pr edit $PR_NUMBER --add-label "approved" 69 | fi 70 | 71 | - name: request for review 72 | run: | 73 | AUTHOR=${{ github.event.pull_request.user.login }} 74 | 75 | # request two reviewers from OWNERS file randomly 76 | PR_NUMBER=${{ github.event.pull_request.number }} 77 | REVIEWERS=$(yq '.reviewers[]' OWNERS | grep -v "^$AUTHOR$" | shuf | head -n 2 | paste -sd ",") 78 | gh pr edit $PR_NUMBER --add-reviewer $REVIEWERS 79 | 80 | # Work for issue & PR, ignore update for now 81 | on-new-comment: 82 | if: (github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && github.event.action == 'created' 83 | runs-on: ubuntu-latest 84 | 85 | steps: 86 | - name: Checkout code 87 | uses: actions/checkout@v4 88 | 89 | - name: Set up Python 90 | uses: actions/setup-python@v4 91 | with: 92 | python-version: "3.x" 93 | 94 | - name: Install yq 95 | run: pip install yq 96 | 97 | - name: Handle Comments 98 | run: | 99 | COMMENT_BODY=$(jq -r '.comment.body' $GITHUB_EVENT_PATH) 100 | ISSUE_NUMBER=$(jq -r '.issue.number' $GITHUB_EVENT_PATH) 101 | COMMENT_USER=$(jq -r '.comment.user.login' $GITHUB_EVENT_PATH) 102 | REPO_NAME=$(jq -r '.repository.full_name' $GITHUB_EVENT_PATH) 103 | EVENT_TYPE="" 104 | 105 | if [[ "${{ github.event.issue.pull_request }}" != "" ]]; then 106 | EVENT_TYPE="pr" 107 | else 108 | EVENT_TYPE="issue" 109 | fi 110 | 111 | # Initialize arrays to store the labels. 112 | LABELS_TO_ADD=() 113 | LABELS_TO_REMOVE=() 114 | ASSIGNEES_TO_ADD=() 115 | ASSIGNEES_TO_REMOVE=() 116 | REVIEWERS_TO_ADD=() 117 | REVIEWERS_TO_REMOVE=() 118 | 119 | while IFS= read -r COMMENT_LINE; do 120 | COMMENT_LINE=$(echo "$COMMENT_LINE" | sed 's/^[ \t]*//;s/[ \t]*$//' | tr -d '\r\n' | awk '{$1=$1};1') 121 | LABEL=$(echo "$COMMENT_LINE" | awk '{print $2}') 122 | REMAINING_CONTENT=$(echo "$COMMENT_LINE" | awk '{ $1=""; print substr($0,2) }') 123 | 124 | if [[ ! "$COMMENT_LINE" =~ ^/ ]]; then 125 | continue 126 | fi 127 | 128 | # Handle PR only. 129 | if [ "$EVENT_TYPE" == "pr" ]; then 130 | 131 | # Handle lgtm command 132 | if [[ "$COMMENT_LINE" =~ ^/lgtm ]]; then 133 | if [[ "$LABEL" =~ ^cancel ]]; then 134 | LABELS_TO_REMOVE+=("lgtm") 135 | ASSIGNEES_TO_REMOVE+=("$COMMENT_USER") 136 | echo "To remove label: lgtm" 137 | else 138 | LABELS_TO_ADD+=("lgtm") 139 | ASSIGNEES_TO_ADD+=("$COMMENT_USER") 140 | echo "To add label: lgtm" 141 | fi 142 | 143 | # Handle approved command 144 | elif [[ "$COMMENT_LINE" =~ ^/approve ]]; then 145 | if yq ".approvers[] | select(. == \"$COMMENT_USER\") " OWNERS | grep -q "$COMMENT_USER"; then 146 | if [[ "$LABEL" =~ ^cancel ]]; then 147 | LABELS_TO_REMOVE+=("approved") 148 | echo "To remove label: approved" 149 | else 150 | LABELS_TO_ADD+=("approved") 151 | echo "To add label: approved" 152 | fi 153 | else 154 | gh pr comment $ISSUE_NUMBER --body "Sorry, @$COMMENT_USER is not authorized to approve/unapprove this PR." 155 | echo "User $COMMENT_USER is not authorized to approve/unapprove this PR" 156 | fi 157 | 158 | # Handle hold command 159 | # hold/unhold the PR immediately in case of unexpected auto-merge. 160 | elif [[ "$COMMENT_LINE" =~ ^/hold ]]; then 161 | if [[ "$LABEL" =~ ^cancel ]]; then 162 | gh pr edit $ISSUE_NUMBER --remove-label do-not-merge/hold 163 | echo "Remove label: do-not-merge/hold" 164 | else 165 | gh pr edit $ISSUE_NUMBER --add-label do-not-merge/hold 166 | echo "Add label: do-not-merge/hold" 167 | fi 168 | 169 | # Handle cc 170 | # Can not require review from the author. 171 | elif [[ "$COMMENT_LINE" =~ ^/cc ]]; then 172 | USERNAME=$(echo "$LABEL" | sed 's/@//') 173 | if [[ -n "$USERNAME" ]]; then 174 | REVIEWERS_TO_ADD+=("$USERNAME") 175 | echo "To require review from $USERNAME" 176 | else 177 | REVIEWERS_TO_ADD+=("$COMMENT_USER") 178 | echo "To require review from $COMMENT_USER" 179 | fi 180 | elif [[ "$COMMENT_LINE" =~ ^/uncc ]]; then 181 | USERNAME=$(echo "$LABEL" | sed 's/@//') 182 | if [[ -n "$USERNAME" ]]; then 183 | REVIEWERS_TO_REMOVE+=("$USERNAME") 184 | echo "To remove review from $USERNAME" 185 | else 186 | REVIEWERS_TO_REMOVE+=("$COMMENT_USER") 187 | echo "To remove review from $COMMENT_USER" 188 | fi 189 | 190 | # handle retest command 191 | elif [[ "$COMMENT_LINE" =~ ^/retest ]]; then 192 | BRANCH_NAME=$(gh pr view $ISSUE_NUMBER --json headRefName -q '.headRefName') 193 | 194 | id=$(gh run list --branch $BRANCH_NAME --limit 10 --json databaseId,status,conclusion,name | jq -r '.[] | select(.conclusion=="failure" and .name=="CI Workflow") | .databaseId' | head -n 1) 195 | echo "The failed CI workflow: $id" 196 | if [[ "$COMMENT_LINE" == "/retest" ]]; then 197 | gh run rerun $id --failed 198 | else 199 | gh run rerun $id 200 | fi 201 | 202 | else 203 | echo "$COMMENT_LINE is not for PR type only, fall back to normal handler" 204 | fi 205 | fi 206 | 207 | # Handle kind command 208 | if [[ "$COMMENT_LINE" =~ ^/kind ]]; then 209 | if [[ "$LABEL" =~ ^lgtm || "$LABEL" =~ ^approve ]]; then 210 | echo "The label '$LABEL' cannot be added using /kind." 211 | else 212 | LABELS_TO_ADD+=("$LABEL") 213 | echo "To add label: '$LABEL'" 214 | LABELS_TO_REMOVE+=("needs-kind") 215 | LABELS_TO_REMOVE+=("do-not-merge/needs-kind") 216 | fi 217 | elif [[ "$COMMENT_LINE" =~ ^/remove-kind ]]; then 218 | if [[ "$LABEL" =~ ^lgtm || "$LABEL" =~ ^approve ]]; then 219 | echo "The label '$LABEL' cannot be removed using /remove-kind." 220 | else 221 | LABELS_TO_REMOVE+=("$LABEL") 222 | echo "To remove label: '$LABEL'" 223 | fi 224 | 225 | # Handle triage 226 | elif [[ "$COMMENT_LINE" =~ ^/wip || "COMMENT_LINE" =~ ^/WIP ]]; then 227 | if [[ "$LABEL" =~ ^cancel ]]; then 228 | LABELS_TO_REMOVE+=("do-not-merge/work-in-progress") 229 | else 230 | LABELS_TO_ADD+=("do-not-merge/work-in-progress") 231 | fi 232 | 233 | # Handle triage 234 | elif [[ "$COMMENT_LINE" =~ ^/triage ]]; then 235 | if [[ "$LABEL" =~ ^accepted ]]; then 236 | LABELS_TO_ADD+=("triage/accepted") 237 | LABELS_TO_REMOVE+=("needs-triage") 238 | LABELS_TO_REMOVE+=("triage/needs-information") 239 | elif [[ "$LABEL" =~ ^needs-information ]]; then 240 | LABELS_TO_ADD+=("triage/needs-information") 241 | fi 242 | 243 | # Handle assignment 244 | elif [[ "$COMMENT_LINE" =~ ^/assign ]]; then 245 | USERNAME=$(echo "$LABEL" | sed 's/@//') 246 | if [[ -n "$USERNAME" ]]; then 247 | ASSIGNEES_TO_ADD+=("$USERNAME") 248 | echo "To assign to $USERNAME" 249 | else 250 | ASSIGNEES_TO_ADD+=("$COMMENT_USER") 251 | echo "To assign to $COMMENT_USER" 252 | fi 253 | elif [[ "$COMMENT_LINE" =~ ^/unassign ]]; then 254 | USERNAME=$(echo "$LABEL" | sed 's/@//') 255 | if [[ -n "$USERNAME" ]]; then 256 | ASSIGNEES_TO_REMOVE+=("$USERNAME") 257 | echo "To unassign to $USERNAME" 258 | else 259 | ASSIGNEES_TO_REMOVE+=("$COMMENT_USER") 260 | echo "To unassign to $COMMENT_USER" 261 | fi 262 | 263 | # Handle help & good-first-issue 264 | elif [[ "$COMMENT_LINE" =~ ^/help ]]; then 265 | LABELS_TO_ADD+=("help wanted") 266 | elif [[ "$COMMENT_LINE" =~ ^/remove-help ]]; then 267 | LABELS_TO_REMOVE+=("help wanted") 268 | elif [[ "$COMMENT_LINE" =~ ^/good-first-issue ]]; then 269 | LABELS_TO_ADD+=("good first issue") 270 | elif [[ "$COMMENT_LINE" =~ ^/remove-good-first-issue ]]; then 271 | LABELS_TO_REMOVE+=("good first issue") 272 | elif [[ "$COMMENT_LINE" =~ ^/retitle ]]; then 273 | gh "$EVENT_TYPE" edit $ISSUE_NUMBER --title "$REMAINING_CONTENT" 274 | elif [[ "$COMMENT_LINE" =~ ^/reopen ]]; then 275 | gh "$EVENT_TYPE" reopen $ISSUE_NUMBER 276 | elif [[ "$COMMENT_LINE" =~ ^/close ]]; then 277 | gh "$EVENT_TYPE" close $ISSUE_NUMBER 278 | 279 | # Handle priority 280 | elif [[ "$COMMENT_LINE" =~ ^/priority ]]; then 281 | LABELS_TO_ADD+=("$LABEL") 282 | LABELS_TO_REMOVE+=("needs-priority") 283 | elif [[ "$COMMENT_LINE" =~ ^/remove-priority ]]; then 284 | LABELS_TO_REMOVE+=("$LABEL") 285 | 286 | # Handle milestone 287 | elif [[ "$COMMENT_LINE" =~ ^/milestone ]]; then 288 | if [[ "$LABEL" =~ ^clear ]]; then 289 | gh "$EVENT_TYPE" edit $ISSUE_NUMBER --milestone "" 290 | else 291 | gh "$EVENT_TYPE" edit $ISSUE_NUMBER --milestone "$LABEL" 292 | fi 293 | 294 | fi 295 | done <<< "$COMMENT_BODY" 296 | 297 | # Convert array to comma-separated strings. 298 | LABELS_TO_ADD_STR=$(IFS=,; echo "${LABELS_TO_ADD[*]}") 299 | LABELS_TO_REMOVE_STR=$(IFS=,; echo "${LABELS_TO_REMOVE[*]}") 300 | ASSIGNEES_TO_ADD_STR=$(IFS=,; echo "${ASSIGNEES_TO_ADD[*]}") 301 | ASSIGNEES_TO_REMOVE_STR=$(IFS=,; echo "${ASSIGNEES_TO_REMOVE[*]}") 302 | REVIEWERS_TO_ADD_STR=$(IFS=,; echo "${REVIEWERS_TO_ADD[*]}") 303 | REVIEWERS_TO_REMOVE_STR=$(IFS=,; echo "${REVIEWERS_TO_REMOVE[*]}") 304 | 305 | if [[ -n "$LABELS_TO_ADD_STR" ]]; then 306 | gh "$EVENT_TYPE" edit $ISSUE_NUMBER --add-label "$LABELS_TO_ADD_STR" 307 | fi 308 | 309 | if [[ -n "$LABELS_TO_REMOVE_STR" ]]; then 310 | gh "$EVENT_TYPE" edit $ISSUE_NUMBER --remove-label "$LABELS_TO_REMOVE_STR" 311 | fi 312 | 313 | if [[ -n "$ASSIGNEES_TO_ADD_STR" ]]; then 314 | # See related issue: https://github.com/kerthcet/github-workflow-as-kube/issues/7 315 | # gh "$EVENT_TYPE" edit $ISSUE_NUMBER --add-assignee "$ASSIGNEES_TO_ADD_STR" 316 | 317 | current_assignees=$(gh api "/repos/${REPO_NAME}/issues/${ISSUE_NUMBER}" --jq '.assignees[].login') 318 | all_assignees=() 319 | 320 | for assignee in $current_assignees; do 321 | echo "current assignee: $assignee" 322 | all_assignees+=("$assignee") 323 | done 324 | 325 | for assignee in "${ASSIGNEES_TO_ADD[@]}"; do 326 | echo "to add assignee: $assignee" 327 | all_assignees+=("$assignee") 328 | done 329 | 330 | for user in "${all_assignees[@]}"; do 331 | ASSIGNEES_ARGS+="-f "assignees[]=$user" " 332 | done 333 | 334 | # for user in "${ASSIGNEES_TO_ADD[@]}"; do 335 | # ASSIGNEES_ARGS+="-f "assignees[]=$user" " 336 | # done 337 | gh api -X PATCH "/repos/${REPO_NAME}/issues/${ISSUE_NUMBER}" ${ASSIGNEES_ARGS} 338 | fi 339 | 340 | if [[ -n "$ASSIGNEES_TO_REMOVE_STR" ]]; then 341 | gh "$EVENT_TYPE" edit $ISSUE_NUMBER --remove-assignee "$ASSIGNEES_TO_REMOVE_STR" 342 | fi 343 | 344 | if [ "$EVENT_TYPE" == "pr" ]; then 345 | if [[ -n "$REVIEWERS_TO_ADD_STR" ]]; then 346 | gh pr edit $ISSUE_NUMBER --add-reviewer "$REVIEWERS_TO_ADD_STR" 347 | fi 348 | 349 | # This is not work due to gh issue, see https://github.com/orgs/community/discussions/23054 350 | if [[ -n "$REVIEWERS_TO_REMOVE_STR" ]]; then 351 | gh pr edit $ISSUE_NUMBER --remove-reviewer "$REVIEWERS_TO_REMOVE_STR" 352 | fi 353 | fi 354 | 355 | auto-merge: 356 | if: (github.event_name == 'pull_request_target' && (github.event.action == 'labeled' || github.event.action == 'unlabeled') && github.event.pull_request.merged == false) || (github.event_name == 'issue_comment' && github.event.issue.pull_request != null && github.event.issue.state == 'open' && contains(github.event.comment.body, '/retest')) 357 | runs-on: ubuntu-latest 358 | 359 | steps: 360 | - name: Checkout code 361 | uses: actions/checkout@v4 362 | 363 | - name: Check lgtm and approved 364 | id: check_label 365 | run: | 366 | if [[ "${{ github.event_name }}" == "issue_comment" ]]; then 367 | PR_NUMBER=$(jq -r '.issue.number' "$GITHUB_EVENT_PATH") 368 | else 369 | PR_NUMBER=${{ github.event.pull_request.number }} 370 | fi 371 | 372 | LABELS=$(gh pr view $PR_NUMBER --json labels --jq '.labels[].name') 373 | if [[ "$LABELS" == *"lgtm"* && "$LABELS" == *"approved"* && "$LABELS" != *"do-not-merge"* ]]; then 374 | echo "Ready to merge" 375 | echo "::set-output name=ready::true" 376 | else 377 | echo "Not ready to merge" 378 | echo "::set-output name=ready::false" 379 | fi 380 | 381 | - name: Check PR Status 382 | id: check_status 383 | if: steps.check_label.outputs.ready == 'true' 384 | run: | 385 | #!/bin/bash 386 | PR_NUMBER=${{ github.event.pull_request.number }} 387 | REPO=${{ github.repository }} 388 | MAX_ATTEMPTS=360 # 30 minutes 389 | SLEEP_INTERVAL=5 390 | STATUS="" 391 | 392 | LABELS=$(gh pr view $PR_NUMBER --json labels --jq '.labels[].name') 393 | if ![[ "$LABELS" == *"lgtm"* && "$LABELS" == *"approved"* && "$LABELS" != *"do-not-merge"* ]]; then 394 | exit 0 395 | fi 396 | 397 | check_status() { 398 | CHECKS=$(gh pr checks $PR_NUMBER --repo $REPO --json state) 399 | 400 | SUCCESS_COUNT=$(echo "$CHECKS" | jq '[.[] | select(.state == "SUCCESS")] | length') 401 | FAILURE_COUNT=$(echo "$CHECKS" | jq '[.[] | select(.state == "FAILURE")] | length') 402 | SKIPPED_COUNT=$(echo "$CHECKS" | jq '[.[] | select(.state == "SKIPPED")] | length') 403 | QUEUED_COUNT=$(echo "$CHECKS" | jq '[.[] | select(.state == "QUEUED")] | length') 404 | WIP_COUNT=$(echo "$CHECKS" | jq '[.[] | select(.state == "IN_PROGRESS")] | length') 405 | 406 | # 1 means the auto-merge itself. 407 | if [ $WIP_COUNT -eq 1 ] && [ $FAILURE_COUNT -eq 0 ] && [ $QUEUED_COUNT -eq 0 ]; then 408 | STATUS=0 409 | echo "All checks passed or skipped." 410 | elif [ $FAILURE_COUNT -gt 0 ]; then 411 | STATUS=1 412 | echo "Some checks failed." 413 | else 414 | STATUS=2 415 | echo "Some checks are still in progress." 416 | fi 417 | } 418 | 419 | attempt=0 420 | while [ $attempt -lt $MAX_ATTEMPTS ]; do 421 | check_status 422 | echo "STATUS: $STATUS" 423 | 424 | if [ $STATUS -eq 0 ]; then 425 | echo "Checks passed. Proceeding with merge." 426 | echo "::set-output name=ready::true" 427 | exit 0 428 | elif [ $STATUS -eq 1 ]; then 429 | echo "Checks failed. Exiting." 430 | echo "::set-output name=ready::false" 431 | exit 1 432 | else 433 | echo "Waiting for checks to complete..." 434 | sleep $SLEEP_INTERVAL 435 | attempt=$((attempt + 1)) 436 | fi 437 | done 438 | 439 | echo "Max attempts reached. Some checks are still in progress. Exiting." 440 | echo "::set-output name=ready::false" 441 | exit 1 442 | 443 | - name: Auto-merge 444 | if: steps.check_label.outputs.ready == 'true' && steps.check_status.outputs.ready == 'true' 445 | run: | 446 | PR_NUMBER=${{ github.event.pull_request.number }} 447 | LABELS=$(gh pr view $PR_NUMBER --json labels --jq '.labels[].name') 448 | TITLE=$(gh pr view $PR_NUMBER --json title --jq '.title') 449 | 450 | echo "Merging PR: $PR_NUMBER" 451 | 452 | # Check again just incase labels removed during waiting time. 453 | if [[ "$LABELS" == *"lgtm"* && "$LABELS" == *"approved"* && "$LABELS" != *"do-not-merge"* ]]; then 454 | gh pr merge $PR_NUMBER --squash 455 | echo "Auto merged" 456 | fi 457 | -------------------------------------------------------------------------------- /.github/workflows/workflow-golang-ci.yaml: -------------------------------------------------------------------------------- 1 | name: Workflow for Golang CI 2 | 3 | on: 4 | workflow_call: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | env: 11 | GO_VERSION: "1.23.0" 12 | GOLANGCI_VERSION: "1.63.4" 13 | CGO_ENABLED: "0" 14 | 15 | jobs: 16 | golang-lint: 17 | name: golang-lint 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/setup-go@v5 21 | with: 22 | go-version: ${{ env.GO_VERSION }} 23 | - uses: actions/checkout@v4 24 | - name: golangci-lint 25 | uses: golangci/golangci-lint-action@v6 26 | with: 27 | version: v${{ env.GOLANGCI_VERSION }} 28 | args: --timeout=30m --config=.golangci.yaml 29 | 30 | unit-test: 31 | runs-on: ubuntu-latest 32 | 33 | steps: 34 | - name: Checkout code 35 | uses: actions/checkout@v2 36 | 37 | - name: Run unit tests 38 | run: | 39 | make test 40 | 41 | integration-test: 42 | runs-on: ubuntu-latest 43 | 44 | steps: 45 | - name: Checkout code 46 | uses: actions/checkout@v2 47 | 48 | - name: Run integration tests 49 | run: | 50 | make test-integration 51 | 52 | e2e-test: 53 | runs-on: ubuntu-latest 54 | 55 | steps: 56 | - name: Checkout code 57 | uses: actions/checkout@v2 58 | 59 | - name: Run e2e tests 60 | run: | 61 | make test-e2e 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerthcet/github-workflow-as-kube/209651f0518a4c8afe1105cfb697c5849619ecd9/.gitignore -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | approvers: 2 | - kerthcet 3 | 4 | reviewers: 5 | - kerthcet -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # github-workflow-as-kube 2 | 3 | [![Latest Release](https://img.shields.io/github/v/release/kerthcet/github-workflow-as-kube?include_prereleases)](https://github.com/kerthcet/github-workflow-as-kube/releases/latest) 4 | 5 | ## Commands 6 | 7 | This workflow following [Kubernetes habits](https://prow.k8s.io/command-help?repo=kubernetes%2Fkubernetes), offering commands: 8 | 9 | | Name | Add | Remove | Note | 10 | | ---- | ----- | ----- | -----| 11 | | Good First Issue | /good-first-issue | /remove-good-first-issue | | 12 | | Help | /help | /remove-help | | 13 | | LGTM | /lgtm | /lgtm cancel | | 14 | | Approve | /approve | /approve cancel | Only approvers have the privilege | 15 | | Hold | /hold | /hold cancel | PR will not be merged once hold | 16 | | Category | /kind feature | /remove-kind feature | Support kinds: `feature`, `cleanup`, `bug`, `documentation` and so on... | 17 | | Assignment | /assign @_somebody_ | /unassign @_somebody_ | When @nobody, will assign/unassign to the commenter | 18 | | Review Request | /cc @_somebody_ | /uncc @_somebody_ | When @nobody, will cc/uncc the commenter | 19 | | Priority | /priority backlog | /remove-priority backlog | Support priorities: `important-critical-urgent`, `important-soon`, `important-longterm`, `backlog`, `awaiting-more-evidence` | 20 | | Title | /retitle _title_name_ | No OP | | 21 | | Lifecycle | /reopen | /close | Authors and collaborators on the repository can trigger this command | 22 | | Milestone | /milestone v0.0.1 | /milestone clear | Create the milestone labels manually in advance | 23 | | Triage | /triage needs-information | /triage accepted | triage accepted will remove the `needs-triage` label | 24 | | WIP | /wip | /wip cancel | | 25 | | API Change | /kind api-change | /remove-kind api-change | | 26 | | ReTest | /retest | No OP | rerun all the failed tests, use `/retest all` to rerun all tests | 27 | 28 | **NOTE**: PR will be auto-merged once have `lgtm`, `approved` and no `do-not-merge/*` labels and all the workflow checks are passed like the ci tests. 29 | 30 | ## How To Use 31 | 32 | To use the workflow, you have to: 33 | 34 | - Provide a [OWNERS](./OWNERS) file, only the approvers have the privilege to tag `/approve` or `/approve cancel` 35 | - Provide a github [secret](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions) with the name of `AGENT_TOKEN`, it should have right permissions, like owning the privilege to create labels, which is required below. 36 | - Add the token owner to the repo's `Collaborators and teams` with written role. 37 | - Add the [init-workflow](./examples/kube-workflow-init.yaml) to your project under the path of `.github/workflows/`, then run the workflow manually, which will help you finish the setup, like creating necessary labels. 38 | - Add the [workflow](./examples/kube-workflow.yaml) to your project under the path of `.github/workflows/`. 39 | 40 | Then it should work now. 41 | 42 | ## Other workflows 43 | 44 | We support other workflows as well, you can select as your needed: 45 | 46 | - Golang ci workflow: running golang ci, golang tests. You should provide `make test`, `make test-integration`, `make test-e2e` primitives and do not edit the workflow name. 47 | 48 | ## Roadmap 49 | 50 | - Dispatch reviewers 51 | - PR review by AI agent. 52 | - PR size detecting support 53 | -------------------------------------------------------------------------------- /examples/golang-workflow.yaml: -------------------------------------------------------------------------------- 1 | # Do not edit the workflow name, used for reference in the CI pipeline. 2 | name: CI Workflow 3 | 4 | on: 5 | pull_request: 6 | types: 7 | - opened 8 | - synchronize 9 | 10 | jobs: 11 | golang-ci: 12 | # Use a released version for stable. 13 | uses: kerthcet/github-workflow-as-kube/.github/workflows/workflow-golang-ci.yaml@main 14 | -------------------------------------------------------------------------------- /examples/kube-workflow-init.yaml: -------------------------------------------------------------------------------- 1 | name: Initialization Workflow 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | init: 8 | # Use a released version for stable. 9 | uses: kerthcet/github-workflow-as-kube/.github/workflows/workflow-as-kubernetes-init.yaml@main 10 | secrets: 11 | AGENT_TOKEN: ${{ secrets.AGENT_TOKEN }} 12 | -------------------------------------------------------------------------------- /examples/kube-workflow.yaml: -------------------------------------------------------------------------------- 1 | name: Event Workflow 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | issue_comment: 8 | types: 9 | - created 10 | pull_request_target: 11 | types: 12 | - opened 13 | - synchronize 14 | - labeled 15 | - unlabeled 16 | 17 | jobs: 18 | event-handler: 19 | # Use a released version for stable. 20 | uses: kerthcet/github-workflow-as-kube/.github/workflows/workflow-as-kubernetes.yaml@main 21 | secrets: 22 | AGENT_TOKEN: ${{ secrets.AGENT_TOKEN }} 23 | --------------------------------------------------------------------------------