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

Add GitLab support #219

Merged
merged 42 commits into from
Aug 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
d2d58cb
Update README.md
Jun 5, 2018
7560740
Remove import and calls to `logger`. #176
chmac Jun 13, 2018
b5d8b72
Ignore Jetbrains specific files
Jul 12, 2018
7c8e962
Add Jest to Standard's environments
Jul 12, 2018
11548e8
Add package-lock.json
Jul 12, 2018
0b497ec
Fix linting errors
ntsim Jul 13, 2018
00680c1
Add `gitlab` package and bump minimum NodeJS version to 8.11.3
ntsim Jul 14, 2018
04e11ec
Refactor GitHub class into GitService superclass
ntsim Jul 15, 2018
08a51dc
Create initial GitLab service class
ntsim Jul 15, 2018
a5a7d6f
Refactor GitHub API to authenticate on instantiation
Jul 16, 2018
5412188
Update GitHub class to use `@octokit/rest` package
Jul 16, 2018
6f53c02
Use `request-promise` to avoid method name collisions
Jul 16, 2018
7c5ccbe
Hook initial GitLab service into Staticman class and fix failing conf…
Jul 17, 2018
434017a
Abstract additional API calls into Git service classes
ntsim Jul 17, 2018
e4929ba
Fix incorrect invitation ID key for call to `acceptRepoInvite`
ntsim Jul 17, 2018
6369d62
Replace deprecated `node-uuid` with `uuid` package
Jul 18, 2018
daf44fe
Polish Staticman class
Jul 18, 2018
cac86da
Fix errors from GitLab requests leaking request/response details
Jul 18, 2018
070d4f7
Rework GitLab pull/commitFile methods to work properly
Jul 18, 2018
95d45b8
Add new `service` parameter to `/entry` endpoint
Jul 18, 2018
09c9bd8
Update `/auth` endpoint to v3 implementation
Jul 19, 2018
d26005f
Add `User` model to encapsulate user identities
Jul 19, 2018
d6e9e7a
Add `Review` model and add extra type validations/docblocks
Jul 19, 2018
bef978f
Add 'use strict' statements
Jul 20, 2018
1bffefd
Fix v2 support for `/auth` endpoint and fix failing tests
Jul 20, 2018
38cb8a0
Add new authentication handling for processing entries
ntsim Jul 21, 2018
56f082b
Add additional tests for GitLab service class
ntsim Jul 22, 2018
a483a08
Speed up tests by using `cloneDeep` for site config
ntsim Jul 22, 2018
55922c2
Bump up remaining v2 API endpoints to v3
ntsim Jul 22, 2018
2b07953
Add email property to `User` model
ntsim Jul 22, 2018
7760cba
Fix OAuth token not being passed correctly to GitLab when checking auth
ntsim Jul 22, 2018
ab72c78
Add `user` generated field type
ntsim Jul 22, 2018
f909574
Add `auth-type` option when requiring auth and separate out v2 auth l…
ntsim Jul 22, 2018
496168c
Add configurable GitHub/GitLab URLs
ntsim Jul 22, 2018
2a2d260
Re-add `githubAuth.required` option for backwards compat. with v2 API
ntsim Jul 22, 2018
9fc5995
Cleanup unnecessary environment variable options in site config
ntsim Jul 22, 2018
dff700d
Bump up NPM package to `3.0.0`
ntsim Jul 22, 2018
9dbd9fd
Bump up Travis CI NodeJS version
ntsim Jul 22, 2018
d4df940
Fix markdown typo
VincentTam Aug 10, 2018
62a812d
Merge pull request #1 from VincentTam/patch-1
ntsim Aug 11, 2018
8447c9a
Update to test coverage to correct percentage and re-add badges
ntsim Aug 12, 2018
d7e5e8f
Fix coverage badge being incorrectly replaced on test runs
ntsim Aug 12, 2018
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
.idea/
*.iml

config.json
config.*.json
!config.test.json
!config.example.json
node_modules/
staticman_key
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ cache:
notifications:
email: false
node_js:
- '6'
- '8.11.3'
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:6.7.0
FROM node:8.11.3

# Create app directory
RUN mkdir -p /app
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<img src="logo.png" width="300">

# Staticman [![coverage](https://img.shields.io/badge/coverage-53%25-red.svg?style=flat)](https://github.com/eduardoboucas/staticman) [![Build Status](https://travis-ci.org/eduardoboucas/staticman.svg?branch=master)](https://travis-ci.org/eduardoboucas/staticman) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
# Staticman [![coverage](https://img.shields.io/badge/coverage-82%25-yellow.svg?style=flat)](https://github.com/eduardoboucas/staticman) [![Build Status](https://travis-ci.org/eduardoboucas/staticman.svg?branch=master)](https://travis-ci.org/eduardoboucas/staticman) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)

> Static sites with superpowers

Expand All @@ -14,7 +14,7 @@ You can download and run the Staticman API on your own infrastructure, or you ca

## Requirements

- Node.js 4.8.3+
- Node.js 8.11.3+
- npm
- A [personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/) for the GitHub account you want to run Staticman with
- An SSH key (click [here](https://help.github.com/articles/connecting-to-github-with-ssh/) to learn how to create one)
Expand Down Expand Up @@ -102,5 +102,6 @@ Would you like to contribute to Staticman? That's great! Here's how:
- [Tyne Time](https://www.tynetime.com) ([Source](https://github.com/Doocey/tyne-time-hugo))
- [BinaryMist](https://binarymist.io/blog) ([Source](https://github.com/binarymist/BinaryMistBlog))
- [La ruta de la cebada](https://larutadelacebada.com) ([Source](https://github.com/lasocial/larutadelacebada.github.io))
- [Gatsby Central](https://www.gatsbycentral.com) ([Source](https://github.com/GatsbyCentral/gatsbycentral.com))

Are you using Staticman? [Let us know!](https://github.com/eduardoboucas/staticman/edit/master/README.md)
30 changes: 30 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,42 @@ const schema = {
default: 'development',
env: 'NODE_ENV'
},
githubAccessTokenUri: {
doc: 'URI for the GitHub authentication provider.',
format: String,
default: 'https://github.com/login/oauth/access_token',
env: 'GITHUB_ACCESS_TOKEN_URI'
},
githubBaseUrl: {
doc: 'Base URL for the GitHub API.',
format: String,
default: 'https://api.github.com',
env: 'GITHUB_BASE_URL'
},
githubToken: {
doc: 'Access token to the GitHub account being used to push files with.',
format: String,
default: null,
env: 'GITHUB_TOKEN'
},
gitlabAccessTokenUri: {
doc: 'URI for the GitLab authentication provider.',
format: String,
default: 'https://gitlab.com/oauth/token',
env: 'GITLAB_ACCESS_TOKEN_URI'
},
gitlabBaseUrl: {
doc: 'Base URL for the GitLab API.',
format: String,
default: 'https://gitlab.com',
env: 'GITLAB_BASE_URL'
},
gitlabToken: {
doc: 'Access token to the GitLab account being used to push files with.',
format: String,
default: null,
env: 'GITLAB_TOKEN'
},
port: {
doc: 'The port to bind the application to.',
format: 'port',
Expand Down
1 change: 1 addition & 0 deletions config.sample.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"gitlabToken": "YOUR_GITLAB_TOKEN",
"githubToken": "YOUR_GITHUB_TOKEN",
"rsaPrivateKey": "-----BEGIN RSA PRIVATE KEY-----YOUR_KEY-----END RSA PRIVATE KEY-----",
"port": 80
Expand Down
1 change: 1 addition & 0 deletions config.test.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"gitlabToken": "r4e3w2q1",
"githubToken": "1q2w3e4r",
"rsaPrivateKey": "-----BEGIN RSA PRIVATE KEY-----MIIEpAIBAAKCAQEA2nX81/5w6nZLolEh0uKks3//FlqizKxFWi8GaEJYbud8FYwLH6l+LDfZkjQiDXvm6mxngDGCjlG9b7hgfa/sfWSuqtJUQ2D1Nenn11gwAUaI7OQNSassE+nVFL2BGedl6DOgpZFkKrDTQT7jyvvc3r/2IqWahAYyFWXImD50qwNWIGyZ2Sry2/WXeCydiUWV8ZG3GOlGZLgtlK8igdmLje/6Ja+1oRBFwdbWrf/nKsMB0fCL3R5MkWTnumR8sGx2Xud8Q3mF7cOYWQiy2AqrFQOzJ+QwH6hO6CGVk8eBh8xRkrjaZEsvolZ33N+0aS9NBxHuOyGq0Te9HMLFqwVGzwIDAQABAoIBAQDPD4gAnb0erdMQXT/m50TekdIQuQWXYy00xl+XUFMLg0L8FUmxz++0L5d72Qfxqd97kBYlzkeFZ3pbOvHSD33ieByJ8mNFnc+tMy+4z3BotRcxGRJzIdfcZAS/7MJB8C6KAO0iIQVE5WbGb7pu+XwmcOH1gutKeajc2SVhD8l7ECRARPuV3JxlhbVt+ylM05h7+5HwwnEmodNQHPeGvLEWp7eODNdcLLE5EFVjmN0dwGu9D+xCFna9bL7p921GOTGf349l3021ONaGvWd77Frhl9tHL1k40hqq4LiXDk3RkiP2oDb65+ohW128Q1QPgjMQuXbbFTCa3++pbz7OMbUhAoGBAPxt6A5B3s5GnnaCTE0YJ13aTeUyqrLimBxWEp7L/WXug8SDBW5tVAmBNHlZC4GuR089E7g26BVr0i3k/dOknKa0GLyGGJNphxrrcD05p+XB6Gd6/N1Yr6amM+8XqU1+UKLL2CBrLOSi6r4J4fPS8sb9362+FROwCJZfxd5Wznd/AoGBAN2NEz+anDBgulxLoguGuvc0FaqSsVIP/cdb5Ve5t29/uheVjLgDXSkV5P/RFfk1ygH1gNjmUrsoxfsGWQa3KMTk292maYRj7GPpOalSlp8BYyiQ9omPdOgusVzY4CEjMiDkKal0YrBswXKV8E2lTokEEkbdrQpToVdBmRCzT1ixAoGBAMNMBQ2CyO6ulEr75CyBU3O5QirhWE+uICFMNnvFNvd14VxYQgt9alcwL3jy+4QJYgJcLrWHRWfNU39Oe1MTOF+BVuIEnV7vdifNn7i+srd/nl7xOFHIG53DWMrSc5oQ8DIDo+LxCHqb0SHWY4pQ2qQ6JqQ1O/lPaFVvI5cxcevhAoGALyNtDQNgaAqsnCabe8hWz3INihVcFRHB9UDgMyIYnWiXt5ziK+TLVYqLBsL94eBH8tLBb2TSBXBPb3GST5N44SuwfCCEIt7/1OIymowv7/TjnCX7zpjvTtdgdVjPlz1d1RG2q49P0CnTnwW3801QwGZVXS6dOq1AjsguQRdlsoECgYBrFqKa3S48UbGOKkocgMb/1HzVs2sxNWMu+/jEiBvI4RrCiun9MALtrVtohgHRml4yZJaOojVk9F0z/MAak9eTgSmHpwxYZMqR1PvM4c/ekfun4hQoK04Thoexi/Z0RfsI6nBdBk8MAra03ldDvnYZY4Fat21YuNBspbrbTW8UoA==-----END RSA PRIVATE KEY-----",
"email": {
Expand Down
64 changes: 64 additions & 0 deletions controllers/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use strict'

const gitFactory = require('../lib/GitServiceFactory')
const oauth = require('../lib/OAuth')
const RSA = require('../lib/RSA')
const Staticman = require('../lib/Staticman')

module.exports = (req, res) => {
const staticman = new Staticman(req.params)
staticman.setConfigPath()

let requestAccessToken

switch (req.params.service) {
case 'gitlab':
requestAccessToken = siteConfig =>
oauth.requestGitLabAccessToken(
req.query.code,
siteConfig.get('gitlabAuth.clientId'),
siteConfig.get('gitlabAuth.clientSecret'),
siteConfig.get('gitlabAuth.redirectUri')
)
break
default:
requestAccessToken = siteConfig =>
oauth.requestGitHubAccessToken(
req.query.code,
siteConfig.get('githubAuth.clientId'),
siteConfig.get('githubAuth.clientSecret'),
siteConfig.get('githubAuth.redirectUri')
)
}

return staticman.getSiteConfig()
.then(requestAccessToken)
.then((accessToken) => {
const git = gitFactory.create(req.params.service, {
oauthToken: accessToken
})

// TODO: Simplify this when v2 support is dropped.
const getUser = req.params.version === '2' && req.params.service === 'github'
? git.api.users.get({}).then(({data}) => data)
: git.getCurrentUser()

return getUser
.then((user) => {
res.send({
accessToken: RSA.encrypt(accessToken),
user
})
})
})
.catch((err) => {
console.log('ERR:', err)

const statusCode = err.statusCode || 401

res.status(statusCode).send({
statusCode,
message: err.message
})
})
}
13 changes: 6 additions & 7 deletions controllers/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@ module.exports = (req, res) => {
const github = new GitHub({
username: req.params.username,
repository: req.params.repository,
branch: req.params.branch
branch: req.params.branch,
token: config.get('githubToken')
})

github.authenticateWithToken(config.get('githubToken'))
return github.api.users.getRepoInvites({}).then(({data}) => {
let invitationId = null

return github.api.users.getRepoInvites({}).then(response => {
let invitationId

const invitation = response.some(invitation => {
const invitation = data.some(invitation => {
if (invitation.repository.full_name === (req.params.username + '/' + req.params.repository)) {
invitationId = invitation.id

Expand All @@ -30,7 +29,7 @@ module.exports = (req, res) => {

if (invitation) {
return github.api.users.acceptRepoInvite({
id: invitationId
invitation_id: invitationId
})
} else {
res.status(404).send('Invitation not found')
Expand Down
32 changes: 0 additions & 32 deletions controllers/githubAuth.js

This file was deleted.

41 changes: 17 additions & 24 deletions controllers/handlePR.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
'use strict'

const path = require('path')
const config = require(path.join(__dirname, '/../config'))
const GitHub = require(path.join(__dirname, '/../lib/GitHub'))
const config = require('../config')
const GitHub = require('../lib/GitHub')
const Staticman = require('../lib/Staticman')

module.exports = (repo, data) => {
Expand All @@ -14,45 +13,39 @@ module.exports = (repo, data) => {
return
}

const github = new GitHub()
const github = new GitHub({
username: data.repository.owner.login,
repository: data.repository.name,
token: config.get('githubToken')
})

github.authenticateWithToken(config.get('githubToken'))
return github.getReview(data.number).then((review) => {
if (review.sourceBranch.indexOf('staticman_')) {
return null
}

return github.api.pullRequests.get({
user: data.repository.owner.login,
repo: data.repository.name,
number: data.number
}).then(response => {
if (response.head.ref.indexOf('staticman_')) {
if (review.state !== 'merged' && review.state !== 'closed') {
return null
}

if (response.merged) {
const bodyMatch = response.body.match(/(?:.*?)<!--staticman_notification:(.+?)-->(?:.*?)/i)
if (review.state === 'merged') {
const bodyMatch = review.body.match(/(?:.*?)<!--staticman_notification:(.+?)-->(?:.*?)/i)

if (bodyMatch && (bodyMatch.length === 2)) {
try {
const parsedBody = JSON.parse(bodyMatch[1])
const staticman = new Staticman(parsedBody.parameters)

staticman.authenticate()
staticman.setConfigPath(parsedBody.configPath)
staticman.processMerge(parsedBody.fields, parsedBody.options).catch(err => {
return Promise.reject(err)
})
staticman.processMerge(parsedBody.fields, parsedBody.options)
.catch(err => Promise.reject(err))
} catch (err) {
return Promise.reject(err)
}
}
}

if (response.state === 'closed') {
return github.api.gitdata.deleteReference({
user: data.repository.owner.login,
repo: data.repository.name,
ref: 'heads/' + response.head.ref
})
}
return github.deleteBranch(review.sourceBranch)
}).then(response => {
if (ua) {
ua.event('Hooks', 'Delete branch').send()
Expand Down
25 changes: 4 additions & 21 deletions controllers/process.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
const path = require('path')
const config = require(path.join(__dirname, '/../config'))
const errorHandler = require('../lib/ErrorHandler')
const logger = require('../lib/Logger')
const reCaptcha = require('express-recaptcha')
const Staticman = require('../lib/Staticman')
const universalAnalytics = require('universal-analytics')
Expand Down Expand Up @@ -69,19 +68,6 @@ function process (staticman, req, res) {
const fields = req.query.fields || req.body.fields
const options = req.query.options || req.body.options || {}

logger.info(
JSON.stringify(
{
url: req.url,
fields,
options,
body: req.body
}
),
null,
2
)

return staticman.processEntry(fields, options).then(data => {
sendResponse(res, {
redirect: data.redirect,
Expand Down Expand Up @@ -137,20 +123,17 @@ function sendResponse (res, data) {
module.exports = (req, res, next) => {
const staticman = new Staticman(req.params)

staticman.authenticate()
staticman.setConfigPath()
staticman.setIp(req.headers['x-forwarded-for'] || req.connection.remoteAddress)
staticman.setUserAgent(req.headers['user-agent'])

return checkRecaptcha(staticman, req).then(usedRecaptcha => {
return process(staticman, req, res)
}).catch(err => {
return sendResponse(res, {
return checkRecaptcha(staticman, req)
.then(usedRecaptcha => process(staticman, req, res))
.catch(err => sendResponse(res, {
err,
redirect: req.body.options && req.body.options.redirect,
redirectError: req.body.options && req.body.options.redirectError
})
})
}))
}

module.exports.checkRecaptcha = checkRecaptcha
Expand Down
Loading