Skip to content
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

feat(2437): Support read-only SCM [4] #41

Merged
merged 7 commits into from
Jun 11, 2021
Merged
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The class has a variety of knobs to tweak when interacting with GitLab.
| config.https (false) | Boolean | Is the Screwdriver API running over HTTPS |
| config.oauthClientId | String | OAuth Client ID provided by GitLab application |
| config.oauthClientSecret | String | OAuth Client Secret provided by GitLab application |
| config.readOnly ({}) | Object | Config with readOnly info: enabled, username, accessToken, cloneType |
| config.fusebox ({}) | Object | [Circuit Breaker configuration][circuitbreaker] |

```js
Expand Down
163 changes: 139 additions & 24 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ class GitlabScm extends Scm {
* @param {String} [options.gitlabProtocol=https] If using Gitlab, the protocol to use
* @param {String} [options.username=sd-buildbot] Gitlab username for checkout
* @param {String} [[email protected]] Gitlab user email for checkout
* @param {String} [options.commentUserToken] Token with public repo permission
* @param {Object} [options.readOnly={}] Read-only SCM instance config with: enabled, username, accessToken, cloneType
* @param {Boolean} [options.https=false] Is the Screwdriver API running over HTTPS
* @param {String} options.oauthClientId OAuth Client ID provided by Gitlab application
* @param {String} options.oauthClientSecret OAuth Client Secret provided by Gitlab application
Expand All @@ -129,6 +131,13 @@ class GitlabScm extends Scm {
gitlabHost: Joi.string().optional().default('gitlab.com'),
username: Joi.string().optional().default('sd-buildbot'),
email: Joi.string().optional().default('[email protected]'),
commentUserToken: Joi.string().optional().description('Token for PR comments'),
readOnly: Joi.object().keys({
enabled: Joi.boolean().optional(),
username: Joi.string().optional(),
accessToken: Joi.string().optional(),
cloneType: Joi.string().valid('https', 'ssh').optional().default('https')
}).optional().default({}),
https: Joi.boolean().optional().default(false),
oauthClientId: Joi.string().required(),
oauthClientSecret: Joi.string().required(),
Expand Down Expand Up @@ -451,11 +460,22 @@ class GitlabScm extends Scm {

// Export environment variables
command.push('echo Exporting environment variables');
command.push('if [ ! -z $SCM_CLONE_TYPE ] && [ $SCM_CLONE_TYPE = ssh ]; ' +
`then export SCM_URL=${sshCheckoutUrl}; ` +
'elif [ ! -z $SCM_USERNAME ] && [ ! -z $SCM_ACCESS_TOKEN ]; ' +
`then export SCM_URL=https://$SCM_USERNAME:$SCM_ACCESS_TOKEN@${checkoutUrl}; ` +
`else export SCM_URL=https://${checkoutUrl}; fi`);

if (Hoek.reach(this.config, 'readOnly.enabled')) {
if (Hoek.reach(this.config, 'readOnly.cloneType') === 'ssh') {
command.push(`export SCM_URL=${sshCheckoutUrl}; `);
} else {
command.push('if [ ! -z $SCM_USERNAME ] && [ ! -z $SCM_ACCESS_TOKEN ]; ' +
`then export SCM_URL=https://$SCM_USERNAME:$SCM_ACCESS_TOKEN@${checkoutUrl}; ` +
`else export SCM_URL=https://${checkoutUrl}; fi`);
}
} else {
command.push('if [ ! -z $SCM_CLONE_TYPE ] && [ $SCM_CLONE_TYPE = ssh ]; ' +
`then export SCM_URL=${sshCheckoutUrl}; ` +
'elif [ ! -z $SCM_USERNAME ] && [ ! -z $SCM_ACCESS_TOKEN ]; ' +
`then export SCM_URL=https://$SCM_USERNAME:$SCM_ACCESS_TOKEN@${checkoutUrl}; ` +
`else export SCM_URL=https://${checkoutUrl}; fi`);
}
command.push('export GIT_URL=$SCM_URL.git');
// git 1.7.1 doesn't support --no-edit with merge, this should do same thing
command.push('export GIT_MERGE_AUTOEDIT=no');
Expand Down Expand Up @@ -779,40 +799,135 @@ class GitlabScm extends Scm {
});
}

/**
* Get all the comments of a particular Pull Request
* @async prComments
* @param {Object} repoId The repo ID
* @param {Integer} prNum The PR number used to fetch the PR
* @return {Promise} Resolves to object containing the list of comments of this PR
*/
async prComments(repoId, prNum) {
let prComments;

try {
prComments = await this.breaker.runCommand({
json: true,
method: 'GET',
auth: {
bearer: this.config.commentUserToken
},
url: `${this.config.gitlabProtocol}://${this.config.gitlabHost}/api/v4` +
`/projects/${repoId}/merge_requests/${prNum}/notes`
});

return { comments: prComments.body };
} catch (err) {
logger.warn(`Failed to fetch PR comments for repo ${repoId}, PR ${prNum}: `, err);

return null;
}
}

/**
* Edit a particular comment in the PR
* @async editPrComment
* @param {Integer} commentId The id of the particular comment to be edited
* @param {Object} repoId The information regarding SCM like repo, owner
* @param {Integer} prNum The PR number used to fetch the PR
* @param {String} comment The new comment body
* @return {Promise} Resolves to object containing PR comment info
*/
async editPrComment(commentId, repoId, prNum, comment) {
try {
const pullRequestComment = await this.breaker.runCommand({
json: true,
method: 'PUT',
auth: {
bearer: this.config.commentUserToken // need to use a token with public_repo permissions
},
url: `${this.config.gitlabProtocol}://${this.config.gitlabHost}/api/v4` +
`/projects/${repoId}/merge_requests/${prNum}/notes/${commentId}`,
qs: {
body: comment
}
});

return pullRequestComment;
Copy link
Contributor

Choose a reason for hiding this comment

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

return pullRequestComment && pullRequestComment.body

Copy link
Member Author

Choose a reason for hiding this comment

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

this change is causing some issues with tests, so leaving as is

} catch (err) {
logger.warn('Failed to edit PR comment: ', err);

return null;
}
}

/**
* Add merge request note
* @async addPrComment
* @param {Object} config Configuration
* @param {String} config.comment The PR comment
* @param {Integer} config.prNum The PR number
* @param {String} config.scmUri The scmUri to get commit sha of
* @param {String} config.scmContext The scm context to which user belongs
* @param {String} config.token The token used to authenticate to the SCM
* @return {Promise}
*/
async _addPrComment({ comment, prNum, scmUri, token }) {
async _addPrComment({ comment, prNum, scmUri }) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Update jsdoc

const { repoId } = getScmUriParts(scmUri);

return this.breaker.runCommand({
json: true,
method: 'POST',
auth: {
bearer: token
},
url: `${this.config.gitlabProtocol}://${this.config.gitlabHost}/api/v4` +
`/projects/${repoId}/merge_requests/${prNum}/notes`,
qs: {
body: comment
const prComments = await this.prComments(repoId, prNum);

if (prComments) {
const botComment = prComments.comments.find(commentObj =>
commentObj.author.username === this.config.username);

if (botComment) {
try {
const pullRequestComment = await this.editPrComment(
botComment.id, repoId, prNum, comment);

if (pullRequestComment.statusCode !== 200) {
throw pullRequestComment;
}

return {
commentId: Hoek.reach(pullRequestComment, 'body.id'),
createTime: Hoek.reach(pullRequestComment, 'body.created_at'),
username: Hoek.reach(pullRequestComment, 'body.author.username')
};
} catch (err) {
logger.error('Failed to addPRComment: ', err);

return null;
}
}
}

try {
const pullRequestComment = await this.breaker.runCommand({
json: true,
method: 'POST',
auth: {
bearer: this.config.commentUserToken
},
url: `${this.config.gitlabProtocol}://${this.config.gitlabHost}/api/v4` +
`/projects/${repoId}/merge_requests/${prNum}/notes`,
qs: {
body: comment
}
});
Comment on lines +904 to +915
Copy link
Contributor

Choose a reason for hiding this comment

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

if it is convenient, refactor the api calls to a single func with url as param

Copy link
Member Author

Choose a reason for hiding this comment

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

will update at a different time


if (pullRequestComment.statusCode !== 200) {
throw pullRequestComment;
}
}).then((response) => {
checkResponseError(response, '_addPrComment');

return {
commentId: response.body.id,
createTime: response.body.created_at,
username: response.body.author.username
commentId: Hoek.reach(pullRequestComment, 'body.id'),
createTime: Hoek.reach(pullRequestComment, 'body.created_at'),
username: Hoek.reach(pullRequestComment, 'body.author.username')
};
});
} catch (err) {
logger.error('Failed to addPRComment: ', err);

return null;
}
}

/**
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"chai": "^3.5.0",
"eslint": "^4.6.0",
"eslint-config-screwdriver": "^3.0.0",
"mocha": "^8.2.1",
"mocha": "^8.4.0",
"mocha-multi-reporters": "^1.5.1",
"mocha-sonarqube-reporter": "^1.0.2",
"mockery": "^2.0.0",
Expand All @@ -45,10 +45,10 @@
},
"dependencies": {
"@hapi/hoek": "^9.2.0",
"circuit-fuses": "^4.0.5",
"circuit-fuses": "^4.0.6",
"joi": "^17.2.0",
"request": "^2.80.0",
"screwdriver-data-schema": "^21.2.9",
"screwdriver-data-schema": "^21.3.0",
"screwdriver-logger": "^1.0.2",
"screwdriver-scm-base": "^7.0.0"
},
Expand Down
Loading