Skip to content

Commit 100c56a

Browse files
authored
Merge pull request NginxProxyManager#2686 from NginxProxyManager/develop
v2.9.20
2 parents 5920b0c + 44bebf3 commit 100c56a

24 files changed

+989
-1807
lines changed

.version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.9.19
1+
2.9.20

Jenkinsfile

+34-54
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import groovy.transform.Field
2+
3+
@Field
4+
def shOutput = ""
5+
def buildxPushTags = ""
6+
17
pipeline {
28
agent {
39
label 'docker-multiarch'
@@ -16,6 +22,8 @@ pipeline {
1622
COMPOSE_FILE = 'docker/docker-compose.ci.yml'
1723
COMPOSE_INTERACTIVE_NO_CLI = 1
1824
BUILDX_NAME = "${COMPOSE_PROJECT_NAME}"
25+
DOCS_BUCKET = 'jc21-npm-site'
26+
DOCS_CDN = 'EN1G6DEWZUTDT'
1927
}
2028
stages {
2129
stage('Environment') {
@@ -26,7 +34,7 @@ pipeline {
2634
}
2735
steps {
2836
script {
29-
env.BUILDX_PUSH_TAGS = "-t docker.io/jc21/${IMAGE}:${BUILD_VERSION} -t docker.io/jc21/${IMAGE}:${MAJOR_VERSION} -t docker.io/jc21/${IMAGE}:latest"
37+
buildxPushTags = "-t docker.io/jc21/${IMAGE}:${BUILD_VERSION} -t docker.io/jc21/${IMAGE}:${MAJOR_VERSION} -t docker.io/jc21/${IMAGE}:latest"
3038
}
3139
}
3240
}
@@ -39,7 +47,7 @@ pipeline {
3947
steps {
4048
script {
4149
// Defaults to the Branch name, which is applies to all branches AND pr's
42-
env.BUILDX_PUSH_TAGS = "-t docker.io/jc21/${IMAGE}:github-${BRANCH_LOWER}"
50+
buildxPushTags = "-t docker.io/jc21/${IMAGE}:github-${BRANCH_LOWER}"
4351
}
4452
}
4553
}
@@ -54,35 +62,28 @@ pipeline {
5462
}
5563
}
5664
}
57-
stage('Frontend') {
65+
stage('Build and Test') {
5866
steps {
59-
sh './scripts/frontend-build'
67+
script {
68+
// Frontend and Backend
69+
def shStatusCode = sh(label: 'Checking and Building', returnStatus: true, script: '''
70+
set -e
71+
./scripts/ci/frontend-build > ${WORKSPACE}/tmp-sh-build 2>&1
72+
./scripts/ci/test-and-build > ${WORKSPACE}/tmp-sh-build 2>&1
73+
''')
74+
shOutput = readFile "${env.WORKSPACE}/tmp-sh-build"
75+
if (shStatusCode != 0) {
76+
error "${shOutput}"
77+
}
78+
}
6079
}
61-
}
62-
stage('Backend') {
63-
steps {
64-
echo 'Checking Syntax ...'
65-
sh 'docker pull nginxproxymanager/nginx-full:certbot-node'
66-
// See: https://github.com/yarnpkg/yarn/issues/3254
67-
sh '''docker run --rm \\
68-
-v "$(pwd)/backend:/app" \\
69-
-v "$(pwd)/global:/app/global" \\
70-
-w /app \\
71-
nginxproxymanager/nginx-full:certbot-node \\
72-
sh -c "yarn install && yarn eslint . && rm -rf node_modules"
73-
'''
74-
75-
echo 'Docker Build ...'
76-
sh '''docker build --pull --no-cache --squash --compress \\
77-
-t "${IMAGE}:ci-${BUILD_NUMBER}" \\
78-
-f docker/Dockerfile \\
79-
--build-arg TARGETPLATFORM=linux/amd64 \\
80-
--build-arg BUILDPLATFORM=linux/amd64 \\
81-
--build-arg BUILD_VERSION="${BUILD_VERSION}" \\
82-
--build-arg BUILD_COMMIT="${BUILD_COMMIT}" \\
83-
--build-arg BUILD_DATE="$(date '+%Y-%m-%d %T %Z')" \\
84-
.
85-
'''
80+
post {
81+
always {
82+
sh 'rm -f ${WORKSPACE}/tmp-sh-build'
83+
}
84+
failure {
85+
npmGithubPrComment("CI Error:\n\n```\n${shOutput}\n```", true)
86+
}
8687
}
8788
}
8889
stage('Integration Tests Sqlite') {
@@ -164,10 +165,8 @@ pipeline {
164165
}
165166
steps {
166167
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
167-
// Docker Login
168-
sh "docker login -u '${duser}' -p '${dpass}'"
169-
// Buildx with push from cache
170-
sh "./scripts/buildx --push ${BUILDX_PUSH_TAGS}"
168+
sh 'docker login -u "${duser}" -p "${dpass}"'
169+
sh "./scripts/buildx --push ${buildxPushTags}"
171170
}
172171
}
173172
}
@@ -181,26 +180,7 @@ pipeline {
181180
}
182181
}
183182
steps {
184-
withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'npm-s3-docs', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
185-
sh """docker run --rm \\
186-
--name \${COMPOSE_PROJECT_NAME}-docs-upload \\
187-
-e S3_BUCKET=jc21-npm-site \\
188-
-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \\
189-
-e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \\
190-
-v \$(pwd):/app \\
191-
-w /app \\
192-
jc21/ci-tools \\
193-
scripts/docs-upload /app/docs/.vuepress/dist/
194-
"""
195-
196-
sh """docker run --rm \\
197-
--name \${COMPOSE_PROJECT_NAME}-docs-invalidate \\
198-
-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \\
199-
-e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \\
200-
jc21/ci-tools \\
201-
aws cloudfront create-invalidation --distribution-id EN1G6DEWZUTDT --paths '/*'
202-
"""
203-
}
183+
npmDocsRelease("$DOCS_BUCKET", "$DOCS_CDN")
204184
}
205185
}
206186
stage('PR Comment') {
@@ -214,7 +194,7 @@ pipeline {
214194
}
215195
steps {
216196
script {
217-
def comment = pullRequest.comment("This is an automated message from CI:\n\nDocker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`\n\n**Note:** ensure you backup your NPM instance before testing this PR image! Especially if this PR contains database changes.")
197+
npmGithubPrComment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`\n\n**Note:** ensure you backup your NPM instance before testing this PR image! Especially if this PR contains database changes.", true)
218198
}
219199
}
220200
}

backend/internal/access-list.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ const internalAccessList = {
218218
// re-fetch with expansions
219219
return internalAccessList.get(access, {
220220
id: data.id,
221-
expand: ['owner', 'items', 'clients', 'proxy_hosts.access_list.[clients,items]']
221+
expand: ['owner', 'items', 'clients', 'proxy_hosts.[certificate,access_list.[clients,items]]']
222222
}, true /* <- skip masking */);
223223
})
224224
.then((row) => {
@@ -256,7 +256,7 @@ const internalAccessList = {
256256
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
257257
.where('access_list.is_deleted', 0)
258258
.andWhere('access_list.id', data.id)
259-
.allowEager('[owner,items,clients,proxy_hosts.[*, access_list.[clients,items]]]')
259+
.allowEager('[owner,items,clients,proxy_hosts.[certificate,access_list.[clients,items]]]')
260260
.omit(['access_list.is_deleted'])
261261
.first();
262262

@@ -507,7 +507,7 @@ const internalAccessList = {
507507
if (typeof item.password !== 'undefined' && item.password.length) {
508508
logger.info('Adding: ' + item.username);
509509

510-
utils.exec('/usr/bin/htpasswd -b "' + htpasswd_file + '" "' + item.username + '" "' + item.password + '"')
510+
utils.execFile('/usr/bin/htpasswd', ['-b', htpasswd_file, item.username, item.password])
511511
.then((/*result*/) => {
512512
next();
513513
})

backend/internal/certificate.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,8 @@ const internalCertificate = {
874874
// Escape single quotes and backslashes
875875
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
876876
const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\'';
877-
const prepareCmd = 'pip install ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies;
877+
// we call `. /opt/certbot/bin/activate` (`.` is alternative to `source` in dash) to access certbot venv
878+
let prepareCmd = '. /opt/certbot/bin/activate && pip install ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies + ' && deactivate';
878879

879880
// Whether the plugin has a --<name>-credentials argument
880881
const hasConfigArg = certificate.meta.dns_provider !== 'route53';

backend/internal/token.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ module.exports = {
2424

2525
return userModel
2626
.query()
27-
.where('email', data.identity)
27+
.where('email', data.identity.toLowerCase().trim())
2828
.andWhere('is_deleted', 0)
2929
.andWhere('is_disabled', 0)
3030
.first()

backend/lib/utils.js

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
const exec = require('child_process').exec;
1+
const exec = require('child_process').exec;
2+
const execFile = require('child_process').execFile;
23

34
module.exports = {
45

@@ -16,5 +17,21 @@ module.exports = {
1617
}
1718
});
1819
});
20+
},
21+
22+
/**
23+
* @param {Array} cmd
24+
* @returns {Promise}
25+
*/
26+
execFile: function (cmd) {
27+
return new Promise((resolve, reject) => {
28+
execFile(cmd, function (err, stdout, /*stderr*/) {
29+
if (err && typeof err === 'object') {
30+
reject(err);
31+
} else {
32+
resolve(stdout.trim());
33+
}
34+
});
35+
});
1936
}
2037
};

backend/package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
"body-parser": "^1.19.0",
1212
"compression": "^1.7.4",
1313
"config": "^3.3.1",
14-
"express": "^4.17.1",
14+
"express": "^4.17.3",
1515
"express-fileupload": "^1.1.9",
1616
"gravatar": "^1.8.0",
1717
"json-schema-ref-parser": "^8.0.0",
18-
"jsonwebtoken": "^8.5.1",
19-
"knex": "^0.20.13",
20-
"liquidjs": "^9.11.10",
18+
"jsonwebtoken": "^9.0.0",
19+
"knex": "^2.4.0",
20+
"liquidjs": "^10.0.0",
2121
"lodash": "^4.17.21",
2222
"moment": "^2.29.4",
2323
"mysql": "^2.18.1",

backend/setup.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,13 @@ const setupCertbotPlugins = () => {
174174

175175
certificates.map(function (certificate) {
176176
if (certificate.meta && certificate.meta.dns_challenge === true) {
177-
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
178-
const packages_to_install = `${dns_plugin.package_name}${dns_plugin.version_requirement || ''} ${dns_plugin.dependencies}`;
177+
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
179178

179+
const packages_to_install = `${dns_plugin.package_name}${dns_plugin.version_requirement || ''} ${dns_plugin.dependencies}`;
180180
if (plugins.indexOf(packages_to_install) === -1) plugins.push(packages_to_install);
181181

182182
// Make sure credentials file exists
183-
const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
183+
const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
184184
// Escape single quotes and backslashes
185185
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
186186
const credentials_cmd = '[ -f \'' + credentials_loc + '\' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'; }';
@@ -189,14 +189,14 @@ const setupCertbotPlugins = () => {
189189
});
190190

191191
if (plugins.length) {
192-
const install_cmd = 'pip install ' + plugins.join(' ');
192+
const install_cmd = '. /opt/certbot/bin/activate && pip install ' + plugins.join(' ') + ' && deactivate';
193193
promises.push(utils.exec(install_cmd));
194194
}
195195

196196
if (promises.length) {
197197
return Promise.all(promises)
198-
.then(() => {
199-
logger.info('Added Certbot plugins ' + plugins.join(', '));
198+
.then(() => {
199+
logger.info('Added Certbot plugins ' + plugins.join(', '));
200200
});
201201
}
202202
}

0 commit comments

Comments
 (0)