mirror of
https://github.com/ditkrg/todo-to-issue-action.git
synced 2026-01-22 13:56:44 +00:00
Refactor action.yml and GitHubClient.py to enhance cross-repository issue creation by separating OWNER and TARGET_REPO inputs, improving clarity in usage and documentation. Update README.md to reflect new configuration requirements and provide clearer examples for users. Adjust example workflow to align with new input structure.
Signed-off-by: Shakar Bakr <5h4k4r.b4kr@gmail.com>
This commit is contained in:
parent
9d1f771660
commit
783f67f10b
3
.github/workflows/example-cross-repo.yml
vendored
3
.github/workflows/example-cross-repo.yml
vendored
@ -23,7 +23,8 @@ jobs:
|
||||
# Use the local version of the action (after you've made the changes)
|
||||
uses: ./
|
||||
with:
|
||||
TARGET_REPO: "my-org/target-repo" # Replace with your target repository
|
||||
TARGET_REPO: "target-repo" # Replace with your target repository name (without org)
|
||||
OWNER: "my-org" # Replace with your organization name
|
||||
TOKEN: ${{ secrets.CROSS_REPO_TOKEN }} # Token with access to the target repository
|
||||
CLOSE_ISSUES: "true" # Optional: close issues when TODOs are removed
|
||||
AUTO_ASSIGN: "true" # Optional: assign issues to the committer
|
||||
|
||||
@ -16,8 +16,25 @@ class GitHubClient(Client):
|
||||
raise EnvironmentError
|
||||
self.base_url = f'{self.github_url}/'
|
||||
self.repos_url = f'{self.base_url}repos/'
|
||||
self.repo = os.getenv('INPUT_REPO')
|
||||
self.target_repo = os.getenv('INPUT_TARGET_REPO', self.repo) # Default to current repo if not specified
|
||||
self.repo = os.getenv('INPUT_REPO')
|
||||
self.target_repo_name = os.getenv('INPUT_TARGET_REPO')
|
||||
self.owner = os.getenv('INPUT_OWNER')
|
||||
|
||||
# Construct the full target repository path
|
||||
if self.target_repo_name and self.owner:
|
||||
self.target_repo = f'{self.owner}/{self.target_repo_name}'
|
||||
elif self.target_repo_name and '/' in self.target_repo_name:
|
||||
# Backward compatibility: if TARGET_REPO contains '/', treat it as full repo path
|
||||
self.target_repo = self.target_repo_name
|
||||
print('WARNING: Using full repository path in TARGET_REPO is deprecated. Use OWNER and TARGET_REPO separately.')
|
||||
else:
|
||||
self.target_repo = self.repo # Default to current repo if not specified
|
||||
|
||||
# Log the repository configuration for debugging
|
||||
if self.target_repo == self.repo:
|
||||
print(f'Creating issues in current repository: {self.repo}')
|
||||
else:
|
||||
print(f'Creating issues in target repository: {self.target_repo} (source: {self.repo})')
|
||||
self.before = os.getenv('INPUT_BEFORE')
|
||||
self.sha = os.getenv('INPUT_SHA')
|
||||
self.commits = json.loads(os.getenv('INPUT_COMMITS')) or []
|
||||
@ -77,14 +94,15 @@ class GitHubClient(Client):
|
||||
# There is a valid before SHA to compare with, or this is a release being created.
|
||||
diff_url = f'{self.repos_url}{self.repo}/compare/{self.before}...{self.sha}'
|
||||
elif len(self.commits) == 1:
|
||||
# There is only one commit.
|
||||
diff_url = f'{self.repos_url}{self.repo}/commits/{self.sha}'
|
||||
# There is only one commit - compare against empty tree to get all files
|
||||
diff_url = f'{self.repos_url}{self.repo}/compare/4b825dc642cb6eb9a060e54bf8d69288fbee4904...{self.sha}'
|
||||
elif len(self.commits) > 1:
|
||||
# There are several commits: compare with the oldest one.
|
||||
oldest = sorted(self.commits, key=self._get_timestamp)[0]['id']
|
||||
diff_url = f'{self.repos_url}{self.repo}/compare/{oldest}...{self.sha}'
|
||||
else:
|
||||
return None
|
||||
# No commits info available, compare against empty tree to get all files
|
||||
diff_url = f'{self.repos_url}{self.repo}/compare/4b825dc642cb6eb9a060e54bf8d69288fbee4904...{self.sha}'
|
||||
|
||||
diff_headers = {
|
||||
'Accept': 'application/vnd.github.v3.diff',
|
||||
|
||||
184
README.md
184
README.md
@ -36,107 +36,135 @@ See [Upgrading](#upgrading) for breaking changes.
|
||||
|
||||
## Usage
|
||||
|
||||
Simply add a line or block comment starting with TODO (or any other comment identifiers configured), followed by a colon and/or space.
|
||||
### Basic Usage (Same Repository)
|
||||
|
||||
Here's an example for Python creating an issue named after the TODO _description_:
|
||||
For most users, you can use the action without any additional configuration. Issues will be created in the same repository where your code and TODOs exist:
|
||||
|
||||
```python
|
||||
def hello_world():
|
||||
# TODO: Come up with a more imaginative greeting
|
||||
print('Hello world!')
|
||||
```yaml
|
||||
name: "TODO to Issue"
|
||||
on: ["push"]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Ensures all commits are available for processing existing TODOs
|
||||
- name: "TODO to Issue"
|
||||
uses: "alstr/todo-to-issue-action@v5"
|
||||
```
|
||||
|
||||
_Multiline_ TODOs are supported, with additional lines inserted into the issue body:
|
||||
### Cross-Repository Usage (Advanced)
|
||||
|
||||
```python
|
||||
def hello_world():
|
||||
# TODO: Come up with a more imaginative greeting
|
||||
# Everyone uses hello world and it's boring.
|
||||
print('Hello world!')
|
||||
If you want to create issues in a different repository (e.g., a GitOps repository), you'll need to set up a GitHub App and provide additional configuration:
|
||||
|
||||
```yaml
|
||||
name: "TODO to Issue"
|
||||
on: ["push"]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: "TODO to Issue"
|
||||
uses: "alstr/todo-to-issue-action@v5"
|
||||
with:
|
||||
TARGET_REPO: "gitops-repo"
|
||||
APP_ID: ${{ secrets.APP_ID }}
|
||||
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
|
||||
OWNER: "your-org"
|
||||
```
|
||||
|
||||
### Cross-Repository Issues
|
||||
## Configuration
|
||||
|
||||
The action supports creating issues in a different repository than the one where the TODO comments are found. This requires:
|
||||
### Required for Cross-Repository Usage
|
||||
|
||||
1. Setting the `TARGET_REPO` input to specify the target repository (e.g., "owner/repo")
|
||||
| Input | Description | Required |
|
||||
| ------------- | --------------------------------------------------------------- | ------------------- |
|
||||
| `TARGET_REPO` | Target repository name to create issues in (e.g. 'gitops-repo') | Only for cross-repo |
|
||||
| `APP_ID` | GitHub App ID for generating tokens | Only for cross-repo |
|
||||
| `PRIVATE_KEY` | Private key for the GitHub App | Only for cross-repo |
|
||||
| `OWNER` | Owner of the target repository (e.g. 'your-org') | Only for cross-repo |
|
||||
|
||||
2. Providing appropriate authentication:
|
||||
- For public repositories or when the target repository is in the same organization, use the default `GITHUB_TOKEN`:
|
||||
```yaml
|
||||
- uses: alstr/todo-to-issue-action@v5
|
||||
with:
|
||||
TARGET_REPO: "my-org/target-repo"
|
||||
```
|
||||
- For private repositories, provide GitHub App credentials:
|
||||
```yaml
|
||||
- uses: alstr/todo-to-issue-action@v5
|
||||
with:
|
||||
TARGET_REPO: "target-org/target-repo"
|
||||
APP_ID: ${{ secrets.APP_ID }}
|
||||
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
|
||||
OWNER: "target-org"
|
||||
```
|
||||
### Optional Inputs
|
||||
|
||||
The action will automatically:
|
||||
1. Use the default `GITHUB_TOKEN` for same-repo or public repository access
|
||||
2. Generate a GitHub App token when `APP_ID` and `PRIVATE_KEY` are provided for private repository access
|
||||
| Input | Description | Default |
|
||||
| ------------------- | ------------------------------------ | --------- |
|
||||
| `CLOSE_ISSUES` | Close issues when TODOs are removed | `"true"` |
|
||||
| `AUTO_P` | Format multiline TODOs as paragraphs | `"true"` |
|
||||
| `AUTO_ASSIGN` | Auto-assign issues to the actor | `"false"` |
|
||||
| `ESCAPE` | Escape Markdown characters | `"true"` |
|
||||
| `INSERT_ISSUE_URLS` | Insert issue URLs into TODO comments | `"false"` |
|
||||
|
||||
### GitHub App Setup
|
||||
## GitHub App Setup (For Cross-Repository Usage)
|
||||
|
||||
To use the action with private repositories, you'll need to:
|
||||
If you want to create issues in a different repository, you'll need to create a GitHub App:
|
||||
|
||||
1. Create a GitHub App:
|
||||
- Go to your GitHub account settings
|
||||
- Navigate to "Developer settings" (bottom of the left sidebar)
|
||||
- Click "GitHub Apps" and then "New GitHub App"
|
||||
- Fill in the following details:
|
||||
- **GitHub App name**: Choose a unique name (e.g., "TODO-to-Issue-App")
|
||||
- **Homepage URL**: Your repository URL
|
||||
- **Webhook**: Leave disabled
|
||||
- **Repository permissions**:
|
||||
- Issues: Read & Write
|
||||
- Pull requests: Read & Write (if using PR integration)
|
||||
- **Where can this GitHub App be installed?**: Any account
|
||||
- Click "Create GitHub App"
|
||||
- On the next page, click "Generate a private key" to download your private key file
|
||||
- Note down the "App ID" shown on the page
|
||||
### 1. Create a GitHub App
|
||||
|
||||
2. Store the following secrets in your repository:
|
||||
- Go to your repository's "Settings" → "Secrets and variables" → "Actions"
|
||||
- Add two new repository secrets:
|
||||
- `APP_ID`: The App ID you noted down
|
||||
- `PRIVATE_KEY`: The entire contents of the private key file you downloaded (including the BEGIN and END lines)
|
||||
1. Go to your GitHub organization/user settings
|
||||
2. Navigate to "Developer settings" > "GitHub Apps"
|
||||
3. Click "New GitHub App"
|
||||
4. Fill in the required fields:
|
||||
- **App name**: Choose a unique name
|
||||
- **Homepage URL**: Your repository or organization URL
|
||||
- **Webhook URL**: Can be a placeholder like `https://example.com`
|
||||
|
||||
3. Install the GitHub App:
|
||||
- Go back to your GitHub App settings
|
||||
- Click "Install App" in the left sidebar
|
||||
- Choose the target repository where you want to create issues
|
||||
- Click "Install"
|
||||
### 2. Set Permissions
|
||||
|
||||
As per the [Google Style Guide](https://google.github.io/styleguide/cppguide.html#TODO_Comments), you can provide a _reference_ after the TODO identifier:
|
||||
Grant the following **Repository permissions**:
|
||||
- **Contents**: Read (to access source code and diffs)
|
||||
- **Issues**: Write (to create and manage issues)
|
||||
- **Metadata**: Read (required)
|
||||
- **Pull requests**: Read (if using with PRs)
|
||||
|
||||
```python
|
||||
def hello_world():
|
||||
# TODO(@alstr): Come up with a more imaginative greeting
|
||||
# This will assign the issue to alstr.
|
||||
print('Hello world!')
|
||||
### 3. Generate and Store Secrets
|
||||
|
||||
# TODO(!urgent): This is wrong
|
||||
# This will add an 'urgent' label.
|
||||
assert 1 + 1 == 3
|
||||
1. **Private Key**: Generate and download the private key
|
||||
2. **App ID**: Note the App ID from the app settings
|
||||
3. Store both in your repository secrets:
|
||||
- `APP_ID`: The numeric App ID
|
||||
- `PRIVATE_KEY`: The entire private key file content
|
||||
|
||||
# TODO(#99): We need error handling here
|
||||
# This will add the comment to the existing issue 99.
|
||||
greeting_time = datetime.fromisoformat(date_string)
|
||||
### 4. Install the App
|
||||
|
||||
# TODO(language): Localise this string
|
||||
# This will prepend the reference to the issue title
|
||||
dialogue = "TODO or not TODO, that is the question."
|
||||
Install the GitHub App on both:
|
||||
- The source repository (where your code/TODOs are)
|
||||
- The target repository (where issues will be created)
|
||||
|
||||
## How It Works
|
||||
|
||||
### First Run
|
||||
When you first add this action to a repository with existing TODOs:
|
||||
- With `fetch-depth: 0`, the action has access to full git history
|
||||
- It compares against an empty tree to treat all files as "new"
|
||||
- **All existing TODOs** in your codebase will be converted to issues
|
||||
|
||||
### Subsequent Runs
|
||||
- Only processes TODOs that were added, modified, or removed in the current push
|
||||
- Updates existing issues when TODOs are modified
|
||||
- Closes issues when TODOs are removed (if `CLOSE_ISSUES` is enabled)
|
||||
|
||||
## Examples
|
||||
|
||||
### Same Repository (Simple)
|
||||
```yaml
|
||||
- uses: "alstr/todo-to-issue-action@v5"
|
||||
```
|
||||
|
||||
Only one reference can be provided. Should you wish to further configure the issue, you can do so via
|
||||
[TODO Options](#todo-options).
|
||||
### Cross Repository with Custom Settings
|
||||
```yaml
|
||||
- uses: "alstr/todo-to-issue-action@v5"
|
||||
with:
|
||||
TARGET_REPO: "issues-repo"
|
||||
APP_ID: ${{ secrets.TODO_APP_ID }}
|
||||
PRIVATE_KEY: ${{ secrets.TODO_PRIVATE_KEY }}
|
||||
OWNER: "myorg"
|
||||
AUTO_ASSIGN: "true"
|
||||
CLOSE_ISSUES: "false"
|
||||
```
|
||||
|
||||
## TODO Options
|
||||
|
||||
|
||||
23
action.yml
23
action.yml
@ -5,18 +5,19 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Extract repository names
|
||||
if: ${{ inputs.TARGET_REPO != '' && inputs.APP_ID != '' }}
|
||||
if: ${{ inputs.TARGET_REPO != '' && inputs.OWNER != '' && inputs.APP_ID != '' }}
|
||||
id: extract-repos
|
||||
run: |
|
||||
SOURCE_REPO_NAME=$(echo "${{ inputs.REPO }}" | cut -d'/' -f2)
|
||||
TARGET_REPO_NAME=$(echo "${{ inputs.TARGET_REPO }}" | cut -d'/' -f2)
|
||||
# TARGET_REPO now only contains the repo name, not the full path
|
||||
TARGET_REPO_NAME="${{ inputs.TARGET_REPO }}"
|
||||
echo "source-repo-name=$SOURCE_REPO_NAME" >> $GITHUB_OUTPUT
|
||||
echo "target-repo-name=$TARGET_REPO_NAME" >> $GITHUB_OUTPUT
|
||||
echo "repositories=$SOURCE_REPO_NAME,$TARGET_REPO_NAME" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Generate token for private repository access
|
||||
if: ${{ inputs.TARGET_REPO != '' && inputs.APP_ID != '' }}
|
||||
if: ${{ inputs.TARGET_REPO != '' && inputs.OWNER != '' && inputs.APP_ID != '' }}
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
@ -80,7 +81,7 @@ inputs:
|
||||
required: false
|
||||
default: ${{ github.token }}
|
||||
TARGET_REPO:
|
||||
description: "Optional target repository to create issues in (e.g. 'owner/repo'). If not provided, issues will be created in the current repository."
|
||||
description: "Optional target repository name to create issues in (e.g. 'gitops-repo'). Use with OWNER to specify the full repository. If not provided, issues will be created in the current repository."
|
||||
required: false
|
||||
APP_ID:
|
||||
description: "GitHub App ID for generating tokens to access private repositories"
|
||||
@ -89,16 +90,16 @@ inputs:
|
||||
description: "Private key for the GitHub App (do not enter the actual secret)"
|
||||
required: false
|
||||
OWNER:
|
||||
description: "Owner of the target repository (required if using GitHub App authentication)"
|
||||
description: "Owner of the target repository (e.g. 'your-org'). Required when using TARGET_REPO or GitHub App authentication."
|
||||
required: false
|
||||
CLOSE_ISSUES:
|
||||
description: "Optional input specifying whether to attempt to close an issue when a TODO is removed"
|
||||
required: false
|
||||
default: true
|
||||
default: "true"
|
||||
AUTO_P:
|
||||
description: "For multiline TODOs, format each line as a new paragraph when creating the issue"
|
||||
required: false
|
||||
default: true
|
||||
default: "true"
|
||||
PROJECT:
|
||||
description: "User or organization project to link issues to, format 'project_type/owner/project_name'"
|
||||
required: false
|
||||
@ -111,7 +112,7 @@ inputs:
|
||||
AUTO_ASSIGN:
|
||||
description: "Automatically assign new issues to the user who triggered the action"
|
||||
required: false
|
||||
default: false
|
||||
default: "false"
|
||||
ACTOR:
|
||||
description: "The username of the person who triggered the action (automatically set)"
|
||||
required: false
|
||||
@ -133,15 +134,15 @@ inputs:
|
||||
ESCAPE:
|
||||
description: "Escape all special Markdown characters"
|
||||
required: false
|
||||
default: true
|
||||
default: "true"
|
||||
LANGUAGES:
|
||||
description: "A collection of comma-delimited URLs or local paths for custom language files"
|
||||
required: false
|
||||
NO_STANDARD:
|
||||
description: "Exclude loading the default 'syntax.json' and 'languages.yml' files from the repository"
|
||||
required: false
|
||||
default: false
|
||||
default: "false"
|
||||
INSERT_ISSUE_URLS:
|
||||
description: "Whether the action should insert the URL for a newly-created issue into the associated TODO comment"
|
||||
required: false
|
||||
default: false
|
||||
default: "false"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user