diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d0ee3b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +venv/ +__pycache__/ +*.pyc diff --git a/README.md b/README.md index 7f40b5d..3a60c7f 100644 --- a/README.md +++ b/README.md @@ -2,23 +2,47 @@ A collection of Python and Bash scripts to automate interactions with Gitea instances. -## Setup +## Supported Instances -A Python virtual environment is included. Activate it using: -```bash -source venv/bin/activate -``` +| Remote | Host | Org / Repo | +|----------------|----------------------------|-------------------------------------| +| `dadeschools` | `gitea.dadeschools.net` | `Contractor / Timesheet` | +| `prgs` | `gitea.prgs.cc` | `Scaled-Tech-Consulting / Timesheet`| ## Authentication These scripts securely extract tokens from the macOS keychain to avoid hardcoding secrets. -- **Dade Schools (gitea.dadeschools.net)**: Retrieved via `git credential fill`. Ensure you have logged in via Git over HTTPS at least once so the keychain caches your credentials. -- **NetBridge / Personal (gitea.prgs.cc)**: Retrieved using `security find-generic-password` for the service `netbridge-gitea-token`. +Credentials are retrieved via `git credential fill` for the target host. Ensure you have +logged in via Git over HTTPS at least once so the keychain caches your credentials. ## Available Scripts -- `./create_issue.py` - Create an issue in the Gitea tracker -- `./create_pr.py` - Open a Pull Request from a branch via the API -- `./create_pr.sh` - Bash equivalent for creating a PR via the API -- `./close_issue.sh` - Close a specific issue via the API +| Script | Description | +|---------------------|-----------------------------------------------------| +| `create_issue.py` | Create an issue (`--remote`, `--title`, `--body`) | +| `create_pr.py` | Open a Pull Request (`--remote`, `--title`, `--head`, `--base`) | +| `close_issue.sh` | Close a specific issue (dadeschools only) | +| `mark_issue.sh` | Claim/release an issue via `status:in-progress` label | +| `manage_labels.py` | Create label set and apply label mappings (`--dry` to preview) | + +### Quick Examples + +```bash +# Create an issue +./create_issue.py --title "Fix PDF output" --body "Blank on Safari" + +# Create a PR +./create_pr.py --title "feat: add validation" --head feat/validation --body "Closes #12" + +# Close issue #5 +./close_issue.sh 5 + +# Claim an issue before working on it +./mark_issue.sh 10 start + +# Release when done +./mark_issue.sh 10 done +``` + +Use `--help` on any Python script for full usage details. diff --git a/close_issue.sh b/close_issue.sh index 2a01463..a590814 100755 --- a/close_issue.sh +++ b/close_issue.sh @@ -1,15 +1,27 @@ #!/usr/bin/env bash +# Close a Gitea issue by setting its state to "closed". +# +# Usage: ./close_issue.sh +# +# Auth: macOS keychain via `git credential fill` (same as the other scripts). +set -euo pipefail + HOST="gitea.dadeschools.net" API="https://$HOST/api/v1" ORG="Contractor" REPO="Timesheet" -ISSUE_NUM=$1 + +ISSUE_NUM="${1:?usage: close_issue.sh }" CREDS=$(printf "host=%s\nprotocol=https\n\n" "$HOST" | git credential fill) USER=$(printf '%s\n' "$CREDS" | sed -n 's/^username=//p') PASS=$(printf '%s\n' "$CREDS" | sed -n 's/^password=//p') -AUTH=(-u "$USER:$PASS") -PAYLOAD='{"state": "closed"}' +curl -sSL -X PATCH \ + -u "$USER:$PASS" \ + -H "Content-Type: application/json" \ + -d '{"state": "closed"}' \ + "$API/repos/$ORG/$REPO/issues/$ISSUE_NUM" -curl -sSL -X PATCH "${AUTH[@]}" -H "Content-Type: application/json" -d "$PAYLOAD" "$API/repos/$ORG/$REPO/issues/$ISSUE_NUM" +echo "" +echo "#$ISSUE_NUM closed" diff --git a/create_pr.sh b/create_pr.sh deleted file mode 100755 index b90ea36..0000000 --- a/create_pr.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -HOST="gitea.dadeschools.net" -API="https://$HOST/api/v1" -ORG="Contractor" -REPO="Timesheet" - -CREDS=$(printf "host=%s\nprotocol=https\n\n" "$HOST" | git credential fill) -USER=$(printf '%s\n' "$CREDS" | sed -n 's/^username=//p') -PASS=$(printf '%s\n' "$CREDS" | sed -n 's/^password=//p') - -AUTH=(-u "$USER:$PASS") -PAYLOAD='{"title": "feat: Support PTO, Sick, Holiday, and Unpaid days", "body": "Closes #6", "head": "feat/6-absence-categories", "base": "main"}' - -curl -sSL -X POST "${AUTH[@]}" -H "Content-Type: application/json" -d "$PAYLOAD" "$API/repos/$ORG/$REPO/pulls" diff --git a/mark_issue.sh b/mark_issue.sh new file mode 100755 index 0000000..364e4d3 --- /dev/null +++ b/mark_issue.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# Claim or release a Gitea issue by toggling the `status:in-progress` label, so +# parallel agents/LLMs don't pick up the same issue. +# +# Usage: +# ./mark_issue.sh # claim (adds status:in-progress) +# ./mark_issue.sh start # claim +# ./mark_issue.sh done # release (removes the label) +# +# Auth: macOS keychain via `git credential fill` (same as the other scripts). +set -euo pipefail + +HOST="gitea.dadeschools.net" +API="https://$HOST/api/v1" +ORG="Contractor" +REPO="Timesheet" +LABEL="status:in-progress" + +NUM="${1:?usage: mark_issue.sh [start|done]}" +ACTION="${2:-start}" + +CREDS=$(printf "host=%s\nprotocol=https\n\n" "$HOST" | git credential fill) +USER=$(printf '%s\n' "$CREDS" | sed -n 's/^username=//p') +PASS=$(printf '%s\n' "$CREDS" | sed -n 's/^password=//p') +AUTH=(-u "$USER:$PASS") + +# Resolve the label name -> id (Gitea's issue label endpoints take ids). +LID=$(curl -sSL "${AUTH[@]}" "$API/repos/$ORG/$REPO/labels?limit=100" \ + | python3 -c "import sys,json;print(next((l['id'] for l in json.load(sys.stdin) if l['name']=='$LABEL'),''))") +if [ -z "$LID" ]; then + echo "Label '$LABEL' not found in $ORG/$REPO -- run manage_labels.py first." >&2 + exit 1 +fi + +case "$ACTION" in + start) + curl -sSL -X POST "${AUTH[@]}" -H "Content-Type: application/json" \ + -d "{\"labels\":[$LID]}" "$API/repos/$ORG/$REPO/issues/$NUM/labels" >/dev/null + echo "#$NUM claimed -> $LABEL" + ;; + done) + curl -sSL -X DELETE "${AUTH[@]}" \ + "$API/repos/$ORG/$REPO/issues/$NUM/labels/$LID" >/dev/null + echo "#$NUM released -> $LABEL removed" + ;; + *) + echo "Unknown action '$ACTION' (expected: start | done)" >&2 + exit 1 + ;; +esac