Metadata-Version: 2.5
Name: render-lab-tasks-github
Version: 0.1.0
Summary: GitHub issue tasks for Render Workflows
Project-URL: Repository, https://github.com/render-lab/render-tasks-python
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: httpx<0.29,>=0.28
Requires-Dist: render==1.0.1
Description-Content-Type: text/markdown

# render-lab-tasks-github

GitHub issue tasks for Render Workflows. Initial subset: `github.listNewIssues`
and `github.applyLabels`. Other TypeScript GitHub tasks have not yet been ported.

This distribution is not yet on PyPI. From this repository:

```sh
uv pip install ./packages/tasks-github
```

```python
from render import TaskContext, Workflows
from render_lab_tasks_github.tasks import app as github_app, list_new_issues

app = Workflows.from_workflows(github_app)


@app.task(name="example.readIssues")
async def read_issues(ctx: TaskContext):
    return await ctx.run(list_new_issues, {"repo": "render-oss/sdk", "limit": 5})
```

Call `app.start()` from the workflow entry point. Raw `list_new_issues_impl` and
`apply_labels_impl` are also exported from `.tasks`; inject `GitHubDeps` with a
`GitHubPort` fake to test or wrap under your own task name. Do not call a wrapped
task directly. Use `await ctx.run(task, input)`.

## Contract

`list_new_issues` takes `repo` (`owner/name`), optional `sinceISO`, and optional
nonnegative integer `limit` (default 50; explicit null also selects 50, matching
TypeScript). Returns plain issue dictionaries with
`number`, `title`, `body`, `url`, `labels`, `createdAt`, and nullable `author`.
Lists open issues newest-created first, skips pull requests, and follows pages
until the requested issue count or the end. `sinceISO` filters by GitHub's
updated-since behavior, matching the TS adapter. Zero limit performs no request.

`apply_labels` takes `repo`, `issueNumber`, and `labels`. Returns `issueNumber`
and the requested `labels`. An empty label list makes no HTTP request. This task
writes when called; dry-run decisions belong to the workflow caller.

## Environment

| Variable | Required | Purpose |
| --- | --- | --- |
| GITHUB_TOKEN | For writes/private repos; optional for public reads | GitHub token with permissions for the requested repository/operation |

Credentials are read during calls, never at import. GitHub responses determine
permission failures. HTTPX uses a 30-second timeout and no automatic retries.
Render retries failed tasks up to three times with a 1000 ms base delay and 2x
backoff. Pagination may make multiple requests within an attempt; writes may be
retried and callers must account for vendor idempotency.

## Render SDK 1.0.1 result limitation

The raw implementation always returns a list. The pinned SDK unwraps a singleton
list after a durable child run, so consumers should normalize the result:

```python
issues = await ctx.run(list_new_issues, {"repo": "owner/repo", "limit": 5})
if isinstance(issues, dict):
    issues = [issues]
```

Both examples include this workaround. See [ADR-0005](../../docs/adr/0005-sdk-singleton-list-results.md).
