From da38a0de6d2814c450ea356412faa03ee54cad68 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Thu, 25 Jun 2020 20:11:28 -0400 Subject: [PATCH] Fix symlink handling in content directory. In preparation to use git-annex for tracking large binary files, we need to solve some problems related to its symlinks. Most importantly is that the nanoc "filesystem" data source really doesn't like broken symlinks, so we modify that to avoid major problems. Next we filter out all missing annex items in a preprocess rule. Ideally we would replace them with stubs but just dropping them is acceptable for now. Finally, for binary items, passthrough rules end up copying the symlink literally from the content to the output directory. In the case of symlinks to the annex directory this is not going to deploy correctly, so we need a dummy filter to break the link. --- Rules | 13 ++++++++++++- lib/copybin.rb | 28 +++++++++++++++++++++++++++ lib/deadlinks.rb | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 lib/copybin.rb create mode 100644 lib/deadlinks.rb diff --git a/Rules b/Rules index 383feb6..5473788 100644 --- a/Rules +++ b/Rules @@ -16,6 +16,13 @@ # along with this program. If not, see . preprocess do + # Remove dead annex keys from processing + @items.delete_if do |item| + l = File.readlink(item.raw_filename) + true if !File.exists?(item.raw_filename) and l =~ %r{/annex/objects/} + rescue Errno::EINVAL + end + commit = nil Open3.popen2("git", "rev-parse", "HEAD") do |stdin, stdout, result| stdin.close @@ -103,6 +110,10 @@ compile '/**/*.scss' do write @item.identifier.without_ext + '.css' end -passthrough '/**/*' +compile '/**/*' do + filter :copybin if @item.binary? + write @item.identifier.to_s +end + layout '/**/*.xsl', :xsl layout '/**/*', :erb diff --git a/lib/copybin.rb b/lib/copybin.rb new file mode 100644 index 0000000..f37ff4c --- /dev/null +++ b/lib/copybin.rb @@ -0,0 +1,28 @@ +# Nick's web site: copybin filter. No-op filter binary that simply copies the +# input to the output. This exists primarily to break symlinks in rules. +# +# Copyright © 2020 Nick Bowler +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +class CopyBinFilter < Nanoc::Filter + identifier :copybin + type :binary + + require 'fileutils' + + def run(filename, params = {}) + FileUtils.cp(filename, output_filename) + end +end diff --git a/lib/deadlinks.rb b/lib/deadlinks.rb new file mode 100644 index 0000000..adcb25f --- /dev/null +++ b/lib/deadlinks.rb @@ -0,0 +1,49 @@ +# Nick's web site: Workarounds for Nanoc's busted handling of broken symlinks. +# +# Copyright © 2020 Nick Bowler +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Monkey patch the filesystem data source to adjust some strange behavoiur. +class Nanoc::DataSources::Filesystem < Nanoc::DataSource + # The filesystem mtime_of crashes on broken symlinks. Let's not do that, + # instead we'll fall back to the link mtime for broken links. + def mtime_of(*args) + mtimes = args.compact.map do |f| + File.stat(f).mtime + rescue Errno::ENOENT + File.lstat(f).mtime + end + mtimes.max + end + + module Tools + @dummy_file = Tempfile.new('dummy') + + # The original resolve_symlink helper uses a readlink loop which + # crashes on broken links. I'm also fairly sure symlinks in path + # components are not correctly resolved. Let's just use realpath + # instead and hope ruby itself doesn't have these bugs. + def resolve_symlink(filename, recursion_limit = nil) + File.realpath(filename) + rescue Errno::ENOENT + # Dead link. Return a valid filename otherwise the filesystem + # source does bizarre things. The actual items are created with + # the original filename so this dummy file does not appear to + # leak outside of the filesystem data source implementation. + @dummy_file.path + end + module_function :resolve_symlink + end +end -- 2.43.0