diff --git a/.gitea/workflows/tag-old-issues.yaml b/.gitea/workflows/tag-old-issues.yaml new file mode 100644 index 0000000..9078d71 --- /dev/null +++ b/.gitea/workflows/tag-old-issues.yaml @@ -0,0 +1,90 @@ +name: tag-old-pull-requests + +on: + schedule: + - cron: '@daily' + +jobs: + tag-old-issues: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Tag Old Issues + env: + BOT_TOKEN: ${{ secrets.BOT_TOKEN }} + INSTANCE_URL: ${{ vars.INSTANCE_URL }} + REPOSITORY: ${{ gitea.repository }} + TAG_NAME: 'stale' + DAYS_OLD: 3 + EXCLUDE_TAG_NAME: '' + REQUIRED_TAG: 'automerge' + run: | + # Install necessary tools + echo ">> Installing tools ..." + sudo apt-get -qq update && sudo apt-get -qq install -y jq curl + + # --- Conditionally build the API URL --- + API_URL="${INSTANCE_URL}/api/v1/repos/${REPOSITORY}/issues?state=open" + if [[ -n "${REQUIRED_TAG}" ]]; then + echo ">> Filtering for issues with the required tag: ${REQUIRED_TAG}" + # URL-encode the tag to handle special characters + ENCODED_TAG=$(jq -s -R -r @uri <<< "${REQUIRED_TAG}") + API_URL="${API_URL}&labels=${ENCODED_TAG}" + else + echo ">> No required tag specified. Checking all open issues." + fi + + # Fetch issues using the constructed URL + echo ">> Fetching issues ..." + echo ">> Using URL:" + echo "${API_URL}" + ISSUES=$(curl -s -S -X GET \ + -H "Authorization: token ${BOT_TOKEN}" \ + -H "Accept: application/json" \ + "${API_URL}") + + # Check if there are open issues + if [[$ISSUES" | jq 'has("created_at")']]; then + echo ">> Processing $(echo "$ISSUES" | jq '. | length') open issues ..."; + else + echo ">> No open issues, exiting ..." + exit 0; + fi + + # Calculate the date ${DAYS_OLD} days ago in ISO 8601 format + OLDER_THAN_DATE=$(date -d "-${DAYS_OLD} days" -u +"%Y-%m-%dT%H:%M:%SZ") + echo ">> Filtering for older than ${OLDER_THAN_DATE}" + + # Filter issues older than the specified date and without the exclusion tag + echo "$ISSUES" | jq -c '.[] | select(.created_at < "'"$OLDER_THAN_DATE"'")' | while read -r issue; do + ISSUE_NUMBER=$(echo "$issue" | jq -r '.number') + LABELS=$(echo "$issue" | jq -r '.labels[].name') + + # Check if the issue has the exclusion tag + if ! echo "$LABELS" | grep -q -w "${EXCLUDE_TAG_NAME}"; then + echo ">> Tagging issue #${ISSUE_NUMBER} as ${TAG_NAME}" + + # Get existing labels for the issue + EXISTING_LABELS=$(curl -s -X GET \ + -H "Authorization: token ${BOT_TOKEN}" \ + -H "Accept: application/json" \ + "${INSTANCE_URL}/api/v1/repos/${REPOSITORY}/issues/${ISSUE_NUMBER}/labels" | jq -r '.[].name') + + # Add the new tag to the list of existing labels + NEW_LABELS=$(echo -e "${EXISTING_LABELS}\n${TAG_NAME}" | sort -u | jq -R -s -c 'split("\n") | map(select(length > 0))') + + # Update the issue with the new set of labels + echo ">> Updating issue on Gitea ..." + curl -s -X PUT \ + -H "Authorization: token ${BOT_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "{\"labels\": $(echo "$NEW_LABELS" | jq -r 'map(select(. != ""))')}" \ + "${INSTANCE_URL}/api/v1/repos/${REPOSITORY}/issues/${ISSUE_NUMBER}/labels" + else + echo ">> Skipping issue #${ISSUE_NUMBER} because it has the '${EXCLUDE_TAG_NAME}' tag." + fi + done + + echo ">> Finished applying tags." diff --git a/.gitea/workflows/tag-old-pull-requests.yaml b/.gitea/workflows/tag-old-pull-requests.yaml index d508230..9779aa8 100644 --- a/.gitea/workflows/tag-old-pull-requests.yaml +++ b/.gitea/workflows/tag-old-pull-requests.yaml @@ -4,14 +4,20 @@ on: schedule: - cron: '@daily' - workflow_dispatch: - jobs: tag-old-pull-requests: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: Install dependencies + run: pip install requests + + - name: Run inline API check script + run: | - name: Tag Old Pull Requests env: @@ -23,60 +29,94 @@ jobs: EXCLUDE_TAG_NAME: '' REQUIRED_TAG: 'automerge' run: | - # Install necessary tools - echo ">> Installing tools ..." - sudo apt-get -qq update && sudo apt-get -qq install -y jq curl + python - <> Filtering for pull requests with the required tag: ${REQUIRED_TAG}" - # URL-encode the tag to handle special characters - ENCODED_TAG=$(jq -s -R -r @uri <<< "${REQUIRED_TAG}") - API_URL="${API_URL}&labels=${ENCODED_TAG}" - else - echo ">> No required tag specified. Checking all open pull requests." - fi + def main(): + """ + Main function to fetch old pull requests from a Gitea repository and tag them. + """ + # --- 1. Get configuration from environment variables --- + try: + instance_url = os.environ['INSTANCE_URL'].rstrip('/') + repository = os.environ['REPOSITORY'] + token = os.environ['BOT_TOKEN'] + days_old = int(os.environ.get('DAYS_OLD', '30')) + tag_name = os.environ['TAG_NAME'] + exclude_tag_name = os.environ.get('EXCLUDE_TAG_NAME') + required_tag = os.environ.get('REQUIRED_TAG') + except KeyError as e: + print(f"Error: Missing required environment variable: {e}", file=sys.stderr) + sys.exit(1) - # Fetch pull requests using the constructed URL - echo ">> Fetching pull requests ..." - PRS=$(curl -s -S -X GET \ - -H "Authorization: token ${BOT_TOKEN}" \ - -H "Accept: application/json" \ - "${API_URL}") + # --- 2. Set up API headers and base URL --- + headers = { + 'Authorization': f'token {token}', + 'Accept': 'application/json', + 'Content-Type': 'application/json' + } + base_api_url = f"{instance_url}/api/v1/repos/{repository}/pulls" - # Calculate the date ${DAYS_OLD} days ago in ISO 8601 format - OLDER_THAN_DATE=$(date -d "-${DAYS_OLD} days" -u +"%Y-%m-%dT%H:%M:%SZ") - echo ">> Filtering for older than ${OLDER_THAN_DATE}" + # --- 3. Conditionally build the request parameters --- + params = {'state': 'open'} + if required_tag: + print(f">> Filtering for pull requests with the required tag: {required_tag}") + params['labels'] = required_tag + else: + print(">> No required tag specified. Checking all open pull requests.") - # Filter pull requests older than the specified date and without the exclusion tag - echo "$PRS" | jq -c '.[] | select(.created_at < "'"$OLDER_THAN_DATE"'")' | while read -r issue; do - ISSUE_NUMBER=$(echo "$issue" | jq -r '.number') - LABELS=$(echo "$issue" | jq -r '.labels[].name') + # --- 4. Fetch pull requests --- + print(">> Fetching pull requests ...") + try: + response = requests.get(base_api_url, headers=headers, params=params, timeout=30) + response.raise_for_status() + pull_requests = response.json() + except requests.exceptions.RequestException as e: + print(f"Error fetching pull requests: {e}", file=sys.stderr) + sys.exit(1) - # Check if the issue has the exclusion tag - if ! echo "$LABELS" | grep -q -w "${EXCLUDE_TAG_NAME}"; then - echo ">> Tagging issue #${ISSUE_NUMBER} as ${TAG_NAME}" + if not pull_requests: + print(">> No open pull requests found, exiting.") + sys.exit(0) - # Get existing labels for the issue - EXISTING_LABELS=$(curl -s -X GET \ - -H "Authorization: token ${BOT_TOKEN}" \ - -H "Accept: application/json" \ - "${INSTANCE_URL}/api/v1/repos/${REPOSITORY}/pulls/${ISSUE_NUMBER}/labels" | jq -r '.[].name') + print(f">> Processing {len(pull_requests)} open pull requests ...") - # Add the new tag to the list of existing labels - NEW_LABELS=$(echo -e "${EXISTING_LABELS}\n${TAG_NAME}" | sort -u | jq -R -s -c 'split("\n") | map(select(length > 0))') + # --- 5. Filter and tag old pull requests --- + older_than_date = datetime.now(timezone.utc) - timedelta(days=days_old) + print(f">> Filtering for pull requests older than {older_than_date.strftime('%Y-%m-%dT%H:%M:%SZ')}") - # Update the issue with the new set of labels - echo ">> Updating issue on Gitea ..." - curl -s -X PUT \ - -H "Authorization: token ${BOT_TOKEN}" \ - -H "Content-Type: application/json" \ - -d "{\"labels\": $(echo "$NEW_LABELS" | jq -r 'map(select(. != ""))')}" \ - "${INSTANCE_URL}/api/v1/repos/${REPOSITORY}/pulls/${ISSUE_NUMBER}/labels" - else - echo ">> Skipping issue #${ISSUE_NUMBER} because it has the '${EXCLUDE_TAG_NAME}' tag." - fi - done + for pr in pull_requests: + pr_number = pr['number'] + pr_created_at = datetime.fromisoformat(pr['created_at'].replace('Z', '+00:00')) + pr_current_labels = {label['name'] for label in pr.get('labels', [])} - echo ">> Finished applying tags." + if pr_created_at < older_than_date: + if exclude_tag_name and exclude_tag_name in pr_current_labels: + print(f">> Skipping PR #{pr_number} because it has the '{exclude_tag_name}' tag.") + continue + + if tag_name not in pr_current_labels: + print(f">> Tagging PR #{pr_number} with '{tag_name}'") + + updated_labels = list(pr_current_labels | {tag_name}) + payload = {'labels': updated_labels} + + update_url = f"{base_api_url}/{pr_number}/labels" + try: + update_response = requests.put(update_url, headers=headers, json=payload, timeout=30) + update_response.raise_for_status() + print(f">> Successfully updated labels for PR #{pr_number}") + except requests.exceptions.RequestException as e: + print(f"Error updating labels for PR #{pr_number}: {e}", file=sys.stderr) + else: + print(f">> Skipping PR #{pr_number} because it already has the '{tag_name}' tag.") + + print(">> Finished processing pull requests.") + + if __name__ == "__main__": + main() + EOF