diff --git a/CHANGELOG.md b/CHANGELOG.md index 265cbe80..ee745baa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +Changes in [0.9.6](https://github.com/vector-im/riot-web/releases/tag/v0.9.6) (2017-01-16) +========================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.6-rc.1...v0.9.6) + + * Update to matrix-js-sdk 0.9.6 for video calling fix + +Changes in [0.9.6-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.9.6-rc.1) (2017-01-13) +==================================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.5...v0.9.6-rc.1) + + * Build the js-sdk in the CI script + [\#2920](https://github.com/vector-im/riot-web/pull/2920) + * Hopefully fix Windows shortcuts + [\#2917](https://github.com/vector-im/riot-web/pull/2917) + * Update README now the js-sdk has a transpile step + [\#2921](https://github.com/vector-im/riot-web/pull/2921) + * Use the role for 'toggle dev tools' + [\#2915](https://github.com/vector-im/riot-web/pull/2915) + * Enable screen sharing easter-egg in desktop app + [\#2909](https://github.com/vector-im/riot-web/pull/2909) + * make electron send email validation URLs with a nextlink of riot.im + [\#2808](https://github.com/vector-im/riot-web/pull/2808) + * add Debian Stretch install steps to readme + [\#2809](https://github.com/vector-im/riot-web/pull/2809) + * Update desktop build instructions fixes #2792 + [\#2793](https://github.com/vector-im/riot-web/pull/2793) + * CSS for the delete threepid button + [\#2784](https://github.com/vector-im/riot-web/pull/2784) + Changes in [0.9.5](https://github.com/vector-im/riot-web/releases/tag/v0.9.5) (2016-12-24) ========================================================================================== [Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.4...v0.9.5) diff --git a/README.md b/README.md index ba59ea26..e4f04339 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ https://riot.im/develop for those who like living dangerously. To host your own copy of Riot, the quickest bet is to use a pre-built released version of Riot: -1. Download the latest version from https://github.com/vector-im/vector-web/releases +1. Download the latest version from https://github.com/vector-im/riot-web/releases 1. Untar the tarball on your web server 1. Move (or symlink) the vector-x.x.x directory to an appropriate name 1. If desired, copy `config.sample.json` to `config.json` and edit it @@ -44,7 +44,7 @@ access to Riot (or other apps) due to sharing the same domain. We have put some coarse mitigations into place to try to protect against this situation, but it's still not good practice to do it in the first place. See -https://github.com/vector-im/vector-web/issues/1977 for more details. +https://github.com/vector-im/riot-web/issues/1977 for more details. Building From Source ==================== @@ -53,8 +53,8 @@ Riot is a modular webapp built with modern ES6 and requires a npm build system to build. 1. Install or update `node.js` so that your `npm` is at least at version `2.0.0` -1. Clone the repo: `git clone https://github.com/vector-im/vector-web.git` -1. Switch to the vector-web directory: `cd vector-web` +1. Clone the repo: `git clone https://github.com/vector-im/riot-web.git` +1. Switch to the riot-web directory: `cd riot-web` 1. Install the prerequisites: `npm install` 1. If you are using the `develop` branch of vector-web, you will probably need to rebuild some of the dependencies, due to @@ -109,7 +109,7 @@ Running as a Desktop app ======================== Riot can also be run as a desktop app, wrapped in electron. You can download a -pre-built version from https://riot.im/download/desktop/ or, if you prefer, +pre-built version from https://riot.im/desktop.html or, if you prefer, built it yourself. To run as a desktop app: @@ -179,13 +179,13 @@ the `component-index.js` for the app (used in future for skinning) development on Riot forcing `matrix-react-sdk` to move fast at the expense of maintaining a clear abstraction between the two.** Hacking on Riot inevitably means hacking equally on `matrix-react-sdk`, and there are bits of -`matrix-react-sdk` behaviour incorrectly residing in the `vector-web` project +`matrix-react-sdk` behaviour incorrectly residing in the `riot-web` project (e.g. matrix-react-sdk specific CSS), and a bunch of Riot specific behaviour in the `matrix-react-sdk` (grep for `vector` / `riot`). This separation problem will be solved asap once development on Riot (and thus matrix-react-sdk) has stabilised. Until then, the two projects should basically be considered as a single unit. In particular, `matrix-react-sdk` issues are currently filed -against `vector-web` in github. +against `riot-web` in github. Please note that Riot is intended to run correctly without access to the public internet. So please don't depend on resources (JS libs, CSS, images, fonts) @@ -220,8 +220,8 @@ Then similarly with `matrix-react-sdk`: Finally, build and start Riot itself: -1. `git clone git@github.com:vector-im/vector-web.git` -1. `cd vector-web` +1. `git clone git@github.com:vector-im/riot-web.git` +1. `cd riot-web` 1. `git checkout develop` 1. `npm install` 1. `rm -r node_modules/matrix-js-sdk; ln -s ../../matrix-js-sdk node_modules/` diff --git a/electron/src/electron-main.js b/electron/src/electron-main.js index 675640a5..a03a8755 100644 --- a/electron/src/electron-main.js +++ b/electron/src/electron-main.js @@ -112,7 +112,16 @@ function startAutoUpdate(update_base_url) { // 204 No Content. On windows it takes a base path and looks for // files under that path. if (process.platform == 'darwin') { - electron.autoUpdater.setFeedURL(update_base_url + 'macos/'); + // include the current version in the URL we hit. Electron doesn't add + // it anywhere (apart from the User-Agent) so it's up to us. We could + // (and previously did) just use the User-Agent, but this doesn't + // rely on NSURLConnection setting the User-Agent to what we expect, + // and also acts as a convenient cache-buster to ensure that when the + // app updates it always gets a fresh value to avoid update-looping. + electron.autoUpdater.setFeedURL( + update_base_url + + 'macos/?localVersion=' + encodeURIComponent(electron.app.getVersion()) + ); } else if (process.platform == 'win32') { electron.autoUpdater.setFeedURL(update_base_url + 'win32/' + process.arch + '/'); } else { diff --git a/package.json b/package.json index b8fd1574..722e0cf0 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "riot-web", "productName": "Riot", "main": "electron/src/electron-main.js", - "version": "0.9.5", + "version": "0.9.6", "description": "A feature-rich client for Matrix.org", "author": "Vector Creations Ltd.", "repository": { diff --git a/scripts/redeploy.py b/scripts/redeploy.py index 36585f53..4394f292 100755 --- a/scripts/redeploy.py +++ b/scripts/redeploy.py @@ -1,13 +1,32 @@ #!/usr/bin/env python +# +# auto-deploy script for https://riot.im/develop +# +# Listens for HTTP hits. When it gets one, downloads the artifact from jenkins +# and deploys it as the new version. +# +# Requires the following python packages: +# +# - requests +# - flask +# from __future__ import print_function import json, requests, tarfile, argparse, os, errno +import time from urlparse import urljoin from flask import Flask, jsonify, request, abort + app = Flask(__name__) -arg_jenkins_url, arg_extract_path, arg_should_clean, arg_symlink, arg_config_location = ( - None, None, None, None, None -) +arg_jenkins_url = None +arg_extract_path = None +arg_bundles_path = None +arg_should_clean = None +arg_symlink = None +arg_config_location = None + +class DeployException(Exception): + pass def download_file(url): local_filename = url.split('/')[-1] @@ -57,6 +76,9 @@ def on_receive_jenkins_poke(): abort(400, "Missing or bad build number") return + return fetch_jenkins_build(job_name, build_num) + +def fetch_jenkins_build(job_name, build_num): artifact_url = urljoin( arg_jenkins_url, "job/%s/%s/api/json" % (job_name, build_num) ) @@ -106,7 +128,31 @@ def on_receive_jenkins_poke(): arg_jenkins_url, "job/%s/%s/artifact/%s" % (job_name, build_num, tar_gz_path) ) - print("Retrieving .tar.gz file: %s" % tar_gz_url) + # we extract into a directory based on the build number. This avoids the + # problem of multiple builds building the same git version and thus having + # the same tarball name. That would lead to two potential problems: + # (a) sometimes jenkins serves corrupted artifacts; we would replace + # a good deploy with a bad one + # (b) we'll be overwriting the live deployment, which means people might + # see half-written files. + build_dir = os.path.join(arg_extract_path, "%s-#%s" % (job_name, build_num)) + try: + deploy_tarball(tar_gz_url, build_dir) + except DeployException as e: + abort(400, e.message) + + return jsonify({}) + +def deploy_tarball(tar_gz_url, build_dir): + """Download a tarball from jenkins and deploy it as the new version + """ + print("Deploying %s to %s" % (tar_gz_url, build_dir)) + + if os.path.exists(build_dir): + raise DeployException( + "Not deploying. We have previously deployed this build." + ) + os.mkdir(build_dir) # we rely on the fact that flask only serves one request at a time to # ensure that we do not overwrite a tarball from a concurrent request. @@ -114,19 +160,6 @@ def on_receive_jenkins_poke(): print("Downloaded file: %s" % filename) try: - # we extract into a directory based on the build number. This avoids the - # problem of multiple builds building the same git version and thus having - # the same tarball name. That would lead to two potential problems: - # (a) sometimes jenkins serves corrupted artifacts; we would replace - # a good deploy with a bad one - # (b) we'll be overwriting the live deployment, which means people might - # see half-written files. - build_dir = os.path.join(arg_extract_path, "%s-#%s" % (job_name, build_num)) - if os.path.exists(build_dir): - abort(400, "Not deploying. We have previously deployed this build.") - return - os.mkdir(build_dir) - untar_to(filename, build_dir) print("Extracted to: %s" % build_dir) finally: @@ -139,9 +172,47 @@ def on_receive_jenkins_poke(): if arg_config_location: create_symlink(source=arg_config_location, linkname=os.path.join(extracted_dir, 'config.json')) + if arg_bundles_path: + extracted_bundles = os.path.join(extracted_dir, 'bundles') + move_bundles(source=extracted_bundles, dest=arg_bundles_path) + + # replace the (hopefully now empty) extracted_bundles dir with a + # symlink to the common dir. + relpath = os.path.relpath(arg_bundles_path, extracted_dir) + os.rmdir(extracted_bundles) + print ("Symlink %s -> %s" % (extracted_bundles, relpath)) + os.symlink(relpath, extracted_bundles) + create_symlink(source=extracted_dir, linkname=arg_symlink) - return jsonify({}) +def move_bundles(source, dest): + """Move the contents of the 'bundles' directory to a common dir + + We check that we will not be overwriting anything before we proceed. + + Args: + source (str): path to 'bundles' within the extracted tarball + dest (str): target common directory + """ + + if not os.path.isdir(dest): + os.mkdir(dest) + + # build a map from source to destination, checking for non-existence as we go. + renames = {} + for f in os.listdir(source): + dst = os.path.join(dest, f) + if os.path.exists(dst): + raise DeployException( + "Not deploying. The bundle includes '%s' which we have previously deployed." + % f + ) + renames[os.path.join(source, f)] = dst + + for (src, dst) in renames.iteritems(): + print ("Move %s -> %s" % (src, dst)) + os.rename(src, dst) + if __name__ == "__main__": parser = argparse.ArgumentParser("Runs a Vector redeployment server.") @@ -161,6 +232,13 @@ if __name__ == "__main__": "The location to extract .tar.gz files to." ) ) + parser.add_argument( + "-b", "--bundles-dir", dest="bundles_dir", help=( + "A directory to move the contents of the 'bundles' directory to. A \ + symlink to the bundles directory will also be written inside the \ + extracted tarball. Example: './bundles'." + ) + ) parser.add_argument( "-c", "--clean", dest="clean", action="store_true", default=False, help=( "Remove .tar.gz files after they have been downloaded and extracted." @@ -179,18 +257,34 @@ if __name__ == "__main__": To this location." ) ) + parser.add_argument( + "--test", dest="tarball_uri", help=( + "Don't start an HTTP listener. Instead download a build from Jenkins \ + immediately." + ), + ) + args = parser.parse_args() if args.jenkins.endswith("/"): # important for urljoin arg_jenkins_url = args.jenkins else: arg_jenkins_url = args.jenkins + "/" arg_extract_path = args.extract + arg_bundles_path = args.bundles_dir arg_should_clean = args.clean arg_symlink = args.symlink arg_config_location = args.config - print( - "Listening on port %s. Extracting to %s%s. Symlinking to %s. Jenkins URL: %s. Config location: %s" % - (args.port, arg_extract_path, - " (clean after)" if arg_should_clean else "", arg_symlink, arg_jenkins_url, arg_config_location) - ) - app.run(host="0.0.0.0", port=args.port, debug=True) + + if not os.path.isdir(arg_extract_path): + os.mkdir(arg_extract_path) + + if args.tarball_uri is not None: + build_dir = os.path.join(arg_extract_path, "test-%i" % (time.time())) + deploy_tarball(args.tarball_uri, build_dir) + else: + print( + "Listening on port %s. Extracting to %s%s. Symlinking to %s. Jenkins URL: %s. Config location: %s" % + (args.port, arg_extract_path, + " (clean after)" if arg_should_clean else "", arg_symlink, arg_jenkins_url, arg_config_location) + ) + app.run(host="0.0.0.0", port=args.port, debug=True) diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss index 8824c659..94a92b23 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss @@ -44,7 +44,7 @@ input[type=text].mx_DirectorySearchBox_input:focus { padding-right: 10px; background-color: $plinth-bg-color; border-radius: 3px; - background-image: url('img/icon-return.svg'); + background-image: url('../../img/icon-return.svg'); background-position: 8px 70%; background-repeat: no-repeat; text-indent: 18px; @@ -61,7 +61,7 @@ input[type=text].mx_DirectorySearchBox_input:focus { .mx_DirectorySearchBox_clear { display: inline-block; vertical-align: middle; - background: url('img/icon_context_delete.svg'); + background: url('../../img/icon_context_delete.svg'); background-position: 0 50%; background-repeat: no-repeat; width: 15px; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss index 22364043..286a709d 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss @@ -65,7 +65,7 @@ limitations under the License. position: absolute; content: ""; border-radius: 40px; - background-image: url("img/icons_ellipsis.svg"); + background-image: url("../../img/icons_ellipsis.svg"); background-size: 25px; width: 24px; height: 24px; diff --git a/src/skins/vector/css/vector-web/_fonts.scss b/src/skins/vector/css/vector-web/_fonts.scss index 719eeebc..52ac95b5 100644 --- a/src/skins/vector/css/vector-web/_fonts.scss +++ b/src/skins/vector/css/vector-web/_fonts.scss @@ -3,44 +3,46 @@ * Includes extended Latin, Greek, Cyrillic and Vietnamese character sets */ +/* the 'src' links are relative to the bundle.css, which is in a subdirectory. + */ @font-face { font-family: 'Open Sans'; - src: url('fonts/Open_Sans/OpenSans-Regular.ttf') format('truetype'); + src: url('../../fonts/Open_Sans/OpenSans-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; } @font-face { font-family: 'Open Sans'; - src: url('fonts/Open_Sans/OpenSans-Italic.ttf') format('truetype'); + src: url('../../fonts/Open_Sans/OpenSans-Italic.ttf') format('truetype'); font-weight: 400; font-style: italic; } @font-face { font-family: 'Open Sans'; - src: url('fonts/Open_Sans/OpenSans-Semibold.ttf') format('truetype'); + src: url('../../fonts/Open_Sans/OpenSans-Semibold.ttf') format('truetype'); font-weight: 600; font-style: normal; } @font-face { font-family: 'Open Sans'; - src: url('fonts/Open_Sans/OpenSans-SemiboldItalic.ttf') format('truetype'); + src: url('../../fonts/Open_Sans/OpenSans-SemiboldItalic.ttf') format('truetype'); font-weight: 600; font-style: italic; } @font-face { font-family: 'Open Sans'; - src: url('fonts/Open_Sans/OpenSans-Bold.ttf') format('truetype'); + src: url('../../fonts/Open_Sans/OpenSans-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; } @font-face { font-family: 'Open Sans'; - src: url('fonts/Open_Sans/OpenSans-BoldItalic.ttf') format('truetype'); + src: url('../../fonts/Open_Sans/OpenSans-BoldItalic.ttf') format('truetype'); font-weight: 700; font-style: italic; } @@ -52,14 +54,14 @@ @font-face { font-family: 'Fira Mono'; - src: url('fonts/Fira_Mono/FiraMono-Regular.ttf') format('truetype'); + src: url('../../fonts/Fira_Mono/FiraMono-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; } @font-face { font-family: 'Fira Mono'; - src: url('fonts/Fira_Mono/FiraMono-Bold.ttf') format('truetype'); + src: url('../../fonts/Fira_Mono/FiraMono-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; } diff --git a/src/skins/vector/css/vector-web/views/elements/_ImageView.scss b/src/skins/vector/css/vector-web/views/elements/_ImageView.scss index 66c26e12..8ed0698a 100644 --- a/src/skins/vector/css/vector-web/views/elements/_ImageView.scss +++ b/src/skins/vector/css/vector-web/views/elements/_ImageView.scss @@ -50,7 +50,7 @@ limitations under the License. max-height: 100%; /* object-fit hack needed for Chrome due to Chrome not re-laying-out until you refresh */ object-fit: contain; - /* background-image: url('img/trans.png'); */ + /* background-image: url('../../img/trans.png'); */ pointer-events: all; } diff --git a/webpack.config.js b/webpack.config.js index e05f877a..3a701965 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -45,7 +45,17 @@ module.exports = { }, output: { path: path.join(__dirname, "webapp"), - filename: "[name].[chunkhash].js", + + // the generated js (and CSS, from the ExtractTextPlugin) are put in a + // unique subdirectory for the build. There will only be one such + // 'bundle' directory in the generated tarball; however, hosting + // servers can collect 'bundles' from multiple versions into one + // directory and symlink it into place - this allows users who loaded + // an older version of the application to continue to access webpack + // chunks even after the app is redeployed. + // + filename: "bundles/[hash]/[name].js", + chunkFilename: "bundles/[hash]/[name].js", devtoolModuleFilenameTemplate: function(info) { // Reading input source maps gives only relative paths here for // everything. Until I figure out how to fix this, this is a @@ -84,7 +94,7 @@ module.exports = { }), new ExtractTextPlugin( - "[name].[contenthash].css", + "bundles/[hash]/[name].css", { allChunks: true }