summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--app/assets/javascripts/main.js13
-rw-r--r--app/assets/javascripts/sw.js32
-rw-r--r--app/assets/stylesheets/errors.scss3
-rw-r--r--app/controllers/application_controller.rb4
-rw-r--r--app/controllers/pwa_controller.rb5
-rw-r--r--app/views/pwa/offline.html.haml14
-rw-r--r--app/views/pwa/service_worker.js.erb31
-rw-r--r--changelogs/unreleased/pwa.yml5
-rw-r--r--config/routes.rb4
-rw-r--r--config/webpack.config.js33
-rw-r--r--lib/gitlab/path_regex.rb1
-rw-r--r--lib/gitlab/webpack/dev_server_middleware.rb6
-rw-r--r--package.json1
-rw-r--r--yarn.lock51
15 files changed, 153 insertions, 51 deletions
diff --git a/.gitignore b/.gitignore
index 0696dd217af..268f5e6fc35 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,6 +58,7 @@ eslint-report.html
/public/assets/
/public/uploads.*
/public/uploads/
+/public/service_worker.js
/shared/artifacts/
/spec/javascripts/fixtures/blob/pdf/
/spec/javascripts/fixtures/blob/balsamiq/
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 701c944a8ab..e7c8b5ae239 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -285,12 +285,11 @@ document.addEventListener('DOMContentLoaded', () => {
// initialize field errors
$('.gl-show-field-errors').each((i, form) => new GlFieldErrors(form));
+ if ('serviceWorker' in navigator && gon.features && gon.features.serviceWorker) {
+ navigator.serviceWorker.register('/service_worker.js', {
+ scope: '/',
+ });
+ }
+
requestIdleCallback(deferredInitialisation);
});
-
-// Register a service worker if our browser allows it
-if (navigator.serviceWorker) {
- navigator.serviceWorker.register('/@service_worker.js', {
- scope: '/',
- });
-}
diff --git a/app/assets/javascripts/sw.js b/app/assets/javascripts/sw.js
new file mode 100644
index 00000000000..53151561e10
--- /dev/null
+++ b/app/assets/javascripts/sw.js
@@ -0,0 +1,32 @@
+const CURRENT_CACHE = '<%= Gitlab.version %>_<%= Gitlab.revision %>';
+const OFFLINE_PAGE = '/-/offline';
+
+// eslint-disable-next-line no-restricted-globals
+self.addEventListener('install', event => {
+ event.waitUntil(caches.open(CURRENT_CACHE).then(cache => cache.add(OFFLINE_PAGE)));
+});
+
+// eslint-disable-next-line no-restricted-globals
+self.addEventListener('activate', event => {
+ event.waitUntil(
+ caches
+ .keys()
+ .then(cacheNames =>
+ Promise.all(
+ cacheNames.map(cache =>
+ cache !== CURRENT_CACHE ? caches.delete(cache) : Promise.resolve(),
+ ),
+ ),
+ ),
+ );
+});
+
+// eslint-disable-next-line no-restricted-globals
+self.addEventListener('fetch', event => {
+ const { request } = event;
+ const { method, mode } = request;
+
+ if (method === 'GET' && mode === 'navigate') {
+ event.respondWith(fetch(request).catch(() => caches.match(OFFLINE_PAGE)));
+ }
+});
diff --git a/app/assets/stylesheets/errors.scss b/app/assets/stylesheets/errors.scss
index 4b82038fd4f..658e0ff638e 100644
--- a/app/assets/stylesheets/errors.scss
+++ b/app/assets/stylesheets/errors.scss
@@ -41,10 +41,9 @@ h3 {
}
img {
+ max-width: 80vw;
display: block;
margin: 40px auto;
- max-width: 80vw;
- width: 500px;
}
a {
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index b7eb6af6d67..57f2ded0448 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -26,6 +26,10 @@ class ApplicationController < ActionController::Base
before_action :set_usage_stats_consent_flag
before_action :check_impersonation_availability
+ before_action do
+ push_frontend_feature_flag(:service_worker)
+ end
+
around_action :set_locale
after_action :set_page_title_header, if: :json_request?
diff --git a/app/controllers/pwa_controller.rb b/app/controllers/pwa_controller.rb
index 3311af499cd..051b9df0803 100644
--- a/app/controllers/pwa_controller.rb
+++ b/app/controllers/pwa_controller.rb
@@ -9,5 +9,10 @@ class PwaController < ApplicationController
end
def service_worker
+ respond_to do |format|
+ format.js do
+ render file: "public/assets/webpack/service_worker.js", layout: nil, content_type: 'text/javascript'
+ end
+ end
end
end
diff --git a/app/views/pwa/offline.html.haml b/app/views/pwa/offline.html.haml
index b62a64ae057..bfec6cb7275 100644
--- a/app/views/pwa/offline.html.haml
+++ b/app/views/pwa/offline.html.haml
@@ -1,5 +1,17 @@
- content_for(:title, 'Offline')
-= image_tag('illustrations/pipelines_failed.svg', alt: 'offline', lazy: false)
+
+-# This SVG is inlined so we can cache it with the page
+%svg{ :viewbox => "0 0 492.50943 453.67966", :xmlns => "http://www.w3.org/2000/svg", :style => "display: block; margin: 40px auto; max-width: 80vw; width: 300px;" }
+ %g{ :fill => "none", "fill-rule" => "evenodd" }
+ %path{ :d => "M491.58891 259.39833l-27.55867-84.81467L409.41291 6.48633c-2.80934-8.648-15.04533-8.648-17.856 0l-54.61867 168.09733H155.57158l-54.62-168.09733c-2.80933-8.648-15.04533-8.648-17.856 0L28.47825 174.58366.92092 259.39833c-2.514669 7.736.24 16.21066 6.82 20.992l238.51333 173.28933 238.51466-173.28933c6.58-4.78134 9.33333-13.256 6.82-20.992", :fill => "#fc6d26" }
+ %path{ :d => "M246.25478 453.67966l90.684-279.096h-181.368z", :fill => "#e24329" }
+ %path{ :d => "M246.25478 453.67912l-90.684-279.09466h-127.092z", :fill => "#fc6d26" }
+ %path{ :d => "M28.47878 174.58406L.92012 259.39873c-2.513336 7.736.24 16.21066 6.82133 20.99066l238.51333 173.28933z", :fill => "#fca326" }
+ %path{ :d => "M28.47878 174.58433h127.092L100.95212 6.487c-2.81067-8.64933-15.04667-8.64933-17.856 0z", :fill => "#e24329" }
+ %path{ :d => "M246.25478 453.67912l90.684-279.09466h127.09199z", :fill => "#fc6d26" }
+ %path{ :d => "M464.03064 174.58406l27.55867 84.81467c2.51333 7.736-.24 16.21066-6.82134 20.99066L246.25465 453.67872z", :fill => "#fca326" }
+ %path{ :d => "M464.03064 174.58433h-127.092L391.55731 6.487c2.81066-8.64933 15.04666-8.64933 17.856 0z", :fill => "#e24329" }
+
.container
%h3
= s_('offline|You don\'t currently have an internet connection')
diff --git a/app/views/pwa/service_worker.js.erb b/app/views/pwa/service_worker.js.erb
deleted file mode 100644
index b409eafb595..00000000000
--- a/app/views/pwa/service_worker.js.erb
+++ /dev/null
@@ -1,31 +0,0 @@
-const CURRENT_CACHE = '<%= Gitlab::VERSION %>_<%= Gitlab.revision %>';
-
-// eslint-disable-next-line no-restricted-globals
-self.addEventListener('install', event => {
- event.waitUntil(caches.open(CURRENT_CACHE).then(cache => cache.addAll(['/-/offline'])));
-});
-
-// eslint-disable-next-line no-restricted-globals
-self.addEventListener('activate', event => {
- event.waitUntil(
- caches.keys().then(cacheNames => {
- return Promise.all(
- cacheNames.map(cache =>
- cache !== CURRENT_CACHE ? caches.delete(cache) : Promise.resolve(),
- ),
- );
- }),
- );
-});
-
-// eslint-disable-next-line no-restricted-globals
-self.addEventListener('fetch', event => {
- const { request } = event;
-
- // We only want to intercept the GET requests for now
- if (request.method === 'GET') {
- event.respondWith(
- fetch(request).catch(() => (request.mode === 'navigate' ? caches.match('/-/offline') : null)),
- );
- }
-});
diff --git a/changelogs/unreleased/pwa.yml b/changelogs/unreleased/pwa.yml
new file mode 100644
index 00000000000..e5d3e086cf6
--- /dev/null
+++ b/changelogs/unreleased/pwa.yml
@@ -0,0 +1,5 @@
+---
+title: Qualifies GitLab as a Progressive Web App by adding basic offline support
+merge_request: 23578
+author:
+type: added
diff --git a/config/routes.rb b/config/routes.rb
index 7a741dd4300..4b00df2e701 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -151,8 +151,6 @@ Rails.application.routes.draw do
root to: "root#index"
- # Service Worker
- get '@service_worker.js', to: 'pwa#service_worker'
-
+ get 'service_worker.js', to: 'pwa#service_worker'
get '*unmatched_route', to: 'application#route_not_found'
end
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 20b3f4c0264..dce09c2f3f5 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -7,6 +7,7 @@ const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
const CompressionPlugin = require('compression-webpack-plugin');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
+const CopyPlugin = require('copy-webpack-plugin');
const ROOT_PATH = path.resolve(__dirname, '..');
const CACHE_PATH = process.env.WEBPACK_CACHE_PATH || path.join(ROOT_PATH, 'tmp/cache');
@@ -106,6 +107,27 @@ if (IS_EE) {
});
}
+function version(content) {
+ const VERSION_FILE = path.resolve(__dirname, '../VERSION');
+ const REVISION_FILE = path.resolve(__dirname, '../REVISION');
+
+ // Return the GitLab version from the version file, or unknown
+ const GITLAB_VERSION = fs.existsSync(VERSION_FILE)
+ ? fs.readFileSync(VERSION_FILE, 'utf8').trim()
+ : 'Unknown';
+
+ // Return the revision from the revision file, or a random string
+ // This helps bust the cache when changes are made locally
+ const GITLAB_REVISION = fs.existsSync(REVISION_FILE)
+ ? fs.readFileSync(REVISION_FILE, 'utf8').trim()
+ : Math.random().toString(36);
+
+ return content
+ .toString()
+ .replace('<%= Gitlab.version %>', GITLAB_VERSION.trim())
+ .replace('<%= Gitlab.revision %>', GITLAB_REVISION.trim());
+}
+
module.exports = {
mode: IS_PRODUCTION ? 'production' : 'development',
@@ -282,6 +304,17 @@ module.exports = {
}
}),
+ // Copies and versions the service workers
+ new CopyPlugin([
+ {
+ from: path.join(ROOT_PATH, 'app/assets/javascripts/sw.js'),
+ to: path.join(ROOT_PATH, 'public/assets/webpack/service_worker.js'),
+ transform(content) {
+ return version(content);
+ },
+ },
+ ]),
+
// compression can require a lot of compute time and is disabled in CI
IS_PRODUCTION && !NO_COMPRESSION && new CompressionPlugin(),
diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb
index 3c888be0710..0d4eebf4a47 100644
--- a/lib/gitlab/path_regex.rb
+++ b/lib/gitlab/path_regex.rb
@@ -51,6 +51,7 @@ module Gitlab
s
search
sent_notifications
+ service_worker.js
slash-command-logo.png
snippets
u
diff --git a/lib/gitlab/webpack/dev_server_middleware.rb b/lib/gitlab/webpack/dev_server_middleware.rb
index fda41da5a94..758a8af214c 100644
--- a/lib/gitlab/webpack/dev_server_middleware.rb
+++ b/lib/gitlab/webpack/dev_server_middleware.rb
@@ -16,12 +16,16 @@ module Gitlab
end
def perform_request(env)
- if @proxy_path && env['PATH_INFO'].start_with?("/#{@proxy_path}")
+ if @proxy_path && env['PATH_INFO'].start_with?("/#{@proxy_path}", "/service_worker.js")
if relative_url_root = Rails.application.config.relative_url_root
env['SCRIPT_NAME'] = ""
env['REQUEST_PATH'].sub!(/\A#{Regexp.escape(relative_url_root)}/, '')
end
+ if env['PATH_INFO'].start_with?("/service_worker.js")
+ env['PATH_INFO'] = "/assets/webpack/service_worker.js"
+ end
+
super(env)
else
@app.call(env)
diff --git a/package.json b/package.json
index cb063c9782c..8dadb7fe4a2 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,7 @@
"clipboard": "^1.7.1",
"codesandbox-api": "^0.0.20",
"compression-webpack-plugin": "^2.0.0",
+ "copy-webpack-plugin": "^5.0.1",
"core-js": "^2.4.1",
"cropper": "^2.3.0",
"css-loader": "^1.0.0",
diff --git a/yarn.lock b/yarn.lock
index 2bdbca103b1..0241363d34d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1864,7 +1864,7 @@ bytes@3.0.0:
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
-cacache@^11.0.2, cacache@^11.2.0:
+cacache@^11.0.2, cacache@^11.2.0, cacache@^11.3.1:
version "11.3.2"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.2.tgz#2d81e308e3d258ca38125b676b98b2ac9ce69bfa"
integrity sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==
@@ -2542,6 +2542,23 @@ copy-to-clipboard@^3.0.8:
dependencies:
toggle-selection "^1.0.3"
+copy-webpack-plugin@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.0.1.tgz#97997989cc5bc69976bf64f660bd19663481f089"
+ integrity sha512-yMTURAkYZO/6h6pGMbHQl2jpKtRNC+0Cy/4kRRP6qUHmpbGGAzNnyMecE6aHgGFCb4ksrL3YcDqYGb8ds3J9cw==
+ dependencies:
+ cacache "^11.3.1"
+ find-cache-dir "^2.0.0"
+ glob-parent "^3.1.0"
+ globby "^7.1.1"
+ is-glob "^4.0.0"
+ loader-utils "^1.1.0"
+ minimatch "^3.0.4"
+ normalize-path "^3.0.0"
+ p-limit "^2.1.0"
+ serialize-javascript "^1.4.0"
+ webpack-log "^2.0.0"
+
core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1:
version "2.5.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
@@ -3271,7 +3288,7 @@ diffie-hellman@^5.0.0:
miller-rabin "^4.0.0"
randombytes "^2.0.0"
-dir-glob@^2.2.1:
+dir-glob@^2.0.0, dir-glob@^2.2.1:
version "2.2.2"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4"
integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==
@@ -4680,6 +4697,18 @@ globby@^6.1.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
+globby@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680"
+ integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA=
+ dependencies:
+ array-union "^1.0.1"
+ dir-glob "^2.0.0"
+ glob "^7.1.2"
+ ignore "^3.3.5"
+ pify "^3.0.0"
+ slash "^1.0.0"
+
globby@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-9.0.0.tgz#3800df736dc711266df39b4ce33fe0d481f94c23"
@@ -5117,6 +5146,11 @@ ignore-walk@^3.0.1:
dependencies:
minimatch "^3.0.4"
+ignore@^3.3.5:
+ version "3.3.10"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
+ integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==
+
ignore@^4.0.3, ignore@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
@@ -7804,10 +7838,10 @@ p-limit@^1.1.0:
dependencies:
p-try "^1.0.0"
-p-limit@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec"
- integrity sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==
+p-limit@^2.0.0, p-limit@^2.1.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2"
+ integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==
dependencies:
p-try "^2.0.0"
@@ -9476,6 +9510,11 @@ sisteransi@^1.0.0:
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c"
integrity sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==
+slash@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
+ integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=
+
slash@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"