1- name : Push → Discussion (GraphQL )
1+ name : Push → Discussion (gh cli )
22
33on :
44 push :
@@ -10,114 +10,135 @@ permissions:
1010 discussions : write
1111
1212env :
13- DISCUSSIONS_CATEGORY : Push Updates # must match the category name in your repo
13+ DISCUSSIONS_CATEGORY : Push Updates
1414
1515jobs :
1616 post :
1717 runs-on : ubuntu-latest
18+ env :
19+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }} # gh uses this
1820 steps :
19- - name : Create a discussion for this push
20- uses : actions/github-script@v7
21- with :
22- github-token : ${{ secrets.GITHUB_TOKEN }}
23- script : |
24- const { owner, repo } = context.repo;
25- const desiredName = (process.env.DISCUSSIONS_CATEGORY || "").trim();
26-
27- // 1) GraphQL: get repo id + discussion categories
28- const query = `
29- query($owner: String!, $name: String!) {
30- repository(owner: $owner, name: $name) {
31- id
32- hasDiscussionsEnabled
33- discussionCategories(first: 50) {
34- nodes { id name }
35- }
21+ - name : Build title and body from push payload
22+ id : build
23+ run : |
24+ set -euo pipefail
25+
26+ EV="$GITHUB_EVENT_PATH"
27+
28+ OWNER="${GITHUB_REPOSITORY%/*}"
29+ REPO="${GITHUB_REPOSITORY#*/}"
30+
31+ BRANCH="$(jq -r '.ref' "$EV" | sed 's#^refs/heads/##')"
32+ [ -z "$BRANCH" ] && BRANCH="(unknown)"
33+
34+ PUSHER="$(jq -r '.pusher.name // .sender.login // "unknown"' "$EV")"
35+
36+ BEFORE="$(jq -r '.before' "$EV")"
37+ AFTER="$(jq -r '.after' "$EV")"
38+ COMPARE="$(jq -r '.compare // empty' "$EV")"
39+ if [ -z "$COMPARE" ]; then
40+ COMPARE="https://github.com/${OWNER}/${REPO}/compare/${BEFORE}...${AFTER}"
41+ fi
42+
43+ COMMIT_COUNT="$(jq -r '.commits | length' "$EV")"
44+
45+ # Commit list (first line of message, author, link)
46+ COMMITS_MD="$(jq -r '
47+ if (.commits | length) == 0 then
48+ ""
49+ else
50+ [.commits[]
51+ | "- [`\(.id[0:7])`](\(.url)) \((.message // "" ) | split("\n")[0]) — _\(.author.name // "unknown")_"
52+ ] | join("\n")
53+ end
54+ ' "$EV")"
55+
56+ TITLE="Push to ${BRANCH} by ${PUSHER} — ${COMMIT_COUNT} commit"
57+ [ "$COMMIT_COUNT" -ne 1 ] && TITLE="${TITLE}s"
58+
59+ {
60+ echo "> _This discussion was created automatically by GitHub Actions from a \`push\` event._"
61+ echo
62+ echo "**Branch:** \`${BRANCH}\`"
63+ echo "**Pusher:** ${PUSHER}"
64+ echo "**Compare:** ${COMPARE}"
65+ echo
66+ echo "### Commits"
67+ if [ -n "$COMMITS_MD" ]; then
68+ echo "$COMMITS_MD"
69+ else
70+ echo "_No commit details available._"
71+ fi
72+ echo
73+ echo "<sub>— end of automated log —</sub>"
74+ } > body.md
75+
76+ echo "title<<EOF" >> $GITHUB_OUTPUT
77+ echo "$TITLE" >> $GITHUB_OUTPUT
78+ echo "EOF" >> $GITHUB_OUTPUT
79+
80+ - name : Resolve repo & category IDs via GraphQL
81+ id : ids
82+ run : |
83+ set -euo pipefail
84+ OWNER="${GITHUB_REPOSITORY%/*}"
85+ REPO="${GITHUB_REPOSITORY#*/}"
86+
87+ # Query repo info + categories
88+ RESP="$(gh api graphql -f query='
89+ query($owner:String!, $name:String!){
90+ repository(owner:$owner, name:$name){
91+ id
92+ hasDiscussionsEnabled
93+ discussionCategories(first:50){
94+ nodes{ id name }
3695 }
3796 }
38- `;
39- let repoInfo;
40- try {
41- repoInfo = await github.graphql(query, { owner, name: repo });
42- } catch (e) {
43- core.setFailed(\`GraphQL repo/category lookup failed: \${e.message}\`);
44- return;
45- }
46-
47- const repository = repoInfo?.repository;
48- if (!repository) {
49- core.setFailed("Repository not found by GraphQL; check owner/repo context.");
50- return;
51- }
52- if (!repository.hasDiscussionsEnabled) {
53- core.setFailed("Discussions are not enabled for this repository (Settings → General → Features → Discussions).");
54- return;
55- }
56-
57- const categories = repository.discussionCategories?.nodes || [];
58- const category = categories.find(c => c.name.toLowerCase() === desiredName.toLowerCase());
59- if (!category) {
60- const names = categories.map(c => `- ${c.name}`).join("\n") || "(none found)";
61- core.setFailed(`Category "${desiredName}" not found.\nAvailable categories:\n${names}`);
62- return;
63- }
64-
65- // 2) Build title/body from push payload
66- const push = context.payload;
67- const branch = (push.ref || "").replace("refs/heads/", "") || "(unknown)";
68- const pusher = push.pusher?.name || push.sender?.login || "unknown";
69- const commits = push.commits || [];
70- const commitCount = commits.length;
71-
72- const title = `Push to ${branch} by ${pusher} — ${commitCount} commit${commitCount === 1 ? "" : "s"}`;
73-
74- const lines = commits.map(c => {
75- const sha7 = (c.id || "").slice(0,7);
76- const first = (c.message || "").split("\n")[0];
77- const author = c.author?.name || "unknown";
78- const url = c.url || \`https://github.com/${owner}/${repo}/commit/\${c.id}\`;
79- return `- [\`${sha7}\`](${url}) ${first} — _${author}_`;
80- });
81-
82- const compareUrl =
83- push.compare ||
84- \`https://github.com/${owner}/${repo}/compare/\${push.before}...\${push.after}\`;
85-
86- const body = [
87- `> _This discussion was created automatically by GitHub Actions from a \`push\` event._`,
88- "",
89- `**Branch:** \`${branch}\``,
90- `**Pusher:** ${pusher}`,
91- `**Compare:** ${compareUrl}`,
92- "",
93- "### Commits",
94- lines.length ? lines.join("\n") : "_No commit details available._",
95- "",
96- "<sub>— end of automated log —</sub>"
97- ].join("\n");
98-
99- // 3) GraphQL: create discussion
100- const mutation = `
101- mutation($repoId: ID!, $catId: ID!, $title: String!, $body: String!) {
102- createDiscussion(input: {
103- repositoryId: $repoId,
104- categoryId: $catId,
105- title: $title,
106- body: $body
107- }) {
108- discussion { url }
97+ }' -f owner="$OWNER" -f name="$REPO")"
98+
99+ REPO_ID="$(echo "$RESP" | jq -r '.data.repository.id')"
100+ DISC_ENABLED="$(echo "$RESP" | jq -r '.data.repository.hasDiscussionsEnabled')"
101+ [ "$DISC_ENABLED" != "true" ] && { echo "Discussions not enabled"; exit 1; }
102+
103+ CAT_NAME="${DISCUSSIONS_CATEGORY}"
104+ CAT_ID="$(echo "$RESP" | jq -r --arg n "$CAT_NAME" '
105+ .data.repository.discussionCategories.nodes[]
106+ | select((.name|ascii_downcase)==($n|ascii_downcase))
107+ | .id
108+ ')"
109+
110+ if [ -z "$CAT_ID" ]; then
111+ echo "Available categories:"
112+ echo "$RESP" | jq -r '.data.repository.discussionCategories.nodes[].name'
113+ echo "Category \"$CAT_NAME\" not found."
114+ exit 1
115+ fi
116+
117+ echo "repo_id=$REPO_ID" >> $GITHUB_OUTPUT
118+ echo "cat_id=$CAT_ID" >> $GITHUB_OUTPUT
119+
120+ - name : Create discussion
121+ run : |
122+ set -euo pipefail
123+ TITLE="${{ steps.build.outputs.title }}"
124+ REPO_ID="${{ steps.ids.outputs.repo_id }}"
125+ CAT_ID="${{ steps.ids.outputs.cat_id }}"
126+ BODY_CONTENT="$(cat body.md)"
127+
128+ gh api graphql \
129+ -f query='
130+ mutation($repoId:ID!, $catId:ID!, $title:String!, $body:String!){
131+ createDiscussion(input:{
132+ repositoryId:$repoId,
133+ categoryId:$catId,
134+ title:$title,
135+ body:$body
136+ }){
137+ discussion{ url }
109138 }
110- }
111- `;
112- try {
113- const res = await github.graphql(mutation, {
114- repoId: repository.id,
115- catId: category.id,
116- title,
117- body
118- });
119- const url = res?.createDiscussion?.discussion?.url;
120- core.info(\`Created discussion: \${url}\`);
121- } catch (e) {
122- core.setFailed(\`Failed to create discussion via GraphQL: \${e.message}\`);
123- }
139+ }' \
140+ -f repoId="$REPO_ID" \
141+ -f catId="$CAT_ID" \
142+ -f title="$TITLE" \
143+ -f body="$BODY_CONTENT" \
144+ --jq '.data.createDiscussion.discussion.url'
0 commit comments