Skip to content

Commit 3a9fb9a

Browse files
authored
feat: allow adding URLs to release assets (#322)
Co-authored-by: Florian Greinacher <[email protected]> Fixes #154
1 parent a8d1ce5 commit 3a9fb9a

File tree

3 files changed

+91
-36
lines changed

3 files changed

+91
-36
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ The plugin can be configured in the [**semantic-release** configuration file](ht
3535
{"path": "dist/asset.min.css", "label": "CSS distribution"},
3636
{"path": "dist/asset.min.js", "label": "JS distribution"},
3737
{"path": "dist/asset.min.js", "label": "v${nextRelease.version}.js"}
38+
{"url": "https://gitlab.com/gitlab-org/gitlab/-/blob/master/README.md"}
3839
]
3940
}],
4041
]
@@ -95,7 +96,8 @@ Can be a [glob](https://github.com/isaacs/node-glob#glob-primer) or and `Array`
9596

9697
| Property | Description | Default |
9798
| -------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------ |
98-
| `path` | **Required.** A [glob](https://github.com/isaacs/node-glob#glob-primer) to identify the files to upload. | - |
99+
| `path` | **Required**, unless `url` is set. A [glob](https://github.com/isaacs/node-glob#glob-primer) to identify the files to upload. | - |
100+
| `url` | Alternative to setting `path` this provides the ability to add links to releases, e.g. URLs to container images. | - |
99101
| `label` | Short description of the file displayed on the GitLab release. Can be dynamically adjusted with [Lodash template](https://lodash.com/docs#template). Allows same variables as [`successComment`](#successComment). Ignored if `path` matches more than one file. | File name extracted from the `path`. |
100102
| `type` | Asset type displayed on the GitLab release. Can be `runbook`, `package`, `image` and `other` (see official documents on [release assets](https://docs.gitlab.com/ee/user/project/releases/#release-assets)). | `other` |
101103
| `filepath` | A filepath for creating a permalink pointing to the asset (requires GitLab 12.9+, see official documents on [permanent links](https://docs.gitlab.com/ee/user/project/releases/#permanent-links-to-release-assets)). Ignored if `path` matches more than one file. | - |

lib/publish.js

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -31,54 +31,69 @@ module.exports = async (pluginConfig, context) => {
3131
debug('milestones: %o', milestones);
3232

3333
if (assets && assets.length > 0) {
34-
const globbedAssets = await getAssets(context, assets);
34+
// Skip glob if url is provided
35+
const urlAssets = assets.filter((asset) => asset.url);
36+
debug('url assets: %o', urlAssets);
37+
const globbedAssets = await getAssets(
38+
context,
39+
assets.filter((asset) => !asset.url)
40+
);
3541
debug('globbed assets: %o', globbedAssets);
42+
const allAssets = [...urlAssets, ...globbedAssets];
43+
debug('all assets: %o', allAssets);
3644

3745
await Promise.all(
38-
globbedAssets.map(async (asset) => {
46+
allAssets.map(async (asset) => {
3947
const {path, type, filepath} = isPlainObject(asset) ? asset : {path: asset};
40-
const file = pathlib.resolve(cwd, path);
4148
const label = asset.label ? template(asset.label)(context) : undefined;
42-
let fileStat;
49+
const _url = asset.url;
50+
if (_url) {
51+
assetsList.push({label, rawUrl: _url, type, filepath});
52+
debug('use link from release setting: %s', _url);
53+
} else {
54+
const file = pathlib.resolve(cwd, path);
4355

44-
try {
45-
fileStat = await stat(file);
46-
} catch {
47-
logger.error('The asset %s cannot be read, and will be ignored.', path);
48-
return;
49-
}
56+
let fileStat;
5057

51-
if (!fileStat || !fileStat.isFile()) {
52-
logger.error('The asset %s is not a file, and will be ignored.', path);
53-
return;
54-
}
58+
try {
59+
fileStat = await stat(file);
60+
} catch {
61+
logger.error('The asset %s cannot be read, and will be ignored.', path);
62+
return;
63+
}
5564

56-
debug('file path: %o', path);
57-
debug('file label: %o', label);
58-
debug('file type: %o', type);
59-
debug('file filepath: %o', filepath);
65+
if (!fileStat || !fileStat.isFile()) {
66+
logger.error('The asset %s is not a file, and will be ignored.', path);
67+
return;
68+
}
6069

61-
// Uploaded assets to the project
62-
const form = new FormData();
63-
form.append('file', createReadStream(file));
70+
debug('file path: %o', path);
71+
debug('file label: %o', label);
72+
debug('file type: %o', type);
73+
debug('file filepath: %o', filepath);
6474

65-
const uploadEndpoint = urlJoin(gitlabApiUrl, `/projects/${encodedRepoId}/uploads`);
75+
// Uploaded assets to the project
76+
const form = new FormData();
77+
form.append('file', createReadStream(file));
6678

67-
debug('POST-ing the file %s to %s', file, uploadEndpoint);
79+
const uploadEndpoint = urlJoin(gitlabApiUrl, `/projects/${encodedRepoId}/uploads`);
6880

69-
let response;
70-
try {
71-
response = await got.post(uploadEndpoint, {...apiOptions, ...proxy, body: form}).json();
72-
} catch (error) {
73-
logger.error('An error occurred while uploading %s to the GitLab project uploads API:\n%O', file, error);
74-
throw error;
75-
}
81+
debug('POST-ing the file %s to %s', file, uploadEndpoint);
82+
83+
let response;
84+
try {
85+
response = await got.post(uploadEndpoint, {...apiOptions, ...proxy, body: form}).json();
86+
} catch (error) {
87+
logger.error('An error occurred while uploading %s to the GitLab project uploads API:\n%O', file, error);
88+
throw error;
89+
}
7690

77-
const {url, alt} = response;
91+
const {url, alt} = response;
7892

79-
assetsList.push({label, alt, url, type, filepath});
93+
assetsList.push({label, alt, url, type, filepath});
8094

81-
logger.log('Uploaded file: %s', url);
95+
logger.log('Uploaded file: %s', url);
96+
}
8297
})
8398
);
8499
}
@@ -93,10 +108,10 @@ module.exports = async (pluginConfig, context) => {
93108
description: notes && notes.trim() ? notes : gitTag,
94109
milestones,
95110
assets: {
96-
links: assetsList.map(({label, alt, url, type, filepath}) => {
111+
links: assetsList.map(({label, alt, url, type, filepath, rawUrl}) => {
97112
return {
98113
name: label || alt,
99-
url: urlJoin(gitlabUrl, repoId, url),
114+
url: rawUrl || urlJoin(gitlabUrl, repoId, url),
100115
link_type: type,
101116
filepath,
102117
};

test/publish.test.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,3 +294,41 @@ test.serial('Publish a release with missing release notes', async (t) => {
294294
t.deepEqual(t.context.log.args[0], ['Published GitLab release: %s', nextRelease.gitTag]);
295295
t.true(gitlab.isDone());
296296
});
297+
298+
test.serial('Publish a release with an asset link', async (t) => {
299+
const cwd = 'test/fixtures/files';
300+
const owner = 'test_user';
301+
const repo = 'test_repo';
302+
const env = {GITLAB_TOKEN: 'gitlab_token'};
303+
const nextRelease = {gitHead: '123', gitTag: 'v1.0.0', notes: 'Test release note body'};
304+
const options = {repositoryUrl: `https://gitlab.com/${owner}/${repo}.git`};
305+
const encodedRepoId = encodeURIComponent(`${owner}/${repo}`);
306+
const encodedGitTag = encodeURIComponent(nextRelease.gitTag);
307+
const link = {
308+
label: 'README.md',
309+
type: 'other',
310+
url: 'https://gitlab.com/gitlab-org/gitlab/-/blob/master/README.md',
311+
};
312+
const assets = [link];
313+
const gitlab = authenticate(env)
314+
.post(`/projects/${encodedRepoId}/releases`, {
315+
tag_name: nextRelease.gitTag,
316+
description: nextRelease.notes,
317+
assets: {
318+
links: [
319+
{
320+
name: 'README.md',
321+
url: `https://gitlab.com/gitlab-org/gitlab/-/blob/master/README.md`,
322+
link_type: 'other',
323+
},
324+
],
325+
},
326+
})
327+
.reply(200);
328+
329+
const result = await publish({assets}, {env, cwd, options, nextRelease, logger: t.context.logger});
330+
331+
t.is(result.url, `https://gitlab.com/${owner}/${repo}/-/releases/${encodedGitTag}`);
332+
t.deepEqual(t.context.log.args[0], ['Published GitLab release: %s', nextRelease.gitTag]);
333+
t.true(gitlab.isDone());
334+
});

0 commit comments

Comments
 (0)