# Complete Issue Lifecycle

Full lifecycle of an issue: create with metadata, assign, comment, update state, link to PR, close, and verify the full timeline.

## 1. Create Issue with Labels and Milestone

```bash
ISSUE=$(curl -s -X POST "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Bug: login form rejects valid email addresses",
    "body": "## Description\n\nWhen entering an email with a + character (e.g. user+tag@example.com), the login form rejects it as invalid.\n\n## Steps to Reproduce\n\n1. Go to /login\n2. Enter `user+tag@example.com`\n3. Click submit\n\n## Expected\n\nLogin succeeds.\n\n## Actual\n\nValidation error: \"Invalid email address\"",
    "labels": [1, 3],
    "milestone": 2
  }')
ISSUE_INDEX=$(echo "$ISSUE" | jq -r '.number')
echo "Created issue #${ISSUE_INDEX}"
```

## 2. Add Assignees

```bash
curl -s -X PATCH "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "assignees": ["{assignee1}", "{assignee2}"]
  }'
```

## 3. Add Comments as Work Progresses

```bash
# Investigation comment
curl -s -X POST "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}/comments" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Investigated — the issue is in `validateEmail()` in `src/utils/validation.ts`. The regex does not allow `+` in the local part. Fix is straightforward."
  }'

# Progress update
curl -s -X POST "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}/comments" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "PR is up: #58. Updated the regex and added test cases for `+`, `.`, and `-` in email local parts."
  }'
```

## 4. Update Labels (State Transition)

First, list available labels to get IDs:

```bash
curl -s "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/labels" \
  -H "Authorization: token ${FORGEJO_PAT}" | jq '.[] | {id, name, color}'
```

Remove old label and add new one:

```bash
# Remove "State/Open" label (ID 10)
curl -s -X DELETE "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}/labels/{label_id}" \
  -H "Authorization: token ${FORGEJO_PAT}"

# Add "State/In Progress" label (ID 11)
curl -s -X POST "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}/labels" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{"labels": [11]}'
```

Or replace all labels at once:

```bash
curl -s -X PUT "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}/labels" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{"labels": [3, 11, 15]}'
```

## 5. Link to PR

PRs reference issues automatically via keywords in the PR body. When creating the PR, include `Closes #N` in the body:

```bash
curl -s -X POST "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/pulls" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "fix: allow + character in email validation",
    "body": "Closes #'"${ISSUE_INDEX}"'\n\nUpdated the email regex to allow RFC 5321 compliant local parts.",
    "head": "{head_branch}",
    "base": "{base_branch}"
  }'
```

Supported keywords (case-insensitive): `closes`, `close`, `fix`, `fixes`, `fixed`, `resolve`, `resolves`, `resolved`.

## 6. Check Dependencies

Forgejo supports issue dependencies (blocking/blocked-by relationships):

```bash
# Add a dependency — issue depends on another issue
curl -s -X POST "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}/dependencies" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{"id": 35}'

# List dependencies
curl -s "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}/dependencies" \
  -H "Authorization: token ${FORGEJO_PAT}" | jq '.[] | {number, title, state}'

# List issues that are blocked by this issue
curl -s "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}/blocks" \
  -H "Authorization: token ${FORGEJO_PAT}" | jq '.[] | {number, title, state}'

# Remove a dependency
curl -s -X DELETE "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}/dependencies" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{"id": 35}'
```

## 7. Close Issue

When the linked PR is merged, the issue closes automatically (if `Closes #N` was used). To close manually:

```bash
curl -s -X PATCH "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{"state": "closed"}'
```

To reopen:

```bash
curl -s -X PATCH "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{"state": "open"}'
```

## 8. Verify with Timeline

The timeline shows the full history of an issue — comments, label changes, assignments, references, and state changes:

```bash
curl -s "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${ISSUE_INDEX}/timeline" \
  -H "Authorization: token ${FORGEJO_PAT}" | jq '.[] | {type, created, user: .user.login, body: .body, label: .label.name, old_ref: .old_ref, new_ref: .new_ref}'
```

Timeline event types include:
- `comment` — a comment was added
- `label` — a label was added or removed
- `milestone` — milestone was changed
- `assignees` — assignees were changed
- `close` — issue was closed
- `reopen` — issue was reopened
- `change_ref` — a branch reference changed
- `commit_ref` — a commit referenced this issue

## Complete Script

```bash
#!/bin/bash
set -euo pipefail

OWNER="{owner}"
REPO="{repo}"

# 1. Create issue
ISSUE=$(curl -s -X POST "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/issues" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Bug: login form rejects valid email addresses",
    "body": "The + character is rejected in email validation.",
    "labels": [1, 3],
    "milestone": 2,
    "assignees": ["{assignee}"]
  }')
ISSUE_INDEX=$(echo "$ISSUE" | jq -r '.number')
echo "Created issue #${ISSUE_INDEX}"

# 2. Add comment
curl -s -X POST "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/issues/${ISSUE_INDEX}/comments" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{"body": "Starting work on this."}'

# 3. Update labels
curl -s -X POST "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/issues/${ISSUE_INDEX}/labels" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{"labels": [11]}'

# 4. Create PR that closes issue
curl -s -X POST "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/pulls" \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "fix: allow + in email validation",
    "body": "Closes #'"${ISSUE_INDEX}"'",
    "head": "{head_branch}",
    "base": "{base_branch}"
  }'

# 5. Verify timeline after merge
curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/issues/${ISSUE_INDEX}/timeline" \
  -H "Authorization: token ${FORGEJO_PAT}" | jq '.[] | {type, created}'
```
