# Generated git-commit.version file
/git-commit.version
+
+# npm-rails
+/node_modules
+/npm-debug.log
gem 'logstash-event'
gem 'safe_yaml'
+
+gem 'npm-rails'
net-ssh (>= 2.6.5)
nokogiri (1.6.6.4)
mini_portile (~> 0.6.0)
+ npm-rails (0.2.1)
+ rails (>= 3.2)
oj (2.11.2)
os (0.9.6)
passenger (4.0.57)
mocha
morrisjs-rails
multi_json
+ npm-rails
oj
passenger
piwik_analytics
wiselinks
BUNDLED WITH
- 1.13.2
+ 1.15.1
//= require raphael
//= require morris
//= require jquery.number.min
+//= require npm-dependencies
+//= require mithril/stream/stream
//= require_tree .
+window.m = Object.assign(window.Mithril, {stream: window.m.stream})
+
jQuery(function($){
$(document).ajaxStart(function(){
$('.modal-with-loading-spinner .spinner').show();
// Need this to trigger input validation/synchronization callbacks because some browsers
// auto-fill form fields (e.g., when navigating "back" to a page where some text
// had been entered in a search box) without triggering a change or input event.
- $('input').trigger('input');
+ $('input').each(function(el) {
+ $(el).trigger($.Event('input', {currentTarget: el}));
+ });
});
HeaderRowFixer = function(selector) {
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+window.CollectionsTable = {
+ maybeLoadMore: function(dom) {
+ var loader = this.loader
+ if (loader.done || loader.loading)
+ // Can't start getting more items anyway: no point in
+ // checking anything else.
+ return
+ var contentRect = dom.getBoundingClientRect()
+ var scroller = window // TODO: use dom's nearest ancestor with scrollbars
+ if (contentRect.bottom < 2 * scroller.innerHeight) {
+ // We have less than 1 page worth of content available
+ // below the visible area. Load more.
+ loader.loadMore()
+ // Indicate loading is in progress.
+ window.requestAnimationFrame(m.redraw)
+ }
+ },
+ oncreate: function(vnode) {
+ vnode.state.maybeLoadMore = vnode.state.maybeLoadMore.bind(vnode.state, vnode.dom)
+ window.addEventListener('scroll', vnode.state.maybeLoadMore)
+ window.addEventListener('resize', vnode.state.maybeLoadMore)
+ vnode.state.timer = window.setInterval(vnode.state.maybeLoadMore, 200)
+ vnode.state.loader = vnode.attrs.loader
+ vnode.state.onupdate(vnode)
+ },
+ onupdate: function(vnode) {
+ vnode.state.loader = vnode.attrs.loader
+ },
+ onremove: function(vnode) {
+ window.clearInterval(vnode.state.timer)
+ window.removeEventListener('scroll', vnode.state.maybeLoadMore)
+ window.removeEventListener('resize', vnode.state.maybeLoadMore)
+ },
+ view: function(vnode) {
+ return m('table.table.table-condensed', [
+ m('thead', m('tr', [
+ m('th'),
+ m('th', 'uuid'),
+ m('th', 'name'),
+ m('th', 'last modified'),
+ ])),
+ m('tbody', [
+ vnode.attrs.loader.items() && vnode.attrs.loader.items().map(function(item) {
+ return m('tr', [
+ m('td', m('a.btn.btn-xs.btn-default', {href: item.session.baseURL.replace('://', '://workbench.')+'collections/'+item.uuid}, 'Show')),
+ m('td.arvados-uuid', item.uuid),
+ m('td', item.name || '(unnamed)'),
+ m('td', m(LocalizedDateTime, {parse: item.modified_at})),
+ ])
+ }),
+ ]),
+ m('tfoot', m('tr', [
+ vnode.attrs.loader.done ? null : m('th[colspan=4]', m('button.btn.btn-xs', {
+ className: vnode.attrs.loader.loading ? 'btn-default' : 'btn-primary',
+ style: {
+ display: 'block',
+ width: '12em',
+ marginLeft: 'auto',
+ marginRight: 'auto',
+ },
+ disabled: vnode.attrs.loader.loading,
+ onclick: function() {
+ vnode.attrs.loader.loadMore()
+ return false
+ },
+ }, vnode.attrs.loader.loading ? '(loading)' : 'Load more')),
+ ])),
+ ])
+ },
+}
+
+window.CollectionsSearch = {
+ oninit: function(vnode) {
+ vnode.state.sessionDB = new SessionDB()
+ vnode.state.searchEntered = m.stream()
+ vnode.state.searchActive = m.stream()
+ // When searchActive changes (e.g., when restoring state
+ // after navigation), update the text field too.
+ vnode.state.searchActive.map(vnode.state.searchEntered)
+ // When searchActive changes, create a new loader that filters
+ // with the given search term.
+ vnode.state.searchActive.map(function(q) {
+ var sessions = vnode.state.sessionDB.loadActive()
+ vnode.state.loader = new MergingLoader({
+ children: Object.keys(sessions).map(function(key) {
+ var session = sessions[key]
+ return new MultipageLoader({
+ sessionKey: key,
+ loadFunc: function(filters) {
+ if (q)
+ filters.push(['any', '@@', q+':*'])
+ return vnode.state.sessionDB.request(session, 'arvados/v1/collections', {
+ data: {
+ filters: JSON.stringify(filters),
+ count: 'none',
+ },
+ }).then(function(resp) {
+ resp.items.map(function(item) {
+ item.session = session
+ })
+ return resp
+ })
+ },
+ })
+ })
+ })
+ })
+ },
+ view: function(vnode) {
+ var sessions = vnode.state.sessionDB.loadAll()
+ return m('form', {
+ onsubmit: function() {
+ vnode.state.searchActive(vnode.state.searchEntered())
+ vnode.state.forgetSavedState = true
+ return false
+ },
+ }, [
+ m(SaveUIState, {
+ defaultState: '',
+ currentState: vnode.state.searchActive,
+ forgetSavedState: vnode.state.forgetSavedState,
+ saveBodyHeight: true,
+ }),
+ vnode.state.loader && [
+ m('.row', [
+ m('.col-md-6', [
+ m('.input-group', [
+ m('input#search.form-control[placeholder=Search]', {
+ oninput: m.withAttr('value', vnode.state.searchEntered),
+ value: vnode.state.searchEntered(),
+ }),
+ m('.input-group-btn', [
+ m('input.btn.btn-primary[type=submit][value="Search"]'),
+ ]),
+ ]),
+ ]),
+ m('.col-md-6', [
+ 'Searching sites: ',
+ vnode.state.loader.children.length == 0
+ ? m('span.label.label-xs.label-danger', 'none')
+ : vnode.state.loader.children.map(function(child) {
+ return [m('span.label.label-xs', {
+ className: child.items() ? 'label-success' : 'label-warning',
+ }, child.sessionKey), ' ']
+ }),
+ ' ',
+ m('a[href="/sessions"]', 'Add/remove sites'),
+ ]),
+ ]),
+ m(CollectionsTable, {
+ loader: vnode.state.loader,
+ }),
+ ],
+ ])
+ },
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+window.LocalizedDateTime = {
+ view: function(vnode) {
+ return m('span', new Date(Date.parse(vnode.attrs.parse)).toLocaleString())
+ },
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+window.SaveUIState = {
+ saveState: function() {
+ var state = history.state || {}
+ state.bodyHeight = window.getComputedStyle(document.body)['height']
+ state.currentState = this.currentState()
+ history.replaceState(state, '')
+ },
+ oninit: function(vnode) {
+ vnode.state.currentState = vnode.attrs.currentState
+ var hstate = history.state || {}
+
+ if (vnode.attrs.saveBodyHeight && hstate.bodyHeight) {
+ document.body.style['min-height'] = hstate.bodyHeight
+ delete hstate.bodyHeight
+ }
+
+ if (hstate.currentState) {
+ vnode.attrs.currentState(hstate.currentState)
+ delete hstate.currentState
+ } else {
+ vnode.attrs.currentState(vnode.attrs.defaultState)
+ }
+
+ history.replaceState(hstate, '')
+ },
+ oncreate: function(vnode) {
+ vnode.state.saveState = vnode.state.saveState.bind(vnode.state)
+ window.addEventListener('beforeunload', vnode.state.saveState)
+ vnode.state.onupdate(vnode)
+ },
+ onupdate: function(vnode) {
+ if (vnode.attrs.saveBodyHeight && vnode.attrs.forgetSavedState) {
+ document.body.style['min-height'] = null
+ }
+ },
+ onremove: function(vnode) {
+ window.removeEventListener('beforeunload', vnode.state.saveState)
+ },
+ view: function(vnode) {
+ return null
+ },
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+$(document).on('ready', function() {
+ var db = new SessionDB()
+ db.checkForNewToken()
+ db.fillMissingUUIDs()
+})
+
+window.SessionsTable = {
+ oninit: function(vnode) {
+ vnode.state.db = new SessionDB()
+ vnode.state.hostToAdd = m.stream('')
+ },
+ view: function(vnode) {
+ var db = vnode.state.db
+ var sessions = db.loadAll()
+ return m('.container', [
+ m('table.table.table-condensed.table-hover', [
+ m('thead', m('tr', [
+ m('th', 'status'),
+ m('th', 'cluster ID'),
+ m('th', 'username'),
+ m('th', 'email'),
+ m('th', 'actions'),
+ m('th'),
+ ])),
+ m('tbody', [
+ Object.keys(sessions).map(function(uuidPrefix) {
+ var session = sessions[uuidPrefix]
+ return m('tr', [
+ session.token && session.user ? [
+ m('td', m('span.label.label-success', 'logged in')),
+ m('td', {title: session.baseURL}, uuidPrefix),
+ m('td', session.user.username),
+ m('td', session.user.email),
+ m('td', session.isFromRails ? null : m('button.btn.btn-xs.btn-default', {
+ uuidPrefix: uuidPrefix,
+ onclick: m.withAttr('uuidPrefix', db.logout),
+ }, 'Log out ', m('span.glyphicon.glyphicon-log-out'))),
+ ] : [
+ m('td', m('span.label.label-default', 'logged out')),
+ m('td', {title: session.baseURL}, uuidPrefix),
+ m('td'),
+ m('td'),
+ m('td', m('a.btn.btn-xs.btn-primary', {
+ uuidPrefix: uuidPrefix,
+ onclick: db.login.bind(db, session.baseURL),
+ }, 'Log in ', m('span.glyphicon.glyphicon-log-in'))),
+ ],
+ m('td', session.isFromRails ? null : m('button.btn.btn-xs.btn-default', {
+ uuidPrefix: uuidPrefix,
+ onclick: m.withAttr('uuidPrefix', db.trash),
+ }, 'Remove ', m('span.glyphicon.glyphicon-trash'))),
+ ])
+ }),
+ ]),
+ ]),
+ m('.row', m('.col-md-6', [
+ m('form', {
+ onsubmit: function() {
+ db.login(vnode.state.hostToAdd())
+ return false
+ },
+ }, [
+ m('.input-group', [
+ m('input.form-control[type=text][name=apiHost][placeholder="API host"]', {
+ oninput: m.withAttr('value', vnode.state.hostToAdd),
+ }),
+ m('.input-group-btn', [
+ m('input.btn.btn-primary[type=submit][value="Log in"]', {
+ disabled: !vnode.state.hostToAdd(),
+ }),
+ ]),
+ ]),
+ ]),
+ ])),
+ ])
+ },
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+window.TestComponent = {
+ view: function(vnode) {
+ return m('div.mithril-test-component', [
+ m('p', {
+ onclick: m.withAttr('zzz', function(){}),
+ }, [
+ 'mithril is working; rendered at t=',
+ (new Date()).getTime(),
+ 'ms (click to re-render)',
+ ]),
+ ])
+ },
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+$(document).on('ready arv:pane:loaded', function() {
+ $('[data-mount-mithril]').each(function() {
+ m.mount(this, window[$(this).data('mount-mithril')])
+ })
+})
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+// MultipageLoader retrieves a multi-page result set from the
+// server. The constructor initiates the first page load.
+//
+// config.loadFunc is a function that accepts an array of
+// paging-related filters, and returns a promise for the API
+// response. loadFunc() must retrieve results in "modified_at desc"
+// order.
+//
+// done is true if there are no more pages to load.
+//
+// loading is true if a network request is in progress.
+//
+// items is a stream that resolves to an array of all items retrieved so far.
+//
+// loadMore() loads the next page, if any.
+window.MultipageLoader = function(config) {
+ var loader = this
+ Object.assign(loader, config, {
+ done: false,
+ loading: false,
+ items: m.stream(),
+ thresholdItem: null,
+ loadMore: function() {
+ if (loader.done || loader.loading)
+ return
+ var filters = loader.thresholdItem ? [
+ ["modified_at", "<=", loader.thresholdItem.modified_at],
+ ["uuid", "!=", loader.thresholdItem.uuid],
+ ] : []
+ loader.loading = true
+ loader.loadFunc(filters).then(function(resp) {
+ var items = loader.items() || []
+ Array.prototype.push.apply(items, resp.items)
+ if (resp.items.length == 0)
+ loader.done = true
+ else
+ loader.thresholdItem = resp.items[resp.items.length-1]
+ loader.loading = false
+ loader.items(items)
+ }).catch(function(err) {
+ loader.err = err
+ loader.loading = false
+ })
+ },
+ })
+ loader.loadMore()
+}
+
+// MergingLoader merges results from multiple loaders (given in the
+// config.children array) into a single result set.
+//
+// new MergingLoader({children: [loader, loader, ...]})
+//
+// The children must retrieve results in "modified_at desc" order.
+window.MergingLoader = function(config) {
+ var loader = this
+ Object.assign(loader, config, {
+ // Sorted items ready to display, merged from all children.
+ items: m.stream(),
+ done: false,
+ loading: false,
+ loadable: function() {
+ // Return an array of children that we could call
+ // loadMore() on. Update loader.done and loader.loading.
+ loader.done = true
+ loader.loading = false
+ return loader.children.filter(function(child) {
+ if (child.done)
+ return false
+ loader.done = false
+ if (!child.loading)
+ return true
+ loader.loading = true
+ return false
+ })
+ },
+ loadMore: function() {
+ // Call loadMore() on children that have reached
+ // lowWaterMark.
+ loader.loadable().map(function(child) {
+ if (child.items().length - child.itemsDisplayed < loader.lowWaterMark) {
+ loader.loading = true
+ child.loadMore()
+ }
+ })
+ },
+ mergeItems: function() {
+ // cutoff is the topmost (recent) of {bottom (oldest) entry of
+ // any child that still has more pages left to fetch}
+ var cutoff
+ loader.children.forEach(function(child) {
+ var items = child.items()
+ if (items.length == 0 || child.done)
+ return
+ var last = items[items.length-1].modified_at
+ if (!cutoff || cutoff < last)
+ cutoff = last
+ })
+ var combined = []
+ loader.children.forEach(function(child) {
+ child.itemsDisplayed = 0
+ child.items().every(function(item) {
+ if (cutoff && item.modified_at < cutoff)
+ // Some other children haven't caught up to this
+ // point, so don't display this item or anything
+ // after it.
+ return false
+ combined.push(item)
+ child.itemsDisplayed++
+ return true // continue
+ })
+ })
+ loader.items(combined.sort(function(a, b) {
+ return a.modified_at < b.modified_at ? 1 : -1
+ }))
+ },
+ // Number of undisplayed items to keep on hand for each result
+ // set. When hitting "load more", if a result set already has
+ // this many additional results available, we don't bother
+ // fetching a new page. This is the _minimum_ number of rows
+ // that will be added to loader.items in each "load more"
+ // event (except for the case where all items are displayed).
+ lowWaterMark: 23,
+ })
+ var childrenReady = m.stream.merge(loader.children.map(function(child) {
+ return child.items
+ }))
+ childrenReady.map(loader.loadable)
+ childrenReady.map(loader.mergeItems)
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+window.SessionDB = function() {
+ var db = this
+ Object.assign(db, {
+ loadFromLocalStorage: function() {
+ try {
+ return JSON.parse(window.localStorage.getItem('sessions')) || {}
+ } catch(e) {}
+ return {}
+ },
+ loadAll: function() {
+ var all = db.loadFromLocalStorage()
+ if (window.defaultSession) {
+ window.defaultSession.isFromRails = true
+ all[window.defaultSession.user.uuid.slice(0, 5)] = window.defaultSession
+ }
+ return all
+ },
+ loadActive: function() {
+ var sessions = db.loadAll()
+ Object.keys(sessions).forEach(function(key) {
+ if (!sessions[key].token)
+ delete sessions[key]
+ })
+ return sessions
+ },
+ save: function(k, v) {
+ var sessions = db.loadAll()
+ sessions[k] = v
+ Object.keys(sessions).forEach(function(key) {
+ if (sessions[key].isFromRails)
+ delete sessions[key]
+ })
+ window.localStorage.setItem('sessions', JSON.stringify(sessions))
+ },
+ trash: function(k) {
+ var sessions = db.loadAll()
+ delete sessions[k]
+ window.localStorage.setItem('sessions', JSON.stringify(sessions))
+ },
+ login: function(host) {
+ // Initiate login procedure with given API host (which can
+ // optionally include scheme://).
+ //
+ // Any page that has a button that invokes login() must
+ // also call checkForNewToken() on (at least) its first
+ // render. Otherwise, the login procedure can't be
+ // completed.
+ var baseURL = host
+ if (baseURL.indexOf('://') < 0)
+ baseURL = 'https://' + baseURL
+ if (!baseURL.endsWith('/'))
+ baseURL = baseURL + '/'
+ document.location = baseURL + 'login?return_to=' + encodeURIComponent(document.location.href.replace(/\?.*/, '')+'?baseURL='+encodeURIComponent(baseURL))
+ return false
+ },
+ logout: function(k) {
+ // Forget the token, but leave the other info in the db so
+ // the user can log in again without providing the login
+ // host again.
+ var sessions = db.loadAll()
+ delete sessions[k].token
+ db.save(k, sessions[k])
+ },
+ checkForNewToken: function() {
+ // If there's a token and baseURL in the location bar (i.e.,
+ // we just landed here after a successful login), save it and
+ // scrub the location bar.
+ if (!document.location.search.startsWith('?'))
+ return
+ var params = {}
+ document.location.search.slice(1).split('&').map(function(kv) {
+ var e = kv.indexOf('=')
+ if (e < 0)
+ return
+ params[decodeURIComponent(kv.slice(0, e))] = decodeURIComponent(kv.slice(e+1))
+ })
+ if (!params.baseURL || !params.api_token)
+ // Have a query string, but it's not a login callback.
+ return
+ params.token = params.api_token
+ delete params.api_token
+ db.save(params.baseURL, params)
+ history.replaceState({}, '', document.location.origin + document.location.pathname)
+ },
+ fillMissingUUIDs: function() {
+ var sessions = db.loadAll()
+ Object.keys(sessions).map(function(key) {
+ if (key.indexOf('://') < 0)
+ return
+ // key is the baseURL placeholder. We need to get our user
+ // record to find out the cluster's real uuid prefix.
+ var session = sessions[key]
+ m.request(session.baseURL+'arvados/v1/users/current', {
+ headers: {
+ authorization: 'OAuth2 '+session.token,
+ },
+ }).then(function(user) {
+ session.user = user
+ db.save(user.uuid.slice(0, 5), session)
+ db.trash(key)
+ })
+ })
+ // m.request(session.baseURL + 'discovery/v1/apis/arvados/v1/rest').then(function(dd) {})
+ },
+ request: function(session, path, opts) {
+ opts = opts || {}
+ opts.headers = opts.headers || {}
+ opts.headers.authorization = 'OAuth2 '+ session.token
+ return m.request(session.baseURL + path, opts)
+ },
+ })
+}
skip_around_filter(:require_thread_api_token,
only: [:show_file, :show_file_links])
skip_before_filter(:find_object_by_uuid,
- only: [:provenance, :show_file, :show_file_links])
+ only: [:provenance, :show_file, :show_file_links, :multisite])
# We depend on show_file to display the user agreement:
skip_before_filter :check_user_agreements, only: :show_file
skip_before_filter :check_user_profile, only: :show_file
# SPDX-License-Identifier: AGPL-3.0
class SessionsController < ApplicationController
- skip_around_filter :require_thread_api_token, :only => [:destroy, :index]
- skip_around_filter :set_thread_api_token, :only => [:destroy, :index]
- skip_before_filter :find_object_by_uuid, :only => [:destroy, :index]
+ skip_around_filter :require_thread_api_token, :only => [:destroy, :logged_out]
+ skip_around_filter :set_thread_api_token, :only => [:destroy, :logged_out]
+ skip_before_filter :find_object_by_uuid
+ skip_before_filter :find_objects_for_index
+ skip_before_filter :ensure_arvados_api_exists
def destroy
session.clear
redirect_to arvados_api_client.arvados_logout_url(return_to: root_url)
end
- def index
+ def logged_out
redirect_to root_url if session[:arvados_api_token]
render_index
end
+
+ def index
+ end
end
--- /dev/null
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+class TestsController < ApplicationController
+ skip_before_filter :find_object_by_uuid
+ def mithril
+ end
+end
--- /dev/null
+<!-- Copyright (C) The Arvados Authors. All rights reserved.
+
+SPDX-License-Identifier: AGPL-3.0 -->
+
+<div data-mount-mithril="CollectionsSearch"></div>
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<meta name="description" content="">
<meta name="author" content="">
+ <% if current_user %>
+ <% content_for :js do %>
+ window.defaultSession = <%=raw({baseURL: Rails.configuration.arvados_v1_base.sub(/\/arvados\/v1$/, '/'), token: Thread.current[:arvados_api_token], user: current_user}.to_json)%>
+ <% end %>
+ <% end %>
<% if current_user and $arvados_api_client.discovery[:websocketUrl] %>
<meta name="arv-websocket-url" content="<%=$arvados_api_client.discovery[:websocketUrl]%>?api_token=<%=Thread.current[:arvados_api_token]%>">
<% end %>
<% if current_user %>
<% if current_user.is_active %>
+ <li>
+ <%= link_to(controller: 'collections', action: 'multisite') do %>
+ Multisite search (beta)
+ <% end %>
+ </li>
<li>
<form class="navbar-form" role="search"
data-search-modal=
--- /dev/null
+<!-- Copyright (C) The Arvados Authors. All rights reserved.
+
+SPDX-License-Identifier: AGPL-3.0 -->
+
+<div data-mount-mithril="SessionsTable"></div>
--- /dev/null
+<div data-mount-mithril="TestComponent"></div>
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
+
+ # npm-rails loads top-level modules like window.Mithril, but we
+ # also pull in some code from node_modules in application.js, like
+ # mithril/stream/stream.
+ config.assets.paths << Rails.root.join('node_modules')
end
end
get '/repositories/:id/tree/:commit/*path' => 'repositories#show_tree', as: :show_repository_tree, format: false
get '/repositories/:id/blob/:commit/*path' => 'repositories#show_blob', as: :show_repository_blob, format: false
get '/repositories/:id/commit/:commit' => 'repositories#show_commit', as: :show_repository_commit
+ resources :sessions
match '/logout' => 'sessions#destroy', via: [:get, :post]
- get '/logged_out' => 'sessions#index'
+ get '/logged_out' => 'sessions#logged_out'
resources :users do
get 'choose', :on => :collection
get 'home', :on => :member
post 'remove_selected_files', on: :member
get 'tags', on: :member
post 'save_tags', on: :member
+ get 'multisite', on: :collection
end
get('/collections/download/:uuid/:reader_token/*file' => 'collections#show_file',
format: false)
match '/_health/ping', to: 'healthcheck#ping', via: [:get]
+ get '/tests/mithril', to: 'tests#mithril'
+
# Send unroutable requests to an arbitrary controller
# (ends up at ApplicationController#render_not_found)
match '*a', to: 'links#render_not_found', via: [:get, :post]
--- /dev/null
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+# Run "rake npm:install"
+
+# Browserify is required.
+npm 'browserify', require: false, development: true
+
+npm 'mithril'
# urls += all_links_in('body')
end
end
+
+ test "mithril test page" do
+ visit page_with_token('active_trustedclient', '/tests/mithril')
+ assert_visit_success
+ assert_selector 'p', text: 'mithril is working'
+ end
end
cp -rlt ubuntu1604/generated common-generated/*
GOTARBALL=go1.8.3.linux-amd64.tar.gz
+NODETARBALL=node-v6.11.2-linux-x64.tar.xz
-common-generated-all: common-generated/$(GOTARBALL)
+common-generated-all: common-generated/$(GOTARBALL) common-generated/$(NODETARBALL)
common-generated/$(GOTARBALL): common-generated
wget -cqO common-generated/$(GOTARBALL) http://storage.googleapis.com/golang/$(GOTARBALL)
+common-generated/$(NODETARBALL): common-generated
+ wget -cqO common-generated/$(NODETARBALL) https://nodejs.org/dist/v6.11.2/$(NODETARBALL)
+
common-generated:
mkdir common-generated
ADD generated/go1.8.3.linux-amd64.tar.gz /usr/local/
RUN ln -s /usr/local/go/bin/go /usr/local/bin/
+# Install nodejs and npm
+ADD generated/node-v6.11.2-linux-x64.tar.xz /usr/local/
+RUN ln -s /usr/local/node-v6.11.2-linux-x64/bin/* /usr/local/bin/
+
# Need to "touch" RPM database to workaround bug in interaction between
# overlayfs and yum (https://bugzilla.redhat.com/show_bug.cgi?id=1213602)
RUN touch /var/lib/rpm/* && yum -q -y install python33
ADD generated/go1.8.3.linux-amd64.tar.gz /usr/local/
RUN ln -s /usr/local/go/bin/go /usr/local/bin/
+# Install nodejs and npm
+ADD generated/node-v6.11.2-linux-x64.tar.xz /usr/local/
+RUN ln -s /usr/local/node-v6.11.2-linux-x64/bin/* /usr/local/bin/
+
# Old versions of setuptools cannot build a schema-salad package.
RUN pip install --upgrade setuptools
/usr/local/rvm/bin/rvm-exec default gem install cure-fpm --version 1.6.0b
# Install golang binary
-COPY generated/go1.8.3.linux-amd64.tar.gz /usr/local/
-RUN cd /usr/local && ls && tar xzvf go1.8.3.linux-amd64.tar.gz
+ADD generated/go1.8.3.linux-amd64.tar.gz /usr/local/
RUN ln -s /usr/local/go/bin/go /usr/local/bin/
+# Install nodejs and npm
+ADD generated/node-v6.11.2-linux-x64.tar.xz /usr/local/
+RUN ln -s /usr/local/node-v6.11.2-linux-x64/bin/* /usr/local/bin/
+
# Old versions of setuptools cannot build a schema-salad package.
RUN pip install --upgrade setuptools
ADD generated/go1.8.3.linux-amd64.tar.gz /usr/local/
RUN ln -s /usr/local/go/bin/go /usr/local/bin/
+# Install nodejs and npm
+ADD generated/node-v6.11.2-linux-x64.tar.xz /usr/local/
+RUN ln -s /usr/local/node-v6.11.2-linux-x64/bin/* /usr/local/bin/
+
# Old versions of setuptools cannot build a schema-salad package.
RUN pip install --upgrade setuptools
ADD generated/go1.8.3.linux-amd64.tar.gz /usr/local/
RUN ln -s /usr/local/go/bin/go /usr/local/bin/
+# Install nodejs and npm
+ADD generated/node-v6.11.2-linux-x64.tar.xz /usr/local/
+RUN ln -s /usr/local/node-v6.11.2-linux-x64/bin/* /usr/local/bin/
+
# Old versions of setuptools cannot build a schema-salad package.
RUN pip install --upgrade setuptools
ADD generated/go1.8.3.linux-amd64.tar.gz /usr/local/
RUN ln -s /usr/local/go/bin/go /usr/local/bin/
+# Install nodejs and npm
+ADD generated/node-v6.11.2-linux-x64.tar.xz /usr/local/
+RUN ln -s /usr/local/node-v6.11.2-linux-x64/bin/* /usr/local/bin/
+
# Old versions of setuptools cannot build a schema-salad package.
RUN pip install --upgrade setuptools
sed -i 's/secret_token: ~/secret_token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/' config/application.yml
sed -i 's/keep_web_url: false/keep_web_url: exampledotcom/' config/application.yml
+ RAILS_ENV=production RAILS_GROUPS=assets bundle exec rake npm:install >/dev/null
RAILS_ENV=production RAILS_GROUPS=assets bundle exec rake assets:precompile >/dev/null
# Remove generated configuration files so they don't go in the package.
echo -n 'gitolite: '
which gitolite \
|| fatal "No gitolite. Try: apt-get install gitolite3"
+ which npm \
+ || fatal "No npm. Try: wget -O- https://nodejs.org/dist/v6.11.2/node-v6.11.2-linux-x64.tar.xz | sudo tar -C /usr/local xJf - && sudo ln -s ../node-v6.11.2-linux-x64/bin/{node,npm} /usr/local/bin/"
}
rotate_logfile() {
install_workbench() {
cd "$WORKSPACE/apps/workbench" \
&& mkdir -p tmp/cache \
- && RAILS_ENV=test bundle_install_trylocal
+ && RAILS_ENV=test bundle_install_trylocal \
+ && RAILS_ENV=test RAILS_GROUPS=assets bundle exec rake npm:install
}
do_install apps/workbench workbench