Skip to content

Commit 09e6caa

Browse files
feat(ci): tag triaged Linear issues with the source repository
Resolve (or create) a team label named after the repository and attach it to each created CVE issue alongside the existing "CVE" label, so issues are filterable by their source repo in Linear. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 18d41ba commit 09e6caa

1 file changed

Lines changed: 38 additions & 4 deletions

File tree

.github/workflows/vulnerability-triage.yml

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -563,9 +563,36 @@ jobs:
563563
exit 1
564564
fi
565565
566+
# Resolve (or create) a team label named after the repository, so each created
567+
# issue is tagged with its source repo (e.g. "sourcebot-dev/sourcebot").
568+
REPO_LABEL_QUERY='query($teamId: String!, $name: String!) { team(id: $teamId) { labels(filter: { name: { eq: $name } }) { nodes { id } } } }'
569+
REPO_LABEL_PAYLOAD=$(jq -n --arg query "$REPO_LABEL_QUERY" --arg teamId "$TEAM_UUID" --arg name "$REPOSITORY" \
570+
'{query: $query, variables: {teamId: $teamId, name: $name}}')
571+
REPO_LABEL_RESPONSE=$(curl -s -X POST https://api.linear.app/graphql \
572+
-H "Content-Type: application/json" \
573+
-H "Authorization: $LINEAR_API_KEY" \
574+
-d "$REPO_LABEL_PAYLOAD")
575+
REPO_LABEL_ID=$(echo "$REPO_LABEL_RESPONSE" | jq -r '.data.team.labels.nodes[0].id // empty')
576+
577+
if [ -z "$REPO_LABEL_ID" ]; then
578+
echo "No '$REPOSITORY' label in Linear team — creating it."
579+
CREATE_LABEL_MUTATION='mutation($teamId: String!, $name: String!) { issueLabelCreate(input: { teamId: $teamId, name: $name }) { success issueLabel { id } } }'
580+
CREATE_LABEL_PAYLOAD=$(jq -n --arg query "$CREATE_LABEL_MUTATION" --arg teamId "$TEAM_UUID" --arg name "$REPOSITORY" \
581+
'{query: $query, variables: {teamId: $teamId, name: $name}}')
582+
CREATE_LABEL_RESPONSE=$(curl -s -X POST https://api.linear.app/graphql \
583+
-H "Content-Type: application/json" \
584+
-H "Authorization: $LINEAR_API_KEY" \
585+
-d "$CREATE_LABEL_PAYLOAD")
586+
REPO_LABEL_ID=$(echo "$CREATE_LABEL_RESPONSE" | jq -r '.data.issueLabelCreate.issueLabel.id // empty')
587+
if [ -z "$REPO_LABEL_ID" ]; then
588+
echo "::warning::Could not create '$REPOSITORY' label: $(echo "$CREATE_LABEL_RESPONSE" | jq -c '.errors // .')"
589+
fi
590+
fi
591+
566592
{
567593
echo "team_uuid=$TEAM_UUID"
568594
echo "label_id=$LABEL_ID"
595+
echo "repo_label_id=$REPO_LABEL_ID"
569596
echo "state_id=$STATE_ID"
570597
echo "viewer_id=$VIEWER_ID"
571598
} >> "$GITHUB_OUTPUT"
@@ -644,17 +671,22 @@ jobs:
644671
REPOSITORY: ${{ github.repository }}
645672
TEAM_UUID: ${{ steps.match.outputs.team_uuid }}
646673
LABEL_ID: ${{ steps.match.outputs.label_id }}
674+
REPO_LABEL_ID: ${{ steps.match.outputs.repo_label_id }}
647675
STATE_ID: ${{ steps.match.outputs.state_id }}
648676
VIEWER_ID: ${{ steps.match.outputs.viewer_id }}
649677
run: |
650678
set -uo pipefail
651-
# Team UUID, "CVE" label, "Triage" state, and the API key owner's user ID were
652-
# already resolved by the "Match existing Linear issues" step and passed in as env.
679+
# Team UUID, the "CVE" + repository labels, "Triage" state, and the API key
680+
# owner's user ID were already resolved by the "Match existing Linear issues"
681+
# step and passed in as env.
653682
STRUCTURED_OUTPUT=$(cat findings.json)
654683
655684
if [ -z "$LABEL_ID" ]; then
656685
echo "::warning::Could not find 'CVE' label in Linear team. Creating issues without label."
657686
fi
687+
if [ -z "$REPO_LABEL_ID" ]; then
688+
echo "::warning::Could not resolve '$REPOSITORY' repository label. Creating issues without it."
689+
fi
658690
if [ -z "$STATE_ID" ]; then
659691
echo "::warning::Could not find 'Triage' state in Linear team. Using default state."
660692
fi
@@ -756,8 +788,10 @@ jobs:
756788
--argjson priority "$PRIORITY" \
757789
'{teamId: $teamId, title: $title, description: $desc, priority: $priority}')
758790
759-
if [ -n "$LABEL_ID" ]; then
760-
VARIABLES=$(echo "$VARIABLES" | jq --arg lid "$LABEL_ID" '. + {labelIds: [$lid]}')
791+
# Attach the "CVE" label and the repository label (dropping any that failed to resolve).
792+
LABEL_IDS=$(jq -n --arg cve "$LABEL_ID" --arg repo "$REPO_LABEL_ID" '[$cve, $repo] | map(select(. != ""))')
793+
if [ "$(echo "$LABEL_IDS" | jq 'length')" -gt 0 ]; then
794+
VARIABLES=$(echo "$VARIABLES" | jq --argjson lids "$LABEL_IDS" '. + {labelIds: $lids}')
761795
fi
762796
if [ -n "$STATE_ID" ]; then
763797
VARIABLES=$(echo "$VARIABLES" | jq --arg sid "$STATE_ID" '. + {stateId: $sid}')

0 commit comments

Comments
 (0)