Skip to content
Closed
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
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,35 @@ Create a [personal access token](https://docs.gitlab.com/ce/user/profile/persona
| `GL_TOKEN` or `GITLAB_TOKEN` | **Required.** The token used to authenticate with GitLab. |
| `GL_URL` or `GITLAB_URL` | The GitLab endpoint. |
| `GL_PREFIX` or `GITLAB_PREFIX` | The GitLab API prefix. |
| `HTTP_PROXY` or `HTTPS_PROXY` | HTTP or HTTPS proxy to use. |

### Options

| Option | Description | Default |
|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `gitlabUrl` | The GitLab endpoint. | `GL_URL` or `GITLAB_URL` environment variable or CI provided environment variables if running on [GitLab CI/CD](https://docs.gitlab.com/ee/ci) or `https://gitlab.com`. |
| `gitlabApiPathPrefix` | The GitLab API prefix. | `GL_PREFIX` or `GITLAB_PREFIX` environment variable or CI provided environment variables if running on [GitLab CI/CD](https://docs.gitlab.com/ee/ci) or `/api/v4`. |
| `proxy` | The proxy to use to access the GitLab API. Set to `false` to disable usage of proxy. See [proxy](#proxy). | `HTTP_PROXY` environment variable. |
| `assets` | An array of files to upload to the release. See [assets](#assets). | - |
| `milestones` | An array of milestone titles to associate to the release. See [GitLab Release API](https://docs.gitlab.com/ee/api/releases/#create-a-release). | - |
#### proxy

Can be `false`, a proxy URL or an `Object` with the following properties:

| Property | Description | Default |
|---------------|----------------------------------------------------------------|--------------------------------------|
| `host` | **Required.** Proxy host to connect to. | - |
| `port` | **Required.** Proxy port to connect to. | File name extracted from the `path`. |
| `secureProxy` | If `true`, then use TLS to connect to the proxy. | `false` |
| `headers` | Additional HTTP headers to be sent on the HTTP CONNECT method. | - |

See [node-https-proxy-agent](https://github.com/TooTallNate/node-https-proxy-agent#new-httpsproxyagentobject-options) and [node-http-proxy-agent](https://github.com/TooTallNate/node-http-proxy-agent) for additional details.

##### proxy examples

`'http://168.63.76.32:3128'`: use the proxy running on host `168.63.76.32` and port `3128` for each GitHub API request.
`{host: '168.63.76.32', port: 3128, headers: {Foo: 'bar'}}`: use the proxy running on host `168.63.76.32` and port `3128` for each GitHub API request, setting the `Foo` header value to `bar`.


#### assets

Expand Down
8 changes: 8 additions & 0 deletions lib/got-pollyfill-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = class GotPollyFillError extends Error {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels dirty but felt like the path of least resistance without rewriting the error handling

constructor(message, statusCode) {
super(message);
this.name = 'GotPollyFillError';
this.response = {};
this.response.statusCode = statusCode;
}
};
30 changes: 23 additions & 7 deletions lib/publish.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ const {stat} = require('fs-extra');
const {isPlainObject} = require('lodash');
const FormData = require('form-data');
const urlJoin = require('url-join');
const got = require('got');
const fetch = require('node-fetch');
const debug = require('debug')('semantic-release:gitlab');
const resolveConfig = require('./resolve-config');
const getRepoId = require('./get-repo-id');
const getAssets = require('./glob-assets');
const GotPollyFillError = require('./got-pollyfill-error');

module.exports = async (pluginConfig, context) => {
const {
Expand All @@ -17,7 +18,7 @@ module.exports = async (pluginConfig, context) => {
nextRelease: {gitTag, gitHead, notes},
logger,
} = context;
const {gitlabToken, gitlabUrl, gitlabApiUrl, assets, milestones} = resolveConfig(pluginConfig, context);
const {gitlabToken, gitlabUrl, gitlabApiUrl, assets, milestones, proxy} = resolveConfig(pluginConfig, context);
const assetsList = [];
const repoId = getRepoId(context, gitlabUrl, repositoryUrl);
const encodedRepoId = encodeURIComponent(repoId);
Expand Down Expand Up @@ -66,13 +67,22 @@ module.exports = async (pluginConfig, context) => {

let response;
try {
response = await got.post(uploadEndpoint, {...apiOptions, body: form}).json();
response = await fetch(uploadEndpoint, {
method: 'POST',
body: form,
agent: proxy,
...apiOptions,
});

if (response.status < 200 || response.status > 399) {
throw new GotPollyFillError('Upload API call error', response.status);
}
} catch (error) {
logger.error('An error occurred while uploading %s to the GitLab project uploads API:\n%O', file, error);
throw error;
}

const {url, alt} = response;
const {url, alt} = await response.json();

assetsList.push({label, alt, url, type, filepath});

Expand Down Expand Up @@ -106,10 +116,16 @@ module.exports = async (pluginConfig, context) => {
debug('POST-ing the following JSON to %s:\n%s', createReleaseEndpoint, JSON.stringify(json, null, 2));

try {
await got.post(createReleaseEndpoint, {
...apiOptions,
json,
const response = await fetch(createReleaseEndpoint, {
method: 'POST',
body: JSON.stringify(json),
agent: proxy,
headers: {'Content-Type': 'application/json', ...apiOptions.headers},
});

if (response.status < 200 || response.status > 399) {
throw new GotPollyFillError('Create release API call error', response.status);
}
} catch (error) {
logger.error('An error occurred while making a request to the GitLab release API:\n%O', error);
throw error;
Expand Down
19 changes: 19 additions & 0 deletions lib/resolve-config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const {castArray, isNil} = require('lodash');
const urlJoin = require('url-join');
const HttpProxyAgent = require('http-proxy-agent');
const HttpsProxyAgent = require('https-proxy-agent');

module.exports = (
{gitlabUrl, gitlabApiPathPrefix, assets, milestones},
Expand All @@ -15,6 +17,8 @@ module.exports = (
GITLAB_URL,
GL_PREFIX,
GITLAB_PREFIX,
HTTP_PROXY,
HTTPS_PROXY,
},
}
) => {
Expand All @@ -41,5 +45,20 @@ module.exports = (
: urlJoin(defaultedGitlabUrl, isNil(userGitlabApiPathPrefix) ? '/api/v4' : userGitlabApiPathPrefix),
assets: assets ? castArray(assets) : assets,
milestones: milestones ? castArray(milestones) : milestones,
proxy: getProxyConfiguration(defaultedGitlabUrl, HTTP_PROXY, HTTPS_PROXY),
};
};

const getProxyConfiguration = (gitlabUrl, HTTP_PROXY, HTTPS_PROXY) => {
const protocol = new URL(gitlabUrl).protocol.replace(':', '');

if (HTTP_PROXY && protocol === 'http') {
return new HttpProxyAgent(HTTP_PROXY);
}

if (HTTPS_PROXY && protocol === 'https') {
return new HttpsProxyAgent(HTTPS_PROXY);
}

return null;
};
37 changes: 19 additions & 18 deletions lib/verify.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
const {isString, isPlainObject, isArray} = require('lodash');
const urlJoin = require('url-join');
const got = require('got');
const debug = require('debug')('semantic-release:gitlab');
const AggregateError = require('aggregate-error');
const resolveConfig = require('./resolve-config');
const getRepoId = require('./get-repo-id');
const getError = require('./get-error');
const fetch = require('node-fetch');
const GotPollyFillError = require('./got-pollyfill-error');

const isNonEmptyString = value => isString(value) && value.trim();
const isStringOrStringArray = value => isNonEmptyString(value) || (isArray(value) && value.every(isNonEmptyString));
Expand All @@ -23,7 +24,7 @@ module.exports = async (pluginConfig, context) => {
logger,
} = context;
const errors = [];
const {gitlabToken, gitlabUrl, gitlabApiUrl, assets} = resolveConfig(pluginConfig, context);
const {gitlabToken, gitlabUrl, gitlabApiUrl, assets, proxy} = resolveConfig(pluginConfig, context);
const repoId = getRepoId(context, gitlabUrl, repositoryUrl);
debug('apiUrl: %o', gitlabApiUrl);
debug('repoId: %o', repoId);
Expand All @@ -46,26 +47,26 @@ module.exports = async (pluginConfig, context) => {

logger.log('Verify GitLab authentication (%s)', gitlabApiUrl);

try {
({
permissions: {project_access: projectAccess, group_access: groupAccess},
} = await got
.get(urlJoin(gitlabApiUrl, `/projects/${encodeURIComponent(repoId)}`), {
headers: {'PRIVATE-TOKEN': gitlabToken},
})
.json());
const response = await fetch(urlJoin(gitlabApiUrl, `/projects/${encodeURIComponent(repoId)}`), {
headers: {'PRIVATE-TOKEN': gitlabToken},
agent: proxy,
});

if (response.status === 200) {
const body = await response.json();

projectAccess = body.permissions.project_access;
groupAccess = body.permissions.group_access;

if (!((projectAccess && projectAccess.access_level >= 30) || (groupAccess && groupAccess.access_level >= 30))) {
errors.push(getError('EGLNOPERMISSION', {repoId}));
}
} catch (error) {
if (error.response && error.response.statusCode === 401) {
errors.push(getError('EINVALIDGLTOKEN', {repoId}));
} else if (error.response && error.response.statusCode === 404) {
errors.push(getError('EMISSINGREPO', {repoId}));
} else {
throw error;
}
} else if (response.status === 401) {
errors.push(getError('EINVALIDGLTOKEN', {repoId}));
} else if (response.status === 404) {
errors.push(getError('EMISSINGREPO', {repoId}));
} else {
throw new GotPollyFillError('Verify API call error', response.status);
}
}

Expand Down
Loading