* feat: convert relative urls to absolute * test: add tests for conversion of relative urls Co-authored-by: Marty Winkler <mrtwnklr@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import * as core from '@actions/core'
|
||||
|
||||
const README_FILEPATH_DEFAULT = './README.md'
|
||||
import * as readmeHelper from './readme-helper'
|
||||
|
||||
interface Inputs {
|
||||
username: string
|
||||
@@ -8,6 +7,8 @@ interface Inputs {
|
||||
repository: string
|
||||
shortDescription: string
|
||||
readmeFilepath: string
|
||||
enableUrlCompletion: boolean
|
||||
imageExtensions: string
|
||||
}
|
||||
|
||||
export function getInputs(): Inputs {
|
||||
@@ -16,7 +17,9 @@ export function getInputs(): Inputs {
|
||||
password: core.getInput('password'),
|
||||
repository: core.getInput('repository'),
|
||||
shortDescription: core.getInput('short-description'),
|
||||
readmeFilepath: core.getInput('readme-filepath')
|
||||
readmeFilepath: core.getInput('readme-filepath'),
|
||||
enableUrlCompletion: Boolean(core.getInput('enable-url-completion')),
|
||||
imageExtensions: core.getInput('image-extensions')
|
||||
}
|
||||
|
||||
// Environment variable input alternatives and their aliases
|
||||
@@ -50,16 +53,31 @@ export function getInputs(): Inputs {
|
||||
inputs.readmeFilepath = process.env['README_FILEPATH']
|
||||
}
|
||||
|
||||
if (!inputs.enableUrlCompletion && process.env['ENABLE_URL_COMPLETION']) {
|
||||
inputs.enableUrlCompletion = Boolean(process.env['ENABLE_URL_COMPLETION'])
|
||||
}
|
||||
|
||||
if (!inputs.imageExtensions && process.env['IMAGE_EXTENSIONS']) {
|
||||
inputs.imageExtensions = process.env['IMAGE_EXTENSIONS']
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
if (!inputs.readmeFilepath) {
|
||||
inputs.readmeFilepath = README_FILEPATH_DEFAULT
|
||||
inputs.readmeFilepath = readmeHelper.README_FILEPATH_DEFAULT
|
||||
}
|
||||
if (!inputs.enableUrlCompletion) {
|
||||
inputs.enableUrlCompletion = readmeHelper.ENABLE_URL_COMPLETION_DEFAULT
|
||||
}
|
||||
if (!inputs.imageExtensions) {
|
||||
inputs.imageExtensions = readmeHelper.IMAGE_EXTENSIONS_DEFAULT
|
||||
}
|
||||
if (!inputs.repository && process.env['GITHUB_REPOSITORY']) {
|
||||
inputs.repository = process.env['GITHUB_REPOSITORY']
|
||||
}
|
||||
|
||||
// Docker repositories must be all lower case
|
||||
// Enforce lower case, where needed
|
||||
inputs.repository = inputs.repository.toLowerCase()
|
||||
inputs.imageExtensions = inputs.imageExtensions.toLowerCase()
|
||||
|
||||
return inputs
|
||||
}
|
||||
|
||||
12
src/main.ts
12
src/main.ts
@@ -1,7 +1,7 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as inputHelper from './input-helper'
|
||||
import * as dockerhubHelper from './dockerhub-helper'
|
||||
import * as fs from 'fs'
|
||||
import * as readmeHelper from './readme-helper'
|
||||
import {inspect} from 'util'
|
||||
|
||||
function getErrorMessage(error: unknown) {
|
||||
@@ -17,9 +17,13 @@ async function run(): Promise<void> {
|
||||
inputHelper.validateInputs(inputs)
|
||||
|
||||
// Fetch the readme content
|
||||
const readmeContent = await fs.promises.readFile(inputs.readmeFilepath, {
|
||||
encoding: 'utf8'
|
||||
})
|
||||
core.info('Reading description source file')
|
||||
const readmeContent = await readmeHelper.getReadmeContent(
|
||||
inputs.readmeFilepath,
|
||||
inputs.enableUrlCompletion,
|
||||
inputs.imageExtensions
|
||||
)
|
||||
core.debug(readmeContent)
|
||||
|
||||
// Acquire a token for the Docker Hub API
|
||||
core.info('Acquiring token')
|
||||
|
||||
175
src/readme-helper.ts
Normal file
175
src/readme-helper.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as fs from 'fs'
|
||||
|
||||
export const README_FILEPATH_DEFAULT = './README.md'
|
||||
export const IMAGE_EXTENSIONS_DEFAULT = 'bmp,gif,jpg,jpeg,png,svg,webp'
|
||||
export const ENABLE_URL_COMPLETION_DEFAULT = false
|
||||
|
||||
const TITLE_REGEX = `(?: +"[^"]+")?`
|
||||
const REPOSITORY_URL = `${process.env['GITHUB_SERVER_URL']}/${process.env['GITHUB_REPOSITORY']}`
|
||||
const BLOB_PREFIX = `${REPOSITORY_URL}/blob/${process.env['GITHUB_REF_NAME']}/`
|
||||
const RAW_PREFIX = `${REPOSITORY_URL}/raw/${process.env['GITHUB_REF_NAME']}/`
|
||||
|
||||
type Rule = {
|
||||
/**
|
||||
* all left of the relative url belonging to the markdown image/link
|
||||
*/
|
||||
left: RegExp
|
||||
/**
|
||||
* relative url
|
||||
*/
|
||||
url: RegExp
|
||||
/**
|
||||
* part to prefix the relative url with (excluding github repository url)
|
||||
*/
|
||||
absUrlPrefix: string
|
||||
}
|
||||
|
||||
export async function getReadmeContent(
|
||||
readmeFilepath: string,
|
||||
enableUrlCompletion: boolean,
|
||||
imageExtensions: string
|
||||
): Promise<string> {
|
||||
// Fetch the readme content
|
||||
let readmeContent = await fs.promises.readFile(readmeFilepath, {
|
||||
encoding: 'utf8'
|
||||
})
|
||||
|
||||
readmeContent = completeRelativeUrls(
|
||||
readmeContent,
|
||||
readmeFilepath,
|
||||
enableUrlCompletion,
|
||||
imageExtensions
|
||||
)
|
||||
|
||||
return readmeContent
|
||||
}
|
||||
|
||||
export function completeRelativeUrls(
|
||||
readmeContent: string,
|
||||
readmeFilepath: string,
|
||||
enableUrlCompletion: boolean,
|
||||
imageExtensions: string
|
||||
): string {
|
||||
if (enableUrlCompletion) {
|
||||
readmeFilepath = readmeFilepath.replace(/^[.][/]/, '')
|
||||
|
||||
// Make relative urls absolute
|
||||
const rules = [
|
||||
...getRelativeReadmeAnchorsRules(readmeFilepath),
|
||||
...getRelativeImageUrlRules(imageExtensions),
|
||||
...getRelativeUrlRules()
|
||||
]
|
||||
|
||||
readmeContent = applyRules(rules, readmeContent)
|
||||
}
|
||||
|
||||
return readmeContent
|
||||
}
|
||||
|
||||
function applyRules(rules: Rule[], readmeContent: string): string {
|
||||
rules.forEach(rule => {
|
||||
const combinedRegex = `${rule.left.source}[(]${rule.url.source}[)]`
|
||||
core.debug(`rule: ${combinedRegex}`)
|
||||
|
||||
const replacement = `$<left>(${rule.absUrlPrefix}$<url>)`
|
||||
core.debug(`replacement: ${replacement}`)
|
||||
|
||||
readmeContent = readmeContent.replace(
|
||||
new RegExp(combinedRegex, 'giu'),
|
||||
replacement
|
||||
)
|
||||
})
|
||||
|
||||
return readmeContent
|
||||
}
|
||||
|
||||
// has to be applied first to avoid wrong results
|
||||
function getRelativeReadmeAnchorsRules(readmeFilepath: string): Rule[] {
|
||||
const prefix = `${BLOB_PREFIX}${readmeFilepath}`
|
||||
|
||||
// matches e.g.:
|
||||
// #table-of-content
|
||||
// #table-of-content "the anchor (a title)"
|
||||
const url = new RegExp(`(?<url>#[^)]+${TITLE_REGEX})`)
|
||||
|
||||
const rules: Rule[] = [
|
||||
// matches e.g.:
|
||||
// [#table-of-content](#table-of-content)
|
||||
// [#table-of-content](#table-of-content "the anchor (a title)")
|
||||
{
|
||||
left: /(?<left>\[[^\]]+\])/,
|
||||
url: url,
|
||||
absUrlPrefix: prefix
|
||||
},
|
||||
|
||||
// matches e.g.:
|
||||
// [](#table-of-content)
|
||||
// [](#table-of-content "title b")
|
||||
{
|
||||
left: /(?<left>\[!\[[^\]]*\]\([^)]+\)\])/,
|
||||
url: url,
|
||||
absUrlPrefix: prefix
|
||||
}
|
||||
]
|
||||
|
||||
return rules
|
||||
}
|
||||
|
||||
function getRelativeImageUrlRules(imageExtensions: string): Rule[] {
|
||||
const extensionsRegex = imageExtensions.replace(/,/g, '|')
|
||||
// matches e.g.:
|
||||
// media/image.svg
|
||||
// media/image.svg "with title"
|
||||
const url = new RegExp(
|
||||
`(?<url>[^:)]+[.](?:${extensionsRegex})${TITLE_REGEX})`
|
||||
)
|
||||
|
||||
const rules: Rule[] = [
|
||||
// matches e.g.:
|
||||
// 
|
||||
// 
|
||||
{
|
||||
left: /(?<left>!\[[^\]]*\])/,
|
||||
url: url,
|
||||
absUrlPrefix: RAW_PREFIX
|
||||
}
|
||||
]
|
||||
|
||||
return rules
|
||||
}
|
||||
|
||||
function getRelativeUrlRules(): Rule[] {
|
||||
// matches e.g.:
|
||||
// .releaserc.yaml
|
||||
// README.md#table-of-content "title b"
|
||||
// .releaserc.yaml "the .releaserc.yaml file (a title)"
|
||||
const url = new RegExp(`(?<url>[^:)]+${TITLE_REGEX})`)
|
||||
|
||||
const rules: Rule[] = [
|
||||
// matches e.g.:
|
||||
// [.releaserc.yaml](.releaserc.yaml)
|
||||
// [.releaserc.yaml](.releaserc.yaml "the .releaserc.yaml file (a title)")
|
||||
{
|
||||
left: /(?<left>\[[^\]]+\])/,
|
||||
url: url,
|
||||
absUrlPrefix: BLOB_PREFIX
|
||||
},
|
||||
|
||||
// matches e.g.:
|
||||
// [](media/image.svg)
|
||||
// [](README.md#table-of-content "title b")
|
||||
// [](media/image.svg)
|
||||
// [](media/image.svg "title b")
|
||||
// [](README.md#table-of-content "title b")
|
||||
{
|
||||
left: new RegExp(
|
||||
`(?<left>\\[!\\[[^\\]]*\\]\\([^)]+${TITLE_REGEX}\\)\\])`
|
||||
),
|
||||
url: url,
|
||||
absUrlPrefix: BLOB_PREFIX
|
||||
}
|
||||
]
|
||||
|
||||
return rules
|
||||
}
|
||||
Reference in New Issue
Block a user