Commit b31d0abc authored by Eric Eastwood's avatar Eric Eastwood

Merge branch 'release/19.37.0'

parents ef64d6c8 b131e934
...@@ -101,3 +101,4 @@ unread-notification.out.html ...@@ -101,3 +101,4 @@ unread-notification.out.html
node_modules_2 node_modules_2
config/overrides config/overrides
.env .env
webpack-report/
...@@ -20,7 +20,6 @@ stages: ...@@ -20,7 +20,6 @@ stages:
- /npm_cache/ - /npm_cache/
- npm_cache/ - npm_cache/
validate: validate:
<<: *node_job <<: *node_job
stage: build_unit_test stage: build_unit_test
...@@ -32,7 +31,6 @@ validate: ...@@ -32,7 +31,6 @@ validate:
# to re-use the scripts that GitLab has, https://gitlab.com/gitlab-org/gitlab-ce/issues/57010 # to re-use the scripts that GitLab has, https://gitlab.com/gitlab-org/gitlab-ce/issues/57010
- npm run prettier -- --check "**/*.js" - npm run prettier -- --check "**/*.js"
test: test:
<<: *node_job <<: *node_job
variables: variables:
...@@ -67,7 +65,7 @@ mobile-asset-build: ...@@ -67,7 +65,7 @@ mobile-asset-build:
<<: *node_job <<: *node_job
stage: build_unit_test stage: build_unit_test
only: only:
- master - master
script: script:
- npm run build-android-assets - npm run build-android-assets
- npm run build-ios-assets - npm run build-ios-assets
...@@ -76,8 +74,6 @@ mobile-asset-build: ...@@ -76,8 +74,6 @@ mobile-asset-build:
- output/android/www - output/android/www
- output/ios/www - output/ios/www
.distribute_job: &distribute_job .distribute_job: &distribute_job
image: registry.gitlab.com/gitlab-org/gitter/webapp/deploy-build-image:latest image: registry.gitlab.com/gitlab-org/gitter/webapp/deploy-build-image:latest
stage: pre_deploy stage: pre_deploy
...@@ -89,14 +85,12 @@ mobile-asset-build: ...@@ -89,14 +85,12 @@ mobile-asset-build:
distribute_beta: distribute_beta:
<<: *distribute_job <<: *distribute_job
only: only:
- develop - develop
variables: variables:
DIST_S3_URL: s3://gitter-deployments/gitter-webapp/beta DIST_S3_URL: s3://gitter-deployments/gitter-webapp/beta
distribute_beta_staging: distribute_beta_staging:
<<: *distribute_job <<: *distribute_job
only:
- /^feature\/.*$/
variables: variables:
DIST_S3_URL: s3://gitter-deployments/gitter-webapp/beta-staging DIST_S3_URL: s3://gitter-deployments/gitter-webapp/beta-staging
...@@ -168,13 +162,12 @@ deploy-build-image: ...@@ -168,13 +162,12 @@ deploy-build-image:
tags: tags:
- internal # This has to be within the Gitter network - internal # This has to be within the Gitter network
deploy_beta: &deploy_beta deploy_beta: &deploy_beta
<<: *deploy_job <<: *deploy_job
dependencies: dependencies:
- distribute_beta - distribute_beta
only: only:
- develop - develop
script: script:
- cd $ANSIBLE_DIR && ansible-playbook -i beta --vault-password-file "/root/.vault_pass" playbooks/gitter/webapp-deploy.yml - cd $ANSIBLE_DIR && ansible-playbook -i beta --vault-password-file "/root/.vault_pass" playbooks/gitter/webapp-deploy.yml
environment: environment:
...@@ -200,6 +193,8 @@ deploy_beta_staging: &deploy_beta_staging ...@@ -200,6 +193,8 @@ deploy_beta_staging: &deploy_beta_staging
deploy_beta_staging_manual: deploy_beta_staging_manual:
<<: *deploy_beta_staging <<: *deploy_beta_staging
when: manual when: manual
only:
- /.*/
deploy_staging: &deploy_staging deploy_staging: &deploy_staging
<<: *deploy_job <<: *deploy_job
...@@ -231,7 +226,6 @@ deploy_prod_manual: &deploy_prod ...@@ -231,7 +226,6 @@ deploy_prod_manual: &deploy_prod
name: prod name: prod
url: https://gitter.im url: https://gitter.im
docker-base: docker-base:
image: docker:latest image: docker:latest
stage: docker_images stage: docker_images
......
# 19.37.0 - 2019-2-19
- Fix inline code blocks showing vertical scrollbar in the dark theme
- Thanks to [@tameo](https://gitlab.com/tameo) for the contribution, https://gitlab.com/gitlab-org/gitter/webapp/merge_requests/1335
Developer facing:
- Upgrade from webpack v1 to latest webpack v4, https://gitlab.com/gitlab-org/gitter/webapp/merge_requests/1322
- JavaScript chunks/bundles are now dynamically loaded based on webpack build manifest/artifact
- Try larger timeout for flakey GitHub integration tests
- https://gitlab.com/gitlab-org/gitter/webapp/merge_requests/1334
- https://gitlab.com/gitlab-org/gitter/webapp/merge_requests/1337
- Remove extraneous `lodash` from frontend webpack bundles (use `underscore`), https://gitlab.com/gitlab-org/gitter/webapp/merge_requests/1336
# 19.36.0 - 2019-2-15 # 19.36.0 - 2019-2-15
- Fix GitLab issue decorations opening in GitHub (404) on mobile, https://gitlab.com/gitlab-org/gitter/webapp/merge_requests/1321 - Fix GitLab issue decorations opening in GitHub (404) on mobile, https://gitlab.com/gitlab-org/gitter/webapp/merge_requests/1321
......
...@@ -28,6 +28,9 @@ upload-to-s3: ...@@ -28,6 +28,9 @@ upload-to-s3:
ci-test: ci-test:
mkdir -p output/ mkdir -p output/
# Create the `output/assets/js/webpack-manifest.json` so
# we know which chunks to serve in `boot-script-utils.js`
gulp clientapp:compile:webpack
gulp test --test-coverage --test-suite docker --test-xunit-reports --test-bail gulp test --test-coverage --test-suite docker --test-xunit-reports --test-bail
test: clean test: clean
......
...@@ -31,26 +31,7 @@ gulp.task('clientapp:compile:copy-files', function() { ...@@ -31,26 +31,7 @@ gulp.task('clientapp:compile:copy-files', function() {
gulp.task('clientapp:compile:webpack', ['clientapp:compile:copy-files'], function() { gulp.task('clientapp:compile:webpack', ['clientapp:compile:copy-files'], function() {
return gulp return gulp
.src('./public/js/webpack.config') .src('./public/js/webpack.config')
.pipe( .pipe(webpack(require('../public/js/webpack.config')))
webpack(require('../public/js/webpack.config'), null, function(err, stats) {
if (!stats) return;
/*
Removed as webpack-bundle-size-analyzer is broken
var webpackBundleSizeAnalyzer = require('webpack-bundle-size-analyzer');
var bundleStats = stats.toJson("normal");
var depTrees = webpackBundleSizeAnalyzer.dependencySizeTree(bundleStats);
gutil.log('-----------------------------------------------');
gutil.log('Webpack Bundle Size Report');
gutil.log('-----------------------------------------------');
depTrees.forEach(function (tree) {
return webpackBundleSizeAnalyzer.printDependencySizeTree(tree);
});
gutil.log('-----------------------------------------------');
*/
})
)
.pipe(gulp.dest('output/assets/js')); .pipe(gulp.dest('output/assets/js'));
}); });
......
...@@ -10,10 +10,13 @@ const mqpacker = require('css-mqpacker'); ...@@ -10,10 +10,13 @@ const mqpacker = require('css-mqpacker');
const csswring = require('csswring'); const csswring = require('csswring');
const styleBuilder = require('./style-builder'); const styleBuilder = require('./style-builder');
const getSourceMapOptions = require('./get-sourcemap-options'); const getSourceMapOptions = require('./get-sourcemap-options');
const webpack = require('webpack-stream');
const uglify = require('gulp-uglify'); const uglify = require('gulp-uglify');
const childProcessPromise = require('./child-process-promise'); const childProcessPromise = require('./child-process-promise');
const extractUrls = require('./extract-urls'); const extractUrls = require('./extract-urls');
const bootScriptUtils = require('gitter-web-templates/lib/boot-script-utils');
// We need access to the `clientapp:compile:webpack` task
require('./gulpfile-clientapp');
var opts = require('yargs') var opts = require('yargs')
.option('android', { .option('android', {
...@@ -42,14 +45,14 @@ if (opts.android) { ...@@ -42,14 +45,14 @@ if (opts.android) {
* Hook into the compile stage * Hook into the compile stage
*/ */
gulp.task('embedded:compile', [ gulp.task('embedded:compile', [
'clientapp:compile:copy-files', 'embedded:compile:copy-files',
'embedded:compile:markup', 'embedded:compile:markup',
'embedded:compile:css', 'embedded:compile:css',
'embedded:compile:webpack' 'embedded:compile:copy-webpack-builds'
]); ]);
// We also copy files after the CSS is compiled in `embedded:post-compile:copy-linked-assets` // We also copy files after the CSS is compiled in `embedded:post-compile:copy-linked-assets`
gulp.task('clientapp:compile:copy-files', function() { gulp.task('embedded:compile:copy-files', function() {
return gulp return gulp
.src( .src(
[ [
...@@ -117,10 +120,14 @@ gulp.task('embedded:compile:css', function() { ...@@ -117,10 +120,14 @@ gulp.task('embedded:compile:css', function() {
}); });
/* Generate embedded native */ /* Generate embedded native */
gulp.task('embedded:compile:webpack', ['clientapp:compile:copy-files'], function() { gulp.task('embedded:compile:copy-webpack-builds', ['clientapp:compile:webpack'], function() {
const assets = bootScriptUtils.generateAssetsForChunk('mobile-native-embedded-chat');
return gulp return gulp
.src('./public/js/webpack-mobile-native.config') .src(assets.map(asset => path.join('output/assets/js/', asset)), {
.pipe(webpack(require('../public/js/webpack-mobile-native.config'))) base: './output/assets/js/',
stat: true
})
.pipe(gulp.dest(path.join(buildPath, 'js'))); .pipe(gulp.dest(path.join(buildPath, 'js')));
}); });
......
...@@ -44,6 +44,7 @@ gulp.task('process:assemble:copy-app:files', function() { ...@@ -44,6 +44,7 @@ gulp.task('process:assemble:copy-app:files', function() {
'npm-shrinkwrap.json', 'npm-shrinkwrap.json',
'preinstall.sh', 'preinstall.sh',
'config/**', 'config/**',
'output/assets/js/webpack-manifest.json',
'public/templates/**', 'public/templates/**',
'public/layouts/**', 'public/layouts/**',
'public/js/**', 'public/js/**',
......
...@@ -58,3 +58,20 @@ which will just 404 on a separate device when you try to sign in. ...@@ -58,3 +58,20 @@ which will just 404 on a separate device when you try to sign in.
Recreate your secrets using your local network IP, see https://gitlab.com/gitlab-org/gitter/webapp#configure-service-secrets Recreate your secrets using your local network IP, see https://gitlab.com/gitlab-org/gitter/webapp#configure-service-secrets
Restart the server. You should now be able to access Gitter over your local IP from other devices Restart the server. You should now be able to access Gitter over your local IP from other devices
## View `webpack` bundle visualization (webpack report)
Run the webapp with the `WEBPACK_REPORT` environment variable set to generate the HTML report
macOS/Linux:
```
WEBPACK_REPORT=1 npm start
```
Windows:
```
set WEBPACK_REPORT=1&&npm start
```
Open `webpack-report/index.html` in your browser
...@@ -4,7 +4,7 @@ var isGitHubUser = require('../shared/is-github-user'); ...@@ -4,7 +4,7 @@ var isGitHubUser = require('../shared/is-github-user');
var avatarCdnResolver = require('../shared/avatar-cdn-resolver'); var avatarCdnResolver = require('../shared/avatar-cdn-resolver');
var extractTwitterAvatarInfo = require('../shared/extract-twitter-avatar-info'); var extractTwitterAvatarInfo = require('../shared/extract-twitter-avatar-info');
var DEFAULT = require('url?limit=1024!../../../public/images/default-avatar.png'); // eslint-disable-line var DEFAULT = require('url-loader?limit=1024!../../../public/images/default-avatar.png'); // eslint-disable-line
function getForGitHubUsername(githubUsername) { function getForGitHubUsername(githubUsername) {
return avatarCdnResolver('/gh/u/' + githubUsername); return avatarCdnResolver('/gh/u/' + githubUsername);
......
...@@ -14,6 +14,9 @@ function assertNoDuplicates(collaborators) { ...@@ -14,6 +14,9 @@ function assertNoDuplicates(collaborators) {
} }
describe('collaborators-service #slow #github', function() { describe('collaborators-service #slow #github', function() {
// These tests timeout at 10000 sometimes otherwise
this.timeout(30000);
fixtureLoader.ensureIntegrationEnvironment('#integrationUser1'); fixtureLoader.ensureIntegrationEnvironment('#integrationUser1');
var fixture = fixtureLoader.setup({ var fixture = fixtureLoader.setup({
......
...@@ -6,6 +6,9 @@ var assert = require('assert'); ...@@ -6,6 +6,9 @@ var assert = require('assert');
var GithubContibutorService = require('..').GitHubContributorService; var GithubContibutorService = require('..').GitHubContributorService;
describe('github-contributor-service #slow #github', function() { describe('github-contributor-service #slow #github', function() {
// These tests timeout at 10000 sometimes otherwise
this.timeout(30000);
it('members should detailed emailed', function(done) { it('members should detailed emailed', function(done) {
var gh = new GithubContibutorService(null); var gh = new GithubContibutorService(null);
......
...@@ -7,6 +7,9 @@ var GitHubRepoService = require('..').GitHubRepoService; ...@@ -7,6 +7,9 @@ var GitHubRepoService = require('..').GitHubRepoService;
var fixtureLoader = require('gitter-web-test-utils/lib/test-fixtures'); var fixtureLoader = require('gitter-web-test-utils/lib/test-fixtures');
describe('github-issue-service #slow #github', function() { describe('github-issue-service #slow #github', function() {
// These tests timeout at 10000 sometimes otherwise
this.timeout(30000);
fixtureLoader.ensureIntegrationEnvironment( fixtureLoader.ensureIntegrationEnvironment(
'GITTER_INTEGRATION_USERNAME', 'GITTER_INTEGRATION_USERNAME',
'GITTER_INTEGRATION_REPO_SCOPE_TOKEN', 'GITTER_INTEGRATION_REPO_SCOPE_TOKEN',
......
...@@ -7,6 +7,9 @@ const proxyquireNoCallThru = require('proxyquire').noCallThru(); ...@@ -7,6 +7,9 @@ const proxyquireNoCallThru = require('proxyquire').noCallThru();
const fixtureLoader = require('gitter-web-test-utils/lib/test-fixtures'); const fixtureLoader = require('gitter-web-test-utils/lib/test-fixtures');
describe('gitlab-issue-service #slow #gitlab', function() { describe('gitlab-issue-service #slow #gitlab', function() {
// These tests timeout at 10000 sometimes otherwise
this.timeout(30000);
fixtureLoader.ensureIntegrationEnvironment( fixtureLoader.ensureIntegrationEnvironment(
'GITLAB_USER_USERNAME', 'GITLAB_USER_USERNAME',
'GITLAB_USER_TOKEN', 'GITLAB_USER_TOKEN',
......
...@@ -5,9 +5,8 @@ function create() { ...@@ -5,9 +5,8 @@ function create() {
var webpackMiddleware = require('webpack-dev-middleware'); var webpackMiddleware = require('webpack-dev-middleware');
return webpackMiddleware(webpack(require('../webpack.config')), { return webpackMiddleware(webpack(require('../webpack.config')), {
noInfo: false, logLevel: 'warn',
quiet: true, lazy: false,
lazy: true,
watchOptions: { watchOptions: {
aggregateTimeout: 400 aggregateTimeout: 400
}, },
......
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
var path = require('path'); var path = require('path');
const IS_PRODUCTION = process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'prod';
var config = { var config = {
mode: IS_PRODUCTION ? 'production' : 'development',
entry: { entry: {
sw: require.resolve('./service-worker/sw') sw: require.resolve('./service-worker/sw')
}, },
......
'use strict';
const _ = require('lodash');
const cdn = require('gitter-web-cdn');
function cdnUrlGenerator(url, options = {}) {
if (options.root) {
return options.root + url;
}
return cdn(url, {});
}
let webpackBuildManifest;
function generateAssetsForChunk(chunkName) {
// We have this loaded just in time so we can wait for the initial Gitter boot-up that creates the manifest
try {
webpackBuildManifest =
// eslint-disable-next-line node/no-unpublished-require, node/no-missing-require
webpackBuildManifest || require('../../../output/assets/js/webpack-manifest.json');
} catch (err) {
throw new Error(
`You probably need to wait for the Gitter webpack build to finish. Error occured while requiring \`output/assets/js/webpack-manifest.json\`: ${err}\n${
err.stack
}`
);
}
const defaultAssets = webpackBuildManifest.entrypoints.default.assets || [];
const entryAssets = webpackBuildManifest.entrypoints[chunkName].assets;
const assets = Object.keys(
defaultAssets
.concat(entryAssets)
.filter(asset => !/.*\.map$/.test(asset))
.reduce((assetMap, asset) => {
assetMap[asset] = true;
return assetMap;
}, {})
);
return assets;
}
const bootScriptHelper = _.memoize(function(chunkName, parameters) {
const options = parameters.hash;
const jsRoot = (options && options.jsRoot) || 'js';
const assets = generateAssetsForChunk(chunkName);
const baseUrl = cdnUrlGenerator(jsRoot + '/', options);
const chunkScriptList = assets.map(asset => {
const cdnUrl = cdnUrlGenerator(`${jsRoot}/${asset}`, options);
return `<script type="text/javascript" src="${cdnUrl}"></script>`;
});
return `
<script type="text/javascript">window.webpackPublicPath = '${baseUrl}';</script>
${chunkScriptList.join('\n')}
`;
});
module.exports = {
generateAssetsForChunk,
bootScriptHelper
};
...@@ -7,37 +7,12 @@ var clientEnv = require('gitter-client-env'); ...@@ -7,37 +7,12 @@ var clientEnv = require('gitter-client-env');
var cdn = require('gitter-web-cdn'); var cdn = require('gitter-web-cdn');
var pluralize = require('../shared/helpers/pluralize'); var pluralize = require('../shared/helpers/pluralize');
var when = require('../shared/helpers/when'); var when = require('../shared/helpers/when');
const bootScriptUtils = require('./boot-script-utils');
exports.cdn = function(url, parameters) { function cdnHelper(url, parameters) {
return cdn(url, parameters ? parameters.hash : null); return cdn(url, parameters ? parameters.hash : null);
};
function cdnUrlGenerator(url, options) {
if (options.root) {
return options.root + url;
}
return cdn(url, {});
} }
exports.bootScript = function(url, parameters) {
var options = parameters.hash;
var jsRoot = (options && options.jsRoot) || 'js';
var baseUrl = cdnUrlGenerator(jsRoot + '/', options);
var vendorScriptUrl = cdnUrlGenerator(jsRoot + '/vendor.js', options);
var bootScriptUrl = cdnUrlGenerator(jsRoot + '/' + url + '.js', options);
return util.format(
"<script type='text/javascript'>window.webpackPublicPath = '%s';</script>" +
"<script type='text/javascript' src='%s'></script>" +
"<script type='text/javascript' src='%s'></script>",
baseUrl,
vendorScriptUrl,
bootScriptUrl
);
};
function createEnv(context, options) { function createEnv(context, options) {
if (options) { if (options) {
return _.extend( return _.extend(
...@@ -50,7 +25,7 @@ function createEnv(context, options) { ...@@ -50,7 +25,7 @@ function createEnv(context, options) {
} }
return clientEnv; return clientEnv;
} }
exports.generateEnv = function(parameters) { function generateEnv(parameters) {
var options = parameters.hash; var options = parameters.hash;
var env = createEnv(this, options); var env = createEnv(this, options);
...@@ -61,9 +36,9 @@ exports.generateEnv = function(parameters) { ...@@ -61,9 +36,9 @@ exports.generateEnv = function(parameters) {
';' + ';' +
'</script>' '</script>'
); );
}; }
exports.generateTroupeContext = function(troupeContext, parameters) { function generateTroupeContext(troupeContext, parameters) {
var options = parameters.hash; var options = parameters.hash;
var env = createEnv(this, options); var env = createEnv(this, options);
...@@ -78,16 +53,13 @@ exports.generateTroupeContext = function(troupeContext, parameters) { ...@@ -78,16 +53,13 @@ exports.generateTroupeContext = function(troupeContext, parameters) {
';' + ';' +
'</script>' '</script>'
); );
}; }
exports.pluralize = pluralize;
exports.when = when;
exports.toLowerCase = function(str) { function toLowerCase(str) {
return str.toLowerCase(); return str.toLowerCase();
}; }
exports.pad = function(options) { function pad(options) {
var content = '' + options.fn(this); var content = '' + options.fn(this);
var width = options.hash.width || 40; var width = options.hash.width || 40;
var directionRight = options.hash.direction ? options.hash.direction === 'right' : true; var directionRight = options.hash.direction ? options.hash.direction === 'right' : true;
...@@ -100,10 +72,10 @@ exports.pad = function(options) { ...@@ -100,10 +72,10 @@ exports.pad = function(options) {
} }
} }
return content; return content;
}; }
// FIXME REMOVE THIS ONCE THE NEW ERRORS PAGES ARE DONE // FIXME REMOVE THIS ONCE THE NEW ERRORS PAGES ARE DONE
exports.typewriter = function(el, str) { function typewriter(el, str) {
return util.format( return util.format(
'<script type="text/javascript">\n' + '<script type="text/javascript">\n' +
'var text = "%s";' + 'var text = "%s";' +
...@@ -122,25 +94,40 @@ exports.typewriter = function(el, str) { ...@@ -122,25 +94,40 @@ exports.typewriter = function(el, str) {
str, str,
el el
); );
}; }
exports.formatNumber = function(n) { function formatNumber(n) {
if (n < 1000) return n; if (n < 1000) return n;
if (n < 1000000) return (n / 1000).toFixed(1) + 'k'; if (n < 1000000) return (n / 1000).toFixed(1) + 'k';
return (n / 100000).toFixed(1) + 'm'; return (n / 100000).toFixed(1) + 'm';
}; }
/** FIXME we do not yet cover the ONE-TO-ONE case, also need to do better default values /** FIXME we do not yet cover the ONE-TO-ONE case, also need to do better default values
* githubTypeToClass() takes a GitHub type and provdides a css class * githubTypeToClass() takes a GitHub type and provdides a css class
* *
*/ */
exports.githubTypeToClass = function(type) { function githubTypeToClass(type) {
if (/_CHANNEL/.test(type)) return 'icon-hash'; if (/_CHANNEL/.test(type)) return 'icon-hash';
else if (/REPO/.test(type)) return 'octicon-repo'; else if (/REPO/.test(type)) return 'octicon-repo';
else if (/ORG/.test(type)) return 'octicon-organization'; else if (/ORG/.test(type)) return 'octicon-organization';
else return 'default'; else return 'default';
}; }
exports.getRoomName = function(name) { function getRoomName(name) {
return name.split('/')[1] || 'general'; return name.split('/')[1] || 'general';
}
module.exports = {
cdn: cdnHelper,
bootScript: bootScriptUtils.bootScriptHelper,
generateEnv,
generateTroupeContext,
pluralize,
when,
toLowerCase,
pad,
typewriter,
formatNumber,
githubTypeToClass,
getRoomName
}; };
This diff is collapsed.