1- import { prompt } from 'inquirer' ;
2- import path from 'path' ;
31import fs from 'fs-extra' ;
42import fetch from 'node-fetch' ;
3+ import os from 'os' ;
4+ import path from 'path' ;
55import showdown from 'showdown' ;
66import * as error from '~/src/error' ;
77
88const converter = new showdown . Converter ( {
99 parseImgDimensions : true ,
1010 simplifiedAutoLink : true ,
1111 tables : true ,
12+ disableForced4SpacesIndentedSublists : true ,
13+ ghCompatibleHeaderId : true ,
1214} ) ;
1315
1416const CONFLUENCE_PAGES = './confluence-pages.json' ;
@@ -23,51 +25,25 @@ export type PublishToConfluenceOptions = {
2325 space : string ;
2426 username : string ;
2527 password : string ;
28+ parent : string ;
29+ debug : boolean ;
2630} ;
2731
28- async function gatherCreds ( ) {
29- const answer = await prompt ( [
30- {
31- type : 'input' ,
32- name : 'domain' ,
33- message :
34- "Confluence domain (the vanity subdomain before '.atlassian.net'):" ,
35- } ,
36- {
37- type : 'input' ,
38- name : 'space' ,
39- message : 'Confluence space key:' ,
40- } ,
41- {
42- type : 'input' ,
43- name : 'username' ,
44- message : 'Confluence username:' ,
45- } ,
46- {
47- type : 'password' ,
48- name : 'password' ,
49- message : 'Confluence password:' ,
50- } ,
51- ] ) ;
52- return {
53- domain : answer . domain ,
54- space : answer . space ,
55- username : answer . username ,
56- password : answer . password ,
57- } ;
58- }
59-
6032function parseLinks (
6133 pageUrl : string ,
6234 html : string ,
6335 confluencePages : Record < string , string >
6436) {
65- const linkRegex = / h r e f = [ ' " ] ( [ \w - ] + \. m d ) ( # .* ) ? [ ' " ] / gm;
66- const match = linkRegex . exec ( html ) ;
67-
68- return match
69- ? html . replace ( linkRegex , `href="${ pageUrl } /${ confluencePages [ match [ 1 ] ] } "` )
70- : html ;
37+ const linkRegex : RegExp = / h r e f = [ ' " ] ( [ \w - ] + \. m d ) ( # .* ) ? [ ' " ] / gm;
38+ const hasLinks = linkRegex . test ( html ) ;
39+
40+ if ( hasLinks ) {
41+ return html . replace ( linkRegex , ( match , p1 , p2 = '' ) => {
42+ return `href="${ pageUrl } /${ confluencePages [ p1 ] } ${ p2 . toLowerCase ( ) } "` ;
43+ } ) ;
44+ } else {
45+ return html ;
46+ }
7147}
7248
7349async function getVersion ( headers : Record < string , string > , page : string ) {
@@ -83,20 +59,20 @@ export default async function publishToConfluence(
8359 source : string ,
8460 options : PublishToConfluenceOptions
8561) {
86- const docsPath = source || path . join ( __dirname , '.. /docs') ;
62+ const docsPath = source || '. /docs';
8763 if ( ! fs . existsSync ( docsPath ) ) {
8864 error . fatal ( 'Please run `psp build` first to generate the policy docs.' ) ;
8965 }
9066
91- const { domain, space, username, password } =
92- options || ( await gatherCreds ( ) ) ;
67+ const { domain, space, username, password, parent, debug } = options ;
9368
9469 const site = `https://${ domain || CONFLUENCE_DOMAIN } .atlassian.net` ;
9570 const baseUrl = `${ site } /wiki/rest/api/content` ;
9671 const pageUrl = `${ site } /wiki/spaces/${ space || CONFLUENCE_SPACE } /pages` ;
9772
9873 const headers = {
9974 'Content-Type' : 'application/json' ,
75+
10076 Accept : 'application/json' ,
10177 Authorization : `Basic ${ Buffer . from (
10278 ( username || CONFLUENCE_USER ) + ':' + ( password || CONFLUENCE_PASS )
@@ -109,6 +85,14 @@ export default async function publishToConfluence(
10985
11086 const worked = [ ] ;
11187 const failed = [ ] ;
88+ let debugPath = '' ;
89+
90+ if ( debug ) {
91+ debugPath = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , 'confluence-html-' ) ) ;
92+ console . log (
93+ `Debug enabled, generated confluence html can be found in ${ debugPath } `
94+ ) ;
95+ }
11296
11397 const docs = fs . readdirSync ( docsPath ) ;
11498
@@ -121,8 +105,8 @@ export default async function publishToConfluence(
121105 if ( doc . endsWith ( '.md' ) ) {
122106 const data = fs . readFileSync ( path . join ( docsPath , doc ) , 'utf8' ) ;
123107 const parsedData = data
108+ . replace ( / # ( [ \w - ] + ) / gm, '$&' . toLowerCase ( ) )
124109 . replace ( / ^ # ( .* ) $ / m, '' ) // removes title
125- . replace ( / ^ { 2 } ( - | \* ) / gm, ' -' ) // fixes sublist indentation
126110 . replace ( / & / gm, '&' )
127111 . replace ( / [ ‘ ’ ] / gm, `'` ) // fixes quote character
128112 . replace ( / [ “ ” ] / gm, `"` ) ;
@@ -143,13 +127,14 @@ export default async function publishToConfluence(
143127 }
144128 const title = match [ 1 ] . trim ( ) ;
145129
146- const body = {
130+ const req = {
147131 version,
148132 type : 'page' ,
149133 title,
150134 space : {
151135 key : space || CONFLUENCE_SPACE ,
152136 } ,
137+ ancestors : parent ? [ { id : parent } ] : [ ] ,
153138 body : {
154139 storage : {
155140 value : parsedHtml ,
@@ -161,20 +146,25 @@ export default async function publishToConfluence(
161146 const options = {
162147 method : pageId ? 'put' : 'post' ,
163148 headers,
164- body : JSON . stringify ( body ) ,
149+ body : JSON . stringify ( req ) ,
165150 } ;
166151
167152 const uri = pageId ? `${ baseUrl } /${ pageId } ` : baseUrl ;
168153 const response = await fetch ( uri , options ) ;
154+ const result = await response . json ( ) ;
169155 if ( response . ok ) {
170- const result = await response . json ( ) ;
171156 confluencePages [ doc ] = pageId || result . id ;
157+ if ( debug ) {
158+ fs . writeFileSync ( `${ debugPath } /${ doc } .html` , parsedHtml ) ;
159+ }
172160 worked . push ( doc ) ;
173161 } else {
174162 failed . push ( doc ) ;
175- fs . writeFileSync ( `./failed-${ doc } .html` , parsedHtml ) ;
163+ if ( debug ) {
164+ fs . writeFileSync ( `${ debugPath } /failed-${ doc } .html` , parsedHtml ) ;
165+ }
176166 console . error ( `publish to confluence failed for ${ doc } ` ) ;
177- console . error ( { response : await response . json ( ) } ) ;
167+ console . error ( result . message ) ;
178168 continue ;
179169 }
180170 }
0 commit comments