Skip to content

Commit

Permalink
chore(project): merge pull request #809 from semantic-release/fix/pro…
Browse files Browse the repository at this point in the history
…ject-id

Use project ID for API calls if available
  • Loading branch information
JonasSchubert authored Jan 13, 2025
2 parents 630e27e + 01957e7 commit 9d34d25
Show file tree
Hide file tree
Showing 13 changed files with 304 additions and 210 deletions.
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

0 comments on commit 9d34d25

Please sign in to comment.