UNPKG

changesets-gitlab

Version:

[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/un-ts/changesets-gitlab/ci.yml?branch=main)](https://github.com/un-ts/changesets-gitlab/actions/workflows/ci.yml?query=branch%3Amain) [![CodeRabbit Pull Request Revie

1,177 lines (1,141 loc) 40.3 kB
'use strict'; var rest = require('@gitbeaker/rest'); var globalAgent = require('global-agent'); var core = require('@actions/core'); var dotenv = require('dotenv'); var errors = require('@changesets/errors'); var humanId = require('human-id'); var markdownTable = require('markdown-table'); var fs$1 = require('node:fs/promises'); var path = require('node:path'); var assembleReleasePlan = require('@changesets/assemble-release-plan'); var config = require('@changesets/config'); var parseChangeset = require('@changesets/parse'); var micromatch = require('micromatch'); var yaml = require('yaml'); var node_child_process = require('node:child_process'); var fs = require('node:fs'); var node_module = require('node:module'); var exec = require('@actions/exec'); var getPackages = require('@manypkg/get-packages'); var mdastUtilToString = require('mdast-util-to-string'); var remarkParse = require('remark-parse'); var remarkStringify = require('remark-stringify'); var unified = require('unified'); var node_url = require('node:url'); var pre = require('@changesets/pre'); var readChangesets = require('@changesets/read'); var pLimit = require('p-limit'); var resolveFrom = require('resolve-from'); var semver = require('semver'); var __defProp$2 = Object.defineProperty; var __defProps$1 = Object.defineProperties; var __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols$2 = Object.getOwnPropertySymbols; var __hasOwnProp$2 = Object.prototype.hasOwnProperty; var __propIsEnum$2 = Object.prototype.propertyIsEnumerable; var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$2 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$2.call(b, prop)) __defNormalProp$2(a, prop, b[prop]); if (__getOwnPropSymbols$2) for (var prop of __getOwnPropSymbols$2(b)) { if (__propIsEnum$2.call(b, prop)) __defNormalProp$2(a, prop, b[prop]); } return a; }; var __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b)); var _a$1, _b, _c; dotenv.config(); let isGitlabTokenValidated = false; const env = __spreadProps$1(__spreadValues$2({}, process.env), { CI_MERGE_REQUEST_IID: +process.env.CI_MERGE_REQUEST_IID, GITLAB_HOST: (_b = (_a$1 = process.env.GITLAB_HOST) != null ? _a$1 : process.env.CI_SERVER_URL) != null ? _b : "https://gitlab.com", GITLAB_CI_USER_EMAIL: process.env.GITLAB_CI_USER_EMAIL || "gitlab[bot]@users.noreply.gitlab.com", GITLAB_COMMENT_TYPE: (_c = process.env.GITLAB_COMMENT_TYPE) != null ? _c : "discussion", // only check for the token if we are explicitly using it get GITLAB_TOKEN() { if (!isGitlabTokenValidated) { isGitlabTokenValidated = true; if (!process.env.GITLAB_TOKEN) { core.setFailed("Please add the `GITLAB_TOKEN` to the changesets action"); } } return process.env.GITLAB_TOKEN; } }); const PROXY_PROPS = ["http_proxy", "https_proxy", "no_proxy"]; let bootstrapped = false; const createApi = (gitlabToken) => { if (!bootstrapped) { bootstrapped = true; globalAgent.bootstrap(); for (const prop of PROXY_PROPS) { const uProp = prop.toUpperCase(); const value = process.env[uProp] || process.env[prop]; if (value) { GLOBAL_AGENT[uProp] = value; } } } const token = gitlabToken || env.GITLAB_TOKEN; const host = env.GITLAB_HOST; if (env.GITLAB_TOKEN_TYPE === "oauth") { return new rest.Gitlab({ host, oauthToken: token }); } return new rest.Gitlab({ host, token }); }; const projectId = process.env.CI_PROJECT_ID; const ref = process.env.CI_COMMIT_REF_NAME; var __defProp$1 = Object.defineProperty; var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols; var __hasOwnProp$1 = Object.prototype.hasOwnProperty; var __propIsEnum$1 = Object.prototype.propertyIsEnumerable; var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$1 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$1.call(b, prop)) __defNormalProp$1(a, prop, b[prop]); if (__getOwnPropSymbols$1) for (var prop of __getOwnPropSymbols$1(b)) { if (__propIsEnum$1.call(b, prop)) __defNormalProp$1(a, prop, b[prop]); } return a; }; var __async$6 = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; const import_meta = {}; const BumpLevels = { dep: 0, patch: 1, minor: 2, major: 3 }; function getVersionsByDirectory(cwd) { return __async$6(this, null, function* () { const { packages } = yield getPackages.getPackages(cwd); return new Map(packages.map((x) => [x.dir, x.packageJson.version])); }); } function getChangedPackages$1(cwd, previousVersions) { return __async$6(this, null, function* () { const { packages } = yield getPackages.getPackages(cwd); const changedPackages = /* @__PURE__ */ new Set(); for (const pkg of packages) { const previousVersion = previousVersions.get(pkg.dir); if (previousVersion !== pkg.packageJson.version) { changedPackages.add(pkg); } } return [...changedPackages]; }); } function getChangelogEntry(changelog, version) { const ast = unified.unified().use(remarkParse).parse(changelog); let highestLevel = BumpLevels.dep; const nodes = ast.children; let headingStartInfo; let endIndex; for (const [i, node] of nodes.entries()) { if (node.type === "heading") { const stringified = mdastUtilToString.toString(node); const match = /(major|minor|patch)/.exec(stringified.toLowerCase()); if (match !== null) { const level = BumpLevels[match[0]]; highestLevel = Math.max(level, highestLevel); } if (headingStartInfo === void 0 && stringified === version) { headingStartInfo = { index: i, depth: node.depth }; continue; } if (endIndex === void 0 && headingStartInfo !== void 0 && headingStartInfo.depth === node.depth) { endIndex = i; break; } } } if (headingStartInfo) { ast.children = ast.children.slice( headingStartInfo.index + 1, // eslint-disable-next-line sonarjs/argument-type endIndex ); } return { content: unified.unified().use(remarkStringify).stringify(ast), highestLevel }; } function execWithOutput(command, args, options) { return __async$6(this, null, function* () { let myOutput = ""; let myError = ""; return { code: yield exec.exec(command, args, __spreadValues$1({ listeners: { stdout: (data) => { myOutput += data.toString(); }, stderr: (data) => { myError += data.toString(); } } }, options)), stdout: myOutput, stderr: myError }; }); } function sortTheThings(a, b) { if (a.private === b.private) { return b.highestLevel - a.highestLevel; } if (a.private) { return 1; } return -1; } const identify = (_) => !!_; function getAllFiles(_0) { return __async$6(this, arguments, function* (dir, base = dir) { const direntList = yield fs.promises.readdir(dir, { withFileTypes: true }); const files = yield Promise.all( // eslint-disable-next-line sonarjs/function-return-type direntList.map((dirent) => { const res = path.resolve(dir, dirent.name); return dirent.isDirectory() ? getAllFiles(res, base) : [path.relative(base, res)]; }) ); return files.flat(); }); } const execSync = (command) => ( // eslint-disable-next-line sonarjs/os-command node_child_process.execSync(command, { stdio: "inherit" }) ); const getOptionalInput = (name) => core.getInput(name) || void 0; const getUsername = (api) => { var _a; return (_a = env.GITLAB_CI_USER_NAME) != null ? _a : api.Users.showCurrentUser().then((currentUser) => currentUser.username); }; const cjsRequire = typeof require === "undefined" ? node_module.createRequire(import_meta.url) : require; const FALSY_VALUES = /* @__PURE__ */ new Set(["false", "0"]); const TRUTHY_VALUES = /* @__PURE__ */ new Set(["true", "1"]); const GITLAB_MAX_TAGS = 4; const HTTP_STATUS_NOT_FOUND = 404; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __async$5 = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; function fetchFile(path) { return fs$1.readFile(path, "utf8"); } const getChangedPackages = (_0) => __async$5(void 0, [_0], function* ({ changedFiles: changedFilesPromise }) { var _a; let hasErrored = false; function fetchJsonFile(path) { return __async$5(this, null, function* () { try { const x = yield fetchFile(path); return JSON.parse(x); } catch (err) { hasErrored = true; console.error(err); return {}; } }); } function fetchTextFile(path) { return __async$5(this, null, function* () { try { return yield fetchFile(path); } catch (err) { hasErrored = true; console.error(err); return ""; } }); } function getPackage(pkgPath) { return __async$5(this, null, function* () { const jsonContent = yield fetchJsonFile( pkgPath + "/package.json" ); return { packageJson: jsonContent, dir: pkgPath }; }); } const rootPackageJsonContentsPromise = fetchJsonFile("package.json"); const configPromise = fetchJsonFile(".changeset/config.json"); const tree = yield getAllFiles(process.cwd()); let preStatePromise; const changesetPromises = []; const potentialWorkspaceDirectories = []; let isPnpm = false; const changedFiles = yield changedFilesPromise; for (const item of tree) { if (item.endsWith("/package.json")) { const dirPath = path.dirname(item); potentialWorkspaceDirectories.push(dirPath); } else if (item === "pnpm-workspace.yaml") { isPnpm = true; } else if (item === ".changeset/pre.json") { preStatePromise = fetchJsonFile(".changeset/pre.json"); } else if (item !== ".changeset/README.md" && item.startsWith(".changeset") && item.endsWith(".md") && changedFiles.includes(item)) { const res = /\.changeset\/([^.]+)\.md/.exec(item); if (!res) { throw new Error("could not get name from changeset filename"); } const id = res[1]; changesetPromises.push( fetchTextFile(item).then((text) => __spreadProps(__spreadValues({}, parseChangeset(text)), { id })) ); } } let tool; if (isPnpm) { tool = { tool: "pnpm", globs: yaml.parse(yield fetchTextFile("pnpm-workspace.yaml")).packages }; } else { const rootPackageJsonContent2 = yield rootPackageJsonContentsPromise; if (rootPackageJsonContent2.workspaces) { tool = { tool: "yarn", globs: Array.isArray(rootPackageJsonContent2.workspaces) ? rootPackageJsonContent2.workspaces : rootPackageJsonContent2.workspaces.packages }; } else if ((_a = rootPackageJsonContent2.bolt) == null ? void 0 : _a.workspaces) { tool = { tool: "bolt", globs: rootPackageJsonContent2.bolt.workspaces }; } } const rootPackageJsonContent = yield rootPackageJsonContentsPromise; const packages = { root: { dir: "/", packageJson: rootPackageJsonContent }, tool: tool ? tool.tool : "root", packages: [] }; if (tool) { if (!Array.isArray(tool.globs) || !tool.globs.every((x) => typeof x === "string")) { throw new Error("globs are not valid: " + JSON.stringify(tool.globs)); } const matches = micromatch(potentialWorkspaceDirectories, tool.globs); packages.packages = yield Promise.all(matches.map((dir) => getPackage(dir))); } else { packages.packages.push(packages.root); } if (hasErrored) { throw new Error("an error occurred when fetching files"); } const config$1 = yield configPromise.then( (rawConfig) => config.parse(rawConfig, packages) ); const releasePlan = assembleReleasePlan( yield Promise.all(changesetPromises), packages, config$1, yield preStatePromise ); return { changedPackages: (packages.tool === "root" ? packages.packages : packages.packages.filter( (pkg) => changedFiles.some((changedFile) => changedFile.includes(pkg.dir)) )).filter( (pkg) => pkg.packageJson.private !== true && !config$1.ignore.includes(pkg.packageJson.name) ).map((x) => x.packageJson.name), releasePlan }; }); var __async$4 = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; var _a; const generatedByBotNote = "Generated By Changesets GitLab Bot"; const getReleasePlanMessage = (releasePlan) => { if (!releasePlan) { return ""; } const publishableReleases = releasePlan.releases.filter( (x) => x.type !== "none" ); const table = markdownTable.markdownTable([ ["Name", "Type"], ...publishableReleases.map((x) => [ x.name, { major: "Major", minor: "Minor", patch: "Patch" }[x.type] ]) ]); return `<details><summary>This MR includes ${releasePlan.changesets.length > 0 ? `changesets to release ${// eslint-disable-next-line sonarjs/no-nested-conditional publishableReleases.length === 1 ? "1 package" : `${publishableReleases.length} packages`}` : "no changesets"}</summary> ${publishableReleases.length > 0 ? table : "When changesets are added to this MR, you'll see the packages that this MR includes changesets for and the associated semver types"} </details>`; }; const customLinks = (_a = env.GITLAB_COMMENT_CUSTOM_LINKS) == null ? void 0 : _a.trim(); const ADD_CHANGESET_URL_PLACEHOLDER_REGEXP = /\{\{\s*addChangesetUrl\s*\}\}/; const getAbsentMessage = (commitSha, addChangesetUrl, newChangesetTemplateFallback, releasePlan) => `### \u26A0\uFE0F No Changeset found Latest commit: ${commitSha} Merging this MR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.** ${getReleasePlanMessage(releasePlan)} ${customLinks ? customLinks.replace(ADD_CHANGESET_URL_PLACEHOLDER_REGEXP, addChangesetUrl) : `[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md). [Click here if you're a maintainer who wants to add a changeset to this MR](${addChangesetUrl})`} ${newChangesetTemplateFallback} __${generatedByBotNote}__ `; const getApproveMessage = (commitSha, addChangesetUrl, newChangesetTemplateFallback, releasePlan) => `### \u{1F98B} Changeset detected Latest commit: ${commitSha} **The changes in this MR will be included in the next version bump.** ${getReleasePlanMessage(releasePlan)} ${customLinks ? customLinks.replace(ADD_CHANGESET_URL_PLACEHOLDER_REGEXP, addChangesetUrl) : `Not sure what this means? [Click here to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md). [Click here if you're a maintainer who wants to add another changeset to this MR](${addChangesetUrl})`} ${newChangesetTemplateFallback} __${generatedByBotNote}__ `; const getNewChangesetTemplate = (changedPackages, title) => `--- ${changedPackages.map((x) => `"${x}": patch`).join("\n")} --- ${title} `; const isMrNote = (discussionOrNote) => "noteable_type" in discussionOrNote && discussionOrNote.noteable_type === "MergeRequest"; const RANDOM_BOT_NAME_PATTERN = /^((?:project|group)_\d+_bot\w*)_[\da-z]+$/i; const isChangesetBotNote = (note, username, random) => { var _a2; return (note.author.username === username || random && ((_a2 = RANDOM_BOT_NAME_PATTERN.exec(note.author.username)) == null ? void 0 : _a2[1]) === username) && // We need to ensure the note is generated by us, but we don't have an app bot like GitHub // @see https://github.com/apps/changeset-bot note.body.includes(generatedByBotNote); }; function getNoteInfo(api, mrIid, commentType, random) { return __async$4(this, null, function* () { const discussionOrNotes = yield commentType === "discussion" ? api.MergeRequestDiscussions.all(projectId, mrIid) : api.MergeRequestNotes.all(projectId, +mrIid); const username = yield getUsername(api); for (const discussionOrNote of discussionOrNotes) { if (isMrNote(discussionOrNote)) { if (isChangesetBotNote(discussionOrNote, username, random)) { return { noteId: discussionOrNote.id }; } continue; } if (!discussionOrNote.notes) { continue; } const changesetBotNote = discussionOrNote.notes.find( (note) => isChangesetBotNote(note, username) ); if (changesetBotNote) { return { discussionId: discussionOrNote.id, noteId: changesetBotNote.id }; } } return random ? null : getNoteInfo(api, mrIid, commentType, true); }); } const hasChangesetBeenAdded = (changedFilesPromise) => __async$4(void 0, null, function* () { const changedFiles = yield changedFilesPromise; return changedFiles.some((file) => { return file.new_file && /^\.changeset\/.+\.md$/.test(file.new_path) && file.new_path !== ".changeset/README.md"; }); }); const comment = () => __async$4(void 0, null, function* () { const mrBranch = env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME; if (!mrBranch) { console.warn("[changesets-gitlab:comment] It should only be used on MR"); return; } const { CI_MERGE_REQUEST_IID: mrIid, GITLAB_COMMENT_TYPE: commentType, GITLAB_ADD_CHANGESET_MESSAGE: commitMessage } = env; if (mrBranch.startsWith("changeset-release")) { return; } const api = createApi(); let errFromFetchingChangedFiles = ""; try { const latestCommitSha = env.CI_MERGE_REQUEST_SOURCE_BRANCH_SHA; const changedFilesPromise = api.MergeRequests.allDiffs( projectId, mrIid ).catch((err) => __async$4(void 0, null, function* () { var _a2; if (!(err instanceof rest.GitbeakerRequestError) || ((_a2 = err.cause) == null ? void 0 : _a2.response.status) !== HTTP_STATUS_NOT_FOUND) { throw err; } const { changes } = yield api.MergeRequests.showChanges( projectId, mrIid ); return changes; })); const [noteInfo, hasChangeset, { changedPackages, releasePlan }] = yield Promise.all([ getNoteInfo(api, mrIid, commentType), hasChangesetBeenAdded(changedFilesPromise), getChangedPackages({ changedFiles: changedFilesPromise.then( (changedFiles) => changedFiles.map(({ new_path }) => new_path) ), api }).catch((err) => { if (err instanceof errors.ValidationError) { errFromFetchingChangedFiles = `<details><summary>\u{1F4A5} An error occurred when fetching the changed packages and changesets in this MR</summary> \`\`\` ${err.message} \`\`\` </details> `; } else { console.error(err); } return { changedPackages: ["@fake-scope/fake-pkg"], releasePlan: null }; }) ]); const newChangesetFileName = `.changeset/${humanId.humanId({ separator: "-", capitalize: false })}.md`; const newChangesetTemplate = getNewChangesetTemplate( changedPackages, env.CI_MERGE_REQUEST_TITLE ); const addChangesetUrl = `${env.CI_MERGE_REQUEST_PROJECT_URL}/-/new/${mrBranch}?file_name=${newChangesetFileName}&file=${encodeURIComponent(newChangesetTemplate)}${commitMessage ? "&commit_message=" + encodeURIComponent(commitMessage) : ""}`; const newChangesetTemplateFallback = ` If the above link doesn't fill the changeset template file name and content which is [a known regression on GitLab >= 16.11](https://gitlab.com/gitlab-org/gitlab/-/issues/532221), you can copy and paste the following template into ${newChangesetFileName} instead: \`\`\`yaml ${newChangesetTemplate} \`\`\` `.trim(); const prComment = (hasChangeset ? getApproveMessage( latestCommitSha, addChangesetUrl, newChangesetTemplateFallback, releasePlan ) : getAbsentMessage( latestCommitSha, addChangesetUrl, newChangesetTemplateFallback, releasePlan )) + errFromFetchingChangedFiles; switch (commentType) { case "discussion": { if (noteInfo) { if (hasChangeset && TRUTHY_VALUES.has(env.GITLAB_COMMENT_DISCUSSION_AUTO_RESOLVE || "1")) { yield api.MergeRequestDiscussions.resolve( projectId, mrIid, noteInfo.discussionId, true ); } return api.MergeRequestDiscussions.editNote( projectId, mrIid, noteInfo.discussionId, noteInfo.noteId, { body: prComment } ); } return api.MergeRequestDiscussions.create( projectId, mrIid, prComment ); } case "note": { if (noteInfo) { return api.MergeRequestNotes.edit( projectId, mrIid, noteInfo.noteId, { body: prComment } ); } return api.MergeRequestNotes.create(projectId, mrIid, prComment); } default: { throw new Error( `Invalid comment type "${commentType}", should be "discussion" or "note"` ); } } } catch (err) { if (err instanceof rest.GitbeakerRequestError && err.cause) { const { description, request, response } = err.cause; console.error(description); try { console.error("request:", yield request.text()); } catch (e) { console.error("The error's request could not be used as plain text"); } try { console.error("response:", yield response.text()); } catch (e) { console.error("The error's response could not be used as plain text"); } } throw err; } }); var __async$3 = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; const setupUser = () => __async$3(void 0, null, function* () { yield exec.exec("git", [ "config", "user.name", env.GITLAB_CI_USER_NAME || env.GITLAB_USER_NAME ]); yield exec.exec("git", ["config", "user.email", env.GITLAB_CI_USER_EMAIL]); }); const push = (_0, ..._1) => __async$3(void 0, [_0, ..._1], function* (branch, { force } = {}) { yield exec.exec( "git", ["push", "origin", `HEAD:${branch}`, force && "--force"].filter(identify) ); }); const pushTags = () => __async$3(void 0, null, function* () { yield exec.exec("git", ["push", "origin", "--tags"]); }); const pushTag = (tag) => __async$3(void 0, null, function* () { yield exec.exec("git", ["push", "origin", tag]); }); const switchToMaybeExistingBranch = (branch) => __async$3(void 0, null, function* () { const { stderr } = yield execWithOutput("git", ["checkout", branch], { ignoreReturnCode: true }); const isCreatingBranch = !stderr.includes(`Switched to branch '${branch}'`) && // it could be a detached HEAD !stderr.includes(`Switched to a new branch '${branch}'`); if (isCreatingBranch) { yield exec.exec("git", ["checkout", "-b", branch]); } }); const reset = (pathSpec, mode = "hard") => __async$3(void 0, null, function* () { yield exec.exec("git", ["reset", `--${mode}`, pathSpec]); }); const commitAll = (message) => __async$3(void 0, null, function* () { yield exec.exec("git", ["add", "-A", "."]); yield exec.exec("git", ["commit", "-m", message]); }); const checkIfClean = () => __async$3(void 0, null, function* () { const { stdout } = yield execWithOutput("git", ["status", "--porcelain"]); return stdout.length === 0; }); var __async$2 = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; function readChangesetState() { return __async$2(this, arguments, function* (cwd = process.cwd()) { const preState = yield pre.readPreState(cwd); const isInPreMode = preState !== void 0 && preState.mode === "pre"; let changesets = yield readChangesets(cwd); if (isInPreMode) { const changesetsToFilter = new Set(preState.changesets); changesets = changesets.filter((x) => !changesetsToFilter.has(x.id)); } return { preState: isInPreMode ? preState : void 0, changesets }; }); } var __async$1 = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; const limit = pLimit(2 * 3); const createRelease = (_0, _1) => __async$1(void 0, [_0, _1], function* (api, { pkg, tagName }) { try { const changelogFileName = path.join(pkg.dir, "CHANGELOG.md"); const changelog = yield fs$1.readFile(changelogFileName, "utf8"); const changelogEntry = getChangelogEntry(changelog, pkg.packageJson.version); if (!changelogEntry) { throw new Error( `Could not find changelog entry for ${pkg.packageJson.name}@${pkg.packageJson.version}` ); } yield api.ProjectReleases.create(projectId, { name: tagName, tag_name: tagName, description: changelogEntry.content, pre_release: pkg.packageJson.version.includes("-") }); } catch (err) { if (err.code !== "ENOENT") { throw err; } } }); function runPublish(_0) { return __async$1(this, arguments, function* ({ script, gitlabToken, createGitlabReleases = true, cwd = process.cwd() }) { const api = createApi(gitlabToken); const [publishCommand, ...publishArgs] = script.split(/\s+/); const changesetPublishOutput = yield execWithOutput( publishCommand, publishArgs, { cwd } ); const { packages, tool } = yield getPackages.getPackages(cwd); const pushAllTags = packages.length <= GITLAB_MAX_TAGS || (yield api.FeatureFlags.show( projectId, "git_push_create_all_pipelines" ).then(({ active }) => active).catch(() => false)); if (pushAllTags) { yield pushTags(); } const releasedPackages = []; if (tool === "root") { if (packages.length !== 1) { throw new Error( `No package found.This is probably a bug in the action, please open an issue` ); } const pkg = packages[0]; const newTagRegex = /New tag:/; for (const line of changesetPublishOutput.stdout.split("\n")) { const match = newTagRegex.exec(line); if (match) { releasedPackages.push(pkg); const tagName = `v${pkg.packageJson.version}`; if (createGitlabReleases) { yield createRelease(api, { pkg, tagName }); } break; } } } else { const newTagRegex = /New tag:\s+(@[^/]+\/[^@]+|[^/]+)@(\S+)/; const packagesByName = new Map(packages.map((x) => [x.packageJson.name, x])); for (const line of changesetPublishOutput.stdout.split("\n")) { const match = newTagRegex.exec(line); if (match === null) { continue; } const pkgName = match[1]; const pkg = packagesByName.get(pkgName); if (pkg === void 0) { throw new Error( `Package "${pkgName}" not found.This is probably a bug in the action, please open an issue` ); } releasedPackages.push(pkg); } if (!pushAllTags) { yield Promise.all( releasedPackages.map( (pkg) => pushTag( `${pkg.packageJson.name}@${pkg.packageJson.version}` ) ) ); } if (createGitlabReleases) { yield Promise.all( releasedPackages.map( (pkg) => limit( () => createRelease(api, { pkg, tagName: `${pkg.packageJson.name}@${pkg.packageJson.version}` }) ) ) ); } } if (releasedPackages.length > 0) { return { published: true, publishedPackages: releasedPackages.map((pkg) => ({ name: pkg.packageJson.name, version: pkg.packageJson.version })) }; } return { published: false }; }); } const requireChangesetsCliPkgJson = (cwd) => { try { return cjsRequire(resolveFrom(cwd, "@changesets/cli/package.json")); } catch (err) { if ((err == null ? void 0 : err.code) === "MODULE_NOT_FOUND") { throw new Error( `Have you forgotten to install \`@changesets/cli\` in "${cwd}"?` ); } throw err; } }; function runVersion(_0) { return __async$1(this, arguments, function* ({ script, gitlabToken, cwd = process.cwd(), mrTitle = "Version Packages", mrTargetBranch = ref, commitMessage = "Version Packages", removeSourceBranch = false, hasPublishScript = false }) { var _a; const currentBranch = ref; const versionBranch = `changeset-release/${currentBranch}`; const api = createApi(gitlabToken); const { preState } = yield readChangesetState(cwd); yield switchToMaybeExistingBranch(versionBranch); yield exec.exec("git", ["fetch", "origin", currentBranch]); yield reset(`origin/${currentBranch}`); const labels = (_a = getOptionalInput("labels")) == null ? void 0 : _a.split(",").map((x) => x.trim()); const versionsByDirectory = yield getVersionsByDirectory(cwd); if (script) { const [versionCommand, ...versionArgs] = script.split(/\s+/); yield exec.exec(versionCommand, versionArgs, { cwd }); } else { const changesetsCliPkgJson = requireChangesetsCliPkgJson(cwd); const cmd = semver.lt(changesetsCliPkgJson.version, "2.0.0") ? "bump" : "version"; yield exec.exec("node", [resolveFrom(cwd, "@changesets/cli/bin.js"), cmd], { cwd }); } const changedPackages = yield getChangedPackages$1(cwd, versionsByDirectory); const mrBodyPromise = (() => __async$1(this, null, function* () { return `This MR was opened by the [changesets-gitlab](https://github.com/un-ts/changesets-gitlab) GitLab CI script. When you're ready to do a release, you can merge this and ${hasPublishScript ? "the packages will be published to npm automatically" : "publish to npm yourself or [setup this action to publish automatically](https://github.com/un-ts/changesets-gitlab#with-publishing)"}. If you're not ready to do a release yet, that's fine, whenever you add more changesets to ${currentBranch}, this MR will be updated. ${preState ? ` \u26A0\uFE0F\u26A0\uFE0F\u26A0\uFE0F\u26A0\uFE0F\u26A0\uFE0F\u26A0\uFE0F \`${currentBranch}\` is currently in **pre mode** so this branch has prereleases rather than normal releases. If you want to exit prereleases, run \`changeset pre exit\` on \`${currentBranch}\`. \u26A0\uFE0F\u26A0\uFE0F\u26A0\uFE0F\u26A0\uFE0F\u26A0\uFE0F\u26A0\uFE0F ` : ""} # Releases ` + (yield Promise.all( changedPackages.map((pkg) => __async$1(this, null, function* () { const changelogContents = yield fs$1.readFile( path.join(pkg.dir, "CHANGELOG.md"), "utf8" ); const entry = getChangelogEntry( changelogContents, pkg.packageJson.version ); return { highestLevel: entry.highestLevel, private: !!pkg.packageJson.private, content: `## ${pkg.packageJson.name}@${pkg.packageJson.version} ` + entry.content }; })) )).filter(Boolean).sort(sortTheThings).map((x) => x.content).join("\n "); }))(); const finalMrTitle = `${mrTitle}${preState ? ` (${preState.tag})` : ""}`; if (!(yield checkIfClean())) { const finalCommitMessage = `${commitMessage}${preState ? ` (${preState.tag})` : ""}`; yield commitAll(finalCommitMessage); } yield push(versionBranch, { force: true }); const searchResult = yield api.MergeRequests.all({ projectId: projectId, state: "opened", sourceBranch: versionBranch, target_branch: mrTargetBranch, maxPages: 1, perPage: 1 }); console.log(JSON.stringify(searchResult, null, 2)); if (searchResult.length === 0) { console.log( `creating merge request from ${versionBranch} to ${mrTargetBranch}.` ); yield api.MergeRequests.create( projectId, versionBranch, mrTargetBranch, finalMrTitle, { description: yield mrBodyPromise, removeSourceBranch, labels } ); } else { console.log(`updating found merge request !${searchResult[0].iid}`); yield api.MergeRequests.edit(projectId, searchResult[0].iid, { title: finalMrTitle, description: yield mrBodyPromise, removeSourceBranch, labels }); } }); } var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; const main = (..._0) => __async(void 0, [..._0], function* ({ published, onlyChangesets } = {}) { const { GITLAB_TOKEN, NPM_TOKEN } = env; core.setOutput("published", false); core.setOutput("publishedPackages", []); if (env.CI) { console.log("setting git user"); yield setupUser(); const url = new node_url.URL(env.GITLAB_HOST); console.log("setting GitLab credentials"); const username = yield getUsername(createApi()); yield exec.exec( "git", [ "remote", "set-url", "origin", `${url.protocol}//${encodeURIComponent(username)}:${GITLAB_TOKEN}@${url.host}${url.pathname.replace(/\/$/, "")}/${env.CI_PROJECT_PATH}.git` ], { silent: !TRUTHY_VALUES.has(env.DEBUG_GITLAB_CREDENTIAL) } ); } const { changesets } = yield readChangesetState(); const publishScript = core.getInput("publish"); const hasChangesets = changesets.length > 0; const hasPublishScript = !!publishScript; switch (true) { case (!hasChangesets && !hasPublishScript): { console.log("No changesets found"); return; } case (!hasChangesets && hasPublishScript): { console.log( "No changesets found, attempting to publish any unpublished packages to npm" ); const npmrcPath = `${env.HOME}/.npmrc`; if (fs.existsSync(npmrcPath)) { console.log("Found existing .npmrc file"); } else if (NPM_TOKEN) { console.log("No .npmrc file found, creating one"); yield fs.promises.writeFile( npmrcPath, `//registry.npmjs.org/:_authToken=${NPM_TOKEN}` ); } else { core.setFailed( "No `.npmrc` found nor `NPM_TOKEN` provided, unable to publish packages" ); return; } const result = yield runPublish({ script: publishScript, gitlabToken: GITLAB_TOKEN, createGitlabReleases: !FALSY_VALUES.has( core.getInput("create_gitlab_releases") ) }); if (result.published) { core.setOutput("published", true); core.setOutput("publishedPackages", result.publishedPackages); core.exportVariable("PUBLISHED", true); core.exportVariable("PUBLISHED_PACKAGES", result.publishedPackages); if (published) { execSync(published); } } return; } case hasChangesets: { yield runVersion({ script: getOptionalInput("version"), gitlabToken: GITLAB_TOKEN, mrTitle: getOptionalInput("title"), mrTargetBranch: getOptionalInput("target_branch"), commitMessage: getOptionalInput("commit"), removeSourceBranch: core.getInput("remove_source_branch") === "true", hasPublishScript }); if (onlyChangesets) { execSync(onlyChangesets); } } } }); exports.comment = comment; exports.createApi = createApi; exports.createRelease = createRelease; exports.env = env; exports.main = main; exports.projectId = projectId; exports.ref = ref; exports.runPublish = runPublish; exports.runVersion = runVersion;