name: contrib

on:
  issue_comment:
    types: [created]
  pull_request_target:
    types:
      - opened
      - closed
      - synchronize
      - labeled
      - unlabeled
      - opened
      - reopened
      - edited
      # For jobs that don't run on draft PRs.
      - ready_for_review

# Only run one instance per PR to ensure in-order execution.
concurrency: pr-${{ github.ref }}

jobs:
  # Dependabot is annoying, but this makes it a bit less so.
  auto-approve-dependabot:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request_target'
    permissions:
      pull-requests: write
    steps:
      - name: auto-approve dependabot
        uses: hmarr/auto-approve-action@v4
        if: github.actor == 'dependabot[bot]'

  cla:
    runs-on: ubuntu-latest
    steps:
      - name: cla
        if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
        uses: contributor-assistant/github-action@v2.5.1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          # the below token should have repo scope and must be manually added by you in the repository's secret
          PERSONAL_ACCESS_TOKEN: ${{ secrets.CDRCOMMUNITY_GITHUB_TOKEN }}
        with:
          remote-organization-name: "coder"
          remote-repository-name: "cla"
          path-to-signatures: "v2022-09-04/signatures.json"
          path-to-document: "https://github.com/coder/cla/blob/main/README.md"
          # branch should not be protected
          branch: "main"
          # Some users have signed a corporate CLA with Coder so are exempt from signing our community one.
          allowlist: "coryb,aaronlehmann,dependabot*"

  release-labels:
    runs-on: ubuntu-latest
    # Skip tagging for draft PRs.
    if: ${{ github.event_name == 'pull_request_target' && !github.event.pull_request.draft }}
    steps:
      - name: release-labels
        uses: actions/github-script@v7
        with:
          # This script ensures PR title and labels are in sync:
          #
          # When release/breaking label is:
          # - Added, rename PR title to include ! (e.g. feat!:)
          # - Removed, rename PR title to strip ! (e.g. feat:)
          #
          # When title is:
          # - Renamed (+!), add the release/breaking label
          # - Renamed (-!), remove the release/breaking label
          script: |
            const releaseLabels = {
              breaking: "release/breaking",
            }

            const { action, changes, label, pull_request } = context.payload
            const { title } = pull_request
            const labels = pull_request.labels.map((label) => label.name)
            const isBreakingTitle = isBreaking(title)

            // Debug information.
            console.log("Action: %s", action)
            console.log("Title: %s", title)
            console.log("Labels: %s", labels.join(", "))

            const params = {
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
            }

            if (action === "opened" || action === "reopened") {
              if (isBreakingTitle && !labels.includes(releaseLabels.breaking)) {
                console.log('Add "%s" label', releaseLabels.breaking)
                await github.rest.issues.addLabels({
                  ...params,
                  labels: [releaseLabels.breaking],
                })
              }
            }

            if (action === "edited" && changes.title) {
              if (isBreakingTitle && !labels.includes(releaseLabels.breaking)) {
                console.log('Add "%s" label', releaseLabels.breaking)
                await github.rest.issues.addLabels({
                  ...params,
                  labels: [releaseLabels.breaking],
                })
              }

              if (!isBreakingTitle && labels.includes(releaseLabels.breaking)) {
                const wasBreakingTitle = isBreaking(changes.title.from)
                if (wasBreakingTitle) {
                  console.log('Remove "%s" label', releaseLabels.breaking)
                  await github.rest.issues.removeLabel({
                    ...params,
                    name: releaseLabels.breaking,
                  })
                } else {
                  console.log('Rename title from "%s" to "%s"', title, toBreaking(title))
                  await github.rest.issues.update({
                    ...params,
                    title: toBreaking(title),
                  })
                }
              }
            }

            if (action === "labeled") {
              if (label.name === releaseLabels.breaking && !isBreakingTitle) {
                console.log('Rename title from "%s" to "%s"', title, toBreaking(title))
                await github.rest.issues.update({
                  ...params,
                  title: toBreaking(title),
                })
              }
            }

            if (action === "unlabeled") {
              if (label.name === releaseLabels.breaking && isBreakingTitle) {
                console.log('Rename title from "%s" to "%s"', title, fromBreaking(title))
                await github.rest.issues.update({
                  ...params,
                  title: fromBreaking(title),
                })
              }
            }

            function isBreaking(t) {
              return t.split(" ")[0].endsWith("!:")
            }

            function toBreaking(t) {
              const parts = t.split(" ")
              return [parts[0].replace(/:$/, "!:"), ...parts.slice(1)].join(" ")
            }

            function fromBreaking(t) {
              const parts = t.split(" ")
              return [parts[0].replace(/!:$/, ":"), ...parts.slice(1)].join(" ")
            }
