Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .cursor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Cursor Setup for tk-framework-alias

This folder contains Cursor skills and rules that help with development workflows in this project.

## Skills

Skills are reusable workflows you can invoke in Cursor chat. Reference them by name or describe what you want to do.

### snyk-update-python-packages

Updates Python packages for Snyk security: creates a Jira issue, creates a git branch with JIRA issue key, runs the update script per Python version, commits, and opens a PR.

**Usage examples:**

```bash
@snyk-update-python-packages Run workflow, Jira project <JIRA_PROJECT>, python 3.10 3.11
```

**Prerequisites:** `JIRA_PROJECT`, `JIRA_BASE_URL`, `JIRA_TOKEN`, `JIRA_USER` (and optionally `JIRA_ASSIGNEE`). See [skills/snyk-update-python-packages/SKILL.md](skills/snyk-update-python-packages/SKILL.md) for full details.

```bash
export JIRA_PROJECT=PROJ
export JIRA_BASE_URL=https://jira.example.com
export JIRA_TOKEN=your_bearer_token
export JIRA_USER=your_username
# export JIRA_ASSIGNEE=assignee_username # optional
```

---

*More skills will be added here as they are created.*

## Rules

Rules provide persistent guidance for the AI (coding standards, security practices, project conventions). They apply automatically when working in this project.

- **Workspace rules** — Check `.cursor/rules/` for project-specific rules (`.mdc` files).
- **Global rules** — User-level rules in Cursor settings may also apply.

---

*More rules will be documented here as they are added.*
253 changes: 253 additions & 0 deletions .cursor/skills/snyk-update-python-packages/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
---
name: snyk-update-python-packages
description: Sequential workflow: create Jira issue, create branch, run update_python_packages.py per Python version, commit/push, open PR. Steps must run in order. Use when updating Python packages for Snyk, creating a Snyk PR, or updating tk-framework-alias dependencies.
---

# Snyk Update Python Packages

Workflow to create a Jira issue, update Python packages in tk-framework-alias, create a branch, run the update script per Python version, and open a PR.

## Prerequisites

- Working directory: tk-framework-alias repo root
- The script `dev/update_python_packages.py` uses `sys.version_info` to target `dist/Python/Python{major}{minor}/` — invoke it with each Python interpreter

### Jira API setup

Create a Jira issue. Use environment variables for credentials (never hardcode):

- `JIRA_PROJECT` — **Required.** Jira project key. If not set and not provided by the user in the prompt, stop immediately before doing anything and ask: "Please provide the Jira project key (e.g. PROJ) or export JIRA_PROJECT."
- `JIRA_BASE_URL` — **Required.** Jira instance URL (no trailing slash). Use `$JIRA_BASE_URL` in all API calls. If not set, stop and ask: "Please export JIRA_BASE_URL (e.g. export JIRA_BASE_URL=https://jira.example.com)."
- `JIRA_TOKEN` — **Required.** Bearer token for API auth. Use `Authorization: Bearer $JIRA_TOKEN` (not Basic Auth). If not set, stop and ask the user to export it.
- `JIRA_USER` — **Required.** Jira username for API auth. If not set, stop and ask the user to export it.
- `JIRA_ASSIGNEE` — Optional. Username for assignee. For Jira Server/Data Center, the assignee uses `name`. To find by display name: `curl -s -H "Authorization: Bearer $JIRA_TOKEN" "$JIRA_BASE_URL/rest/api/2/user/search?username=..."`.

### GitHub CLI (gh) setup for SAML/SSO orgs

If the repo is in an organization with SAML/SSO `gh pr create` will fail until the token is authorized:

1. **Re-authenticate**: Run `gh auth login` and complete the flow. When prompted, authorize for the organization.
2. **Or use a Personal Access Token**: GitHub Settings → Developer settings → Personal access tokens → Create token (classic). Required scopes: `repo`, `read:org`. For fine-grained tokens, enable **Pull requests** with read/write. Then click **Configure SSO** next to the token and **Authorize** for the organization.
3. **One-time authorization**: If `gh pr create` fails with a URL like `https://github.com/enterprises/.../sso?authorization_request=...`, open that URL in a browser and authorize. After that, `gh` should work for that org.
4. **Token in shell profile**: Set `export GH_TOKEN="ghp_xxx"` in `~/.bashrc` or `~/.bash_profile`. gh will use it. Ensure the token is authorized for SAML/SSO orgs. Do not commit the profile if it contains the token.

## Python Version Selection

- **Default**: List `dist/Python/` and parse folder names `Python37`, `Python310`, etc. to get versions `(3,7)`, `(3,10)`, etc.
- **Override**: If the user specifies versions (e.g., "use python 3.10 and 3.11"), use only those.

**Which Python is used?** The script reads `sys.version_info` from the interpreter that runs it. So the version that gets updated is the version of the Python executable you invoke — e.g., running with Python 3.10 updates `dist/Python/Python310/`.

### Finding Python on Windows (when user specifies versions)

When the user specifies versions (e.g., "use python 3.10 and 3.11"), look for Python in `C:\Program Files`:

- `C:\Program Files\Python310\python.exe`
- `C:\Program Files\Python311\python.exe`

Or `C:\Program Files\Python 3.10\`, `C:\Program Files\Python 3.11\` (with space). Invoke directly if found, e.g.:

```powershell
& "C:\Program Files\Python310\python.exe" dev/update_python_packages.py
```

If not in Program Files, try `py -3.10` / `py -3.11` (py launcher) or `C:\Users\<user>\AppData\Local\Programs\Python\Python310\python.exe`.

## Workflow

**CRITICAL: Execute steps strictly in order. Do not parallelize or reorder. Each step depends on the previous one. Complete Step N fully before starting Step N+1. The precheck must run first — if local changes are detected, stop immediately and do not create a Jira ticket. Then verify JIRA vars before Step 0.**

Copy this checklist and mark items as you complete them:

```
Task Progress:
- [ ] Precheck: Verify no local changes; verify JIRA_PROJECT, JIRA_BASE_URL, JIRA_TOKEN, and JIRA_USER
- [ ] Step 0: Create Jira issue, assign, transition to In Progress
- [ ] Step 1: Create branch $ISSUE_KEY/snyk-update-packages
- [ ] Step 2: Run update_python_packages.py per Python version
- [ ] Step 3: Commit and push
- [ ] Step 4: Create PR
```

### Precheck: No local changes, then verify JIRA vars

**Before doing anything else** (including creating a Jira ticket), run these checks in order:

**1. Check for local changes.** If the working tree has uncommitted changes, **stop immediately**. Do not create a Jira issue. Tell the user: "Local changes detected. Please commit or stash your changes, then run the workflow again."

```bash
if [ -n "$(git status --porcelain)" ]; then
echo "Error: Local changes detected. Please commit or stash your changes, then run the workflow again."
exit 1
fi
```

**2. Verify JIRA variables.** Only after confirming a clean working tree, check that all required variables are available:

- **JIRA_PROJECT**: Check if set in the environment. If not, check if the user provided the project key in their prompt (e.g. "use PROJ", "project PROJ", "Jira project: PROJ"). If neither, **stop immediately** and ask: "Please provide the Jira project key (e.g. PROJ) or export JIRA_PROJECT." If the user provided it in the prompt, set it for the session: `export JIRA_PROJECT=<key>`.
- **JIRA_BASE_URL**: Check if set in the environment. If not, **stop immediately** and ask: "Please export JIRA_BASE_URL (e.g. export JIRA_BASE_URL=https://jira.example.com)." Do not use hardcoded fallback URLs.
- **JIRA_TOKEN**: Check if set in the environment. If not, **stop immediately** and ask the user to export it.
- **JIRA_USER**: Check if set in the environment. If not, **stop immediately** and ask the user to export it.

Do not create issues, branches, or run any commands until the working tree is clean and all four JIRA variables are confirmed. Only after all are set, proceed to Step 0.

### Step 0: Create Jira issue

Create a Jira issue in project `$JIRA_PROJECT` with the following details, then assign (if `JIRA_ASSIGNEE` is set) and set status to In Progress.

**Issue fields:**
- Project: `$JIRA_PROJECT`
- Title: Snyk: Update Python Package
- Component: tk-framework-alias
- Description: Snyk security requires python packages update. TODO: add details
- Assignee: (from `JIRA_ASSIGNEE` if set)
- Status: In Progress

**1. Create the issue** (use Bearer auth: `Authorization: Bearer $JIRA_TOKEN`). For API v2, use `/rest/api/2/issue` with plain-text description and `"issuetype": {"name": "Task"}`. Use `$JIRA_PROJECT` for the project key:

```bash
if [ -z "$JIRA_PROJECT" ]; then
echo "Error: JIRA_PROJECT is not set. Please export JIRA_PROJECT or provide the Jira project key."
exit 1
fi
if [ -z "$JIRA_BASE_URL" ]; then
echo "Error: JIRA_BASE_URL is not set. Please export JIRA_BASE_URL."
exit 1
fi
if [ -z "$JIRA_TOKEN" ]; then
echo "Error: JIRA_TOKEN is not set. Please export JIRA_TOKEN."
exit 1
fi
if [ -z "$JIRA_USER" ]; then
echo "Error: JIRA_USER is not set. Please export JIRA_USER."
exit 1
fi
JIRA_URL="$JIRA_BASE_URL"
RESPONSE=$(curl -s -X POST \
-H "Authorization: Bearer $JIRA_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"fields": {
"project": {"key": "'"$JIRA_PROJECT"'"},
"issuetype": {"name": "Task"},
"summary": "Snyk: Update Python Package",
"description": "Snyk security requires python packages update. TODO: add details",
"components": [{"name": "tk-framework-alias"}]
}
}' \
"$JIRA_URL/rest/api/2/issue")
ISSUE_KEY=$(echo "$RESPONSE" | jq -r '.key // empty')
# Stop if creation failed: if [ -z "$ISSUE_KEY" ]; then echo "Jira issue creation failed: $RESPONSE"; exit 1; fi
```

**2. Assign** (uses `JIRA_ASSIGNEE`; for Jira Server/Data Center (API v2), use `name`). Only run if `JIRA_ASSIGNEE` is set; verify the username exists in Jira:

```bash
if [ -n "$JIRA_ASSIGNEE" ]; then
curl -s -X PUT \
-H "Authorization: Bearer $JIRA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "'"$JIRA_ASSIGNEE"'"}' \
"$JIRA_URL/rest/api/2/issue/$ISSUE_KEY/assignee"
fi
```

**3. Transition to In Progress** — fetch available transitions, find the "In Progress" id, then POST:

```bash
# Get transitions and extract the id for "In Progress"
TRANSITIONS=$(curl -s -H "Authorization: Bearer $JIRA_TOKEN" \
-H "Accept: application/json" \
"$JIRA_URL/rest/api/2/issue/$ISSUE_KEY/transitions")
TRANSITION_ID=$(echo "$TRANSITIONS" | jq -r '.transitions[] | select(.name | startswith("In Progress")) | .id' | head -1)
if [ -n "$TRANSITION_ID" ]; then
curl -s -X POST \
-H "Authorization: Bearer $JIRA_TOKEN" \

Check notice on line 166 in .cursor/skills/snyk-update-python-packages/SKILL.md

View check run for this annotation

ShotGrid Chorus / security/gitleaks

http-header-authorization

HTTP Header Authorization: possible secret committed to source code
-H "Content-Type: application/json" \
-d '{"transition": {"id": "'"$TRANSITION_ID"'"}}' \
"$JIRA_URL/rest/api/2/issue/$ISSUE_KEY/transitions"
fi
```

**Required:** Keep `$ISSUE_KEY` (e.g. PROJ-1234) — it is used for the branch name in Step 1 and the PR title. **Do not proceed until the Jira issue exists and $ISSUE_KEY is set.**

### Step 1: Create branch

Ensure you are on main with the latest changes. Create a branch prefixed with the Jira ticket key from Step 0 (e.g. `PROJ-1234/snyk-update-packages`):

```bash
git checkout main
git pull origin main
git checkout -b "$ISSUE_KEY/snyk-update-packages"
```

On Windows (PowerShell):

```powershell
git checkout main
git pull origin main
git checkout -b "$ISSUE_KEY/snyk-update-packages"
```

If the default branch is not `main`, use the branch that `origin/HEAD` points to (e.g., `develop`).

**Only after the branch is created**, proceed to Step 2.

### Step 2: Run script per Python version

For each version (e.g., 3.7, 3.10, 3.11), run from tk-framework-alias root:

**Windows** — Try in order:
1. `C:\Program Files\Python3X\python.exe` or `C:\Program Files\Python 3.X\python.exe` (when user specifies versions)
2. `py -3.X` (py launcher)

```bash
py -3.7 dev/update_python_packages.py
py -3.10 dev/update_python_packages.py
py -3.11 dev/update_python_packages.py
```

**Linux/Mac**:

```bash
python3.7 dev/update_python_packages.py
python3.10 dev/update_python_packages.py
python3.11 dev/update_python_packages.py
```

Skip versions where the interpreter is not installed. If a run fails, report the error and stop. **Only after all Python versions have been updated**, proceed to Step 3.

### Step 3: Commit and push

Dist files are in `.gitignore`; use `-f` to force-add them. Use `--force` on push so `pkgs.zip` and other large dist files are pushed.

```bash
git add -f dist/Python/Python*/packages/frozen_requirements.txt dist/Python/Python*/packages/pkgs.zip
git commit -m "Snyk: update python packages"
git push -u origin "$ISSUE_KEY/snyk-update-packages" --force
```

**Only after the branch is pushed**, proceed to Step 4.

### Step 4: Create PR

1. Check if GitHub CLI is available: `gh --version` or `gh pr create --help`
2. **If gh is installed**:
```bash
gh pr create --title "$ISSUE_KEY: Snyk update python packages" --body "Updates Python packages"
```
3. **If gh is not available**: After pushing, provide the compare URL. Get remote URL with `git remote get-url origin`, then construct:
```
https://github.com/<owner>/<repo>/compare/<base-branch>...$ISSUE_KEY/snyk-update-packages?expand=1
```
Tell the user to open this URL to create the PR in the browser.

## Checklist

- [ ] Precheck: no local changes; verify JIRA_PROJECT, JIRA_BASE_URL, JIRA_TOKEN, and JIRA_USER
- [ ] Create Jira issue (title, component tk-framework-alias, assign if JIRA_ASSIGNEE set, status In Progress)
- [ ] Checkout main, pull latest, create branch `$ISSUE_KEY/snyk-update-packages`
- [ ] Run update script for each Python version (default: from dist/Python folders; override if user specified)
- [ ] Commit and push
- [ ] Create PR via `gh pr create` or provide compare URL (Jira key in title only, not in body)