#
# REF:
#   1. https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrixinclude
#   2. https://github.com/JasonEtco/create-an-issue
#   3. https://docs.github.com/en/actions/learn-github-actions/variables
#   4. https://github.com/actions/github-script
#   5. https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds
#
name: Nightly Build

on:
  push:
    branches:
      - nightly   # Just for test purpose only with the nightly repo
  # This schedule will run only from the default branch
  schedule:
    - cron: '15 0 * * *' # run at 00:15 AM UTC
  workflow_dispatch:

defaults:
  run:
    shell: bash

jobs:
  prepare:
    name: Prepare
    runs-on: ubuntu-latest
    # This job is required by the release job, so we should make it run both from Nushell repo and nightly repo
    # if: github.repository == 'nushell/nightly'
    # Map a step output to a job output
    outputs:
      skip: ${{ steps.vars.outputs.skip }}
      build_date: ${{ steps.vars.outputs.build_date }}
      nightly_tag: ${{ steps.vars.outputs.nightly_tag }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        if: github.repository == 'nushell/nightly'
        with:
          ref: main
          fetch-depth: 0
          # Configure PAT here: https://github.com/settings/tokens for the push operation in the following steps
          token: ${{ secrets.WORKFLOW_TOKEN }}

      - name: Setup Nushell
        uses: hustcer/setup-nu@v3
        if: github.repository == 'nushell/nightly'
        with:
          version: 0.105.1

      # Synchronize the main branch of nightly repo with the main branch of Nushell official repo
      - name: Prepare for Nightly Release
        shell: nu {0}
        if: github.repository == 'nushell/nightly'
        run: |
          cd $env.GITHUB_WORKSPACE
          git checkout main
          # We can't push if no user name and email are configured
          git config user.name 'hustcer'
          git config user.email 'hustcer@outlook.com'
          git pull origin main
          git remote add src https://github.com/nushell/nushell.git
          git fetch src main
          # All the changes will be overwritten by the upstream main branch
          git reset --hard src/main
          git push origin main -f

      - name: Create Tag and Output Tag Name
        if: github.repository == 'nushell/nightly'
        id: vars
        shell: nu {0}
        run: |
          let date = date now | format date %m%d
          let version = open Cargo.toml | get package.version
          let sha_short = (git rev-parse --short origin/main | str trim | str substring 0..6)
          let latest_meta = http get https://api.github.com/repos/nushell/nightly/releases
                            | sort-by -r created_at
                            | where tag_name =~ nightly
                            | get tag_name?.0? | default ''
                            | parse '{version}-nightly.{build}+{hash}'
          if ($latest_meta.0?.hash? | default '') == $sha_short {
            print $'(ansi g)Latest nightly build is up-to-date, skip rebuilding.(ansi reset)'
            $'skip=true(char nl)' o>> $env.GITHUB_OUTPUT
            exit 0
          }
          let prev_ver = $latest_meta.0?.version? | default '0.0.0'
          let build = if ($latest_meta | is-empty) or ($version != $prev_ver) { 1 } else {
              ($latest_meta | get build?.0? | default 0 | into int) + 1
            }
          let nightly_tag = $'($version)-nightly.($build)+($sha_short)'
          $'build_date=($date)(char nl)' o>> $env.GITHUB_OUTPUT
          $'nightly_tag=($nightly_tag)(char nl)' o>> $env.GITHUB_OUTPUT
          if (git ls-remote --tags origin $nightly_tag | is-empty) {
            ls **/Cargo.toml | each {|file|
              open --raw $file.name
                  | str replace --all $'version = "($version)"' $'version = "($version)-nightly.($build)"'
                  | save --force $file.name
            }
            # Disable the following two workflows for the automatic committed changes
            rm .github/workflows/ci.yml
            rm .github/workflows/audit.yml

            git add .
            git commit -m $'Update version to ($version)-nightly.($build)'
            git tag -a $nightly_tag -m $'Nightly build from ($sha_short)'
            git push origin --tags
            git push origin main -f
          }

  release:
    name: Nu
    needs: prepare
    if: needs.prepare.outputs.skip != 'true'
    strategy:
      fail-fast: false
      matrix:
        target:
        - aarch64-apple-darwin
        - x86_64-apple-darwin
        - x86_64-pc-windows-msvc
        - aarch64-pc-windows-msvc
        - x86_64-unknown-linux-gnu
        - x86_64-unknown-linux-musl
        - aarch64-unknown-linux-gnu
        - aarch64-unknown-linux-musl
        - armv7-unknown-linux-gnueabihf
        - armv7-unknown-linux-musleabihf
        - riscv64gc-unknown-linux-gnu
        - loongarch64-unknown-linux-gnu
        - loongarch64-unknown-linux-musl
        include:
        - target: aarch64-apple-darwin
          os: macos-latest
        - target: x86_64-apple-darwin
          os: macos-latest
        - target: x86_64-pc-windows-msvc
          os: windows-latest
        - target: aarch64-pc-windows-msvc
          os: windows-11-arm
        - target: x86_64-unknown-linux-gnu
          os: ubuntu-22.04
        - target: x86_64-unknown-linux-musl
          os: ubuntu-22.04
        - target: aarch64-unknown-linux-gnu
          os: ubuntu-22.04
        - target: aarch64-unknown-linux-musl
          os: ubuntu-22.04
        - target: armv7-unknown-linux-gnueabihf
          os: ubuntu-22.04
        - target: armv7-unknown-linux-musleabihf
          os: ubuntu-22.04
        - target: riscv64gc-unknown-linux-gnu
          os: ubuntu-22.04
        - target: loongarch64-unknown-linux-gnu
          os: ubuntu-22.04
        - target: loongarch64-unknown-linux-musl
          os: ubuntu-22.04

    runs-on: ${{matrix.os}}
    steps:
    - uses: actions/checkout@v4
      with:
        ref: main
        fetch-depth: 0

    - name: Install Wix Toolset 6 for Windows
      shell: pwsh
      if: ${{ startsWith(matrix.os, 'windows') }}
      run: |
        dotnet tool install --global wix --version 6.0.0
        dotnet workload install wix
        $wixPath = "$env:USERPROFILE\.dotnet\tools"
        echo "$wixPath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
        $env:PATH = "$wixPath;$env:PATH"
        wix --version

    - name: Update Rust Toolchain Target
      run: |
        echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml

    - name: Setup Rust toolchain and cache
      uses: actions-rust-lang/setup-rust-toolchain@v1
      # WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
      with:
        cache: false
        rustflags: ''

    - name: Setup Nushell
      uses: hustcer/setup-nu@v3
      with:
        version: 0.105.1

    - name: Release Nu Binary
      id: nu
      run: nu .github/workflows/release-pkg.nu
      env:
        OS: ${{ matrix.os }}
        REF: ${{ github.ref }}
        TARGET: ${{ matrix.target }}

    - name: Create an Issue for Release Failure
      if: ${{ failure() }}
      uses: JasonEtco/create-an-issue@v2
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        update_existing: true
        search_existing: open
        filename: .github/AUTO_ISSUE_TEMPLATE/nightly-build-fail.md

    # REF: https://github.com/marketplace/actions/gh-release
    # Create a release only in nushell/nightly repo
    - name: Publish Archive
      uses: softprops/action-gh-release@v2.0.9
      if: ${{ startsWith(github.repository, 'nushell/nightly') }}
      with:
        prerelease: true
        files: |
          ${{ steps.nu.outputs.msi }}
          ${{ steps.nu.outputs.archive }}
        tag_name: ${{ needs.prepare.outputs.nightly_tag }}
        name: ${{ needs.prepare.outputs.build_date }}-${{ needs.prepare.outputs.nightly_tag }}
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  sha256sum:
    needs: [prepare, release]
    name: Create Sha256sum
    runs-on: ubuntu-latest
    if: github.repository == 'nushell/nightly'
    steps:
    - name: Download Release Archives
      env:
        GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: >-
        gh release download ${{ needs.prepare.outputs.nightly_tag }}
        --repo ${{ github.repository }}
        --pattern '*'
        --dir release
    - name: Create Checksums
      run: cd release && shasum -a 256 * > ../SHA256SUMS
    - name: Publish Checksums
      uses: softprops/action-gh-release@v2.0.9
      with:
        draft: false
        prerelease: true
        files: SHA256SUMS
        tag_name: ${{ needs.prepare.outputs.nightly_tag }}
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  cleanup:
    name: Cleanup
    # Should only run in nushell/nightly repo
    if: github.repository == 'nushell/nightly'
    needs: [release, sha256sum]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: main

      - name: Setup Nushell
        uses: hustcer/setup-nu@v3
        with:
          version: 0.105.1

      # Keep the last a few releases
      - name: Delete Older Releases
        shell: nu {0}
        run: |
          let KEEP_COUNT = 10
          let deprecated = (http get https://api.github.com/repos/nushell/nightly/releases | sort-by -r created_at | select tag_name id | slice $KEEP_COUNT..)
          for release in $deprecated {
            print $'Deleting tag ($release.tag_name)'
            git push origin --delete $release.tag_name
            print $'Deleting release ($release.tag_name)'
            let delete_url = $'https://api.github.com/repos/nushell/nightly/releases/($release.id)'
            let version = "X-GitHub-Api-Version: 2022-11-28"
            let accept = "Accept: application/vnd.github+json"
            let auth = "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"
            # http delete $delete_url -H $version -H $auth -H $accept
            curl -L -X DELETE -H $accept -H $auth -H $version $delete_url
          }
