X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/63f9b5374e01394486a07dba4f9a43cfd76707e3..42c20b25e1325124b88e3b9b285544dc41122b56:/apps/workbench/app/models/repository.rb diff --git a/apps/workbench/app/models/repository.rb b/apps/workbench/app/models/repository.rb index 06c70f4688..fd30be9462 100644 --- a/apps/workbench/app/models/repository.rb +++ b/apps/workbench/app/models/repository.rb @@ -1,5 +1,119 @@ +# Copyright (C) The Arvados Authors. All rights reserved. +# +# SPDX-License-Identifier: AGPL-3.0 + class Repository < ArvadosBase def self.creatable? - current_user and current_user.is_admin + false + end + def attributes_for_display + super.reject { |x| x[0] == 'fetch_url' } + end + def editable_attributes + if current_user.is_admin + super + else + [] + end + end + + def show commit_sha1 + refresh + run_git 'show', commit_sha1 + end + + def cat_file commit_sha1, path + refresh + run_git 'cat-file', 'blob', commit_sha1 + ':' + path + end + + def ls_tree_lr commit_sha1 + refresh + run_git 'ls-tree', '-l', '-r', commit_sha1 + end + + # subtree returns a list of files under the given path at the + # specified commit. Results are returned as an array of file nodes, + # where each file node is an array [file mode, blob sha1, file size + # in bytes, path relative to the given directory]. If the path is + # not found, [] is returned. + def ls_subtree commit, path + path = path.chomp '/' + subtree = [] + ls_tree_lr(commit).each_line do |line| + mode, type, sha1, size, filepath = line.split + next if type != 'blob' + if filepath[0,path.length] == path and + (path == '' or filepath[path.length] == '/') + subtree << [mode.to_i(8), sha1, size.to_i, + filepath[path.length,filepath.length]] + end + end + subtree + end + + # http_fetch_url returns the first http:// or https:// url (if any) + # in the api response's clone_urls attribute. + def http_fetch_url + clone_urls.andand.select { |u| /^http/ =~ u }.first + end + + protected + + # refresh fetches the latest repository content into the local + # cache. It is a no-op if it has already been run on this object: + # this (pretty much) avoids doing more than one remote git operation + # per Workbench request. + def refresh + run_git 'fetch', http_fetch_url, '+*:*' unless @fresh + @fresh = true + end + + # run_git sets up the ARVADOS_API_TOKEN environment variable, + # creates a local git directory for this repository if necessary, + # executes "git --git-dir localgitdir {args to run_git}", and + # returns the output. It raises GitCommandError if git exits + # non-zero. + def run_git *gitcmd + if not @workdir + workdir = File.expand_path uuid+'.git', Rails.configuration.Workbench.RepositoryCache + if not File.exists? workdir + FileUtils.mkdir_p Rails.configuration.Workbench.RepositoryCache + [['git', 'init', '--bare', workdir], + ].each do |cmd| + system(*cmd, in: "/dev/null") + raise GitCommandError.new($?.to_s) unless $?.exitstatus == 0 + end + end + @workdir = workdir + end + [['git', '--git-dir', @workdir, 'config', '--local', + "credential.#{http_fetch_url}.username", 'none'], + ['git', '--git-dir', @workdir, 'config', '--local', + "credential.#{http_fetch_url}.helper", + '!cred(){ cat >/dev/null; if [ "$1" = get ]; then echo password=$ARVADOS_API_TOKEN; fi; };cred'], + ['git', '--git-dir', @workdir, 'config', '--local', + 'http.sslVerify', + Rails.configuration.TLS.Insecure ? 'false' : 'true'], + ].each do |cmd| + system(*cmd, in: "/dev/null") + raise GitCommandError.new($?.to_s) unless $?.exitstatus == 0 + end + env = {}. + merge(ENV). + merge('ARVADOS_API_TOKEN' => Thread.current[:arvados_api_token], + 'GIT_TERMINAL_PROMPT' => '0') + cmd = ['git', '--git-dir', @workdir] + gitcmd + io = IO.popen(env, cmd, err: [:child, :out], in: "/dev/null") + output = io.read + io.close + # "If [io] is opened by IO.popen, close sets $?." --ruby 2.2.1 docs + unless $?.exitstatus == 0 + raise GitCommandError.new("`git #{gitcmd.join ' '}` #{$?}: #{output}") + end + output + end + + class GitCommandError < StandardError end end