#  D: Manual Dispatch
#  M: Merge release PR
#  C: Commit
#  ┌───────────┐     ┌─────────────┐     ┌────────────────┐
#  │Development├──D──►RC-Unreleased│  ┌──►Final-Unreleased│
#  └───────────┘     └─┬─────────▲─┘  │  └─┬────────────▲─┘
#                      │         │    │    │            │
#                      M         C    D    M            C
#                      │         │    │    │            │
#                     ┌▼─────────┴┐   │   ┌▼────────────┴┐
#                     │RC-Released├───┘   │Final-Released│
#                     └───────────┘       └──────────────┘
name: Release Cycle

on:
  push:
    branches:
      - release-v*
  workflow_dispatch: {}

concurrency: ${{ github.workflow }}-${{ github.ref }}

jobs:
  state:
    name: Check state
    permissions:
      pull-requests: read
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup
      - id: state
        name: Get state
        uses: actions/github-script@v6
        env:
          TRIGGERING_ACTOR: ${{ github.triggering_actor }}
        with:
          result-encoding: string
          script: await require('./scripts/release/workflow/state.js')({ github, context, core })
    outputs:
      # Job Flags
      start: ${{ steps.state.outputs.start }}
      changesets: ${{ steps.state.outputs.changesets }}
      promote: ${{ steps.state.outputs.promote }}
      publish: ${{ steps.state.outputs.publish }}
      merge: ${{ steps.state.outputs.merge }}

      # Global variables
      is_prerelease: ${{ steps.state.outputs.is_prerelease }}

  start:
    needs: state
    name: Start new release candidate
    permissions:
      contents: write
      actions: write
    if: needs.state.outputs.start == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup
      - run: bash scripts/git-user-config.sh
      - id: start
        name: Create branch with release candidate
        run: bash scripts/release/workflow/start.sh
      - name: Re-run workflow
        uses: actions/github-script@v6
        env:
          REF: ${{ steps.start.outputs.branch }}
        with:
          script: await require('./scripts/release/workflow/rerun.js')({ github, context })

  promote:
    needs: state
    name: Promote to final release
    permissions:
      contents: write
      actions: write
    if: needs.state.outputs.promote == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup
      - run: bash scripts/git-user-config.sh
      - name: Exit prerelease state
        if: needs.state.outputs.is_prerelease == 'true'
        run: bash scripts/release/workflow/exit-prerelease.sh
      - name: Re-run workflow
        uses: actions/github-script@v6
        with:
          script: await require('./scripts/release/workflow/rerun.js')({ github, context })

  changesets:
    needs: state
    name: Update PR to release
    permissions:
      contents: write
      pull-requests: write
    if: needs.state.outputs.changesets == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # To get all tags
      - name: Set up environment
        uses: ./.github/actions/setup
      - name: Set release title
        uses: actions/github-script@v6
        with:
          result-encoding: string
          script: await require('./scripts/release/workflow/set-changesets-pr-title.js')({ core })
      - name: Create PR
        uses: changesets/action@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PRERELEASE: ${{ needs.state.outputs.is_prerelease }}
        with:
          version: npm run version
          title: ${{ env.TITLE }}
          commit: ${{ env.TITLE }}
          body: | # Wait for support on this https://github.com/changesets/action/pull/250
            This is an automated PR for releasing ${{ github.repository }}
            Check [CHANGELOG.md](${{ github.repository }}/CHANGELOG.md)

  publish:
    needs: state
    name: Publish to npm
    environment: npm
    permissions:
      contents: write
    if: needs.state.outputs.publish == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup
      - id: pack
        name: Pack
        run: bash scripts/release/workflow/pack.sh
        env:
          PRERELEASE: ${{ needs.state.outputs.is_prerelease }}
      - name: Upload tarball artifact
        uses: actions/upload-artifact@v3
        with:
          name: ${{ github.ref_name }}
          path: ${{ steps.pack.outputs.tarball }}
      - name: Publish
        run: bash scripts/release/workflow/publish.sh
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
          TARBALL: ${{ steps.pack.outputs.tarball }}
          TAG: ${{ steps.pack.outputs.tag }}
      - name: Create Github Release
        uses: actions/github-script@v6
        env:
          PRERELEASE: ${{ needs.state.outputs.is_prerelease }}
        with:
          script: await require('./scripts/release/workflow/github-release.js')({ github, context })
    outputs:
      tarball_name: ${{ steps.pack.outputs.tarball_name }}

  integrity_check:
    needs: publish
    name: Tarball Integrity Check
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Download tarball artifact
        id: artifact
        # Replace with actions/upload-artifact@v3 when
        # https://github.com/actions/download-artifact/pull/194 gets released
        uses: actions/download-artifact@e9ef242655d12993efdcda9058dee2db83a2cb9b
        with:
          name: ${{ github.ref_name }}
      - name: Check integrity
        run: bash scripts/release/workflow/integrity-check.sh
        env:
          TARBALL: ${{ steps.artifact.outputs.download-path }}/${{ needs.publish.outputs.tarball_name }}

  merge:
    needs: state
    name: Create PR back to master
    permissions:
      contents: write
      pull-requests: write
    if: needs.state.outputs.merge == 'true'
    runs-on: ubuntu-latest
    env:
      MERGE_BRANCH: merge/${{ github.ref_name }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # All branches
      - name: Set up environment
        uses: ./.github/actions/setup
      - run: bash scripts/git-user-config.sh
      - name: Create branch to merge
        run: |
          git checkout -B "$MERGE_BRANCH" "$GITHUB_REF_NAME"
          git push -f origin "$MERGE_BRANCH"
      - name: Create PR back to master
        uses: actions/github-script@v6
        with:
          script: |
            await github.rest.pulls.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              head: process.env.MERGE_BRANCH,
              base: 'master',
              title: '${{ format('Merge {0} branch', github.ref_name) }}'
            });
