Skip to content

Commit

Permalink
Merge pull request #7 from m7kvqbe1/feat/default-column-label-remove
Browse files Browse the repository at this point in the history
Return to default column on label removal
  • Loading branch information
m7kvqbe1 authored Oct 15, 2024
2 parents 3b9e95f + 7bfcfe8 commit 915d0e7
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 33 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/create_test_issue.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Create Test Issue on PR

on:
pull_request:
types: [opened, synchronize, reopened]

jobs:
create_test_issue:
runs-on: ubuntu-latest
steps:
- name: Create test issue
id: create_issue
uses: actions/github-script@v7
with:
github-token: ${{ secrets.PAT_TOKEN }}
script: |
const issue = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: 'Test issue for PR #' + context.issue.number,
body: 'This is a test issue created to verify the issue mover script.',
labels: ['Size: Small']
});
console.log('Test issue created:', issue.data.html_url);
core.setOutput('issue_number', issue.data.number);
- name: Wait for move action
run: sleep 30

- name: Clean up test issue
if: always()
uses: actions/github-script@v7
with:
github-token: ${{ secrets.PAT_TOKEN }}
script: |
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ steps.create_issue.outputs.issue_number }},
state: 'closed'
});
console.log('Test issue closed');
21 changes: 11 additions & 10 deletions .github/workflows/move_issue.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
name: Test Move Issue Action
name: Move Labeled Issues

on:
workflow_dispatch:
inputs:
branch:
description: "Name of branch to test"
required: true
type: string
issues:
types: [labeled]

env:
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}
PROJECT_URL: ${{ secrets.PROJECT_URL }}

jobs:
test-move-issue:
move_issues:
runs-on: ubuntu-latest
steps:
- name: Move Issue to Project Column
uses: m7kvqbe1/github-action-move-issues@${{ github.event.inputs.branch }}
uses: m7kvqbe1/github-action-move-issues@main
with:
github-token: ${{ secrets.PAT_TOKEN }}
project-url: ${{ secrets.PROJECT_URL }}
project-url: ${{ env.PROJECT_URL }}
target-labels: "Size: Small, Size: Medium"
target-column: "Todo"
ignored-columns: "In Progress, Done"
default-column: "Candidates"
30 changes: 20 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
# Move Issue to Project Column

A GitHub Action to move issues between GitHub Projects V2 columns based on specific labels and criteria.
A GitHub Action to move issues between GitHub Projects V2 columns based on specific labels and criteria. It can handle both labeling and unlabeling events.

## Inputs

| Input | Description | Required |
| ----------------- | -------------------------------------------------------------------------------------------------- | -------- |
| `github-token` | Create a Personal Access Token (Classic) with the `public_repo` and `project` scopes. | Yes |
| `project-url` | The URL of the GitHub Project V2. | Yes |
| `target-labels` | Comma-separated list of labels that should trigger the action (e.g., "Size: Small, Size: Medium"). | Yes |
| `target-column` | The target column name to move the issue to (e.g., "Candidates for Ready"). | Yes |
| `ignored-columns` | Comma-separated list of column names to ignore (e.g., "Ready, In Progress, In Review, Done"). | Yes |
| Input | Description | Required |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------ | -------- |
| `github-token` | Create a Personal Access Token (Classic) with the `public_repo` and `project` scopes. | Yes |
| `project-url` | The URL of the GitHub Project V2. | Yes |
| `target-labels` | Comma-separated list of labels that should trigger the action (e.g., "Size: Small, Size: Medium"). | Yes |
| `target-column` | The target column name to move the issue to when labeled (e.g., "Candidates for Ready"). | Yes |
| `ignored-columns` | Comma-separated list of column names to ignore (e.g., "Ready, In Progress, In Review, Done"). | Yes |
| `default-column` | The column to move the issue to when a target label is removed. If not specified, no action will be taken on unlabeling. | No |

## Example Workflow

```yaml
name: Move Issue on Label
name: Move Issue on Label Change

on:
issues:
types: [labeled]
types: [labeled, unlabeled]

jobs:
move-issue:
Expand All @@ -33,6 +34,15 @@ jobs:
target-labels: "Size: Small, Size: Medium"
target-column: "Candidates for Ready"
ignored-columns: "Ready, In Progress, In Review, Done"
default-column: "To Do" # Optional: Remove this line if you don't want issues moved when labels are removed
```
Get the latest `{release}` tag from https://github.com/m7kvqbe1/github-action-move-issues/releases.

## Behavior

- When an issue is labeled with one of the target labels, it will be moved to the specified target column.
- When all target labels are removed from an issue:
- If a default column is specified, the issue will be moved to that column.
- If no default column is specified, no action will be taken.
- The action will not move issues that are already in one of the ignored columns.
9 changes: 6 additions & 3 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: "Move Issue to Project Column"
description: "A GitHub Action to move issues between GitHub Projects V2 columns based on specific labels and criteria."
name: "Move Issue in Project"
description: "A GitHub Action to move issues between GitHub Projects V2 columns based on labeling and unlabeling events."
author: "m7kvqbe1"

inputs:
Expand All @@ -13,11 +13,14 @@ inputs:
description: 'Comma-separated list of labels that should trigger the action (e.g., "Size: Small, Size: Medium")'
required: true
target-column:
description: 'The target column name to move the issue to (e.g., "Candidates for Ready")'
description: 'The target column name to move the issue to when labeled (e.g., "Candidates for Ready")'
required: true
ignored-columns:
description: 'Comma-separated list of column names to ignore (e.g., "Ready, In Progress, In Review, Done")'
required: true
default-column:
description: 'The column to move the issue to when a target label is removed. If not specified, no action will be taken on unlabeling.'
required: false

runs:
using: "node20"
Expand Down
135 changes: 125 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,34 +258,149 @@ const processIssueItem = async (
console.log(`Moved issue #${issue.number} to "${TARGET_COLUMN}"`);
};

const handleLabeledEvent = async (
octokit,
issue,
projectData,
TARGET_COLUMN,
IGNORED_COLUMNS,
TARGET_LABELS
) => {
validateIssue(issue, TARGET_LABELS);

await processIssueItem(
octokit,
projectData,
issue,
TARGET_COLUMN,
IGNORED_COLUMNS
);
};

const handleUnlabeledEvent = async (
octokit,
issue,
projectData,
DEFAULT_COLUMN,
IGNORED_COLUMNS,
TARGET_LABELS
) => {
const removedLabel = github.context.payload.label.name;
if (!TARGET_LABELS.includes(removedLabel)) {
return;
}

const hasTargetLabel = issue.labels.some((label) =>
TARGET_LABELS.includes(label.name)
);

if (hasTargetLabel) {
console.log(
`Issue #${issue.number} still has a target label. Not moving to default column.`
);
return;
}

await moveIssueToDefaultColumn(
octokit,
projectData,
issue,
DEFAULT_COLUMN,
IGNORED_COLUMNS
);
};

const moveIssueToDefaultColumn = async (
octokit,
projectData,
issue,
defaultColumn,
ignoredColumns
) => {
const statusField = await getStatusField(octokit, projectData.id);
const defaultStatusOption = getTargetStatusOption(statusField, defaultColumn);

if (!defaultStatusOption) {
throw new Error(`Default column "${defaultColumn}" not found in project`);
}

let issueItemData = await getIssueItemData(
octokit,
projectData.id,
issue.node_id
);

if (!issueItemData) {
console.log(`Issue #${issue.number} is not in the project. Skipping.`);
return;
}

const currentStatus = getCurrentStatus(issueItemData);

if (ignoredColumns.includes(currentStatus)) {
console.log(
`Issue #${issue.number} is in an ignored column (${currentStatus}). Skipping.`
);
return;
}

await updateIssueStatus(
octokit,
projectData.id,
issueItemData.id,
statusField.id,
defaultStatusOption.id
);
console.log(`Moved issue #${issue.number} back to "${defaultColumn}"`);
};

const run = async () => {
try {
const token = core.getInput("github-token");
const projectUrl = core.getInput("project-url");
const targetLabels = core.getInput("target-labels");
const targetColumn = core.getInput("target-column");
const ignoredColumns = core.getInput("ignored-columns");
const defaultColumn = core.getInput("default-column", { required: false });

const TARGET_COLUMN = targetColumn.trim();
const TARGET_LABELS = parseCommaSeparatedInput(targetLabels);
const IGNORED_COLUMNS = parseCommaSeparatedInput(ignoredColumns);
const DEFAULT_COLUMN = defaultColumn ? defaultColumn.trim() : null;

const octokit = github.getOctokit(token);
const issue = github.context.payload.issue;

validateIssue(issue, TARGET_LABELS);
const action = github.context.payload.action;

const projectData = await getProjectData(octokit, projectUrl);

await processIssueItem(
octokit,
projectData,
issue,
TARGET_COLUMN,
IGNORED_COLUMNS
);
if (action === "labeled") {
await handleLabeledEvent(
octokit,
issue,
projectData,
TARGET_COLUMN,
IGNORED_COLUMNS,
TARGET_LABELS
);
return;
}

if (action === "unlabeled" && DEFAULT_COLUMN) {
await handleUnlabeledEvent(
octokit,
issue,
projectData,
DEFAULT_COLUMN,
IGNORED_COLUMNS,
TARGET_LABELS
);
return;
}

console.log(`No action taken for ${action} event.`);
} catch (error) {
core.setFailed(`Error moving issue: ${error.message}`);
core.setFailed(`Error processing issue: ${error.message}`);
}
};

Expand Down

0 comments on commit 915d0e7

Please sign in to comment.