diff --git a/.github/workflows/labeler-cache-retention.yml b/.github/workflows/labeler-cache-retention.yml new file mode 100644 index 000000000000..151ff1c9d09d --- /dev/null +++ b/.github/workflows/labeler-cache-retention.yml @@ -0,0 +1,40 @@ +# Workflow template imported and updated from: +# https://github.com/dotnet/issue-labeler/wiki/Onboarding +# +# See labeler.md for more information +# +# Regularly restore the prediction models from cache to prevent cache eviction +name: "Labeler: Cache Retention" + +# For more information about GitHub's action cache limits and eviction policy, see: +# https://docs.github.com/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy + +on: + schedule: + - cron: "32 16 * * *" # 16:32 every day (arbitrary time daily) + + workflow_dispatch: + inputs: + cache_key: + description: "The cache key suffix to use for restoring the model from cache. Defaults to 'ACTIVE'." + required: true + default: "ACTIVE" + +env: + CACHE_KEY: ${{ inputs.cache_key || 'ACTIVE' }} + +jobs: + restore-cache: + # Do not automatically run the workflow on forks outside the 'dotnet' org + if: ${{ github.event_name == 'workflow_dispatch' || github.repository_owner == 'dotnet' }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + type: ["issues", "pulls"] + steps: + - uses: dotnet/issue-labeler/restore@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + type: ${{ matrix.type }} + cache_key: ${{ env.CACHE_KEY }} + fail-on-cache-miss: true diff --git a/.github/workflows/labeler-predict-issues.yml b/.github/workflows/labeler-predict-issues.yml new file mode 100644 index 000000000000..a12efc1512af --- /dev/null +++ b/.github/workflows/labeler-predict-issues.yml @@ -0,0 +1,60 @@ +# Workflow template imported and updated from: +# https://github.com/dotnet/issue-labeler/wiki/Onboarding +# +# See labeler.md for more information +# +# Predict labels for Issues using a trained model +name: "Labeler: Predict (Issues)" + +on: + # Only automatically predict area labels when issues are first opened + issues: + types: opened + + # Allow dispatching the workflow via the Actions UI, specifying ranges of numbers + workflow_dispatch: + inputs: + issues: + description: "Issue Numbers (comma-separated list of ranges)." + required: true + cache_key: + description: "The cache key suffix to use for restoring the model. Defaults to 'ACTIVE'." + required: true + default: "ACTIVE" + +env: + # Do not allow failure for jobs triggered automatically (as this causes red noise on the workflows list) + ALLOW_FAILURE: ${{ github.event_name == 'workflow_dispatch' }} + + LABEL_PREFIX: "area-" + THRESHOLD: 0.40 + DEFAULT_LABEL: "needs-area-label" + +jobs: + predict-issue-label: + # Do not automatically run the workflow on forks outside the 'dotnet' org + if: ${{ github.event_name == 'workflow_dispatch' || github.repository_owner == 'dotnet' }} + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: "Restore issues model from cache" + id: restore-model + uses: dotnet/issue-labeler/restore@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + type: issues + fail-on-cache-miss: ${{ env.ALLOW_FAILURE }} + quiet: true + + - name: "Predict issue labels" + id: prediction + if: ${{ steps.restore-model.outputs.cache-hit == 'true' }} + uses: dotnet/issue-labeler/predict@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + issues: ${{ inputs.issues || github.event.issue.number }} + label_prefix: ${{ env.LABEL_PREFIX }} + threshold: ${{ env.THRESHOLD }} + default_label: ${{ env.DEFAULT_LABEL }} + env: + GITHUB_TOKEN: ${{ github.token }} + continue-on-error: ${{ !env.ALLOW_FAILURE }} diff --git a/.github/workflows/labeler-predict-pulls.yml b/.github/workflows/labeler-predict-pulls.yml new file mode 100644 index 000000000000..7a89fa03f679 --- /dev/null +++ b/.github/workflows/labeler-predict-pulls.yml @@ -0,0 +1,73 @@ +# Workflow template imported and updated from: +# https://github.com/dotnet/issue-labeler/wiki/Onboarding +# +# See labeler.md for more information +# +# Predict labels for Pull Requests using a trained model +name: "Labeler: Predict (Pulls)" + +on: + # Per to the following documentation: + # https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target + # + # The `pull_request_target` event runs in the context of the base of the pull request, rather + # than in the context of the merge commit, as the `pull_request` event does. This prevents + # execution of unsafe code from the head of the pull request that could alter the repository + # or steal any secrets you use in your workflow. This event allows your workflow to do things + # like label or comment on pull requests from forks. + # + # Only automatically predict area labels when pull requests are first opened + pull_request_target: + types: opened + + # Configure the branches that need to have PRs labeled + branches: + - main + + # Allow dispatching the workflow via the Actions UI, specifying ranges of numbers + workflow_dispatch: + inputs: + pulls: + description: "Pull Request Numbers (comma-separated list of ranges)." + required: true + cache_key: + description: "The cache key suffix to use for restoring the model. Defaults to 'ACTIVE'." + required: true + default: "ACTIVE" + +env: + # Do not allow failure for jobs triggered automatically (this can block PR merge) + ALLOW_FAILURE: ${{ github.event_name == 'workflow_dispatch' }} + + LABEL_PREFIX: "area-" + THRESHOLD: 0.40 + DEFAULT_LABEL: "needs-area-label" + +jobs: + predict-pull-label: + # Do not automatically run the workflow on forks outside the 'dotnet' org + if: ${{ github.event_name == 'workflow_dispatch' || github.repository_owner == 'dotnet' }} + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: "Restore pulls model from cache" + id: restore-model + uses: dotnet/issue-labeler/restore@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + type: pulls + fail-on-cache-miss: ${{ env.ALLOW_FAILURE }} + quiet: true + + - name: "Predict pull labels" + id: prediction + if: ${{ steps.restore-model.outputs.cache-hit == 'true' }} + uses: dotnet/issue-labeler/predict@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + pulls: ${{ inputs.pulls || github.event.number }} + label_prefix: ${{ env.LABEL_PREFIX }} + threshold: ${{ env.THRESHOLD }} + default_label: ${{ env.DEFAULT_LABEL }} + env: + GITHUB_TOKEN: ${{ github.token }} + continue-on-error: ${{ !env.ALLOW_FAILURE }} diff --git a/.github/workflows/labeler-promote.yml b/.github/workflows/labeler-promote.yml new file mode 100644 index 000000000000..c01086c51779 --- /dev/null +++ b/.github/workflows/labeler-promote.yml @@ -0,0 +1,54 @@ +# Workflow template imported and updated from: +# https://github.com/dotnet/issue-labeler/wiki/Onboarding +# +# See labeler.md for more information +# +# Promote a model from staging to 'ACTIVE', backing up the currently 'ACTIVE' model +name: "Labeler: Promotion" + +on: + # Dispatched via the Actions UI, promotes the staged models from + # a staged slot into the prediction environment + workflow_dispatch: + inputs: + issues: + description: "Issues: Promote Model" + type: boolean + required: true + pulls: + description: "Pulls: Promote Model" + type: boolean + required: true + staged_key: + description: "The cache key suffix to use for promoting a staged model to 'ACTIVE'. Defaults to 'staged'." + required: true + default: "staged" + backup_key: + description: "The cache key suffix to use for backing up the currently active model. Defaults to 'backup'." + default: "backup" + +permissions: + actions: write + +jobs: + promote-issues: + if: ${{ inputs.issues }} + runs-on: ubuntu-latest + steps: + - name: "Promote Model for Issues" + uses: dotnet/issue-labeler/promote@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + type: "issues" + staged_key: ${{ inputs.staged_key }} + backup_key: ${{ inputs.backup_key }} + + promote-pulls: + if: ${{ inputs.pulls }} + runs-on: ubuntu-latest + steps: + - name: "Promote Model for Pull Requests" + uses: dotnet/issue-labeler/promote@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + type: "pulls" + staged_key: ${{ inputs.staged_key }} + backup_key: ${{ inputs.backup_key }} diff --git a/.github/workflows/labeler-train.yml b/.github/workflows/labeler-train.yml new file mode 100644 index 000000000000..79ad2db0cced --- /dev/null +++ b/.github/workflows/labeler-train.yml @@ -0,0 +1,161 @@ +# Workflow template imported and updated from: +# https://github.com/dotnet/issue-labeler/wiki/Onboarding +# +# See labeler.md for more information +# +# Train the Issues and Pull Requests models for label prediction +name: "Labeler: Training" + +on: + workflow_dispatch: + inputs: + type: + description: "Issues or Pull Requests" + type: choice + required: true + default: "Both" + options: + - "Both" + - "Issues" + - "Pull Requests" + + steps: + description: "Training Steps" + type: choice + required: true + default: "All" + options: + - "All" + - "Download Data" + - "Train Model" + - "Test Model" + + limit: + description: "Max number of items to download for training/testing the model (newest items are used). Defaults to the max number of pages times the page size." + type: number + page_size: + description: "Number of items per page in GitHub API requests. Defaults to 100 for issues, 25 for pull requests." + type: number + page_limit: + description: "Maximum number of pages to download for training/testing the model. Defaults to 1000 for issues, 4000 for pull requests." + type: number + cache_key_suffix: + description: "The cache key suffix to use for staged data/models (use 'ACTIVE' to bypass staging). Defaults to 'staged'." + required: true + default: "staged" + +env: + CACHE_KEY: ${{ inputs.cache_key_suffix }} + REPOSITORY: ${{ github.repository }} + LABEL_PREFIX: "area-" + THRESHOLD: "0.40" + LIMIT: ${{ inputs.limit }} + PAGE_SIZE: ${{ inputs.page_size }} + PAGE_LIMIT: ${{ inputs.page_limit }} + +jobs: + download-issues: + if: ${{ contains(fromJSON('["Both", "Issues"]'), inputs.type) && contains(fromJSON('["All", "Download Data"]'), inputs.steps) }} + runs-on: ubuntu-latest + permissions: + issues: read + steps: + - name: "Download Issues" + uses: dotnet/issue-labeler/download@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + type: "issues" + cache_key: ${{ env.CACHE_KEY }} + repository: ${{ env.REPOSITORY }} + label_prefix: ${{ env.LABEL_PREFIX }} + limit: ${{ env.LIMIT }} + page_size: ${{ env.PAGE_SIZE }} + page_limit: ${{ env.PAGE_LIMIT }} + env: + GITHUB_TOKEN: ${{ github.token }} + + download-pulls: + if: ${{ contains(fromJSON('["Both", "Pull Requests"]'), inputs.type) && contains(fromJSON('["All", "Download Data"]'), inputs.steps) }} + runs-on: ubuntu-latest + permissions: + pull-requests: read + steps: + - name: "Download Pull Requests" + uses: dotnet/issue-labeler/download@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + type: "pulls" + cache_key: ${{ env.CACHE_KEY }} + repository: ${{ env.REPOSITORY }} + label_prefix: ${{ env.LABEL_PREFIX }} + limit: ${{ env.LIMIT }} + page_size: ${{ env.PAGE_SIZE }} + page_limit: ${{ env.PAGE_LIMIT }} + env: + GITHUB_TOKEN: ${{ github.token }} + + train-issues: + if: ${{ always() && contains(fromJSON('["Both", "Issues"]'), inputs.type) && contains(fromJSON('["All", "Train Model"]'), inputs.steps) && contains(fromJSON('["success", "skipped"]'), needs.download-issues.result) }} + runs-on: ubuntu-latest + permissions: {} + needs: download-issues + steps: + - name: "Train Model for Issues" + uses: dotnet/issue-labeler/train@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + type: "issues" + data_cache_key: ${{ env.CACHE_KEY }} + model_cache_key: ${{ env.CACHE_KEY }} + + train-pulls: + if: ${{ always() && contains(fromJSON('["Both", "Pull Requests"]'), inputs.type) && contains(fromJSON('["All", "Train Model"]'), inputs.steps) && contains(fromJSON('["success", "skipped"]'), needs.download-pulls.result) }} + runs-on: ubuntu-latest + permissions: {} + needs: download-pulls + steps: + - name: "Train Model for Pull Requests" + uses: dotnet/issue-labeler/train@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + type: "pulls" + data_cache_key: ${{ env.CACHE_KEY }} + model_cache_key: ${{ env.CACHE_KEY }} + + test-issues: + if: ${{ always() && contains(fromJSON('["Both", "Issues"]'), inputs.type) && contains(fromJSON('["All", "Test Model"]'), inputs.steps) && contains(fromJSON('["success", "skipped"]'), needs.train-issues.result) }} + runs-on: ubuntu-latest + permissions: + issues: read + needs: train-issues + steps: + - name: "Test Model for Issues" + uses: dotnet/issue-labeler/test@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + type: "issues" + cache_key: ${{ env.CACHE_KEY }} + repository: ${{ env.REPOSITORY }} + label_prefix: ${{ env.LABEL_PREFIX }} + threshold: ${{ env.THRESHOLD }} + limit: ${{ env.LIMIT }} + page_size: ${{ env.PAGE_SIZE }} + page_limit: ${{ env.PAGE_LIMIT }} + env: + GITHUB_TOKEN: ${{ github.token }} + + test-pulls: + if: ${{ always() && contains(fromJSON('["Both", "Pull Requests"]'), inputs.type) && contains(fromJSON('["All", "Test Model"]'), inputs.steps) && contains(fromJSON('["success", "skipped"]'), needs.train-pulls.result) }} + runs-on: ubuntu-latest + permissions: + pull-requests: read + needs: train-pulls + steps: + - name: "Test Model for Pull Requests" + uses: dotnet/issue-labeler/test@46125e85e6a568dc712f358c39f35317366f5eed # v2.0.0 + with: + type: "pulls" + cache_key: ${{ env.CACHE_KEY }} + repository: ${{ env.REPOSITORY }} + label_prefix: ${{ env.LABEL_PREFIX }} + threshold: ${{ env.THRESHOLD }} + limit: ${{ env.LIMIT }} + page_size: ${{ env.PAGE_SIZE }} + page_limit: ${{ env.PAGE_LIMIT }} + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/labeler.md b/.github/workflows/labeler.md new file mode 100644 index 000000000000..502ebb98a8d0 --- /dev/null +++ b/.github/workflows/labeler.md @@ -0,0 +1,32 @@ +# Issue-Labeler Workflows + +This repository uses actions from [dotnet/issue-labeler](https://github.com/dotnet/issue-labeler) to predict area labels for issues and pull requests. + +The following workflow templates were imported and updated from [dotnet/issue-labeler/wiki/Onboarding](https://github.com/dotnet/issue-labeler/wiki/Onboarding): + +1. `labeler-cache-retention.yml` +2. `labeler-predict-issues.yml` +3. `labeler-predict-pulls.yml` +4. `labeler-promote.yml` +5. `labeler-train.yml` + +## Repository Configuration + +Across these workflows, the following changes were made to configure the issue labeler for this repository: + +1. Set `LABEL_PREFIX` to `"area-"`: + - `labeler-predict-issues.yml` + - `labeler-predict-pulls.yml` + - `labeler-train.yml` +2. Set `DEFAULT_LABEL` to `"needs-area-label"`: + - `labeler-predict-issues.yml` + - `labeler-predict-pulls.yml` +3. Remove the `EXCLUDED_AUTHORS` value as we do not bypass labeling for any authors' issues/pulls in this repository: + - `labeler-predict-issues.yml` + - `labeler-predict-pulls.yml` +4. Update the pull request labeling branches to include `main` and `release/*`: + - `labeler-predict-pulls.yml` +5. Remove the `repository` input for training the models against another repository: + - `labeler-train.yml` +6. Update the cache retention cron schedule to an arbitrary time of day: + - `labeler-cache-retention.yml`