Skip to content

Commit 77ad2d6

Browse files
chore: onbaording docs-as-code-confluence action
1 parent 7f97f0a commit 77ad2d6

19 files changed

+37829
-1
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Release GitHub Actions
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
tag:
7+
description: "Tag for the release"
8+
required: true
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
release:
15+
permissions:
16+
actions: read
17+
id-token: write
18+
contents: write
19+
20+
uses: step-security/reusable-workflows/.github/workflows/actions_release.yaml@v1
21+
with:
22+
tag: "${{ github.event.inputs.tag }}"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: NPM Audit Fix Run
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
force:
7+
description: "Use --force flag for npm audit fix?"
8+
required: true
9+
type: boolean
10+
base_branch:
11+
description: "Specify a base branch"
12+
required: false
13+
default: "main"
14+
schedule:
15+
- cron: "0 0 * * 1"
16+
17+
jobs:
18+
audit-fix:
19+
uses: step-security/reusable-workflows/.github/workflows/audit_fix.yml@v1
20+
with:
21+
force: ${{ inputs.force || false }}
22+
base_branch: ${{ inputs.base_branch || 'main' }}
23+
24+
permissions:
25+
contents: write
26+
pull-requests: write
27+
packages: read
28+
issues: write
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Auto Cherry-Pick from Upstream
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
base_branch:
7+
description: "Base branch to create the PR against"
8+
required: true
9+
default: "main"
10+
11+
permissions:
12+
contents: write
13+
pull-requests: write
14+
packages: read
15+
issues: write
16+
17+
jobs:
18+
cherry-pick:
19+
uses: step-security/reusable-workflows/.github/workflows/auto_cherry_pick.yaml@v1
20+
with:
21+
original-owner: "Bhacaz"
22+
repo-name: "docs-as-code-confluence"
23+
base_branch: ${{ inputs.base_branch }}

.github/workflows/guarddog.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: Run GuardDog Scan on PRs
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
call-guarddog-scan:
14+
uses: step-security/reusable-workflows/.github/workflows/guarddog.yml@v1

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
docs
2+
node_modules

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2021 Jean-Francis Bastien
4+
Copyright (c) 2025 StepSecurity
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
THE SOFTWARE.

README.md

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,76 @@
1-
# docs-as-code-confluence
1+
# Docs as Code - Confluence
2+
3+
Publish a folder of documentation to Confluence.
4+
5+
Create a Confluence Page for each markdown file. Each folder will create a _parent_ page to reflect
6+
the directory structure.
7+
8+
## Parameters
9+
10+
| Name | Description | Required |
11+
|-----------------------| --- | --- |
12+
| `folder` | The folder to sync | true |
13+
| `username` | Confluence username or email | true |
14+
| `password` | Confluence password or [API token](https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/) | true |
15+
| `confluence-base-url` | Your Confluence URL (with `wiki`). Example: `https://mydomain.atlassian.net/wiki` | true |
16+
| `space-key` | Confluence space key to publish the documentation. Located after `spaces` in the URL. `https://mydomain.atlassian.net/wiki/spaces/<<~1234>>`. <br> Or in _Space settings_ > _Space details_ > _Key_. | true |
17+
| `parent-page-id` | Page id under which the documentation will be published. Located after `pages` in the URL. `https://mydomain.atlassian.net/wiki/spaces/~1234/pages/<<1234>>/My+Parent+Page` | true |
18+
19+
## TODO
20+
21+
* Renaming a file
22+
* Moving/Removing a file
23+
* Not updating Confluence pages when there is no change
24+
* Add commit link to the new page version
25+
* Add markdown images with url source
26+
27+
## Example of workflow
28+
29+
```yml
30+
name: Sync Docs as Code - Confluence
31+
on:
32+
push:
33+
branches:
34+
- main
35+
paths:
36+
- 'docs/**'
37+
jobs:
38+
docs-as-code:
39+
runs-on: ubuntu-latest
40+
name: Sync Docs as Code - Confluence
41+
steps:
42+
- name: Checkout
43+
uses: actions/checkout@v4
44+
- name: Sync Docs as Code - Confluence
45+
uses: step-security/docs-as-code-confluence@v3
46+
with:
47+
folder: docs
48+
username: abc@xyz.com
49+
password: ${{ secrets.API_TOKEN }}
50+
confluence-base-url: https://mydomain.atlassian.net/wiki
51+
space-key: ~1234
52+
parent-page-id: 123456789
53+
```
54+
55+
## Example of usage in a repository
56+
57+
[step-security/docs-as-code-confluence-demo](https://github.com/step-security/docs-as-code-confluence-demo)
58+
59+
## Alternatives
60+
61+
* [markdown-confluence/publish-action](https://github.com/markdown-confluence/publish-action)
62+
* [mbovo/mark2confluence](https://github.com/mbovo/mark2confluence)
63+
64+
## Development
65+
66+
**Test**
67+
68+
```bash
69+
npm run test
70+
```
71+
72+
**Build**
73+
74+
```bash
75+
npm run build
76+
```

SECURITY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Security Policy
2+
3+
## Reporting a Vulnerability
4+
5+
Please report security vulnerabilities to security@stepsecurity.io

__tests__/utils/file.test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
const filesStructure = require('../../utils/files');
4+
5+
jest.mock('fs');
6+
jest.mock('path');
7+
8+
describe('filesStructure', () => {
9+
it('returns structure of markdown files in directory and only select .md files', () => {
10+
// file1.md
11+
// file2.md
12+
// subdir/
13+
// file3.md
14+
// image.png
15+
// subdir_only_image/
16+
// image2.png
17+
const mockFiles = ['file1.md', 'file2.md', 'subdir/file3.md', 'subdir/image.png', 'subdir_only_image/image2.png'];
18+
fs.statSync.mockImplementation((file) => {
19+
return { isDirectory: () => !file.includes('.') };
20+
});
21+
22+
fs.readdirSync.mockReturnValue(mockFiles);
23+
path.join.mockImplementation((...args) => args.join('/'));
24+
25+
const result = filesStructure('root');
26+
27+
expect(result).toEqual([['file1.md'], ['file2.md'], ['subdir', 'file3.md']]);
28+
});
29+
30+
it('returns empty array when directory is empty', () => {
31+
fs.statSync.mockReturnValue({ isDirectory: () => true });
32+
fs.readdirSync.mockReturnValue([]);
33+
path.join.mockImplementation((...args) => args.join('/'));
34+
35+
const result = filesStructure('root');
36+
37+
expect(result).toEqual([]);
38+
});
39+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const fs = require('fs');
2+
const Marked = require('marked');
3+
const markdownForFile = require('../../utils/markdownToHtml');
4+
5+
jest.mock('fs');
6+
jest.mock('marked');
7+
8+
describe('markdownForFile', () => {
9+
it('reads file and parses markdown when file exists', (done) => {
10+
const mockData = '# Hello World';
11+
const mockHtml = '<h1 id="hello-world">Hello World</h1>\n';
12+
fs.readFile.mockImplementationOnce((path, options, cb) => cb(null, mockData));
13+
Marked.parse.mockImplementationOnce((data, cb) => cb(null, mockHtml));
14+
15+
markdownForFile('path/to/file', (err, data) => {
16+
expect(err).toBeNull();
17+
expect(data).toBe(mockHtml);
18+
done();
19+
});
20+
});
21+
22+
it('returns error when file does not exist', (done) => {
23+
const mockError = new Error('File not found');
24+
fs.readFile.mockImplementationOnce((path, options, cb) => cb(mockError));
25+
26+
markdownForFile('path/to/nonexistent/file', (err, data) => {
27+
expect(err).toEqual(mockError);
28+
expect(data).toBeUndefined();
29+
done();
30+
});
31+
});
32+
33+
it('returns error when markdown parsing fails', (done) => {
34+
const mockData = '# Hello World';
35+
const mockError = new Error('Markdown parsing error');
36+
fs.readFile.mockImplementationOnce((path, options, cb) => cb(null, mockData));
37+
Marked.parse.mockImplementationOnce((data, cb) => cb(mockError));
38+
39+
markdownForFile('path/to/file', (err, data) => {
40+
expect(err).toEqual(mockError);
41+
expect(data).toBeUndefined();
42+
done();
43+
});
44+
});
45+
});

0 commit comments

Comments
 (0)