Skip to content

Commit edd03c5

Browse files
authored
Creating action (#2)
1 parent 01153c7 commit edd03c5

16 files changed

+439
-0
lines changed

.editorconfig

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
root = true
2+
3+
[.*]
4+
charset = utf-8
5+
end_of_line = lf
6+
indent_size = 2
7+
indent_style = space
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
11+
[*.md]
12+
charset = utf-8
13+
end_of_line = lf
14+
insert_final_newline = true
15+
trim_trailing_whitespace = true
16+
17+
[*.{yml,yaml}]
18+
charset = utf-8
19+
end_of_line = lf
20+
indent_size = 2
21+
indent_style = space
22+
insert_final_newline = true
23+
trim_trailing_whitespace = true
24+
25+
[Makefile]
26+
charset = utf-8
27+
end_of_line = lf
28+
indent_style = tab
29+
insert_final_newline = true
30+
trim_trailing_whitespace = true
31+
32+
[*.sh]
33+
charset = utf-8
34+
end_of_line = lf
35+
indent_size = 2
36+
indent_style = space
37+
insert_final_newline = true
38+
trim_trailing_whitespace = true

.github/dependabot.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: github-actions
4+
directory: /
5+
schedule:
6+
interval: weekly

.github/workflows/test.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Tests
2+
3+
on:
4+
workflow_dispatch: {}
5+
pull_request:
6+
branches:
7+
- main
8+
9+
jobs:
10+
tests:
11+
name: Tests
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v5
15+
16+
- name: Run tests
17+
shell: bash
18+
run: make test

.shellcheckrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
external-sources=true

Makefile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.PHONY: all test clean
2+
3+
PLATFORM := $(shell docker version --format '{{.Server.Os}}/{{.Server.Arch}}')
4+
DOCKER := docker run --rm --network none --platform $(PLATFORM)
5+
6+
test: unit-tests lint
7+
8+
unit-tests:
9+
@set -e; \
10+
for f in tests/test*.sh; do \
11+
echo "sh $$f"; \
12+
sh "$$f"; \
13+
done
14+
15+
lint:
16+
$(DOCKER) -v ./Makefile:/work/Makefile:ro backplane/checkmake Makefile
17+
$(DOCKER) -v .:/workspace:ro mstruebing/editorconfig-checker ec -exclude '^\.git/'
18+
$(DOCKER) -v .:/mnt:ro koalaman/shellcheck -a -s sh --source-path=tests src/** tests/**

README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
[![Build and Test](https://github.com/codereaper/create-issue-action/actions/workflows/test.yaml/badge.svg)](https://github.com/codereaper/create-issue-action/actions/workflows/test.yaml)
2+
3+
# Create Issue Action
4+
5+
A simple GitHub Action that **creates, updates, comments on, or closes issues** using the GitHub CLI (`gh`).
6+
7+
Ideal for CI/CD workflows that need to:
8+
9+
- Automatically open or update tracking issues
10+
- Comment on existing issues from automation
11+
- Close issues after builds or deployments are complete
12+
13+
## Features
14+
15+
- Create new issues with titles, bodies, templates, labels, and assignees
16+
- Update existing issues automatically
17+
- Add comments to existing issues
18+
- Close issues by title and label search
19+
- Uses `gh` CLI under the hood (no extra dependencies)
20+
21+
## Inputs
22+
23+
| Name | Description | Default | Required |
24+
| ----------- | --------------------------------------------------------------------------------- | -------------------------- | -------- |
25+
| `token` | GitHub token (PAT or `${{ github.token }}`) used for authentication | `${{ github.token }}` | Yes |
26+
| `mode` | Operation mode: `create` or `close` | `create` | Yes |
27+
| `state` | Issue state filter when searching for existing issues: `open`, `closed`, or `all` | `open` | Yes |
28+
| `title` | Title of the issue to create or update || Yes |
29+
| `labels` | Comma-separated list of labels used for creation and search || No |
30+
| `assignees` | Comma-separated list of users to assign the issue to || No |
31+
| `body` | Custom body text for the issue (overrides `template`) || No |
32+
| `comment` | Optional comment text to add to an existing issue || No |
33+
| `repo` | Repository to operate on (`owner/repo`) | `${{ github.repository }}` | Yes |
34+
35+
## Example Usage
36+
37+
```yaml
38+
name: Reporting on failed builds
39+
on:
40+
push:
41+
branches:
42+
- main
43+
44+
jobs:
45+
build:
46+
runs-on: ubuntu-latest
47+
steps:
48+
- name: Checkout
49+
uses: actions/checkout@v4
50+
51+
- name: Build
52+
run: make build
53+
54+
report-failure:
55+
runs-on: ubuntu-latest
56+
needs: build
57+
if: failure()
58+
permissions:
59+
contents: read
60+
issues: write
61+
steps:
62+
- name: Report build failure
63+
uses: CodeReaper/create-issue-action@v1
64+
with:
65+
title: "{{ github.workflow }} failed to build"
66+
labels: automation
67+
assignees: "@me"
68+
body: See [the action log](https://github.com/{{ github.repository }}/actions/runs/{{ github.run_id }}) for more details.
69+
```
70+
71+
# License
72+
73+
This project is released under the [MIT License](LICENSE)

action.yaml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
name: Create issue
2+
description: Creates an issue
3+
4+
branding:
5+
icon: git-pull-request
6+
color: gray-dark
7+
8+
inputs:
9+
token:
10+
description: Your Github PAT, defaults to actions token
11+
default: ${{ github.token }}
12+
required: true
13+
repo:
14+
description: GitHub repository to create/update/close an issue in
15+
default: ${{ github.repository }}
16+
required: true
17+
mode:
18+
description: >
19+
Dictates whether to create, update or close an issue.
20+
Valid options: create | close
21+
default: create
22+
state:
23+
description: >
24+
State of issue to create, update or close.
25+
Valid options: open | closed | all
26+
default: open
27+
title:
28+
description: Title of issue to create or update
29+
required: true
30+
labels:
31+
description: Labels (comma-separated) to both create the issue with and to filter the existing issue search with
32+
required: false
33+
assignees:
34+
description: GitHub handle of the user(s) to assign the issue (comma-separated), only used for issue creation
35+
required: false
36+
body:
37+
description: Body text of the issue
38+
required: false
39+
comment:
40+
description: >
41+
If set, an existing issue have this comment added to the issue.
42+
Note, if the mode is set to create, then any previously added comment is updated instead
43+
required: false
44+
45+
outputs:
46+
url:
47+
description: URL of the issue that was created
48+
value: ${{ steps.action.outputs.url }}
49+
50+
runs:
51+
using: composite
52+
steps:
53+
- name: Check dependencies are installed
54+
shell: bash
55+
run: |
56+
if ! command -v gh >/dev/null 2>&1; then
57+
echo "Command gh not found. This CLI tool is required."
58+
exit 1
59+
fi
60+
61+
- name: Create issue
62+
id: action
63+
shell: bash
64+
env:
65+
GH_TOKEN: ${{ inputs.token }}
66+
INPUT_REPO: ${{ inputs.repo }}
67+
INPUT_MODE: ${{ inputs.mode }}
68+
INPUT_STATE: ${{ inputs.state }}
69+
INPUT_TITLE: ${{ inputs.title }}
70+
INPUT_LABELS: ${{ inputs.labels }}
71+
INPUT_ASSIGNEES: ${{ inputs.assignees }}
72+
INPUT_BODY: ${{ inputs.body }}
73+
INPUT_COMMENT: ${{ inputs.comment }}
74+
run: chmod +x "${{ github.action_path }}/scripts/main.sh" && "${{ github.action_path }}/src/main.sh"

src/main.sh

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/bin/sh
2+
3+
# cspell:ignore endgroup
4+
5+
set -eu
6+
7+
MODE="${INPUT_MODE:-create}"
8+
STATE="${INPUT_STATE:-open}"
9+
TITLE="${INPUT_TITLE}"
10+
LABELS="${INPUT_LABELS:-}"
11+
ASSIGNEES="${INPUT_ASSIGNEES:-}"
12+
BODY="${INPUT_BODY:-}"
13+
COMMENT="${INPUT_COMMENT:-}"
14+
REPO="${INPUT_REPO:-}"
15+
16+
BODY_PRESENCE=${BODY:+(set)}
17+
COMMENT_PRESENCE=${COMMENT:+(set)}
18+
19+
echo "::group::Debug info:"
20+
echo " REPO: ${REPO}"
21+
echo " MODE: ${MODE}"
22+
echo " STATE: ${STATE}"
23+
echo " TITLE: ${TITLE}"
24+
echo " LABELS: ${LABELS}"
25+
echo " ASSIGNEES: ${ASSIGNEES}"
26+
echo " BODY: ${BODY_PRESENCE:-(not set)}"
27+
echo " COMMENT: ${COMMENT_PRESENCE:-(not set)}"
28+
echo '::endgroup::'
29+
30+
ISSUE_NUMBER=$(gh issue list --repo "${REPO}" --state "${STATE}" --label "${LABELS}" --search "in:title \"${TITLE}\"" --limit 1 --json number --jq '.[].number' || true)
31+
32+
# Perform action based on mode
33+
case "${MODE}" in
34+
create)
35+
# Determine body args
36+
if [ -n "${BODY}" ]; then
37+
BODY_ARGS="\"${BODY}\""
38+
else
39+
BODY_ARGS="\"Auto-generated issue with title: ${TITLE}\""
40+
fi
41+
42+
if [ -n "${ISSUE_NUMBER}" ]; then
43+
echo "Issue already exists (#${ISSUE_NUMBER}), updating instead."
44+
if [ -n "${COMMENT}" ]; then
45+
echo "Adding comment to existing issue..."
46+
gh issue comment "${ISSUE_NUMBER}" --repo "${REPO}" --body "${COMMENT}"
47+
else
48+
echo "Updating issue body..."
49+
gh issue edit "${ISSUE_NUMBER}" --repo "${REPO}" --title "${TITLE}" --body "${BODY_ARGS}"
50+
fi
51+
else
52+
echo "Creating new issue..."
53+
gh issue create --repo "${REPO}" --title "${TITLE}" --body "${BODY_ARGS}" --label "${LABELS}" --assignee "${ASSIGNEES}"
54+
fi
55+
;;
56+
close)
57+
if [ -z "${ISSUE_NUMBER}" ]; then
58+
echo "No matching issue found to close."
59+
exit 0
60+
fi
61+
if [ -n "${COMMENT}" ]; then
62+
echo "Adding closure comment..."
63+
gh issue comment "${ISSUE_NUMBER}" --repo "${REPO}" --body "${COMMENT}"
64+
fi
65+
echo "Closing issue #${ISSUE_NUMBER}..."
66+
gh issue close "${ISSUE_NUMBER}" --repo "${REPO}"
67+
;;
68+
*)
69+
echo "Invalid mode '${MODE}'. Valid options: create | close"
70+
exit 1
71+
;;
72+
esac
73+
74+
echo "Done."

tests/gh.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
case "$*" in
5+
*"issue list"*)
6+
# Simulate no issue found unless overridden
7+
if [ "${GH_FAKE_MODE:-none}" = "issue-exists" ]; then
8+
echo '42'
9+
else
10+
echo ''
11+
fi
12+
;;
13+
*"issue create"*)
14+
echo "FAKE: created issue"
15+
;;
16+
*"issue edit"*)
17+
echo "FAKE: edited issue"
18+
;;
19+
*"issue comment"*)
20+
echo "FAKE: added comment"
21+
;;
22+
*"issue close"*)
23+
echo "FAKE: closed issue"
24+
;;
25+
*)
26+
echo "FAKE: unknown gh command $*" >&2
27+
exit 1
28+
;;
29+
esac
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
export GH_FAKE_MODE="issue-exists"
5+
# shellcheck source=utils.sh
6+
. tests/utils.sh
7+
8+
export INPUT_TITLE="Hello world"
9+
export INPUT_COMMENT="Hello comment of world"
10+
output=$(bash "${SCRIPT_PATH}" 2>&1)
11+
assert_contains "$output" "Adding comment to existing issue..."
12+
assert_contains "$output" "FAKE: added comment"
13+
echo "passed"

0 commit comments

Comments
 (0)