name: chango Action
description: Composite Action for automatically creating a chango fragment for a PR.
branding:
  icon: book-open
  color: gray-dark
inputs:
  python-version:
    description: Python version to use. See actions/setup-python for more information. Default is 3.x
    required: false
    default: 3.x
  commit-and-push:
    description: Whether to commit and push the changes to the PR branch. Default is true.
    required: false
    default: true
  pyproject-toml:
    description: Path to the ``pyproject.toml`` file. Takes the same input as ``chango.config.get_chango_instance``.
    required: false
    # Using python naming here ensures that we can directly use this value below.
    default: None
  data:
    description: Additional JSON data to pass to the parameter ``data`` of ``chango.abc.ChanGo.build_from_github_event()``. Defaults to an instance of ``chango.action.ChanGoActionData``.
    required: false
    default: None
  github-token:
    description: GitHub Token or Personal Access Token (PAT) used to authenticate with GitHub. Defaults to the ``GITHUB_TOKEN``.
    required: false
  query-issue-types:
    description: Whether to query the issue types of the linked issues. Can only be used on organizations with issue types enabled. In this case, an organization scoped PAT is required.
    required: false
    default: false

runs:
  using: composite
  steps:
    - name: Checkout action repository
      uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
      env:
        ACTION_REPO: ${{ github.action_repository }}
        ACTION_REF: ${{ github.action_ref }}
      with:
        repository: ${{ env.ACTION_REPO }}
        ref: ${{ env.ACTION_REF }}
        path: ./.chango-action

    - name: Checkout target repository
      uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
      with:
        # setting repo is required for PRs coming from forks
        repository: ${{ github.event.pull_request.head.repo.full_name }}
        ref: ${{ github.head_ref }}
        path: ./target-repo
        token: ${{ inputs.github-token || github.token }}

    - name: Set up Python
      id: setup-python
      uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
      with:
        python-version: ${{ inputs.python-version }}
        cache: 'pip'
        cache-dependency-path: |
          **/requirements*.txt
          pyproject.toml

    - name: Install chango
      id: install-chango
      shell: bash
      run: |
        python -W ignore -m pip install ./.chango-action

    - name: Fetch Linked Issues & Parent PR (Custom GitHub Token)
      id: fetch-linked-issues-custom-token
      uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
      env:
        github_token: ${{ inputs.github-token }}
        query_issue_types: ${{ inputs.query-issue-types }}
      if: ${{ env.github_token != null && env.github_token != '' }}
      with:
        github-token: ${{ env.github_token }}
        script: |
          const script = require('./.chango-action/action.js');
          const query_issue_types = `${process.env.query_issue_types}`;
          await script({github, context, core, query_issue_types});
    - name: Fetch Linked Issues & Parent PR (Default GitHub Token)
      id: fetch-linked-issues-default-token
      uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
      env:
        github_token: ${{ inputs.github-token }}
        query_issue_types: ${{ inputs.query-issue-types }}
      if: ${{ env.github_token == null || env.github_token == '' }}
      with:
        script: |
          const script = require('./.chango-action/action.js');
          const query_issue_types = `${process.env.query_issue_types}`;
          await script({github, context, core, query_issue_types});

    - name: Create chango fragment
      id: create-chango-fragment
      uses: jannekem/run-python-script-action@bbfca66c612a28f3eeca0ae40e1f810265e2ea68 # v1.7
      env:
        CUSTOM_OUTPUT: ${{ steps.fetch-linked-issues-custom-token.outputs.data }}
        DEFAULT_OUTPUT: ${{ steps.fetch-linked-issues-default-token.outputs.data }}
      with:
        script: |
          import base64
          import subprocess
          import os
          from chango.config import get_chango_instance
          from chango.action import ChanGoActionData
          
          os.chdir('./target-repo')
          
          chango_instance = get_chango_instance(${{ inputs.pyproject-toml }})
          
          null = None
          false = False
          true = True
          
          # Merge additionally fetched data into the data passed by the user
          output = (
              ${{ toJson(env.CUSTOM_OUTPUT) }}
              or ${{ toJson(env.DEFAULT_OUTPUT) }}
          )
          data = (
              ${{ inputs.data }}
              or ChanGoActionData.model_validate_json(output)
          )
          
          change_note = chango_instance.build_github_event_change_note(
              event=${{ toJson(github.event) }},
              data=data
          )
          
          should_commit = False
          if change_note is not None:
            path = chango_instance.write_change_note(change_note, version=None)
            
            last_commiter = subprocess.run(
              ["git", "log", "-1", "--format=%ce", "--", path],
              capture_output=True,
              text=True,
              check=True
            ).stdout.strip()
          
            # Do not override manual changes
            if last_commiter in ("github-actions[bot]", ""):
              should_commit = True
          
          set_output("should_commit", "true" if should_commit else "false")
          
          if should_commit:
            # b64 encoding avoits problems with new lines in the output
            encoded_content = base64.b64encode(change_note.to_string().encode()).decode()
            set_output("change_note_content", encoded_content)
            set_output("change_note_path", path.relative_to(os.getcwd()).as_posix())
          else:
            set_output("change_note_content", "")
            set_output("change_note_path", "")

    - name: Commit and Push
      id: commit-and-push
      if: ${{ inputs.commit-and-push == 'true' && steps.create-chango-fragment.outputs.should_commit == 'true' }}
      uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0
      with:
        commit_message: "Add chango fragment for PR #${{ github.event.pull_request.number }}"
        repository: ./target-repo

    - name: Set Job Summary
      id: job-summary
      # Run only if the commit & push step failed.
      # We use this for PRs coming from forks. Here we can't easily push back to the PR branch or
      # create PR reviews, so we fail the job and set the job summary
      if: ${{ failure() && steps.create-chango-fragment.outputs.should_commit == 'true' }}
      env:
        CHANGE_NOTE_PATH: ${{ steps.create-chango-fragment.outputs.change_note_path }}
        CHANGE_NOTE_CONTENT: ${{ steps.create-chango-fragment.outputs.change_note_content }}
      uses: jannekem/run-python-script-action@bbfca66c612a28f3eeca0ae40e1f810265e2ea68 # v1.7
      with:
        script: |
          import base64
          from pathlib import Path

          file_path = Path("${{ env.CHANGE_NOTE_PATH }}")
          encoded_content = "${{ env.CHANGE_NOTE_CONTENT }}"
          decoded_content = base64.b64decode(encoded_content).decode()
          
          text = f"""
          Please create a [chango](https://chango.readthedocs.io/stable/) fragment for this PR.
          I suggest adding the following content to `{file_path}` in the repository:
          
          ```{file_path.suffix[1:]}
          {decoded_content}
          ```
          """
          set_summary(text)
          error("Chango fragment should be updated. See the job summary for details.")
