Sync with template v16 #446
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Synchronizes the repo's labels with a centralized `labels.yml` file. | |
| # Requires `GITHUB_TOKEN` to have write permissions; if not, replace it with a custom token. | |
| name: Sync Labels | |
| permissions: | |
| issues: write | |
| pull-requests: write | |
| on: | |
| push: | |
| paths: [ | |
| '.github/workflows/sync-labels.yml', | |
| '.github/labels.yml' | |
| ] | |
| schedule: | |
| # At 00:00 UTC on Sunday | |
| - cron: '0 0 * * 0' | |
| workflow_dispatch: | |
| inputs: | |
| allow_custom: | |
| type: boolean | |
| description: Allow Custom Labels | |
| required: true | |
| default: true | |
| jobs: | |
| sync-labels: | |
| runs-on: ubuntu-latest | |
| steps: | |
| # Checkout the repository under $GITHUB_WORKSPACE, so the workflow can access it. | |
| # https://github.com/actions/checkout | |
| - name: Checkout sources | |
| uses: actions/checkout@v6 | |
| # Copy the local labels.yml if it exists, else fetch from remote. | |
| - name: Prepare labels.yml | |
| run: | | |
| if [ -f ".github/labels.yml" ]; then | |
| echo "Using local labels.yml file" | |
| cp .github/labels.yml ./labels.yml | |
| else | |
| echo "Local labels.yml file not found, fetching from remote" | |
| curl -sL "https://raw.githubusercontent.com/TerminalMC/.github/HEAD/.github/labels.yml" -o ./labels.yml | |
| fi | |
| - name: Convert to JSON | |
| run: yq -o=json ./labels.yml > ./labels.json | |
| - name: Sync labels | |
| uses: actions/github-script@v9 | |
| env: | |
| ALLOW_CUSTOM: ${{ github.event.inputs.allow_custom || 'true' }} | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require('fs'); | |
| // Parse the standard labels | |
| const standardLabels = JSON.parse(fs.readFileSync('./labels.json', 'utf8')); | |
| const { owner, repo } = context.repo; | |
| // Fetch the existing labels | |
| // https://docs.github.com/en/rest/issues/labels#list-labels-for-a-repository | |
| const existingLabels = await github.paginate(github.rest.issues.listLabelsForRepo, { | |
| owner, | |
| repo, | |
| per_page: 100 | |
| }); | |
| // Process the standard labels | |
| for (const standardLabel of standardLabels) { | |
| const name = standardLabel.name; | |
| if (!name) { | |
| console.error("Skipping label with no name"); | |
| continue; | |
| } | |
| const color = (standardLabel.color || 'ededed').replace(/^#/, '').toLowerCase(); | |
| const description = standardLabel.description || ''; | |
| const aliases = standardLabel.aliases || []; | |
| const shouldDelete = standardLabel.delete === true; | |
| // Initially assume we'll need to create it | |
| let create = !shouldDelete; | |
| // Iterate over existing labels, reversed to allow removal | |
| for (let i = existingLabels.length - 1; i >= 0; i--) { | |
| const existingLabel = existingLabels[i]; | |
| const existingName = existingLabel.name; | |
| // Check for a match against the name or any alias | |
| let matched = false; | |
| if (existingName === name) { | |
| matched = true; | |
| // label exists: no need to create | |
| create = false; | |
| console.log(`Found an existing label matching name "${name}"`) | |
| } else { | |
| if (aliases.includes(existingName)) { | |
| matched = true; | |
| console.log(`Found an existing label matching alias "${existingName}" of name "${name}"`) | |
| } | |
| } | |
| if (matched) { | |
| // Match found: delete or update | |
| if (shouldDelete) { | |
| // Delete | |
| // https://docs.github.com/en/rest/issues/labels#delete-a-label | |
| console.log(`Deleting label: "${existingName}"`); | |
| try { | |
| await github.rest.issues.deleteLabel({ owner, repo, name: existingName }); | |
| } catch (error) { | |
| console.error(`Failed to delete "${existingName}": ${error.message}`); | |
| } | |
| } else { | |
| // Update | |
| // https://docs.github.com/en/rest/issues/labels#update-a-label | |
| const rename = existingName !== name; | |
| const recolor = existingLabel.color !== color; | |
| const redesc = existingLabel.description !== description; | |
| if (rename || recolor || redesc) { | |
| console.log(`Updating label: "${existingName}"${rename ? " -> " + name : ""}`); | |
| try { | |
| await github.rest.issues.updateLabel({ | |
| owner, | |
| repo, | |
| name: existingName, | |
| new_name: rename ? name : undefined, | |
| color, | |
| description | |
| }); | |
| } catch (error) { | |
| console.error(`Failed to update "${existingName}": ${error.message}`); | |
| } | |
| } else { | |
| console.log(`No change for label: "${existingName}"`) | |
| } | |
| } | |
| // Remove from the list so it doesn't get deleted or updated again | |
| existingLabels.splice(i, 1); | |
| } | |
| } | |
| if (create) { | |
| // Label doesn't exist yet: create it | |
| // https://docs.github.com/en/rest/issues/labels#create-a-label | |
| console.log(`Creating label: "${name}"`); | |
| try { | |
| await github.rest.issues.createLabel({ | |
| owner, | |
| repo, | |
| name, | |
| color, | |
| description | |
| }); | |
| existingLabels.push(name); | |
| } catch (error) { | |
| console.error(`Failed to create "${name}": ${error.message}`); | |
| } | |
| } | |
| } | |
| // At this point we've deleted or updated every existing label that matched a | |
| // standard name or alias, so the existing list now only contains custom labels. | |
| if (process.env.ALLOW_CUSTOM !== "true") { | |
| console.log(`Cleaning up ${existingLabels.length} custom label(s)`) | |
| for (const existingLabel of existingLabels) { | |
| const existingName = existingLabel.name; | |
| // Delete | |
| // https://docs.github.com/en/rest/issues/labels#delete-a-label | |
| console.log(`Deleting label: "${existingName}"`); | |
| try { | |
| await github.rest.issues.deleteLabel({ owner, repo, name: existingName }); | |
| } catch (error) { | |
| console.error(`Failed to delete "${existingName}": ${error.message}`); | |
| } | |
| } | |
| } |