Skip to content

Commit ed8d05f

Browse files
committed
ci: add workflow to sync issues to a target GitHub project
Adds the sync-issues-to-project.yml workflow triggered on issue open/close and workflow_dispatch. Uses PSYNC_TARGET (repo variable) and PSYNC_PAT (repo secret) for configuration. Closes #1235 Signed-off-by: David Gutierrez <david.magallanes@gmail.com>
1 parent bb585e9 commit ed8d05f

2 files changed

Lines changed: 706 additions & 0 deletions

File tree

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
<!--
2+
Copyright 2021-Present The Serverless Workflow Specification Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
17+
# Sync Issues to Target GitHub Project
18+
19+
Workflow file: `sync-issues-to-project.yml`
20+
21+
Automatically adds issues to a GitHub Project in another organization when they
22+
are opened, sets configurable initial field values, updates the Status when they
23+
are closed, and optionally imports pre-existing repo issues on demand.
24+
25+
---
26+
27+
## How it works
28+
29+
| Event | Action |
30+
|---|---|
31+
| Issue opened | Issue is added to the target project; initial field values are applied (skipped if `PSYNC_ENABLED=false` or `off`) |
32+
| Issue closed | Project item Status is updated to the configured close status (skipped if `PSYNC_ENABLED=false` or `off`) |
33+
| `workflow_dispatch` | If `PSYNC_IMPORT_EXISTING=true`, imports all open repo issues not yet in the project with initial field values applied |
34+
35+
The project item is natively linked to the source issue — no custom fields are
36+
needed. Clicking the item in the project board opens the original issue.
37+
38+
---
39+
40+
## Setup
41+
42+
### 1. Create a Personal Access Token (PAT)
43+
44+
The PAT must belong to a user with access to the target org's project.
45+
46+
Required scopes:
47+
- `project` — read/write access to GitHub Projects v2
48+
- `read:org` — required to resolve the org's project by number
49+
- `repo` (private repos) or `public_repo` (public repos) — required only if
50+
`PSYNC_INITIAL_VALUES` includes `Assignees=...`, as the workflow calls the
51+
REST API to add assignees directly to the source repo issue
52+
53+
> Classic PATs only. Fine-grained PATs do not yet support Projects v2 mutations.
54+
55+
### 2. Add the secret
56+
57+
Go to **Repo → Settings → Secrets and variables → Actions → Secrets**:
58+
59+
| Name | Value |
60+
|---|---|
61+
| `PSYNC_PAT` | The PAT created above |
62+
63+
### 3. Add the variables
64+
65+
Go to **Repo → Settings → Secrets and variables → Actions → Variables**:
66+
67+
| Name | Required | Default | Description | Example |
68+
|---|---|---|---|---|
69+
| `PSYNC_TARGET` | yes | — | Target project in `org:project_number` format | `my-org:1` |
70+
| `PSYNC_INITIAL_VALUES` | no | — | Comma-separated `field=value` pairs applied to new project items | `Status=Backlog, Area=Tooling, Assignees=user1` |
71+
| `PSYNC_CLOSE_STATUS` | no | `Done` | Status option name set on the project item when the issue is closed | `Done` |
72+
| `PSYNC_ENABLED` | no | `true` | Set to `false` or `off` to pause syncing without removing the workflow | `false` |
73+
| `PSYNC_IMPORT_EXISTING` | no | `false` | Set to `true` and trigger manually to bulk-import all open issues not yet in the project | `true` |
74+
| `PSYNC_AUTHORS_FILTER` | no | — | Comma-separated list of GitHub usernames; only issues opened by these users are synced. Empty means all authors are included | `user1, user2` |
75+
76+
The project number is visible in the project URL:
77+
`https://github.com/orgs/<org>/projects/<number>`
78+
79+
**`PSYNC_ENABLED`** — set to `false` or `off` to pause syncing without removing
80+
the workflow. `workflow_dispatch` runs (e.g. for importing) are not affected.
81+
82+
**`PSYNC_AUTHORS_FILTER`** — comma-separated list of GitHub usernames. When set, only
83+
issues created by one of the listed authors are synced to the target project. If
84+
unset or empty, all authors are included.
85+
86+
**`PSYNC_IMPORT_EXISTING`** — set to `true` and trigger the workflow manually
87+
via **Actions → Run workflow** to import all open repo issues not already in the
88+
project. The same `PSYNC_INITIAL_VALUES` rules apply. Issues already in the
89+
project are skipped. The run prints a summary: `Imported: N, Failed: N`.
90+
91+
#### `PSYNC_INITIAL_VALUES` format
92+
93+
Comma-separated `field=value` pairs. Field names must match the project field
94+
names exactly (case-sensitive). Example:
95+
96+
```
97+
Status=Backlog, Area=Tooling, Assignees=user1
98+
```
99+
100+
Supported field types:
101+
102+
| Field type | Behaviour |
103+
|---|---|
104+
| Single-select | Matches by option name |
105+
| Text | Sets the text value directly |
106+
| `Assignees` | Adds assignees to the source issue via the REST API; space-separate multiple users: `Assignees=user1 user2` |
107+
108+
> Number and date fields are not currently supported.
109+
110+
### 4. Ensure the target project has a Status field
111+
112+
The workflow looks for a **single-select field named exactly `Status`**. The
113+
option names used by `PSYNC_INITIAL_VALUES` and `PSYNC_CLOSE_STATUS` must
114+
exist in the project (case-sensitive).
115+
116+
Default options required unless overridden:
117+
118+
| Option | Used when |
119+
|---|---|
120+
| `Done` | Issue closed (default close Status) |
121+
122+
---
123+
124+
## Limitations
125+
126+
- **Sub-issues are not synced** — GitHub does not emit webhook events for
127+
sub-issues; only top-level issues trigger the `issues` event.
128+
- **Status field name is hardcoded** — the field must be named `Status`.
129+
- **Number and date fields** in `PSYNC_INITIAL_VALUES` are not supported;
130+
only single-select and text fields.
131+
132+
---
133+
134+
## Troubleshooting
135+
136+
### `gh: To use GitHub CLI in a GitHub Actions workflow, set the GH_TOKEN environment variable`
137+
138+
`PSYNC_PAT` is not set or is empty. Verify it exists under **Repo → Settings → Secrets and variables → Actions → Secrets**.
139+
140+
### `Error: Process completed with exit code 1` on the GraphQL steps
141+
142+
Run the query manually to inspect the response:
143+
144+
```bash
145+
gh api graphql -f query='
146+
query($org: String!, $number: Int!) {
147+
organization(login: $org) {
148+
projectV2(number: $number) {
149+
id
150+
}
151+
}
152+
}' \
153+
-f org="<target-org>" \
154+
-F number=<project-number>
155+
```
156+
157+
Common causes:
158+
- The PAT does not have access to the target org's project
159+
- The project number is wrong
160+
- The org name in `PSYNC_TARGET` has a typo
161+
162+
### Item not found on close (`item_id` is empty)
163+
164+
If the issue is not already in the project when it is closed (e.g., it was opened before the workflow was installed, or the `opened` sync failed), the workflow automatically adds it to the project and then sets the close Status.
165+
166+
If the close path still fails, likely causes are:
167+
- The PAT lacks `project` write access to the target org's project
168+
- The project ID lookup failed (check `PSYNC_TARGET` format and PAT scopes)
169+
170+
### Status not updated on close
171+
172+
Verify the target project has:
173+
- A single-select field named exactly `Status`
174+
- An option matching the value of `PSYNC_CLOSE_STATUS` (default: `Done`)
175+
176+
Field and option names are case-sensitive.
177+
178+
### Field not found warning in `Set initial field values`
179+
180+
The step prints `Warning: field '<name>' not found, skipping.` when a key in
181+
`PSYNC_INITIAL_VALUES` does not match any field in the project. Check for
182+
typos or extra spaces in the variable value.

0 commit comments

Comments
 (0)