name: chain-specs-periodic-update

on:
  schedule:
    - cron: '0 8 * * *'  # every day at 8am
  workflow_dispatch: # allow triggering through the UI

jobs:
  download-spec:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        # This starts one parallel job for each of these addresses.
        rpc-node-address: [
          "wss://rpc.polkadot.io",
          "wss://kusama-rpc.polkadot.io",
          "wss://westend-rpc.polkadot.io",
          "wss://rococo-rpc.polkadot.io",
          "wss://rpc.dotters.network/paseo"
        ]
      fail-fast: false   # Don't automatically cancel the jobs of the other RPC nodes if one fails

    steps:
    - uses: actions/checkout@v4
      with:
        persist-credentials: false
        path: repo
        ref: main
    - run: |
        curl -L -O https://github.com/vi/websocat/releases/download/v1.9.0/websocat_linux64
        chmod +x websocat_linux64
    - run: |
        echo '{"id":1,"jsonrpc":"2.0","method":"sync_state_genSyncSpec","params":[true]}' |
          ./websocat_linux64 -n1 -B 99999999 ${{ matrix.rpc-node-address }} > rpc_answer.json
    - run: cat ./rpc_answer.json | jq .result > chain_spec.json
    - id: get-chain-id  # Reads the `id` field in the newly-downloaded chain spec
      run: echo "id=`jq -r .id ./chain_spec.json`" >> $GITHUB_OUTPUT
    - if: ${{ steps.get-chain-id.outputs.id == '' }}
      uses: actions/upload-artifact@v4
      with:
        name: failed-response-${{ github.run_id }}
        path: |
          rpc_answer.json
    - run: |  # Overwrite the `lightSyncState` field of the existing chain spec with the value of spec that's been downloaded.
        tmp=$(mktemp)
        output=./repo/packages/connect-known-chains/specs/${{ steps.get-chain-id.outputs.id }}.json
        jq --slurpfile downloaded ./chain_spec.json '.lightSyncState = $downloaded[0].lightSyncState' "$output" > "$tmp"
        mv "$tmp" "$output"
    - run: |  # Do the same as the previous step, but for the chain spec of the extension. This is done separately in case the spec is not the same with the one in `connect`
        tmp=$(mktemp)
        output=./repo/projects/extension/assets/chainspecs/${{ steps.get-chain-id.outputs.id }}.json
        jq --slurpfile downloaded ./chain_spec.json '.lightSyncState = $downloaded[0].lightSyncState' "$output" > "$tmp"
        mv "$tmp" "$output"
    - run: |  # Do the same as the previous step, but for the chain spec of the extension. This is done separately in case the spec is not the same with the one in `connect`
        tmp=$(mktemp)
        output=./repo/projects/wallet-template/assets/chainspecs/${{ steps.get-chain-id.outputs.id }}.json
        jq --slurpfile downloaded ./chain_spec.json '.lightSyncState = $downloaded[0].lightSyncState' "$output" > "$tmp"
        mv "$tmp" "$output"
    - uses: actions/upload-artifact@v4
      with:
        name: chain-spec-${{ steps.get-chain-id.outputs.id }}
        # Note that passing `repo/**` maintains paths under `repo`. This is a bit of magic by the upload-artifact action.
        path: |
          repo/**/${{ steps.get-chain-id.outputs.id }}.json

  create-pr:
    runs-on: ubuntu-latest
    if: ${{ always() }} # Run this job even if one of the steps of download-spec has failed
    needs: download-spec
    steps:
    - name: Generate a token
      id: gen_token
      uses: actions/create-github-app-token@v2
      with:
          app-id: ${{ secrets.SUBSTRATE_CONNECT_PR_APP_ID }}
          private-key: ${{ secrets.SUBSTRATE_CONNECT_PR_APP_KEY }}
          owner: ${{ github.repository_owner }}
    - uses: actions/checkout@v4.1.7
      with:
        path: repo
        ref: main
    - uses: actions/download-artifact@v4
      with:
        # Since we're not passing a name, this automatically downloads *all* artifacts
        # Unfortunately, this creates intermediary directories.
        path: .
    - run: cp -r ./chain-spec-*/* ./repo
    - uses: peter-evans/create-pull-request@v6
      id: create-pr
      with:
        token: ${{ steps.gen_token.outputs.token }}
        committer: CICD team <cicd-team@parity.io>
        author: CICD team <cicd-team@parity.io>
        path: repo
        branch: automatic-checkpoints-update
        base: main
        title: "chore: update checkpoints in chain specifications"
        # Note that the `download-spec` job above fails if the downloaded specification doesn't
        # correspond to an existing file. It is therefore impossible that the pull request
        # accidentally adds new specifications.
        body: >
          This pull request has been automatically generated by downloading chain
          specifications from various JSON-RPC endpoints and extracting their checkpoints.

          Keep in mind that introducing a malicious checkpoint can redirect users to the wrong
          chain. If this pull request looks suspicious, please be cautious.
        labels: "automerge"
        commit-message: "chore: update checkpoints in chain specifications"
        delete-branch: true
    - name: Check PR creation status
      if: steps.create-pr.outcome != 'success'
      run: |
        echo "Failed to create pull request"
        exit 1
    - name: Create issue on failure
      if: failure()
      uses: actions/github-script@v7
      with:
        github-token: ${{ steps.gen_token.outputs.token }}
        script: |
          github.rest.issues.create({
            owner: context.repo.owner,
            repo: context.repo.repo,
            title: 'Failed to create pull request for updating chain specifications',
            body: 'The create-pr step failed in the chain-specs-periodic-update workflow. Please investigate the issue.',
            labels: ['bug']
          })
