Skip to content

Feat/detect ci catalog project #855

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

Draft
wants to merge 2 commits into
base: beta
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 5 additions & 12 deletions lib/publish.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import getAssets from "./glob-assets.js";
import { RELEASE_NAME } from "./definitions/constants.js";
import getProjectContext from "./get-project-context.js";
import glab from "./glab.js";
import shouldPublishToCatalog from "./should-publish-to-catalog.js";

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

export default async (pluginConfig, context) => {
Expand All @@ -22,17 +24,8 @@ export default async (pluginConfig, context) => {
nextRelease: { gitTag, gitHead, notes, version },
logger,
} = context;
const {
gitlabToken,
gitlabUrl,
gitlabApiUrl,
assets,
milestones,
proxy,
retryLimit,
retryStatusCodes,
publishToCatalog,
} = resolveConfig(pluginConfig, context);
const config = resolveConfig(pluginConfig, context);
const { gitlabToken, gitlabUrl, gitlabApiUrl, assets, milestones, proxy, retryLimit, retryStatusCodes } = config;
const assetsList = [];
const { projectPath, projectApiUrl } = getProjectContext(context, gitlabUrl, gitlabApiUrl, repositoryUrl);

Expand Down Expand Up @@ -202,7 +195,7 @@ export default async (pluginConfig, context) => {

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

if (publishToCatalog) {
if (await shouldPublishToCatalog({ ...config, projectPath }, context)) {
try {
await glab(["repo", "publish", "catalog", gitTag], {
cwd,
Expand Down
9 changes: 8 additions & 1 deletion lib/resolve-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default (
HTTP_PROXY,
HTTPS_PROXY,
NO_PROXY,
CI_API_GRAPHQL_URL,
},
}
) => {
Expand Down Expand Up @@ -72,7 +73,13 @@ export default (
assignee,
retryLimit: retryLimit ?? DEFAULT_RETRY_LIMIT,
retryStatusCodes: DEFAULT_RETRY_STATUS_CODES,
publishToCatalog: publishToCatalog ?? false,
publishToCatalog,
gitlabGraphQlApiUrl:
userGitlabUrl
? urlJoin(userGitlabUrl, "/graphql")
: service === "gitlab" && CI_API_GRAPHQL_URL
? CI_API_GRAPHQL_URL
: urlJoin(defaultedGitlabUrl, "/graphql"),
};
};

Expand Down
30 changes: 30 additions & 0 deletions lib/should-publish-to-catalog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import got from "got";

export default async ({ gitlabGraphQlApiUrl, gitlabToken, projectPath, publishToCatalog }, { logger }) => {
if (publishToCatalog !== undefined) {
return publishToCatalog;
}
try {
const query = `
query {
project(fullPath: "${projectPath}") {
isCatalogResource
}
}
`;
const response = await got
.post(gitlabGraphQlApiUrl, {
headers: {
"Private-Token": gitlabToken,
"Content-Type": "application/json",
Accept: "application/graphql-response+json",
},
json: { query },
})
.json();
return !!response.data.project.isCatalogResource;
} catch (error) {
logger.error("Error making GraphQL request:", error.message);
throw error;
}
};
17 changes: 12 additions & 5 deletions lib/verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import resolveConfig from "./resolve-config.js";
import getProjectContext from "./get-project-context.js";
import getError from "./get-error.js";
import glab from "./glab.js";
import shouldPublishToCatalog from "./should-publish-to-catalog.js";

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

debug("apiUrl: %o", gitlabApiUrl);
Expand Down Expand Up @@ -93,7 +92,15 @@ export default async (pluginConfig, context) => {
}
}

if (publishToCatalog === true) {
let finalPublishToCatalog = false;
try {
finalPublishToCatalog = await shouldPublishToCatalog({ ...config, projectPath }, context);
} catch (error) {
logger.error("Could not access \n%O", error);
errors.push(getError("EGLABNOTINSTALLED"));
}

if (finalPublishToCatalog) {
try {
logger.log("Verifying that the GitLab CLI is installed");
await glab(["version"], {
Expand Down
111 changes: 94 additions & 17 deletions test/publish.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ test.serial("Publish a release", async (t) => {
const owner = "test_user";
const repo = "test_repo";
const env = { GITLAB_TOKEN: "gitlab_token" };
const pluginConfig = {};
const pluginConfig = { publishToCatalog: false };
const nextRelease = { gitHead: "123", gitTag: "v1.0.0", notes: "Test release note body" };
const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
const encodedProjectPath = encodeURIComponent(`${owner}/${repo}`);
Expand Down Expand Up @@ -81,7 +81,10 @@ test.serial("Publish a release with templated path", async (t) => {
.post(`/projects/${encodedProjectPath}/uploads`, /Content-Disposition/g)
.reply(200, uploaded);

const result = await publish({ assets }, { env, cwd, options, nextRelease, logger: t.context.logger });
const result = await publish(
{ assets, publishToCatalog: false },
{ 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", `https://gitlab.com${uploaded.full_path}`]);
Expand Down Expand Up @@ -122,7 +125,10 @@ test.serial("Publish a release with assets", async (t) => {
.post(`/projects/${encodedProjectPath}/uploads`, /filename="file.css"/gm)
.reply(200, uploaded);

const result = await publish({ assets }, { env, cwd, options, nextRelease, logger: t.context.logger });
const result = await publish(
{ assets, publishToCatalog: false },
{ 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", `https://gitlab.com${uploaded.full_path}`]);
Expand Down Expand Up @@ -168,7 +174,10 @@ test.serial("Publish a release with generics", async (t) => {
)
.reply(200, uploaded);

const result = await publish({ assets }, { env, cwd, options, nextRelease, logger: t.context.logger });
const result = await publish(
{ assets, publishToCatalog: false },
{ 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 (%s)", expectedUrl, uploaded.file.url]);
Expand Down Expand Up @@ -214,7 +223,10 @@ test.serial("Publish a release with generics and external storage provider (http
)
.reply(200, uploaded);

const result = await publish({ assets }, { env, cwd, options, nextRelease, logger: t.context.logger });
const result = await publish(
{ assets, publishToCatalog: false },
{ 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 (%s)", expectedUrl, uploaded.file.url]);
Expand Down Expand Up @@ -260,7 +272,10 @@ test.serial("Publish a release with generics and external storage provider (http
)
.reply(200, uploaded);

const result = await publish({ assets }, { env, cwd, options, nextRelease, logger: t.context.logger });
const result = await publish(
{ assets, publishToCatalog: false },
{ 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 (%s)", expectedUrl, uploaded.file.url]);
Expand Down Expand Up @@ -306,7 +321,10 @@ test.serial("Publish a release with generics and external storage provider (ftp)
)
.reply(200, uploaded);

const result = await publish({ assets }, { env, cwd, options, nextRelease, logger: t.context.logger });
const result = await publish(
{ assets, publishToCatalog: false },
{ 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 (%s)", expectedUrl, uploaded.file.url]);
Expand Down Expand Up @@ -358,7 +376,10 @@ test.serial("Publish a release with asset type and permalink", async (t) => {
.post(`/projects/${encodedProjectPath}/uploads`, /filename="file.css"/gm)
.reply(200, uploaded);

const result = await publish({ assets }, { env, cwd, options, nextRelease, logger: t.context.logger });
const result = await publish(
{ assets, publishToCatalog: false },
{ 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", `https://gitlab.com${uploaded.full_path}`]);
Expand Down Expand Up @@ -409,7 +430,10 @@ test.serial("Publish a release with an asset with a template label", async (t) =
.post(`/projects/${encodedProjectPath}/uploads`, /filename="file.css"/gm)
.reply(200, uploaded);

const result = await publish({ assets }, { env, cwd, options, nextRelease, logger: t.context.logger });
const result = await publish(
{ assets, publishToCatalog: false },
{ 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", `https://gitlab.com${uploaded.full_path}`]);
Expand Down Expand Up @@ -472,7 +496,10 @@ test.serial("Publish a release (with an link) with variables", async (t) => {
const gitlabUpload = authenticate(env)
.post(`/projects/${encodedProjectPath}/uploads`, /filename="file.css"/gm)
.reply(200, uploaded);
const result = await publish({ assets }, { env, cwd, options, nextRelease, logger: t.context.logger });
const result = await publish(
{ assets, publishToCatalog: false },
{ 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", `https://gitlab.com${uploaded.full_path}`]);
Expand All @@ -488,7 +515,7 @@ test.serial("Publish a release with a milestone", async (t) => {
const owner = "test_user";
const repo = "test_repo";
const env = { GITLAB_TOKEN: "gitlab_token" };
const pluginConfig = { milestones: ["1.2.3"] };
const pluginConfig = { publishToCatalog: false, milestones: ["1.2.3"] };
const nextRelease = { gitHead: "123", gitTag: "v1.0.0", notes: "Test release note body" };
const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
const encodedProjectPath = encodeURIComponent(`${owner}/${repo}`);
Expand Down Expand Up @@ -531,7 +558,10 @@ test.serial("Publish a release with array of missing assets", async (t) => {
},
})
.reply(200);
const result = await publish({ assets }, { env, cwd, options, nextRelease, logger: t.context.logger });
const result = await publish(
{ assets, publishToCatalog: false },
{ 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]);
Expand Down Expand Up @@ -571,7 +601,10 @@ test.serial("Publish a release with one asset and custom label", async (t) => {
.post(`/projects/${encodedProjectPath}/uploads`, /filename="upload.txt"/gm)
.reply(200, uploaded);

const result = await publish({ assets }, { env, cwd, options, nextRelease, logger: t.context.logger });
const result = await publish(
{ assets, publishToCatalog: false },
{ 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", `https://gitlab.com${uploaded.full_path}`]);
Expand All @@ -584,7 +617,7 @@ test.serial("Publish a release with missing release notes", async (t) => {
const owner = "test_user";
const repo = "test_repo";
const env = { GITLAB_TOKEN: "gitlab_token" };
const pluginConfig = {};
const pluginConfig = { publishToCatalog: false };
const nextRelease = { gitHead: "123", gitTag: "v1.0.0" };
const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
const encodedProjectPath = encodeURIComponent(`${owner}/${repo}`);
Expand Down Expand Up @@ -637,7 +670,10 @@ test.serial("Publish a release with an asset link", async (t) => {
})
.reply(200);

const result = await publish({ assets }, { env, cwd, options, nextRelease, logger: t.context.logger });
const result = await publish(
{ assets, publishToCatalog: false },
{ 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]);
Expand All @@ -648,7 +684,7 @@ test.serial("Publish a release with error response", async (t) => {
const owner = "test_user";
const repo = "test_repo";
const env = { GITLAB_TOKEN: "gitlab_token" };
const pluginConfig = {};
const pluginConfig = { publishToCatalog: false };
const nextRelease = { gitHead: "123", gitTag: "v1.0.0", notes: "Test release note body" };
const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
const encodedProjectPath = encodeURIComponent(`${owner}/${repo}`);
Expand All @@ -668,7 +704,7 @@ test.serial("Publish a release with error response", async (t) => {
t.true(gitlab.isDone());
});

test.serial("Publish a release to CI catalog", async (t) => {
test.serial("Publish a release to CI catalog (user-configured)", async (t) => {
const owner = "test_user";
const repo = "test_repo";
const env = { GITLAB_TOKEN: "gitlab_token" };
Expand Down Expand Up @@ -700,6 +736,47 @@ test.serial("Publish a release to CI catalog", async (t) => {
t.true(gitlab.isDone());
});

test.serial("Publish a release to CI catalog (auto-detected)", async (t) => {
const owner = "test_user";
const repo = "test_repo";
const env = { GITLAB_TOKEN: "gitlab_token" };
const pluginConfig = { publishToCatalog: undefined };
const nextRelease = { gitHead: "123", gitTag: "v1.0.0", notes: "Test release note body" };
const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
const projectPath = `${owner}/${repo}`;
const encodedProjectPath = encodeURIComponent(projectPath);
const gitlab = authenticate(env)
.post(`/projects/${encodedProjectPath}/releases`, {
tag_name: nextRelease.gitTag,
description: nextRelease.notes,
assets: {
links: [],
},
})
.reply(200);

const gitlabGraphQl = nock("https://gitlab.com", { reqheaders: { "Private-Token": "gitlab_token" } })
.post("/graphql", {
query:
'\n query {\n project(fullPath: "test_user/test_repo") {\n isCatalogResource\n }\n }\n ',
})
.reply(200, { data: { project: { isCatalogResource: true } } });

const execa = (await td.replaceEsm("execa")).execa;
td.when(
execa("glab", ["repo", "publish", "catalog", nextRelease.gitTag], {
cwd: undefined,
timeout: 30000,
env: { GITLAB_TOKEN: env.GITLAB_TOKEN },
})
).thenResolve();
const publishWithMockExeca = (await import("../lib/publish.js")).default;
await publishWithMockExeca(pluginConfig, { env, options, nextRelease, logger: t.context.logger });
t.true(gitlab.isDone());
t.true(gitlabGraphQl.isDone());
t.deepEqual(t.context.log.args[1], ["Published tag %s to the CI catalog", nextRelease.gitTag]);
});

test.serial("Publish a release to CI catalog with error", async (t) => {
const owner = "test_user";
const repo = "test_repo";
Expand Down
Loading
Loading