Skip to content

Commit 18e854a

Browse files
authored
Merge pull request #11 from grid-x/feat/replace-topic-content
feat: Allow for replacing a single post's content
2 parents b8d5672 + 7fc2e78 commit 18e854a

File tree

5 files changed

+86
-35
lines changed

5 files changed

+86
-35
lines changed

README.md

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Post a string or the contents of a file to a Discourse topic
22

33
This repository provides a GitHub Action to post a string or the contents of a
4-
given file to a specific Discourse topic.
4+
given file to a specific Discourse topic, either adding a new reply or replacing
5+
a single post's content.
56

67
We use it as part of our API documentation management suite:
78
[Rapidoc](https://rapidocweb.com/) as a [Discourse](https://discourse.org/)
@@ -17,10 +18,17 @@ finally upload the changelogs with the action from this repository.
1718

1819
- `discourse_url` - your discourse instance domain, e.g.
1920
"community.developer.gridx.de
20-
- `discourse_topic_id` - the ID of the Discourse post to reply to. You can find
21-
the ID, e.g., by inspecting your post in the browser and looking for
22-
`data-topic-id="<n>"` in the `article` element. (XPath `//h1/@data-topic-id`)
23-
![Discourse Topic ID](doc-topic-id.png)
21+
- `discourse_topic_id` - Use this to reply to a topic: the ID of the Discourse
22+
topic to reply to. You can find the ID, e.g., by inspecting your post in the
23+
browser and looking for `data-topic-id="<n>"` in the `article` element. (XPath
24+
`//h1/@data-topic-id`). **If both topic and post ID are given, topic ID takes
25+
precedence and a new reply is added**. ![Discourse Topic ID](doc-topic-id.png)
26+
- `discourse_post_id` - Use this to replace the contents of a post: the ID of
27+
the Discourse post to reply to. You can find the ID, e.g., by inspecting your
28+
post in the browser and looking for `data-post-id="<n>"` in the `article`
29+
element. (XPath `//article/@data-post-id`). If there are multiple posts in the
30+
topic, make sure to find the correct one to replace. **If both topic and post
31+
ID are given, topic ID takes precedence and a new reply is added**.
2432
- `discourse_api_key` - your discourse API key. It needs `topics:write`
2533
permissions. ![Discourse API Key](doc-discourse-api-key.png)
2634
- `discourse_user` - the discourse user on whose behalf the action should be
@@ -29,9 +37,11 @@ finally upload the changelogs with the action from this repository.
2937
obtained using `$GITHUB_SHA` when running in an action or with
3038
`git rev-parse --short HEAD`
3139
- `content_file` - the (text) file containing the contents to be posted,
32-
relative to the repositories root. Use either this or `content` directly.
40+
relative to the repositories root. Use either this or `content` directly. **If
41+
both are present, content file takes precedence.**
3342
- `content` - the contents to be posted. Use either this or `content_file` to
34-
load the content from a file. D'uh.
43+
load the content from a file. D'uh. **If both are present, content file takes
44+
precedence.**
3545

3646
## Instructions
3747

@@ -40,19 +50,36 @@ finally upload the changelogs with the action from this repository.
4050
1. Get the required parameters as described above
4151
1. Configure the action in your GH workflow, preferably on release
4252

53+
Example: Adding a reply from a file
54+
4355
```yaml
4456
- name: Test Action against gridX community example post
4557
uses: ./
4658
with:
4759
discourse_url: ${{secrets.DISCOURSE_URL}}
4860
discourse_topic_id: <n>
61+
# OR discourse_post_id: <n>
4962
discourse_api_key: ${{secrets.DISCOURSE_API_KEY}}
5063
discourse_user: ${{secrets.DISCOURSE_USER}}
5164
github_sha: ${{env.SHORT_SHA}}
5265
content_file: ./changelog.md
5366
# OR content: "content as string"
5467
```
5568

69+
Example: Replacing a single post with a fixed string
70+
71+
```yaml
72+
- name: Test Action against gridX community example post
73+
uses: ./
74+
with:
75+
discourse_url: ${{secrets.DISCOURSE_URL}}
76+
discourse_post_id: <n>
77+
discourse_api_key: ${{secrets.DISCOURSE_API_KEY}}
78+
discourse_user: ${{secrets.DISCOURSE_USER}}
79+
github_sha: ${{env.SHORT_SHA}}
80+
content: 'Post <n> will be replaced by this'
81+
```
82+
5683
## Development
5784
5885
- You can run the action locally using `npm run test:run`, providing the

action.yml

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,33 @@ branding:
1010
# Define your inputs here.
1111
inputs:
1212
discourse_url:
13-
description: 'your discourse instance domain, e.g. "community.acme.com'
13+
description: 'Your discourse instance domain, e.g. "community.acme.com'
1414
required: true
1515
discourse_topic_id:
16-
description: 'the ID of the Discourse topic to reply to'
17-
required: true
16+
description:
17+
'The ID of the Discourse topic to reply to. Leave empty and set
18+
`discourse_post_id` to update the original post.'
19+
required: false
20+
discourse_post_id:
21+
description:
22+
'The ID of the Discourse post to replace with the content given. Leave
23+
empty and set `discourse_topic_id` to add a new reply to the topic.'
24+
required: false
1825
discourse_api_key:
19-
description: 'your discourse API key. It needs `topics:write`` permissions'
26+
description: 'Your discourse API key. It needs `topics:write` permissions'
2027
required: true
2128
discourse_user:
2229
description:
23-
'the discourse user on whose behalf the action should be executed'
30+
'The discourse user on whose behalf the action should be executed'
2431
required: true
2532
content_file:
26-
description: 'the file to load the content to post from'
33+
description: 'The file to load the content to post from'
2734
required: false
2835
content:
29-
description: 'the string to post'
36+
description: 'The string to post'
3037
required: false
3138
github_sha:
32-
description: 'short commit hash to be put into to post for traceability'
39+
description: 'Short commit hash to be put into to post for traceability'
3340
required: true
3441

3542
runs:

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as core from '@actions/core'
77
// eslint-disable-next-line @typescript-eslint/no-floating-promises
88
const discourseUrl: string = core.getInput('discourse_url')
99
const discourseTopicId: string = core.getInput('discourse_topic_id')
10+
const discoursePostId: string = core.getInput('discourse_post_id')
1011
const discourseApiKey: string = core.getInput('discourse_api_key')
1112
const discourseUser: string = core.getInput('discourse_user')
1213
const commit: string = core.getInput('github_sha')
@@ -17,6 +18,7 @@ const contentFile: string = core.getInput('content_file')
1718
run(
1819
discourseUrl,
1920
discourseTopicId,
21+
discoursePostId,
2022
discourseApiKey,
2123
discourseUser,
2224
commit,

src/main.ts

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { title } from 'process'
1010
export async function run(
1111
discourseUrl: string,
1212
discourseTopicId: string,
13+
discoursePostId: string,
1314
discourseApiKey: string,
1415
discourseUser: string,
1516
commit: string,
@@ -38,29 +39,37 @@ export async function run(
3839
Accept: 'application/json'
3940
}
4041
})
41-
return http
42-
.post('/posts.json', {
43-
raw: postBody,
44-
topic_id: discourseTopicId,
45-
reply_to_post_number: discourseTopicId,
46-
skip_validations: true
47-
})
48-
.then(({ data }) => {
49-
core.debug(JSON.stringify(data, null, 2))
50-
})
51-
.catch(e => {
52-
console.error(
53-
'Error uploading file to Discourse',
54-
JSON.stringify(e, null, 2)
55-
)
56-
throw e
57-
})
42+
const commonProps = {
43+
raw: postBody,
44+
skip_validations: true
45+
}
46+
47+
return discourseTopicId
48+
? http // topic ID given, post reply
49+
.post('/posts.json', {
50+
...commonProps,
51+
topic_id: discourseTopicId,
52+
reply_to_post_number: discourseTopicId
53+
})
54+
: http // post ID given, update this post
55+
.put(`/posts/${discoursePostId}.json`, {
56+
...commonProps
57+
})
58+
.then(({ data }) => {
59+
core.debug(JSON.stringify(data, null, 2))
60+
})
61+
.catch(e => {
62+
console.error(
63+
'Error uploading file to Discourse',
64+
JSON.stringify(e, null, 2)
65+
)
66+
throw e
67+
})
5868
}
5969

6070
const postBody = (content: string, commit: string): string =>
6171
content
62-
? `# Changelog ${new Date().toISOString()}
63-
${content}
72+
? `${content}
6473
6574
(sha ${commit.trim()})
6675
`

src/test.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,28 @@ import { execSync } from 'node:child_process'
44
/*
55
export DISCOURSE_URL=community.developer.gridx.de
66
export DISCOURSE_TOPIC_ID=<n>
7+
export DISCOURSE_POST_ID=<n>
78
export DISCOURSE_API_KEY=...
89
export DISCOURSE_USER=wwerner
910
*/
1011

1112
const discourseUrl: string = process.env.DISCOURSE_URL ?? ''
1213
const discourseTopicId: string = process.env.DISCOURSE_TOPIC_ID ?? ''
14+
const discoursePostId: string = process.env.DISCOURSE_POST_ID ?? ''
1315
const discourseApiKey: string = process.env.DISCOURSE_API_KEY ?? ''
1416
const discourseUser: string = process.env.DISCOURSE_USER ?? ''
1517
const commit: string = execSync('git rev-parse --short HEAD').toString()
1618

17-
const content = ''
19+
const content = 'I am the replacement!'
1820
const contentFile = './test-post.md'
1921

22+
// for exploratory testing,
23+
// replace the topic id w/ "" to update a post and
24+
// replace contentFile w/ "" to use content over contentFile
2025
run(
2126
discourseUrl,
2227
discourseTopicId,
28+
discoursePostId,
2329
discourseApiKey,
2430
discourseUser,
2531
commit,

0 commit comments

Comments
 (0)