Skip to content

Commit

Permalink
feat(generics): allow upload of build assets as generic packages #174 (
Browse files Browse the repository at this point in the history
…#343)

Co-authored-by: Florian Greinacher <florian@greinacher.de>
  • Loading branch information
JonasSchubert and fgreinacher committed Jan 30, 2023
1 parent 236c74f commit 0067996
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 13 deletions.
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -35,7 +35,7 @@ The plugin can be configured in the [**semantic-release** configuration file](ht
"gitlabUrl": "https://custom.gitlab.com",
"assets": [
{ "path": "dist/asset.min.css", "label": "CSS distribution" },
{ "path": "dist/asset.min.js", "label": "JS distribution" },
{ "path": "dist/asset.min.js", "label": "JS distribution", "type": "generic_package" },
{ "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 @@ -106,6 +106,8 @@ Can be a [glob](https://github.com/isaacs/node-glob#glob-primer) or and `Array`
| `label` | Short description of the file displayed on the GitLab release. Ignored if `path` matches more than one file. Supports [Lodash templating](https://lodash.com/docs#template). | 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)). Supports [Lodash templating](https://lodash.com/docs#template). | `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. Supports [Lodash templating](https://lodash.com/docs#template). | - |
| `target` | Controls where the file is uploaded to. Can be set to `project_upload` for storing the file as [project upload](https://docs.gitlab.com/ee/api/projects.html#upload-a-file) or `generic_package` for storing the file as [generic package](https://docs.gitlab.com/ee/user/packages/generic_packages/). | `project_upload` |
| `status` | This is only applied, if `target` is set to `generic_package`. The generic package status. Can be `default` and `hidden` (see official documents on [generic packages](https://docs.gitlab.com/ee/user/packages/generic_packages/)). | `default` |

Each entry in the `assets` `Array` is globbed individually. A [glob](https://github.com/isaacs/node-glob#glob-primer)
can be a `String` (`"dist/**/*.js"` or `"dist/mylib.js"`) or an `Array` of `String`s that will be globbed together
Expand Down
58 changes: 46 additions & 12 deletions lib/publish.js
Expand Up @@ -50,6 +50,9 @@ export default async (pluginConfig, context) => {
const label = asset.label ? template(asset.label)(context) : undefined;
const type = asset.type ? template(asset.type)(context) : undefined;
const filepath = asset.filepath ? template(asset.filepath)(context) : undefined;
const target = asset.target ? template(asset.target)(context) : undefined;
const status = asset.status ? template(asset.status)(context) : undefined;

if (_url) {
assetsList.push({ label, rawUrl: _url, type, filepath });
debug("use link from release setting: %s", _url);
Expand All @@ -74,28 +77,59 @@ export default async (pluginConfig, context) => {
debug("file label: %o", label);
debug("file type: %o", type);
debug("file filepath: %o", filepath);
debug("file target: %o", target);
debug("file status: %o", status);

// Uploaded assets to the project
const form = new FormData();
form.append("file", createReadStream(file));

const uploadEndpoint = urlJoin(gitlabApiUrl, `/projects/${encodedRepoId}/uploads`);
let uploadEndpoint;
let response;

debug("POST-ing the file %s to %s", file, uploadEndpoint);
if (target === "generic_package") {
// Upload generic packages
const encodedLabel = encodeURIComponent(label);
uploadEndpoint = urlJoin(
gitlabApiUrl,
`/projects/${encodedRepoId}/packages/generic/release/${encodedGitTag}/${encodedLabel}?${
status ? `status=${status}&` : ""
}select=package_file`
);

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("PUT-ing the file %s to %s", file, uploadEndpoint);

try {
response = await got.put(uploadEndpoint, { ...apiOptions, body: form }).json();
} catch (error) {
logger.error("An error occurred while uploading %s to the GitLab generics package API:\n%O", file, error);
throw error;
}

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

assetsList.push({ label, alt, url, type, filepath });
assetsList.push({ label, alt: "release", url, type: "package", filepath });

logger.log("Uploaded file: %s", url);
logger.log("Uploaded file: %s", url);
} else {
// Handle normal assets
uploadEndpoint = urlJoin(gitlabApiUrl, `/projects/${encodedRepoId}/uploads`);

debug("POST-ing the file %s to %s", file, uploadEndpoint);

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;

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

logger.log("Uploaded file: %s", url);
}
}
})
);
Expand Down
44 changes: 44 additions & 0 deletions test/publish.test.js
Expand Up @@ -83,6 +83,50 @@ test.serial("Publish a release with assets", async (t) => {
t.true(gitlab.isDone());
});

test.serial("Publish a release with generics", 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 uploaded = { file: { url: "/uploads/file.css" } };
const generic = { path: "file.css", label: "Style package", target: "generic_package", status: "hidden" };
const assets = [generic];
const encodedLabel = encodeURIComponent(generic.label);
const gitlab = authenticate(env)
.post(`/projects/${encodedRepoId}/releases`, {
tag_name: nextRelease.gitTag,
description: nextRelease.notes,
assets: {
links: [
{
name: "Style package",
url: `https://gitlab.com/${owner}/${repo}${uploaded.file.url}`,
link_type: "package",
},
],
},
})
.reply(200);
const gitlabUpload = authenticate(env)
.put(
`/projects/${encodedRepoId}/packages/generic/release/${encodedGitTag}/${encodedLabel}?status=${generic.status}&select=package_file`,
/filename="file.css"/gm
)
.reply(200, uploaded);

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], ["Uploaded file: %s", uploaded.file.url]);
t.deepEqual(t.context.log.args[1], ["Published GitLab release: %s", nextRelease.gitTag]);
t.true(gitlabUpload.isDone());
t.true(gitlab.isDone());
});

test.serial("Publish a release with asset type and permalink", async (t) => {
const cwd = "test/fixtures/files";
const owner = "test_user";
Expand Down

0 comments on commit 0067996

Please sign in to comment.