Skip to content

Commit

Permalink
Force symlinks to be preserved
Browse files Browse the repository at this point in the history
If the chosen adapter supports creation of symlinks, any symlink in
the git repo will be uploaded as a symlink. Otherwise, uploading of
that file will be skipped. Support has only been added to the SFTP
adapter so far.
  • Loading branch information
dphoyes committed Jul 20, 2016
1 parent d34ac97 commit dedf5c0
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 32 deletions.
3 changes: 3 additions & 0 deletions lib/dandelion/adapter/noop.rb
Expand Up @@ -9,6 +9,9 @@ def write(path, data)

def delete(path)
end

def symlink(path, data)
end
end
end
end
13 changes: 13 additions & 0 deletions lib/dandelion/adapter/sftp.rb
Expand Up @@ -55,6 +55,19 @@ def delete(file)
end
end

def symlink(file, target)
begin
@sftp.symlink!(target, path(file))
rescue Net::SFTP::StatusException => e
if e.code == 2
mkdir_p(File.dirname(path(file)))
else
@sftp.remove!(path(file))
end
@sftp.symlink!(target, path(file))
end
end

def to_s
"sftp://#{@config['username']}@#{@config['host']}/#{@config['path']}"
end
Expand Down
1 change: 1 addition & 0 deletions lib/dandelion/change.rb
@@ -1,6 +1,7 @@
module Dandelion
class Change
attr_reader :path, :type
attr_accessor :type

def initialize(path, type, read = nil)
@path = path
Expand Down
1 change: 1 addition & 0 deletions lib/dandelion/changeset.rb
Expand Up @@ -24,6 +24,7 @@ def each
if change.type == :delete
yield Change.new(path, change.type)
else
change.type = :symlink if @tree.is_symlink?(change.path)
read = -> { @tree.data(change.path) }
yield Change.new(path, change.type, read)
end
Expand Down
7 changes: 7 additions & 0 deletions lib/dandelion/deployer.rb
Expand Up @@ -50,6 +50,13 @@ def deploy_change!(change)
when :delete
log.debug("Deleting file: #{change.path}")
@adapter.delete(change.path)
when :symlink
if @adapter.respond_to?(:symlink)
log.debug("Creating symlink: #{change.path}")
@adapter.symlink(change.path, change.data)
else
log.debug("Skipped creating symlink: #{change.path} -> #{change.data}")
end
end
end

Expand Down
38 changes: 8 additions & 30 deletions lib/dandelion/tree.rb
Expand Up @@ -7,6 +7,11 @@ def initialize(repo, commit)
@commit = commit
end

def is_symlink?(path)
# https://github.com/libgit2/libgit2/blob/development/include/git2/types.h
@commit.tree.path(path)[:filemode] == 0120000
end

def data(path)
submodule = @repo.submodules[path]

Expand All @@ -15,48 +20,21 @@ def data(path)
nil
else
info, obj = object(path)
content(info, obj)
blob_content(obj)
end
end

private

def object(path)
object = @commit.tree
info = {}

path.split('/').each do |name|
info = object[name]
return nil unless info
return nil unless info[:type]

object = @repo.lookup(info[:oid])

return nil unless object
end

info = @commit.tree.path(path)
object = @repo.lookup(info[:oid])
[info, object]
end

def content(info, object)
# https://github.com/libgit2/libgit2/blob/development/include/git2/types.h
if info[:filemode] == 0120000
symlink_content(object)
else
blob_content(object)
end
end

def blob_content(object)
object.read_raw.data
end

def symlink_content(object)
path = object.read_raw.data

result = data(path)
result ||= IO.binread(path) # external link
result
end
end
end
26 changes: 26 additions & 0 deletions spec/dandelion/changeset_spec.rb
Expand Up @@ -70,4 +70,30 @@
end
end
end

context 'diff adds symlink' do
let(:changeset) { test_changeset_with_symlinks }

describe '#enumerable' do
let(:changes) { changeset.to_a }

it 'returns all changes' do
expect(changes).to be_a(Array)
expect(changes.length).to eq 1
expect(changes.map(&:path)).to eq ['link']
expect(changes.map(&:type)).to eq [:symlink]
end

it 'returns data for write changes' do
expect(changes.select { |c| c.type != :delete }.map(&:data)).to eq ["baz/bar"]
end
end

describe '#empty?' do
it 'returns false' do
expect(changeset.empty?).to eq false
end
end
end

end
4 changes: 2 additions & 2 deletions spec/dandelion/tree_spec.rb
Expand Up @@ -16,8 +16,8 @@
let(:repo) { test_repo('repo_symlink') }
let(:tree) { test_tree(repo: repo, commit: repo.lookup('4c19bbe7ba04230a0ae2281c1abbc48a76a66550')) }

it 'returns content of link source path' do
expect(tree.data('link')).to eq "bar\n"
it 'returns target path of the symlink' do
expect(tree.data('link')).to eq "baz/bar"
end
end

Expand Down
8 changes: 8 additions & 0 deletions spec/spec_helper.rb
Expand Up @@ -27,6 +27,14 @@ def test_changeset(options = {})
Dandelion::Changeset.new(test_tree, test_commits.first, options)
end

def test_changeset_with_symlinks(options = {})
repo = test_repo('repo_symlink')
commit0 = repo.lookup('3d9b743acb4a84dd99002d2c6f3fcf1a47e9f06b')
commit1 = repo.lookup('4c19bbe7ba04230a0ae2281c1abbc48a76a66550')
tree = test_tree(repo: repo, commit: commit1)
Dandelion::Changeset.new(tree, commit0, options)
end

def test_diff
test_changeset.diff
end

0 comments on commit dedf5c0

Please sign in to comment.