Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow specify url in release assets #322

Merged
merged 23 commits into from May 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3133b39
feat: accept url from release setting for skipping upload
awcjack Jan 23, 2022
0fc0cf9
Update README.md
awcjack Apr 16, 2022
571797b
update publish.js for syntax
awcjack Apr 16, 2022
edcaa34
fix: lint
awcjack Apr 17, 2022
0f2eeb0
test: add unit test of publish release with asset link
awcjack Apr 17, 2022
54b9187
fix: url asset skip glob
awcjack Apr 17, 2022
c2e11b4
test: publish release with asset link allow empty path
awcjack Apr 17, 2022
c88e872
fix: link asset should not join url with original gitlabUrl
awcjack Apr 17, 2022
1313bed
test: publish release with asset link testcase update
awcjack Apr 17, 2022
2b3ea8b
feat: allow loading variable from release url
awcjack Apr 17, 2022
48da5e8
test: unit test of public release with asset link with variable
awcjack Apr 17, 2022
f3e6d81
docs: reorder assets path & url to weight path higher
awcjack Apr 26, 2022
3d4ad21
fix: typo in url variable regex
awcjack Apr 26, 2022
a98c008
fix: url variable replace globally
awcjack Apr 26, 2022
45968b3
test: extra testcase for using same variable in url multiple time
awcjack Apr 26, 2022
9c48cbb
test: extra testcase for using multiple variable in one url
awcjack Apr 26, 2022
1c15ed2
test: url variable testcase format update prevent disable eslint
awcjack Apr 29, 2022
aafa9e2
fix: merge error
awcjack May 12, 2022
acf006d
test: fix testcase link asset release url compare
awcjack May 12, 2022
240a7a1
fix: link asset remove variable replacement code
awcjack May 12, 2022
b0e27bb
test: remove link asset variable replacement testcase
awcjack May 12, 2022
868f303
style: lint link asset code
awcjack May 13, 2022
9ec0365
docs: simplify option description
fgreinacher May 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -35,6 +35,7 @@ The plugin can be configured in the [**semantic-release** configuration file](ht
{"path": "dist/asset.min.css", "label": "CSS distribution"},
{"path": "dist/asset.min.js", "label": "JS distribution"},
{"path": "dist/asset.min.js", "label": "v${nextRelease.version}.js"}
{"url": "https://gitlab.com/gitlab-org/gitlab/-/blob/master/README.md"}
]
}],
]
Expand Down Expand Up @@ -95,7 +96,8 @@ Can be a [glob](https://github.com/isaacs/node-glob#glob-primer) or and `Array`

| Property | Description | Default |
| -------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `path` | **Required.** A [glob](https://github.com/isaacs/node-glob#glob-primer) to identify the files to upload. | - |
| `path` | **Required**, unless `url` is set. A [glob](https://github.com/isaacs/node-glob#glob-primer) to identify the files to upload. | - |
| `url` | Alternative to setting `path` this provides the ability to add links to releases, e.g. URLs to container images. | - |
| `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`. |
| `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` |
| `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. | - |
Expand Down
85 changes: 50 additions & 35 deletions lib/publish.js
Expand Up @@ -31,54 +31,69 @@ module.exports = async (pluginConfig, context) => {
debug('milestones: %o', milestones);

if (assets && assets.length > 0) {
const globbedAssets = await getAssets(context, assets);
// Skip glob if url is provided
const urlAssets = assets.filter((asset) => asset.url);
debug('url assets: %o', urlAssets);
const globbedAssets = await getAssets(
context,
assets.filter((asset) => !asset.url)
);
debug('globbed assets: %o', globbedAssets);
const allAssets = [...urlAssets, ...globbedAssets];
debug('all assets: %o', allAssets);

await Promise.all(
globbedAssets.map(async (asset) => {
allAssets.map(async (asset) => {
const {path, type, filepath} = isPlainObject(asset) ? asset : {path: asset};
const file = pathlib.resolve(cwd, path);
const label = asset.label ? template(asset.label)(context) : undefined;
let fileStat;
const _url = asset.url;
if (_url) {
assetsList.push({label, rawUrl: _url, type, filepath});
fgreinacher marked this conversation as resolved.
Show resolved Hide resolved
debug('use link from release setting: %s', _url);
} else {
const file = pathlib.resolve(cwd, path);

try {
fileStat = await stat(file);
} catch {
logger.error('The asset %s cannot be read, and will be ignored.', path);
return;
}
let fileStat;

if (!fileStat || !fileStat.isFile()) {
logger.error('The asset %s is not a file, and will be ignored.', path);
return;
}
try {
fileStat = await stat(file);
} catch {
logger.error('The asset %s cannot be read, and will be ignored.', path);
return;
}

debug('file path: %o', path);
debug('file label: %o', label);
debug('file type: %o', type);
debug('file filepath: %o', filepath);
if (!fileStat || !fileStat.isFile()) {
logger.error('The asset %s is not a file, and will be ignored.', path);
return;
}

// Uploaded assets to the project
const form = new FormData();
form.append('file', createReadStream(file));
debug('file path: %o', path);
debug('file label: %o', label);
debug('file type: %o', type);
debug('file filepath: %o', filepath);

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

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

let response;
try {
response = await got.post(uploadEndpoint, {...apiOptions, ...proxy, body: form}).json();
} catch (error) {
logger.error('An error occurred while uploading %s to the GitLab project uploads API:\n%O', file, error);
throw error;
}
debug('POST-ing the file %s to %s', file, uploadEndpoint);

let response;
try {
response = await got.post(uploadEndpoint, {...apiOptions, ...proxy, body: form}).json();
} catch (error) {
logger.error('An error occurred while uploading %s to the GitLab project uploads API:\n%O', file, error);
throw error;
}

const {url, alt} = response;
const {url, alt} = response;

assetsList.push({label, alt, url, type, filepath});
assetsList.push({label, alt, url, type, filepath});

logger.log('Uploaded file: %s', url);
logger.log('Uploaded file: %s', url);
}
})
);
}
Expand All @@ -93,10 +108,10 @@ module.exports = async (pluginConfig, context) => {
description: notes && notes.trim() ? notes : gitTag,
milestones,
assets: {
links: assetsList.map(({label, alt, url, type, filepath}) => {
links: assetsList.map(({label, alt, url, type, filepath, rawUrl}) => {
return {
name: label || alt,
url: urlJoin(gitlabUrl, repoId, url),
url: rawUrl || urlJoin(gitlabUrl, repoId, url),
link_type: type,
filepath,
};
Expand Down
38 changes: 38 additions & 0 deletions test/publish.test.js
Expand Up @@ -294,3 +294,41 @@ test.serial('Publish a release with missing release notes', async (t) => {
t.deepEqual(t.context.log.args[0], ['Published GitLab release: %s', nextRelease.gitTag]);
t.true(gitlab.isDone());
});

test.serial('Publish a release with an asset link', async (t) => {
const cwd = 'test/fixtures/files';
const owner = 'test_user';
const repo = 'test_repo';
const env = {GITLAB_TOKEN: 'gitlab_token'};
const nextRelease = {gitHead: '123', gitTag: 'v1.0.0', notes: 'Test release note body'};
const options = {repositoryUrl: `https://gitlab.com/${owner}/${repo}.git`};
const encodedRepoId = encodeURIComponent(`${owner}/${repo}`);
const encodedGitTag = encodeURIComponent(nextRelease.gitTag);
const link = {
label: 'README.md',
type: 'other',
url: 'https://gitlab.com/gitlab-org/gitlab/-/blob/master/README.md',
};
const assets = [link];
const gitlab = authenticate(env)
.post(`/projects/${encodedRepoId}/releases`, {
tag_name: nextRelease.gitTag,
description: nextRelease.notes,
assets: {
links: [
{
name: 'README.md',
url: `https://gitlab.com/gitlab-org/gitlab/-/blob/master/README.md`,
link_type: 'other',
},
],
},
})
.reply(200);

const result = await publish({assets}, {env, cwd, options, nextRelease, logger: t.context.logger});

t.is(result.url, `https://gitlab.com/${owner}/${repo}/-/releases/${encodedGitTag}`);
t.deepEqual(t.context.log.args[0], ['Published GitLab release: %s', nextRelease.gitTag]);
t.true(gitlab.isDone());
});