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

Use project ID for API calls if available #809

Merged
merged 10 commits into from
Jan 13, 2025
26 changes: 13 additions & 13 deletions lib/definitions/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,20 @@ Your configuration for the \`labels\` option is \`${stringify(labels)}\`.`,
}),
EINVALIDGITLABURL: () => ({
message: 'The git repository URL is not a valid GitLab URL.',
details: `The **semantic-release** \`repositoryUrl\` option must a valid GitLab URL with the format \`<GitLab_URL>/<repoId>.git\`.
details: `The **semantic-release** \`repositoryUrl\` option must a valid GitLab URL with the format \`<GitLab_URL>/<projectPath>.git\`.

By default the \`repositoryUrl\` option is retrieved from the \`repository\` property of your \`package.json\` or the [git origin url](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) of the repository cloned by your CI environment.`,
}),
EINVALIDGLTOKEN: ({repoId}) => ({
EINVALIDGLTOKEN: ({projectPath}) => ({
message: 'Invalid GitLab token.',
details: `The [GitLab token](${linkify(
'README.md#gitlab-authentication'
)}) configured in the \`GL_TOKEN\` or \`GITLAB_TOKEN\` environment variable must be a valid [personal access token](https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html) allowing to push to the repository ${repoId}.
)}) configured in the \`GL_TOKEN\` or \`GITLAB_TOKEN\` environment variable must be a valid [personal access token](https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html) allowing to push to the repository ${projectPath}.

Please make sure to set the \`GL_TOKEN\` or \`GITLAB_TOKEN\` environment variable in your CI with the exact value of the GitLab personal token.`,
}),
EMISSINGREPO: ({repoId}) => ({
message: `The repository ${repoId} doesn't exist.`,
EMISSINGREPO: ({projectPath}) => ({
message: `The repository ${projectPath} doesn't exist.`,
details: `The **semantic-release** \`repositoryUrl\` option must refer to your GitLab repository. The repository must be accessible with the [GitLab API](https://docs.gitlab.com/ce/api/README.html).

By default the \`repositoryUrl\` option is retrieved from the \`repository\` property of your \`package.json\` or the [git origin url](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) of the repository cloned by your CI environment.
Expand All @@ -63,21 +63,21 @@ If you are using [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee)
'README.md#options'
)}).`,
}),
EGLNOPUSHPERMISSION: ({repoId}) => ({
message: `The GitLab token doesn't allow to push on the repository ${repoId}.`,
EGLNOPUSHPERMISSION: ({projectPath}) => ({
message: `The GitLab token doesn't allow to push on the repository ${projectPath}.`,
details: `The user associated with the [GitLab token](${linkify(
'README.md#gitlab-authentication'
)}) configured in the \`GL_TOKEN\` or \`GITLAB_TOKEN\` environment variable must allows to push to the repository ${repoId}.
)}) configured in the \`GL_TOKEN\` or \`GITLAB_TOKEN\` environment variable must allows to push to the repository ${projectPath}.

Please make sure the GitLab user associated with the token has the [permission to push](https://docs.gitlab.com/ee/user/permissions.html#project-members-permissions) to the repository ${repoId}.`,
Please make sure the GitLab user associated with the token has the [permission to push](https://docs.gitlab.com/ee/user/permissions.html#project-members-permissions) to the repository ${projectPath}.`,
}),
EGLNOPULLPERMISSION: ({repoId}) => ({
message: `The GitLab token doesn't allow to pull from the repository ${repoId}.`,
EGLNOPULLPERMISSION: ({projectPath}) => ({
message: `The GitLab token doesn't allow to pull from the repository ${projectPath}.`,
details: `The user associated with the [GitLab token](${linkify(
'README.md#gitlab-authentication'
)}) configured in the \`GL_TOKEN\` or \`GITLAB_TOKEN\` environment variable must allow pull from the repository ${repoId}.
)}) configured in the \`GL_TOKEN\` or \`GITLAB_TOKEN\` environment variable must allow pull from the repository ${projectPath}.

Please make sure the GitLab user associated with the token has the [permission to push](https://docs.gitlab.com/ee/user/permissions.html#project-members-permissions) to the repository ${repoId}.`,
Please make sure the GitLab user associated with the token has the [permission to push](https://docs.gitlab.com/ee/user/permissions.html#project-members-permissions) to the repository ${projectPath}.`,
}),
ENOGLTOKEN: ({repositoryUrl}) => ({
message: 'No GitLab token specified.',
Expand Down
10 changes: 5 additions & 5 deletions lib/fail.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import got from "got";
import _debug from "debug";
const debug = _debug("semantic-release:gitlab");
import resolveConfig from "./resolve-config.js";
import getRepoId from "./get-repo-id.js";
import getFailComment from "./get-fail-comment.js";
import getProjectContext from "./get-project-context.js";

export default async (pluginConfig, context) => {
const {
Expand All @@ -25,8 +25,8 @@ export default async (pluginConfig, context) => {
assignee,
retryLimit,
} = resolveConfig(pluginConfig, context);
const repoId = getRepoId(context, gitlabUrl, repositoryUrl);
const encodedRepoId = encodeURIComponent(repoId);
const { encodedProjectPath, projectApiUrl } = getProjectContext(context, gitlabUrl, gitlabApiUrl, repositoryUrl);

const apiOptions = {
headers: { "PRIVATE-TOKEN": gitlabToken },
retry: { limit: retryLimit },
Expand All @@ -42,7 +42,7 @@ Using 'false' for 'failComment' or 'failTitle' is deprecated and will be removed
const encodedFailTitle = encodeURIComponent(failTitle);
const description = failComment ? template(failComment)({ branch, errors }) : getFailComment(branch, errors);

const issuesEndpoint = urlJoin(gitlabApiUrl, `/projects/${encodedRepoId}/issues`);
const issuesEndpoint = urlJoin(projectApiUrl, `issues`);
const openFailTitleIssueEndpoint = urlJoin(issuesEndpoint, `?state=opened&search=${encodedFailTitle}`);

const openFailTitleIssues = await got(openFailTitleIssueEndpoint, { ...apiOptions }).json();
Expand All @@ -67,7 +67,7 @@ Using 'false' for 'failComment' or 'failTitle' is deprecated and will be removed
const { id, web_url } = existingIssue;
logger.log("Commented on issue #%d: %s.", id, web_url);
} else {
const newIssue = { id: encodedRepoId, description, labels, title: failTitle, assignee_id: assignee };
const newIssue = { id: encodedProjectPath, description, labels, title: failTitle, assignee_id: assignee };
debug("create issue: %O", newIssue);

/* eslint camelcase: off */
Expand Down
27 changes: 27 additions & 0 deletions lib/get-project-context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import escapeStringRegexp from "escape-string-regexp";
import parseUrl from "parse-url";
import urlJoin from "url-join";

export default (
{ envCi: { service } = {}, env: { CI_PROJECT_ID, CI_PROJECT_PATH } },
gitlabUrl,
gitlabApiUrl,
repositoryUrl
) => {
const projectId = service === "gitlab" && CI_PROJECT_ID ? CI_PROJECT_ID : null;
const projectPath =
service === "gitlab" && CI_PROJECT_PATH
? CI_PROJECT_PATH
: parseUrl(repositoryUrl)
.pathname.replace(new RegExp(`^${escapeStringRegexp(parseUrl(gitlabUrl).pathname)}`), "")
.replace(/^\//, "")
.replace(/\/$/, "")
.replace(/\.git$/, "");
const encodedProjectPath = encodeURIComponent(projectPath);
const projectApiUrl = urlJoin(gitlabApiUrl, `/projects/${projectId ?? encodedProjectPath}`);
return {
projectPath,
encodedProjectPath,
projectApiUrl,
};
};
11 changes: 0 additions & 11 deletions lib/get-repo-id.js

This file was deleted.

25 changes: 11 additions & 14 deletions lib/publish.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import got from "got";
import _debug from "debug";
const debug = _debug("semantic-release:gitlab");
import resolveConfig from "./resolve-config.js";
import getRepoId from "./get-repo-id.js";
import getAssets from "./glob-assets.js";
import { RELEASE_NAME } from "./definitions/constants.js";
import getProjectContext from "./get-project-context.js";

const isUrlScheme = (value) => /^(https|http|ftp):\/\//.test(value);

Expand All @@ -27,8 +27,8 @@ export default async (pluginConfig, context) => {
context
);
const assetsList = [];
const repoId = getRepoId(context, gitlabUrl, repositoryUrl);
const encodedRepoId = encodeURIComponent(repoId);
const { projectPath, projectApiUrl } = getProjectContext(context, gitlabUrl, gitlabApiUrl, repositoryUrl);

const encodedGitTag = encodeURIComponent(gitTag);
const encodedVersion = encodeURIComponent(version);
const apiOptions = {
Expand All @@ -52,7 +52,7 @@ export default async (pluginConfig, context) => {
retry: { limit: retryLimit },
};

debug("repoId: %o", repoId);
debug("projectPath: %o", projectPath);
debug("release name: %o", gitTag);
debug("release ref: %o", gitHead);
debug("milestones: %o", milestones);
Expand Down Expand Up @@ -114,8 +114,8 @@ export default async (pluginConfig, context) => {
const encodedLabel = encodeURIComponent(label);
// https://docs.gitlab.com/ee/user/packages/generic_packages/#publish-a-package-file
uploadEndpoint = urlJoin(
gitlabApiUrl,
`/projects/${encodedRepoId}/packages/generic/release/${encodedVersion}/${encodedLabel}?${
projectApiUrl,
`packages/generic/release/${encodedVersion}/${encodedLabel}?${
status ? `status=${status}&` : ""
}select=package_file`
);
Expand All @@ -130,17 +130,14 @@ export default async (pluginConfig, context) => {
}

// https://docs.gitlab.com/ee/user/packages/generic_packages/#download-package-file
const url = urlJoin(
gitlabApiUrl,
`/projects/${encodedRepoId}/packages/generic/release/${encodedVersion}/${encodedLabel}`
);
const url = urlJoin(projectApiUrl, `packages/generic/release/${encodedVersion}/${encodedLabel}`);

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

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

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

Expand All @@ -167,7 +164,7 @@ export default async (pluginConfig, context) => {

debug("Create a release for git tag %o with commit %o", gitTag, gitHead);

const createReleaseEndpoint = urlJoin(gitlabApiUrl, `/projects/${encodedRepoId}/releases`);
const createReleaseEndpoint = urlJoin(projectApiUrl, "releases");

const json = {
/* eslint-disable camelcase */
Expand All @@ -178,7 +175,7 @@ export default async (pluginConfig, context) => {
links: assetsList.map(({ label, alt, url, type, filepath, rawUrl }) => {
return {
name: label || alt,
url: rawUrl || (isUrlScheme(url) ? url : urlJoin(gitlabUrl, repoId, url)),
url: rawUrl || (isUrlScheme(url) ? url : urlJoin(gitlabUrl, projectPath, url)),
link_type: type,
filepath,
};
Expand All @@ -202,7 +199,7 @@ export default async (pluginConfig, context) => {

logger.log("Published GitLab release: %s", gitTag);

const releaseUrl = urlJoin(gitlabUrl, repoId, `/-/releases/${encodedGitTag}`);
const releaseUrl = urlJoin(gitlabUrl, projectPath, `/-/releases/${encodedGitTag}`);

return { name: RELEASE_NAME, url: releaseUrl };
};
10 changes: 3 additions & 7 deletions lib/success.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import got from "got";
import _debug from "debug";
const debug = _debug("semantic-release:gitlab");
import resolveConfig from "./resolve-config.js";
import getRepoId from "./get-repo-id.js";
import getProjectContext from "./get-project-context.js";
import getSuccessComment from "./get-success-comment.js";

export default async (pluginConfig, context) => {
Expand All @@ -17,8 +17,7 @@ export default async (pluginConfig, context) => {
} = context;
const { gitlabToken, gitlabUrl, gitlabApiUrl, successComment, successCommentCondition, proxy, retryLimit } =
resolveConfig(pluginConfig, context);
const repoId = getRepoId(context, gitlabUrl, repositoryUrl);
const encodedRepoId = encodeURIComponent(repoId);
const { projectApiUrl } = getProjectContext(context, gitlabUrl, gitlabApiUrl, repositoryUrl);
const apiOptions = {
headers: { "PRIVATE-TOKEN": gitlabToken },
retry: { limit: retryLimit },
Expand Down Expand Up @@ -77,10 +76,7 @@ Using 'false' for 'successComment' is deprecated and will be removed in a future
};

const getRelatedMergeRequests = async (commitHash) => {
const relatedMergeRequestsEndpoint = urlJoin(
gitlabApiUrl,
`/projects/${encodedRepoId}/repository/commits/${commitHash}/merge_requests`
);
const relatedMergeRequestsEndpoint = urlJoin(projectApiUrl, `repository/commits/${commitHash}/merge_requests`);
debug("Getting MRs from %s", relatedMergeRequestsEndpoint);
const relatedMergeRequests = await got
.get(relatedMergeRequestsEndpoint, {
Expand Down
21 changes: 10 additions & 11 deletions lib/verify.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { isString, isPlainObject, isNil, isArray } from "lodash-es";
import urlJoin from "url-join";
import got from "got";
import _debug from "debug";
const debug = _debug("semantic-release:gitlab");
import AggregateError from "aggregate-error";
import resolveConfig from "./resolve-config.js";
import getRepoId from "./get-repo-id.js";
import getProjectContext from "./get-project-context.js";
import getError from "./get-error.js";

const isNonEmptyString = (value) => isString(value) && value.trim();
Expand All @@ -32,10 +31,10 @@ export default async (pluginConfig, context) => {
logger,
} = context;
const { gitlabToken, gitlabUrl, gitlabApiUrl, proxy, ...options } = resolveConfig(pluginConfig, context);
const repoId = getRepoId(context, gitlabUrl, repositoryUrl);
const { projectPath, projectApiUrl } = getProjectContext(context, gitlabUrl, gitlabApiUrl, repositoryUrl);

debug("apiUrl: %o", gitlabApiUrl);
debug("repoId: %o", repoId);
debug("projectPath: %o", projectPath);

const isValid = (option, value) => {
const validator = VALIDATORS[option];
Expand All @@ -46,15 +45,15 @@ export default async (pluginConfig, context) => {
.filter(([option, value]) => !isValid(option, value))
.map(([option, value]) => getError(`EINVALID${option.toUpperCase()}`, { [option]: value }));

if (!repoId) {
if (!projectPath) {
errors.push(getError("EINVALIDGITLABURL"));
}

if (!gitlabToken) {
errors.push(getError("ENOGLTOKEN", { repositoryUrl }));
}

if (gitlabToken && repoId) {
if (gitlabToken && projectPath) {
let projectAccess;
let groupAccess;

Expand All @@ -64,7 +63,7 @@ export default async (pluginConfig, context) => {
({
permissions: { project_access: projectAccess, group_access: groupAccess },
} = await got
.get(urlJoin(gitlabApiUrl, `/projects/${encodeURIComponent(repoId)}`), {
.get(projectApiUrl, {
headers: { "PRIVATE-TOKEN": gitlabToken },
...proxy,
})
Expand All @@ -73,17 +72,17 @@ export default async (pluginConfig, context) => {
context.options.dryRun &&
!((projectAccess && projectAccess.access_level >= 10) || (groupAccess && groupAccess.access_level >= 10))
) {
errors.push(getError("EGLNOPULLPERMISSION", { repoId }));
errors.push(getError("EGLNOPULLPERMISSION", { projectPath }));
} else if (
!((projectAccess && projectAccess.access_level >= 30) || (groupAccess && groupAccess.access_level >= 30))
) {
errors.push(getError("EGLNOPUSHPERMISSION", { repoId }));
errors.push(getError("EGLNOPUSHPERMISSION", { projectPath }));
}
} catch (error) {
if (error.response && error.response.statusCode === 401) {
errors.push(getError("EINVALIDGLTOKEN", { repoId }));
errors.push(getError("EINVALIDGLTOKEN", { projectPath }));
} else if (error.response && error.response.statusCode === 404) {
errors.push(getError("EMISSINGREPO", { repoId }));
errors.push(getError("EMISSINGREPO", { projectPath }));
} else {
throw error;
}
Expand Down
Loading
Loading