@@ -145,6 +145,20 @@ jobs:
145145 return `${nextBlock || nextBody}\n\n${currentBody}`;
146146 }
147147
148+ function trackerKeyFromBody(body) {
149+ const markerMatch = (body || '').match(/<!--\s*user-story-key:\s*([^>]+?)\s*-->/);
150+ if (markerMatch) {
151+ return markerMatch[1].trim();
152+ }
153+
154+ const storyKeyMatch = (body || '').match(/Story Key:\s*`([^`]+)`/);
155+ if (storyKeyMatch) {
156+ return storyKeyMatch[1].trim();
157+ }
158+
159+ return null;
160+ }
161+
148162 async function ensureLabel(labelName) {
149163 const definition = labelDefinitions[labelName] || {
150164 color: 'ededed',
@@ -172,21 +186,36 @@ jobs:
172186 }
173187 }
174188
175- async function findMatchingIssue(issue) {
176- const query = `repo:${context.repo.owner}/${context.repo.repo} is:issue "${issue.tracker_key}"`;
177- const candidates = await github.paginate(
178- github.rest.search.issuesAndPullRequests,
189+ async function loadExistingIssueIndex() {
190+ const repositoryIssues = await github.paginate(
191+ github.rest.issues.listForRepo,
179192 {
180- q: query,
193+ owner: context.repo.owner,
194+ repo: context.repo.repo,
195+ state: 'all',
181196 per_page: 100,
182197 }
183198 );
184199
185- return candidates.find((candidate) => {
186- const body = candidate.body || '';
187- return body.includes(`user-story-key: ${issue.tracker_key}`) ||
188- body.includes(`Story Key: \`${issue.tracker_key}\``);
189- });
200+ const issueIndex = new Map();
201+ let indexedCount = 0;
202+
203+ for (const repositoryIssue of repositoryIssues) {
204+ if (repositoryIssue.pull_request) {
205+ continue;
206+ }
207+
208+ const trackerKey = trackerKeyFromBody(repositoryIssue.body || '');
209+ if (!trackerKey || issueIndex.has(trackerKey)) {
210+ continue;
211+ }
212+
213+ issueIndex.set(trackerKey, repositoryIssue);
214+ indexedCount += 1;
215+ }
216+
217+ core.info(`Indexed ${indexedCount} existing user story issue(s) without using GitHub Search.`);
218+ return issueIndex;
190219 }
191220
192221 let projectCache = null;
@@ -257,23 +286,51 @@ jobs:
257286 return projectCache;
258287 }
259288
260- async function syncProjectItem(issueNodeId) {
261- const project = await loadProject();
262- const addResult = await github.graphql(
263- `mutation($projectId: ID!, $contentId: ID!) {
264- addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) {
265- item {
266- id
289+ async function findProjectItemId(projectId, issueNodeId) {
290+ const result = await github.graphql(
291+ `query($issueNodeId: ID!) {
292+ node(id: $issueNodeId) {
293+ ... on Issue {
294+ projectItems(first: 50) {
295+ nodes {
296+ id
297+ project {
298+ id
299+ }
300+ }
301+ }
267302 }
268303 }
269304 }`,
270- {
271- projectId: project.id,
272- contentId: issueNodeId,
273- }
305+ { issueNodeId }
274306 );
275307
276- const itemId = addResult.addProjectV2ItemById.item.id;
308+ const projectItems = (result.node && result.node.projectItems && result.node.projectItems.nodes) || [];
309+ const existingItem = projectItems.find((item) => item.project && item.project.id === projectId);
310+ return existingItem ? existingItem.id : null;
311+ }
312+
313+ async function syncProjectItem(issueNodeId) {
314+ const project = await loadProject();
315+ let itemId = await findProjectItemId(project.id, issueNodeId);
316+
317+ if (!itemId) {
318+ const addResult = await github.graphql(
319+ `mutation($projectId: ID!, $contentId: ID!) {
320+ addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) {
321+ item {
322+ id
323+ }
324+ }
325+ }`,
326+ {
327+ projectId: project.id,
328+ contentId: issueNodeId,
329+ }
330+ );
331+
332+ itemId = addResult.addProjectV2ItemById.item.id;
333+ }
277334
278335 await github.graphql(
279336 `mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
@@ -310,8 +367,10 @@ jobs:
310367 await ensureLabel(label);
311368 }
312369
370+ const existingIssueIndex = await loadExistingIssueIndex();
371+
313372 for (const issue of issues) {
314- let current = await findMatchingIssue (issue);
373+ let current = existingIssueIndex.get (issue.tracker_key );
315374
316375 if (!current) {
317376 try {
@@ -324,6 +383,7 @@ jobs:
324383 assignees: assignees.length ? assignees : undefined,
325384 });
326385 current = created.data;
386+ existingIssueIndex.set(issue.tracker_key, current);
327387 core.info(`Created ${issue.tracker_key}: ${current.html_url}`);
328388 } catch (error) {
329389 if (!assignees.length) {
@@ -339,6 +399,7 @@ jobs:
339399 labels: issue.labels,
340400 });
341401 current = created.data;
402+ existingIssueIndex.set(issue.tracker_key, current);
342403 core.info(`Created ${issue.tracker_key}: ${current.html_url}`);
343404 }
344405 } else {
0 commit comments