├── .nvmrc
├── .gitignore
├── assets
└── screenshot.png
├── .prettierrc.yml
├── .github
├── FUNDING.yml
└── workflows
│ └── release.yml
├── action.yml
├── LICENSE
├── src
├── schema.js
└── index.js
├── package.json
├── CHANGELOG.md
└── README.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.18.1
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .assets
3 |
--------------------------------------------------------------------------------
/assets/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dessant/issue-states/HEAD/assets/screenshot.png
--------------------------------------------------------------------------------
/.prettierrc.yml:
--------------------------------------------------------------------------------
1 | singleQuote: true
2 | bracketSpacing: false
3 | arrowParens: 'avoid'
4 | trailingComma: 'none'
5 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: dessant
2 | patreon: dessant
3 | custom:
4 | - https://armin.dev/go/paypal
5 | - https://armin.dev/go/bitcoin
6 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Create release
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v[0-9]+.[0-9]+.[0-9]+'
7 |
8 | jobs:
9 | release:
10 | name: Release on GitHub
11 | runs-on: ubuntu-22.04
12 | permissions:
13 | contents: write
14 | steps:
15 | - name: Create GitHub release
16 | uses: softprops/action-gh-release@v1
17 | with:
18 | tag_name: ${{ github.ref_name }}
19 | name: ${{ github.ref_name }}
20 | body: >
21 | Learn more about this release from the [changelog](https://github.com/dessant/issue-states/blob/main/CHANGELOG.md#changelog).
22 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Issue States'
2 | description: 'Close or reopen issues when they are moved to a project column'
3 | author: 'Armin Sebastian'
4 | inputs:
5 | github-token:
6 | description: 'GitHub access token'
7 | default: '${{ github.token }}'
8 | open-issue-columns:
9 | description: 'Reopen issues that are moved to these project columns, value must be a comma separated list of project columns'
10 | default: ''
11 | closed-issue-columns:
12 | description: 'Close issues that are moved to these project columns, value must be a comma separated list of project columns'
13 | default: 'Closed, Done'
14 | log-output:
15 | description: 'Log output parameters, value must be either `true` or `false`'
16 | default: false
17 | outputs:
18 | issues:
19 | description: 'Issues that have been either closed or reopened, value is a JSON string'
20 | runs:
21 | using: 'node16'
22 | main: 'dist/index.js'
23 | branding:
24 | icon: 'box'
25 | color: 'red'
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018-2023 Armin Sebastian
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/schema.js:
--------------------------------------------------------------------------------
1 | const Joi = require('joi');
2 |
3 | const extendedJoi = Joi.extend(joi => {
4 | return {
5 | type: 'stringList',
6 | base: joi.array(),
7 | coerce: {
8 | from: 'string',
9 | method(value) {
10 | value = value.trim();
11 | if (value) {
12 | value = value
13 | .split(',')
14 | .map(item => item.trim())
15 | .filter(Boolean);
16 | }
17 |
18 | return {value};
19 | }
20 | }
21 | };
22 | });
23 |
24 | const schema = Joi.object({
25 | 'github-token': Joi.string().trim().max(100),
26 |
27 | 'open-issue-columns': Joi.alternatives()
28 | .try(
29 | extendedJoi
30 | .stringList()
31 | .items(Joi.string().trim().max(140))
32 | .min(1)
33 | .max(30)
34 | .unique(),
35 | Joi.string().trim().valid('')
36 | )
37 | .default(''),
38 |
39 | 'closed-issue-columns': Joi.alternatives()
40 | .try(
41 | extendedJoi
42 | .stringList()
43 | .items(Joi.string().trim().max(140))
44 | .min(1)
45 | .max(30)
46 | .unique(),
47 | Joi.string().trim().valid('')
48 | )
49 | .default('Closed, Done'),
50 |
51 | 'log-output': Joi.boolean().default(false)
52 | });
53 |
54 | module.exports = schema;
55 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "issue-states",
3 | "version": "3.0.0",
4 | "description": "A GitHub Action that closes or reopens issues when they are moved to a project column.",
5 | "author": "Armin Sebastian",
6 | "license": "MIT",
7 | "homepage": "https://github.com/dessant/issue-states",
8 | "repository": {
9 | "url": "https://github.com/dessant/issue-states.git",
10 | "type": "git"
11 | },
12 | "bugs": {
13 | "url": "https://github.com/dessant/issue-states/issues"
14 | },
15 | "main": "src/index.js",
16 | "scripts": {
17 | "build": "ncc build src/index.js -o dist",
18 | "update": "ncu --upgrade",
19 | "release": "standard-version",
20 | "push": "git push --tags origin main"
21 | },
22 | "dependencies": {
23 | "@actions/core": "^1.10.0",
24 | "@actions/github": "^5.1.1",
25 | "joi": "^17.7.0"
26 | },
27 | "devDependencies": {
28 | "@vercel/ncc": "^0.34.0",
29 | "npm-check-updates": "^16.4.3",
30 | "prettier": "^2.8.0",
31 | "standard-version": "^9.5.0"
32 | },
33 | "engines": {
34 | "node": ">=16.0.0"
35 | },
36 | "keywords": [
37 | "github",
38 | "issues",
39 | "projects",
40 | "project columns",
41 | "project cards",
42 | "to do",
43 | "in progress",
44 | "done",
45 | "github projects",
46 | "open issues",
47 | "close issues",
48 | "automation",
49 | "github actions",
50 | "project management",
51 | "bot"
52 | ],
53 | "private": true
54 | }
55 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const core = require('@actions/core');
2 | const github = require('@actions/github');
3 |
4 | const schema = require('./schema');
5 |
6 | async function run() {
7 | try {
8 | const config = getConfig();
9 | const client = github.getOctokit(config['github-token']);
10 |
11 | const app = new App(config, client);
12 |
13 | const output = await app.processCard();
14 |
15 | core.debug('Setting output (issues)');
16 | if (output) {
17 | core.setOutput('issues', JSON.stringify(output));
18 |
19 | if (config['log-output']) {
20 | core.info('Output (issues):');
21 | core.info(JSON.stringify(output, null, 2));
22 | }
23 | } else {
24 | core.setOutput('issues', '');
25 | }
26 | } catch (err) {
27 | core.setFailed(err);
28 | }
29 | }
30 |
31 | class App {
32 | constructor(config, client) {
33 | this.config = config;
34 | this.client = client;
35 | }
36 |
37 | async processCard() {
38 | const payload = github.context.payload;
39 |
40 | if (payload.sender.type === 'Bot') {
41 | return;
42 | }
43 |
44 | const contentUrl = payload.project_card.content_url || '';
45 | const match = contentUrl.match(/\/repos\/(.+)\/(.+)\/issues\/([0-9]+)$/);
46 | if (!match) {
47 | return;
48 | }
49 | const [owner, repo, issue_number] = match.slice(1);
50 | const issue = {owner, repo, issue_number};
51 |
52 | const openIssueColumns = this.config['open-issue-columns'];
53 | const closedIssueColumns = this.config['closed-issue-columns'];
54 |
55 | const {
56 | data: {name: columnName}
57 | } = await this.client.rest.projects.getColumn({
58 | column_id: payload.project_card.column_id
59 | });
60 |
61 | let newState;
62 | if (openIssueColumns.includes(columnName)) {
63 | newState = 'open';
64 | } else if (closedIssueColumns.includes(columnName)) {
65 | newState = 'closed';
66 | }
67 | if (!newState) {
68 | return;
69 | }
70 |
71 | const {data: issueData} = await this.client.rest.issues.get(issue);
72 | if (issueData.state === newState || issueData.pull_request) {
73 | return;
74 | }
75 |
76 | core.debug('Updating issue state');
77 | await this.client.rest.issues.update({...issue, state: newState});
78 |
79 | return [{...issue, state: newState}];
80 | }
81 | }
82 |
83 | function getConfig() {
84 | const input = Object.fromEntries(
85 | Object.keys(schema.describe().keys).map(item => [item, core.getInput(item)])
86 | );
87 |
88 | const {error, value} = schema.validate(input, {abortEarly: false});
89 | if (error) {
90 | throw error;
91 | }
92 |
93 | return value;
94 | }
95 |
96 | run();
97 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ## [3.0.0](https://github.com/dessant/issue-states/compare/v2.2.0...v3.0.0) (2022-12-04)
6 |
7 |
8 | ### ⚠ BREAKING CHANGES
9 |
10 | * the action now requires Node.js 16
11 |
12 | ### Bug Fixes
13 |
14 | * update dependencies ([fcdd3b4](https://github.com/dessant/issue-states/commit/fcdd3b4fdb9a29ed97af9fe8c87d4120d65d6f8e))
15 | * update docs ([52df266](https://github.com/dessant/issue-states/commit/52df2667583607628dd132efe0f67463dda4cbc3))
16 |
17 | ## [2.2.0](https://github.com/dessant/issue-states/compare/v2.1.1...v2.2.0) (2021-10-03)
18 |
19 |
20 | ### Features
21 |
22 | * add option for logging output parameters ([54551cb](https://github.com/dessant/issue-states/commit/54551cb00c04d17949509251854b8e1d628c0124))
23 |
24 | ### [2.1.1](https://github.com/dessant/issue-states/compare/v2.1.0...v2.1.1) (2021-07-09)
25 |
26 |
27 | ### Bug Fixes
28 |
29 | * update GitHub API calls ([b14c1e5](https://github.com/dessant/issue-states/commit/b14c1e53f4bc45f94ba2ff6d9115d0c8bfa85b46))
30 |
31 | ## [2.1.0](https://github.com/dessant/issue-states/compare/v2.0.0...v2.1.0) (2021-07-09)
32 |
33 |
34 | ### Features
35 |
36 | * make github-token optional and document the use of personal access tokens ([453ac36](https://github.com/dessant/issue-states/commit/453ac3617d92732e1a800f1a010300d750c6dbc0))
37 |
38 |
39 | ### Bug Fixes
40 |
41 | * declare required permissions ([5721b51](https://github.com/dessant/issue-states/commit/5721b514c82e1be6db9cfa42cfa8dbee1dce5dba))
42 |
43 | ## [2.0.0](https://github.com/dessant/issue-states/compare/v1.0.1...v2.0.0) (2021-01-21)
44 |
45 |
46 | ### ⚠ BREAKING CHANGES
47 |
48 | * The deployment method and configuration options have changed.
49 |
50 | ### Features
51 |
52 | * move to GitHub Actions ([dc414db](https://github.com/dessant/issue-states/commit/dc414dbff89e3e96131d3c3abe29e98508eb1099))
53 |
54 | ### [1.0.1](https://github.com/dessant/issue-states/compare/v1.0.0...v1.0.1) (2019-10-25)
55 |
56 |
57 | ### Bug Fixes
58 |
59 | * update dependencies ([d27ab6d](https://github.com/dessant/issue-states/commit/d27ab6d6bab4b3f90cc56c4abf422d8ac684ba63))
60 |
61 | ## [1.0.0](https://github.com/dessant/issue-states/compare/v0.3.4...v1.0.0) (2019-06-10)
62 |
63 |
64 | ### Features
65 |
66 | * update dependencies ([fccb39c](https://github.com/dessant/issue-states/commit/fccb39c))
67 |
68 |
69 | ### BREAKING CHANGES
70 |
71 | * probot < 9.2.13 no longer supported.
72 |
73 |
74 |
75 |
76 | ## [0.3.4](https://github.com/dessant/issue-states/compare/v0.3.3...v0.3.4) (2019-01-20)
77 |
78 |
79 | ### Bug Fixes
80 |
81 | * update dependencies ([2045b1f](https://github.com/dessant/issue-states/commit/2045b1f))
82 |
83 |
84 |
85 |
86 | ## [0.3.3](https://github.com/dessant/issue-states/compare/v0.3.2...v0.3.3) (2019-01-20)
87 |
88 |
89 | ### Bug Fixes
90 |
91 | * apply stricter config validation ([06ccf1e](https://github.com/dessant/issue-states/commit/06ccf1e))
92 |
93 |
94 |
95 |
96 | ## [0.3.2](https://github.com/dessant/issue-states/compare/v0.3.1...v0.3.2) (2018-10-03)
97 |
98 |
99 | ### Bug Fixes
100 |
101 | * allow newer versions of node ([7e34e45](https://github.com/dessant/issue-states/commit/7e34e45))
102 |
103 |
104 |
105 |
106 | ## [0.3.1](https://github.com/dessant/issue-states/compare/v0.3.0...v0.3.1) (2018-07-24)
107 |
108 |
109 | ### Bug Fixes
110 |
111 | * pass owner and repo to sendMessage ([1a4f609](https://github.com/dessant/issue-states/commit/1a4f609))
112 |
113 |
114 |
115 |
116 | # [0.3.0](https://github.com/dessant/issue-states/compare/v0.2.0...v0.3.0) (2018-07-23)
117 |
118 |
119 | ### Features
120 |
121 | * notify maintainers about configuration errors ([5d681d8](https://github.com/dessant/issue-states/commit/5d681d8))
122 |
123 |
124 |
125 |
126 | # [0.2.0](https://github.com/dessant/issue-states/compare/v0.1.0...v0.2.0) (2018-06-27)
127 |
128 |
129 | ### Features
130 |
131 | * log additional data and add DRY_RUN env var ([cf70fac](https://github.com/dessant/issue-states/commit/cf70fac))
132 |
133 |
134 |
135 |
136 | # 0.1.0 (2018-06-15)
137 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Issue States
2 |
3 | > :warning: The action does not work with modern projects: https://github.com/dessant/issue-states/issues/2
4 |
5 | Issue States is a GitHub Action that closes or reopens issues
6 | when they are moved to a project column.
7 |
8 | 
9 |
10 | ## Supporting the Project
11 |
12 | The continued development of Issue States is made possible
13 | thanks to the support of awesome backers. If you'd like to join them,
14 | please consider contributing with
15 | [Patreon](https://armin.dev/go/patreon?pr=issue-states&src=repo),
16 | [PayPal](https://armin.dev/go/paypal?pr=issue-states&src=repo) or
17 | [Bitcoin](https://armin.dev/go/bitcoin?pr=issue-states&src=repo).
18 |
19 | ## Usage
20 |
21 | 1. Create the `issue-states.yml` workflow file in the `.github/workflows`
22 | directory, use one of the [example workflows](#examples) to get started
23 | 2. Start adding or moving issues to the project columns declared
24 | in `open-issue-columns` and `closed-issue-columns`
25 |
26 | Issues which were already in the respective columns before the action
27 | was set up will not be processed. To process these issues,
28 | move them to a different column, then move them back.
29 |
30 | Care must be taken during the use of the action to not conflict
31 | with project automation presets on GitHub.
32 |
33 | ### Inputs
34 |
35 | The action can be configured using [input parameters](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepswith).
36 |
37 |
38 | - **`github-token`**
39 | - GitHub access token, value must be `${{ github.token }}` or an encrypted
40 | secret that contains a [personal access token](#using-a-personal-access-token)
41 | - Optional, defaults to `${{ github.token }}`
42 | - **`open-issue-columns`**
43 | - Reopen issues that are moved to these project columns, value must be
44 | a comma separated list of project columns
45 | - Optional, defaults to `''`
46 | - **`closed-issue-columns`**
47 | - Close issues that are moved to these project columns, value must be
48 | a comma separated list of project columns
49 | - Optional, defaults to `Closed, Done`
50 | - **`log-output`**
51 | - Log output parameters, value must be either `true` or `false`
52 | - Optional, defaults to `false`
53 |
54 | ### Outputs
55 |
56 |
57 | - **`issues`**
58 | - Issues that have been either closed or reopened, value is a JSON string
59 | in the form of `[{"owner": "actions", "repo": "toolkit", "issue_number": 1,
60 | "state": "closed"}]`, value of `state` is either `open` or `closed`
61 | - Defaults to `''`
62 |
63 | ## Examples
64 |
65 | The following workflow will close issues when they are moved
66 | to the `Closed` or `Done` project column.
67 |
68 |
69 | ```yaml
70 | name: 'Issue States'
71 |
72 | on:
73 | project_card:
74 | types: [created, edited, moved]
75 |
76 | permissions:
77 | repository-projects: read
78 | issues: write
79 | pull-requests: write
80 |
81 | jobs:
82 | action:
83 | runs-on: ubuntu-latest
84 | steps:
85 | - uses: dessant/issue-states@v3
86 | ```
87 |
88 | ### Available input parameters
89 |
90 | This workflow declares all the available input parameters of the action
91 | and their default values. Any of the parameters can be omitted.
92 |
93 |
94 | ```yaml
95 | name: 'Issue States'
96 |
97 | on:
98 | project_card:
99 | types: [created, edited, moved]
100 |
101 | permissions:
102 | repository-projects: read
103 | issues: write
104 | pull-requests: write
105 |
106 | jobs:
107 | action:
108 | runs-on: ubuntu-latest
109 | steps:
110 | - uses: dessant/issue-states@v3
111 | with:
112 | github-token: ${{ github.token }}
113 | open-issue-columns: ''
114 | closed-issue-columns: 'Closed, Done'
115 | log-output: false
116 | ```
117 |
118 | ### Using a personal access token
119 |
120 | The action uses an installation access token by default to interact with GitHub.
121 | You may also authenticate with a personal access token to perform actions
122 | as a GitHub user instead of the `github-actions` app.
123 |
124 | Create a [personal access token](https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token)
125 | with the `repo` or `public_repo` scopes enabled, and add the token as an
126 | [encrypted secret](https://docs.github.com/en/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository)
127 | for the repository or organization, then provide the action with the secret
128 | using the `github-token` input parameter.
129 |
130 |
131 | ```yaml
132 | steps:
133 | - uses: dessant/issue-states@v3
134 | with:
135 | github-token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
136 | ```
137 |
138 | ## License
139 |
140 | Copyright (c) 2018-2023 Armin Sebastian
141 |
142 | This software is released under the terms of the MIT License.
143 | See the [LICENSE](LICENSE) file for further information.
144 |
--------------------------------------------------------------------------------