summaryrefslogtreecommitdiff
path: root/lib/oxidized/output/gitcrypt.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/oxidized/output/gitcrypt.rb')
-rw-r--r--lib/oxidized/output/gitcrypt.rb244
1 files changed, 244 insertions, 0 deletions
diff --git a/lib/oxidized/output/gitcrypt.rb b/lib/oxidized/output/gitcrypt.rb
new file mode 100644
index 0000000..b0d80f2
--- /dev/null
+++ b/lib/oxidized/output/gitcrypt.rb
@@ -0,0 +1,244 @@
+module Oxidized
+ class GitCrypt < Output
+ class GitCryptError < OxidizedError; end
+ begin
+ require 'git'
+ rescue LoadError
+ raise OxidizedError, 'git not found: sudo gem install ruby-git'
+ end
+
+ attr_reader :commitref
+
+ def initialize
+ @cfg = Oxidized.config.output.gitcrypt
+ @gitcrypt_cmd = "/usr/bin/git-crypt"
+ @gitcrypt_init = @gitcrypt_cmd + " init"
+ @gitcrypt_unlock = @gitcrypt_cmd + " unlock"
+ @gitcrypt_lock = @gitcrypt_cmd + " lock"
+ @gitcrypt_adduser = @gitcrypt_cmd + " add-gpg-user --trusted "
+ end
+
+ def setup
+ if @cfg.empty?
+ Oxidized.asetus.user.output.gitcrypt.user = 'Oxidized'
+ Oxidized.asetus.user.output.gitcrypt.email = 'o@example.com'
+ Oxidized.asetus.user.output.gitcrypt.repo = File.join(Config::Root, 'oxidized.git')
+ Oxidized.asetus.save :user
+ raise NoConfig, 'no output git config, edit ~/.config/oxidized/config'
+ end
+
+ if @cfg.repo.respond_to?(:each)
+ @cfg.repo.each do |group, repo|
+ @cfg.repo["#{group}="] = File.expand_path repo
+ end
+ else
+ @cfg.repo = File.expand_path @cfg.repo
+ end
+ end
+
+ def crypt_init repo
+ repo.chdir do
+ system(@gitcrypt_init)
+ @cfg.users.each do |user|
+ system("#{@gitcrypt_adduser} #{user}")
+ end
+ File.write(".gitattributes", "* filter=git-crypt diff=git-crypt\n.gitattributes !filter !diff")
+ repo.add(".gitattributes")
+ repo.commit("Initial commit: crypt all config files")
+ end
+ end
+
+ def lock repo
+ repo.chdir do
+ system(@gitcrypt_lock)
+ end
+ end
+
+ def unlock repo
+ repo.chdir do
+ system(@gitcrypt_unlock)
+ end
+ end
+
+ def store file, outputs, opt={}
+ @msg = opt[:msg]
+ @user = (opt[:user] or @cfg.user)
+ @email = (opt[:email] or @cfg.email)
+ @opt = opt
+ @commitref = nil
+ repo = @cfg.repo
+
+ outputs.types.each do |type|
+ type_cfg = ''
+ type_repo = File.join(File.dirname(repo), type + '.git')
+ outputs.type(type).each do |output|
+ (type_cfg << output; next) if not output.name
+ type_file = file + '--' + output.name
+ if @cfg.type_as_directory?
+ type_file = type + '/' + type_file
+ type_repo = repo
+ end
+ update type_repo, type_file, output
+ end
+ update type_repo, file, type_cfg
+ end
+
+ update repo, file, outputs.to_cfg
+ end
+
+
+ def fetch node, group
+ begin
+ repo, path = yield_repo_and_path(node, group)
+ repo = Git.open repo
+ unlock repo
+ index = repo.index
+ # Empty repo ?
+ empty = File.exists? index.path
+ if empty
+ raise 'Empty git repo'
+ else
+ File.read path
+ end
+ lock repo
+ rescue
+ 'node not found'
+ end
+ end
+
+ # give a hash of all oid revision for the given node, and the date of the commit
+ def version node, group
+ begin
+ repo, path = yield_repo_and_path(node, group)
+
+ repo = Git.open repo
+ unlock repo
+ walker = repo.log.path(path)
+ i = -1
+ tab = []
+ walker.each do |commit|
+ hash = {}
+ hash[:date] = commit.date.to_s
+ hash[:oid] = commit.objectish
+ hash[:author] = commit.author
+ hash[:message] = commit.message
+ tab[i += 1] = hash
+ end
+ walker.reset
+ tab
+ rescue
+ 'node not found'
+ end
+ end
+
+ #give the blob of a specific revision
+ def get_version node, group, oid
+ begin
+ repo, path = yield_repo_and_path(node, group)
+ repo = Git.open repo
+ unlock repo
+ repo.gtree(oid).files[path].contents
+ rescue
+ 'version not found'
+ ensure
+ lock repo
+ end
+ end
+
+ #give a hash with the patch of a diff between 2 revision and the stats (added and deleted lines)
+ def get_diff node, group, oid1, oid2
+ begin
+ diff_commits = nil
+ repo, path = yield_repo_and_path(node, group)
+ repo = Git.open repo
+ unlock repo
+ commit = repo.gcommit(oid1)
+
+ if oid2
+ commit_old = repo.gcommit(oid2)
+ diff = repo.diff(commit_old, commit)
+ stats = [diff.stats[:files][node.name][:insertions], diff.stats[:files][node.name][:deletions]]
+ diff.each do |patch|
+ if /#{node.name}\s+/.match(patch.patch.to_s.lines.first)
+ diff_commits = {:patch => patch.patch.to_s, :stat => stats}
+ break
+ end
+ end
+ else
+ stat = commit.parents[0].diff(commit).stats
+ stat = [stat[:files][node.name][:insertions],stat[:files][node.name][:deletions]]
+ patch = commit.parents[0].diff(commit).patch
+ diff_commits = {:patch => patch, :stat => stat}
+ end
+ lock repo
+ diff_commits
+ rescue
+ 'no diffs'
+ ensure
+ lock repo
+ end
+ end
+
+ private
+
+ def yield_repo_and_path(node, group)
+ repo, path = node.repo, node.name
+
+ if group and @cfg.single_repo?
+ path = "#{group}/#{node.name}"
+ end
+
+ [repo, path]
+ end
+
+ def update repo, file, data
+ return if data.empty?
+
+ if @opt[:group]
+ if @cfg.single_repo?
+ file = File.join @opt[:group], file
+ else
+ repo = if repo.is_a?(::String)
+ File.join File.dirname(repo), @opt[:group] + '.git'
+ else
+ repo[@opt[:group]]
+ end
+ end
+ end
+
+ begin
+ update_repo repo, file, data, @msg, @user, @email
+ rescue Git::GitExecuteError, ArgumentError => open_error
+ Oxidized.logger.debug "open_error #{open_error} #{file}"
+ begin
+ grepo = Git.init repo
+ crypt_init grepo
+ rescue => create_error
+ raise GitCryptError, "first '#{open_error.message}' was raised while opening git repo, then '#{create_error.message}' was while trying to create git repo"
+ end
+ retry
+ end
+ end
+
+ def update_repo repo, file, data, msg, user, email
+ grepo = Git.open repo
+ grepo.config('user.name', user)
+ grepo.config('user.email', email)
+ grepo.chdir do
+ unlock grepo
+ File.write(file, data)
+ grepo.add(file)
+ if grepo.status[file].nil?
+ grepo.commit(msg)
+ @commitref = grepo.log(1).first.objectish
+ true
+ elsif !grepo.status[file].type.nil?
+ grepo.commit(msg)
+ @commitref = grepo.log(1).first.objectish
+ true
+ end
+ lock grepo
+ end
+ end
+ end
+end