mirror of
https://github.com/peter-evans/dockerhub-description.git
synced 2024-11-24 04:59:32 +01:00
* 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:
parent
bbef094b30
commit
e139363f61
9 changed files with 774 additions and 18 deletions
35
README.md
35
README.md
|
@ -25,6 +25,8 @@ This is useful if you `docker push` your images to Docker Hub. It provides an ea
|
|||
| `repository` | Docker Hub repository in the format `<namespace>/<name>`. | `github.repository` |
|
||||
| `short-description` | Docker Hub repository short description. | |
|
||||
| `readme-filepath` | Path to the repository readme. | `./README.md` |
|
||||
| `enable-url-completion` | Enables completion of relative URLs to absolute ones. See also [Known Issues](#known-issues). | `false` |
|
||||
| `image-extensions` | File extensions that will be treated as images. | `bmp,gif,jpg,jpeg,png,svg,webp` |
|
||||
|
||||
#### Content limits
|
||||
|
||||
|
@ -86,6 +88,7 @@ jobs:
|
|||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
repository: peterevans/dockerhub-description
|
||||
short-description: ${{ github.event.repository.description }}
|
||||
enable-url-completion: true
|
||||
```
|
||||
|
||||
Updates the Docker Hub repository description whenever a new release is created.
|
||||
|
@ -122,6 +125,38 @@ docker run -v $PWD:/workspace \
|
|||
peterevans/dockerhub-description:3
|
||||
```
|
||||
|
||||
## Known Issues
|
||||
|
||||
The completion of relative urls has some known issues:
|
||||
|
||||
1. Relative markdown links in inline-code and code blocks **are also converted**:
|
||||
|
||||
```markdown
|
||||
[link in inline code](#table-of-content)
|
||||
```
|
||||
|
||||
will be converted into
|
||||
|
||||
```markdown
|
||||
[link in inline code](https://github.com/peter-evans/dockerhub-description/blob/main/./README.md#table-of-content)
|
||||
```
|
||||
|
||||
2. Links containing square brackets (`]`) in the text fragment **are not converted**:
|
||||
|
||||
```markdown
|
||||
[[link text with square brackets]](#table-of-content)
|
||||
```
|
||||
|
||||
3. [Reference-style links/images](https://www.markdownguide.org/basic-syntax/#reference-style-links) **are not converted**.
|
||||
|
||||
```markdown
|
||||
[table-of-content][toc]
|
||||
|
||||
...
|
||||
|
||||
[toc]: #table-of-content "Table of content"
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
|
335
__test__/readme-helper.unit.test.ts
Normal file
335
__test__/readme-helper.unit.test.ts
Normal file
|
@ -0,0 +1,335 @@
|
|||
import {completeRelativeUrls} from '../src/readme-helper'
|
||||
|
||||
describe('complete relative urls tests', () => {
|
||||
const GITHUB_SERVER_URL = process.env['GITHUB_SERVER_URL']
|
||||
const GITHUB_REPOSITORY = process.env['GITHUB_REPOSITORY']
|
||||
const GITHUB_REF_NAME = process.env['GITHUB_REF_NAME']
|
||||
|
||||
const README_FILEPATH = './README.md'
|
||||
const EXPECTED_REPOSITORY_URL = `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}`
|
||||
const EXPECTED_BLOB_URL = `${EXPECTED_REPOSITORY_URL}/blob/${GITHUB_REF_NAME}`
|
||||
const EXPECTED_RAW_URL = `${EXPECTED_REPOSITORY_URL}/raw/${GITHUB_REF_NAME}`
|
||||
|
||||
// known issues
|
||||
test('reference-style links/image sources are not converted', async () => {
|
||||
const content = [
|
||||
'table-of-content][toc]',
|
||||
'',
|
||||
'[toc]: #table-of-content "Table of content"'
|
||||
].join('\n')
|
||||
expect(completeRelativeUrls(content, README_FILEPATH, true, '')).toEqual(
|
||||
content
|
||||
)
|
||||
})
|
||||
|
||||
test('links containing square brackets in the text fragment are not converted', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[[text with square brackets]](README.md)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual('[[text with square brackets]](README.md)')
|
||||
})
|
||||
|
||||
test('links containing square brackets in the text fragment are not converted', async () => {
|
||||
expect(
|
||||
completeRelativeUrls('`[text](README.md)`', README_FILEPATH, true, '')
|
||||
).toEqual(`\`[text](${EXPECTED_BLOB_URL}/README.md)\``)
|
||||
})
|
||||
|
||||
// misc
|
||||
test('do not change content when disabled', async () => {
|
||||
expect(
|
||||
completeRelativeUrls('[text](README.md)', README_FILEPATH, false, '')
|
||||
).toEqual('[text](README.md)')
|
||||
})
|
||||
|
||||
test('do not change link with mailto protocol', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[text](mailto:mail@example.com)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual(`[text](mailto:mail@example.com)`)
|
||||
})
|
||||
|
||||
test('do not change link with ftp protocol', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[text](ftp://example.com)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual(`[text](ftp://example.com)`)
|
||||
})
|
||||
|
||||
test('do not change link with http protocol', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[text](http://example.com)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual(`[text](http://example.com)`)
|
||||
})
|
||||
|
||||
test('do not change link with https protocol', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[text](https://example.com)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual(`[text](https://example.com)`)
|
||||
})
|
||||
|
||||
test('do not change link with protocol-like beginning', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[text](abc://example.com)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual(`[text](abc://example.com)`)
|
||||
})
|
||||
|
||||
test('do not change image from absolute source with absolute link', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[![alttext](https://example.com/image.svg)](https://example.com/image.svg)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
'svg'
|
||||
)
|
||||
).toEqual(
|
||||
`[![alttext](https://example.com/image.svg)](https://example.com/image.svg)`
|
||||
)
|
||||
})
|
||||
|
||||
// anchors
|
||||
test('anchor referencing the current document', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[text](#relative-anchor)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual(`[text](${EXPECTED_BLOB_URL}/README.md#relative-anchor)`)
|
||||
})
|
||||
|
||||
test('anchor referencing the current document with a title', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[text](#relative-anchor "the anchor (a title)")',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual(
|
||||
`[text](${EXPECTED_BLOB_URL}/README.md#relative-anchor "the anchor (a title)")`
|
||||
)
|
||||
})
|
||||
|
||||
test('anchor referencing the current document with a title and unicode', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[text with 🌬](#relative-anchor "the anchor (a title with 🌬)")',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual(
|
||||
`[text with 🌬](${EXPECTED_BLOB_URL}/README.md#relative-anchor "the anchor (a title with 🌬)")`
|
||||
)
|
||||
})
|
||||
|
||||
test('anchor referencing another document', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[text](OTHER.md#absolute-anchor)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual(`[text](${EXPECTED_BLOB_URL}/OTHER.md#absolute-anchor)`)
|
||||
})
|
||||
|
||||
test('anchor referencing another document with a title', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[text](OTHER.md#absolute-anchor "the anchor (a title)")',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual(
|
||||
`[text](${EXPECTED_BLOB_URL}/OTHER.md#absolute-anchor "the anchor (a title)")`
|
||||
)
|
||||
})
|
||||
|
||||
test('anchor with image referencing the current document', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[![alttext](image.svg)](#absolute-anchor "the anchor (a title)")',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
'svg'
|
||||
)
|
||||
).toEqual(
|
||||
`[![alttext](${EXPECTED_RAW_URL}/image.svg)](${EXPECTED_BLOB_URL}/README.md#absolute-anchor "the anchor (a title)")`
|
||||
)
|
||||
})
|
||||
|
||||
test('anchor with image referencing another document', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[![alttext](image.svg)](OTHER.md#absolute-anchor "the anchor (a title)")',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
'svg'
|
||||
)
|
||||
).toEqual(
|
||||
`[![alttext](${EXPECTED_RAW_URL}/image.svg)](${EXPECTED_BLOB_URL}/OTHER.md#absolute-anchor "the anchor (a title)")`
|
||||
)
|
||||
})
|
||||
|
||||
// documents
|
||||
test('text document', async () => {
|
||||
expect(
|
||||
completeRelativeUrls('[text](document.yaml)', README_FILEPATH, true, '')
|
||||
).toEqual(`[text](${EXPECTED_BLOB_URL}/document.yaml)`)
|
||||
})
|
||||
|
||||
test('pdf document', async () => {
|
||||
expect(
|
||||
completeRelativeUrls('[text](document.pdf)', README_FILEPATH, true, '')
|
||||
).toEqual(`[text](${EXPECTED_BLOB_URL}/document.pdf)`)
|
||||
})
|
||||
|
||||
test('document with a title', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[text](document.pdf "the document (a title)")',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual(
|
||||
`[text](${EXPECTED_BLOB_URL}/document.pdf "the document (a title)")`
|
||||
)
|
||||
})
|
||||
|
||||
test('document with a title and unicode', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[text with 🌬](document.pdf "the document (a title with 🌬)")',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
''
|
||||
)
|
||||
).toEqual(
|
||||
`[text with 🌬](${EXPECTED_BLOB_URL}/document.pdf "the document (a title with 🌬)")`
|
||||
)
|
||||
})
|
||||
|
||||
// images
|
||||
test('image with supported file extension', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'![alttext](image.svg)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
'svg'
|
||||
)
|
||||
).toEqual(`![alttext](${EXPECTED_RAW_URL}/image.svg)`)
|
||||
})
|
||||
|
||||
test('image with unsupported file extension', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'![alttext](image.svg)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
'jpeg'
|
||||
)
|
||||
).toEqual(`![alttext](${EXPECTED_BLOB_URL}/image.svg)`)
|
||||
})
|
||||
|
||||
test('image without alternate text', async () => {
|
||||
expect(
|
||||
completeRelativeUrls('![](image.svg)', README_FILEPATH, true, 'svg')
|
||||
).toEqual(`![](${EXPECTED_RAW_URL}/image.svg)`)
|
||||
})
|
||||
|
||||
test('image with a title', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'![alttext](image.svg "the image (a title)")',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
'svg'
|
||||
)
|
||||
).toEqual(`![alttext](${EXPECTED_RAW_URL}/image.svg "the image (a title)")`)
|
||||
})
|
||||
|
||||
test('image with relative link', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[![alttext](image.svg)](image.svg)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
'svg'
|
||||
)
|
||||
).toEqual(
|
||||
`[![alttext](${EXPECTED_RAW_URL}/image.svg)](${EXPECTED_BLOB_URL}/image.svg)`
|
||||
)
|
||||
})
|
||||
|
||||
test('image with a title, unicode and relative link', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[![alttext with 🌬](image.🌬.svg "the image.🌬.svg (a title)")](image.🌬.svg)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
'svg'
|
||||
)
|
||||
).toEqual(
|
||||
`[![alttext with 🌬](${EXPECTED_RAW_URL}/image.🌬.svg "the image.🌬.svg (a title)")](${EXPECTED_BLOB_URL}/image.🌬.svg)`
|
||||
)
|
||||
})
|
||||
|
||||
test('image from absolute source with relative link', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[![alttext](https://example.com/image.svg)](image.svg)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
'svg'
|
||||
)
|
||||
).toEqual(
|
||||
`[![alttext](https://example.com/image.svg)](${EXPECTED_BLOB_URL}/image.svg)`
|
||||
)
|
||||
})
|
||||
|
||||
test('image with absolute link', async () => {
|
||||
expect(
|
||||
completeRelativeUrls(
|
||||
'[![alttext](image.svg)](https://example.com/image.svg)',
|
||||
README_FILEPATH,
|
||||
true,
|
||||
'svg'
|
||||
)
|
||||
).toEqual(
|
||||
`[![alttext](${EXPECTED_RAW_URL}/image.svg)](https://example.com/image.svg)`
|
||||
)
|
||||
})
|
||||
})
|
|
@ -18,6 +18,14 @@ inputs:
|
|||
description: >
|
||||
Path to the repository readme
|
||||
Default: `./README.md`
|
||||
enable-url-completion:
|
||||
description: >
|
||||
Enables completion of relative urls to absolute ones
|
||||
Default: `false`
|
||||
image-extensions:
|
||||
description: >
|
||||
Extensions of files that you be treated as images
|
||||
Default: `bmp,gif,jpg,jpeg,png,svg,webp`
|
||||
runs:
|
||||
using: 'node16'
|
||||
main: 'dist/index.js'
|
||||
|
|
192
dist/index.js
vendored
192
dist/index.js
vendored
|
@ -121,14 +121,16 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.validateInputs = exports.getInputs = void 0;
|
||||
const core = __importStar(__nccwpck_require__(2186));
|
||||
const README_FILEPATH_DEFAULT = './README.md';
|
||||
const readmeHelper = __importStar(__nccwpck_require__(3367));
|
||||
function getInputs() {
|
||||
const inputs = {
|
||||
username: core.getInput('username'),
|
||||
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
|
||||
if (!inputs.username && process.env['DOCKERHUB_USERNAME']) {
|
||||
|
@ -155,15 +157,28 @@ function getInputs() {
|
|||
if (!inputs.readmeFilepath && process.env['README_FILEPATH']) {
|
||||
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;
|
||||
}
|
||||
exports.getInputs = getInputs;
|
||||
|
@ -222,7 +237,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|||
const core = __importStar(__nccwpck_require__(2186));
|
||||
const inputHelper = __importStar(__nccwpck_require__(5480));
|
||||
const dockerhubHelper = __importStar(__nccwpck_require__(1812));
|
||||
const fs = __importStar(__nccwpck_require__(7147));
|
||||
const readmeHelper = __importStar(__nccwpck_require__(3367));
|
||||
const util_1 = __nccwpck_require__(3837);
|
||||
function getErrorMessage(error) {
|
||||
if (error instanceof Error)
|
||||
|
@ -236,9 +251,9 @@ function run() {
|
|||
core.debug(`Inputs: ${(0, util_1.inspect)(inputs)}`);
|
||||
inputHelper.validateInputs(inputs);
|
||||
// Fetch the readme content
|
||||
const readmeContent = yield fs.promises.readFile(inputs.readmeFilepath, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
core.info('Reading description source file');
|
||||
const readmeContent = yield readmeHelper.getReadmeContent(inputs.readmeFilepath, inputs.enableUrlCompletion, inputs.imageExtensions);
|
||||
core.debug(readmeContent);
|
||||
// Acquire a token for the Docker Hub API
|
||||
core.info('Acquiring token');
|
||||
const token = yield dockerhubHelper.getToken(inputs.username, inputs.password);
|
||||
|
@ -256,6 +271,167 @@ function run() {
|
|||
run();
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 3367:
|
||||
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.completeRelativeUrls = exports.getReadmeContent = exports.ENABLE_URL_COMPLETION_DEFAULT = exports.IMAGE_EXTENSIONS_DEFAULT = exports.README_FILEPATH_DEFAULT = void 0;
|
||||
const core = __importStar(__nccwpck_require__(2186));
|
||||
const fs = __importStar(__nccwpck_require__(7147));
|
||||
exports.README_FILEPATH_DEFAULT = './README.md';
|
||||
exports.IMAGE_EXTENSIONS_DEFAULT = 'bmp,gif,jpg,jpeg,png,svg,webp';
|
||||
exports.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']}/`;
|
||||
function getReadmeContent(readmeFilepath, enableUrlCompletion, imageExtensions) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
// Fetch the readme content
|
||||
let readmeContent = yield fs.promises.readFile(readmeFilepath, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
readmeContent = completeRelativeUrls(readmeContent, readmeFilepath, enableUrlCompletion, imageExtensions);
|
||||
return readmeContent;
|
||||
});
|
||||
}
|
||||
exports.getReadmeContent = getReadmeContent;
|
||||
function completeRelativeUrls(readmeContent, readmeFilepath, enableUrlCompletion, imageExtensions) {
|
||||
if (enableUrlCompletion) {
|
||||
readmeFilepath = readmeFilepath.replace(/^[.][/]/, '');
|
||||
// Make relative urls absolute
|
||||
const rules = [
|
||||
...getRelativeReadmeAnchorsRules(readmeFilepath),
|
||||
...getRelativeImageUrlRules(imageExtensions),
|
||||
...getRelativeUrlRules()
|
||||
];
|
||||
readmeContent = applyRules(rules, readmeContent);
|
||||
}
|
||||
return readmeContent;
|
||||
}
|
||||
exports.completeRelativeUrls = completeRelativeUrls;
|
||||
function applyRules(rules, readmeContent) {
|
||||
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) {
|
||||
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 = [
|
||||
// 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.:
|
||||
// [![media/image.svg](media/image.svg)](#table-of-content)
|
||||
// [![media/image.svg](media/image.svg "title a")](#table-of-content "title b")
|
||||
{
|
||||
left: /(?<left>\[!\[[^\]]*\]\([^)]+\)\])/,
|
||||
url: url,
|
||||
absUrlPrefix: prefix
|
||||
}
|
||||
];
|
||||
return rules;
|
||||
}
|
||||
function getRelativeImageUrlRules(imageExtensions) {
|
||||
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 = [
|
||||
// matches e.g.:
|
||||
// ![media/image.svg](media/image.svg)
|
||||
// ![media/image.svg](media/image.svg "with title")
|
||||
{
|
||||
left: /(?<left>!\[[^\]]*\])/,
|
||||
url: url,
|
||||
absUrlPrefix: RAW_PREFIX
|
||||
}
|
||||
];
|
||||
return rules;
|
||||
}
|
||||
function getRelativeUrlRules() {
|
||||
// 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 = [
|
||||
// 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](media/image.svg)](media/image.svg)
|
||||
// [![media/image.svg](media/image.svg)](README.md#table-of-content "title b")
|
||||
// [![media/image.svg](media/image.svg "title a")](media/image.svg)
|
||||
// [![media/image.svg](media/image.svg "title a")](media/image.svg "title b")
|
||||
// [![media/image.svg](media/image.svg "title a")](README.md#table-of-content "title b")
|
||||
{
|
||||
left: new RegExp(`(?<left>\\[!\\[[^\\]]*\\]\\([^)]+${TITLE_REGEX}\\)\\])`),
|
||||
url: url,
|
||||
absUrlPrefix: BLOB_PREFIX
|
||||
}
|
||||
];
|
||||
return rules;
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 7351:
|
||||
|
|
|
@ -9,3 +9,8 @@ module.exports = {
|
|||
},
|
||||
verbose: true
|
||||
}
|
||||
process.env = Object.assign(process.env, {
|
||||
GITHUB_SERVER_URL: 'https://github.com',
|
||||
GITHUB_REPOSITORY: 'peter-evans/dockerhub-description',
|
||||
GITHUB_REF_NAME: 'main'
|
||||
})
|
|
@ -9,7 +9,7 @@
|
|||
"format": "prettier --write '**/*.ts'",
|
||||
"format-check": "prettier --check '**/*.ts'",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"test": "jest --passWithNoTests"
|
||||
"test": "jest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -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.:
|
||||
// [![media/image.svg](media/image.svg)](#table-of-content)
|
||||
// [![media/image.svg](media/image.svg "title a")](#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.:
|
||||
// ![media/image.svg](media/image.svg)
|
||||
// ![media/image.svg](media/image.svg "with title")
|
||||
{
|
||||
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](media/image.svg)](media/image.svg)
|
||||
// [![media/image.svg](media/image.svg)](README.md#table-of-content "title b")
|
||||
// [![media/image.svg](media/image.svg "title a")](media/image.svg)
|
||||
// [![media/image.svg](media/image.svg "title a")](media/image.svg "title b")
|
||||
// [![media/image.svg](media/image.svg "title a")](README.md#table-of-content "title b")
|
||||
{
|
||||
left: new RegExp(
|
||||
`(?<left>\\[!\\[[^\\]]*\\]\\([^)]+${TITLE_REGEX}\\)\\])`
|
||||
),
|
||||
url: url,
|
||||
absUrlPrefix: BLOB_PREFIX
|
||||
}
|
||||
]
|
||||
|
||||
return rules
|
||||
}
|
Loading…
Reference in a new issue