diff --git a/README.md b/README.md index 36914354..52c34296 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ If you need to bypass the proxy for some hosts, configure the `NO_PROXY` environ | `failCommentCondition` | Use this as condition, when to comment on or create an issues in case of failures. See [failCommentCondition](#failCommentCondition). | - | | `labels` | The [labels](https://docs.gitlab.com/ee/user/project/labels.html#labels) to add to the issue created when a release fails. Set to `false` to not add any label. Labels should be comma-separated as described in the [official docs](https://docs.gitlab.com/ee/api/issues.html#new-issue), e.g. `"semantic-release,bot"`. | `semantic-release` | | `assignee` | The [assignee](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#assignee) to add to the issue created when a release fails. | - | +| `retryLimit` | The maximum number of retries for failing HTTP requests. | `3` | #### assets diff --git a/lib/fail.js b/lib/fail.js index 018f4519..31d42bbc 100644 --- a/lib/fail.js +++ b/lib/fail.js @@ -14,11 +14,14 @@ export default async (pluginConfig, context) => { errors, logger, } = context; - const { gitlabToken, gitlabUrl, gitlabApiUrl, failComment, failTitle, failCommentCondition, labels, assignee } = + const { gitlabToken, gitlabUrl, gitlabApiUrl, failComment, failTitle, failCommentCondition, labels, assignee, retryLimit } = resolveConfig(pluginConfig, context); const repoId = getRepoId(context, gitlabUrl, repositoryUrl); const encodedRepoId = encodeURIComponent(repoId); - const apiOptions = { headers: { "PRIVATE-TOKEN": gitlabToken } }; + const apiOptions = { + headers: { "PRIVATE-TOKEN": gitlabToken }, + retry: { limit: retryLimit } + }; if (failComment === false || failTitle === false) { logger.log("Skip issue creation."); diff --git a/lib/publish.js b/lib/publish.js index 12b60adc..a706925a 100644 --- a/lib/publish.js +++ b/lib/publish.js @@ -22,7 +22,7 @@ export default async (pluginConfig, context) => { nextRelease: { gitTag, gitHead, notes, version }, logger, } = context; - const { gitlabToken, gitlabUrl, gitlabApiUrl, assets, milestones, proxy } = resolveConfig(pluginConfig, context); + const { gitlabToken, gitlabUrl, gitlabApiUrl, assets, milestones, proxy, retryLimit } = resolveConfig(pluginConfig, context); const assetsList = []; const repoId = getRepoId(context, gitlabUrl, repositoryUrl); const encodedRepoId = encodeURIComponent(repoId); @@ -46,6 +46,7 @@ export default async (pluginConfig, context) => { }, ], }, + retry: { limit: retryLimit } }; debug("repoId: %o", repoId); diff --git a/lib/resolve-config.js b/lib/resolve-config.js index 295fce3a..69bc829c 100644 --- a/lib/resolve-config.js +++ b/lib/resolve-config.js @@ -15,6 +15,7 @@ export default ( failCommentCondition, labels, assignee, + retryLimit, }, { envCi: { service } = {}, @@ -34,6 +35,7 @@ export default ( }, } ) => { + const DEFAULT_RETRY_LIMIT = 3 const userGitlabApiPathPrefix = isNil(gitlabApiPathPrefix) ? isNil(GL_PREFIX) ? GITLAB_PREFIX @@ -64,6 +66,7 @@ export default ( failCommentCondition, labels: isNil(labels) ? "semantic-release" : labels === false ? false : labels, assignee, + retryLimit: retryLimit ?? DEFAULT_RETRY_LIMIT }; }; diff --git a/lib/success.js b/lib/success.js index 910f0201..dad827ca 100644 --- a/lib/success.js +++ b/lib/success.js @@ -15,13 +15,16 @@ export default async (pluginConfig, context) => { commits, releases, } = context; - const { gitlabToken, gitlabUrl, gitlabApiUrl, successComment, successCommentCondition, proxy } = resolveConfig( + const { gitlabToken, gitlabUrl, gitlabApiUrl, successComment, successCommentCondition, proxy, retryLimit } = resolveConfig( pluginConfig, context ); const repoId = getRepoId(context, gitlabUrl, repositoryUrl); const encodedRepoId = encodeURIComponent(repoId); - const apiOptions = { headers: { "PRIVATE-TOKEN": gitlabToken } }; + const apiOptions = { + headers: { "PRIVATE-TOKEN": gitlabToken }, + retry: { limit: retryLimit } + }; if (successComment === false) { logger.log("Skip commenting on issues and pull requests."); diff --git a/test/resolve-config.test.js b/test/resolve-config.test.js index 91efa766..68c3757f 100644 --- a/test/resolve-config.test.js +++ b/test/resolve-config.test.js @@ -17,6 +17,7 @@ const defaultOptions = { labels: "semantic-release", assignee: undefined, proxy: {}, + retryLimit: 3 }; test("Returns user config", (t) => { @@ -27,10 +28,11 @@ test("Returns user config", (t) => { const postComments = true; const proxy = {}; const labels = false; + const retryLimit = 42 t.deepEqual( resolveConfig( - { gitlabUrl, gitlabApiPathPrefix, assets, postComments, labels }, + { gitlabUrl, gitlabApiPathPrefix, assets, postComments, labels, retryLimit }, { env: { GITLAB_TOKEN: gitlabToken } } ), { @@ -40,6 +42,7 @@ test("Returns user config", (t) => { gitlabApiUrl: urlJoin(gitlabUrl, gitlabApiPathPrefix), assets, labels: false, + retryLimit } ); diff --git a/test/success.test.js b/test/success.test.js index 9e6272f3..9efed4c2 100644 --- a/test/success.test.js +++ b/test/success.test.js @@ -305,3 +305,31 @@ test.serial("Does not post comments when successCommentCondition is set to false t.true(gitlab.isDone()); }); + + +test.serial("Retries requests when rate limited", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITLAB_TOKEN: "gitlab_token" }; + const pluginConfig = {}; + const nextRelease = { version: "1.0.0" }; + const releases = [{ name: RELEASE_NAME, url: "https://gitlab.com/test_user/test_repo/-/releases/v1.0.0" }]; + const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` }; + const encodedRepoId = encodeURIComponent(`${owner}/${repo}`); + const commits = [{ hash: "abcdef" }]; + const retryLimit = 3 + const gitlab = authenticate(env) + .get(`/projects/${encodedRepoId}/repository/commits/abcdef/merge_requests`) + .times(retryLimit) + .reply(429) + .get(`/projects/${encodedRepoId}/repository/commits/abcdef/merge_requests`) + .reply(200, [{ project_id: 100, iid: 1, state: "merged" }]) + .get(`/projects/100/merge_requests/1/closes_issues`) + .reply(200, []) + .post(`/projects/100/merge_requests/1/notes`) + .reply(200) + + await success(pluginConfig, { env, options, nextRelease, logger: t.context.logger, commits, releases, retryLimit }); + + t.true(gitlab.isDone()); +});