aboutsummaryrefslogblamecommitdiff
path: root/_plugins/linter.rb
blob: c0059c918a4eb248680a6fc54a159e7fd9c42334 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

             

                                                 



                          

                  



                                   
                                    

                                           
                         



                               
                        

                                                        


         
























                                                                
                           


                            

                             

     
                                                 











































                                                                                                             


                                   


                                                         
           



                                

                                                           


         
                        





                                                                









                                                               
           


         







































                                                                                                                                                           

                             
                              
                                 
                            
       

















                                                 

     
require 'set'

IGNORED_PAGES = Set['sitemap.xml']
LANGS = Set['en', 'pt', 'fr', 'eo'] # jp zh es de

module Jekyll
  class Linter < Generator
    safe true
    priority :high

    def insert_id(name, document)
      lang = document.data['lang']
      ref = document.data['ref']
      id = "#{name}:#{lang}:#{ref}"
      if @known_ids.include? id then
        raise "Duplicate ID found: '#{id}'"
      else
        @known_ids.add id
      end
    end

    def assert_unique_ids(site)
      @known_ids = Set[]
      all_documents(site) do |collection_name, document|
        insert_id collection_name, document
      end
    end

    def slugify(s)
      s.ljust(100)
        .gsub(/[\W]+/, ' ')
        .strip
        .gsub(/\s\s+/, '-')
        .downcase
        .gsub(' ', '-')
        .gsub('_', '-')
    end

    def assert(value, message)
      unless value
        raise message
      end
      value
    end

    def assert_field(document, field)
      f = document.data[field]
      raise "Undefined '#{field}' for #{document.path}" unless f
      f
    end

    COLLECTION_LAYOUTS = {
      'page' => 'page',
      'slides' => 'slides',
      'articles' => 'post',
      'pastebins' => 'post',
      'tils' => 'post',
      'podcasts' => 'post',
      'screencasts' => 'post'
    }

    def assert_frontmatter_fields(name, document)
      title = assert_field document, 'title'
      lang = assert_field document, 'lang'
      ref = assert_field document, 'ref'
      layout = assert_field document, 'layout'
      date = document.date.strftime('%Y-%m-%d') unless layout == 'page'
      slug = layout == 'page' ? ref : assert_field(document, 'slug')
      extension = name == 'slides' ? 'slides' : 'md'

      unless LANGS.member? lang
        raise "Invalid lang '#{lang}' in #{document.path}"
      end

      if COLLECTION_LAYOUTS[name] != layout
        raise "Layout mismatch: expected '#{COLLECTION_LAYOUTS[name]}', got '#{layout}' for #{document.path}"
      end

      if lang == 'en'
        unless ['index', 'root', 'tils'].include? ref
          if slugify(title) != ref then
            raise "#{ref} isn't a slug of the title.\nref:        '#{ref}'\ntitle slug: '#{slugify(title)}'"
            p slugify(title)
          end
        end
      end

      unless layout == 'page' then
        path = "_#{name}/#{date}-#{slug}.#{extension}"
        unless path == document.relative_path then
          raise "date/filename mismatch:\ndate+slug: #{path}\nfilename:  #{document.relative_path}"
        end

        if lang == 'en' then
          unless ref == slug then
            raise "ref/slug mismatch:\nref:  #{ref}\nslug: #{slug}"
          end
        end
      end

      if name == 'podcasts' then
        flac = "resources/podcasts/#{date}-#{slug}.flac"
        unless File.exist? flac then
          raise "Missing FLAC file '#{flac}'"
        end

      end

      if name == 'screencasts' then
        mkv = "resources/screencasts/#{date}-#{slug}.mkv"
        unless File.exist? mkv then
          raise "Missing MKV file '#{mkv}'"
        end
      end
    end

    def assert_frontmatter(site)
      all_documents(site) do |collection_name, document|
        assert_frontmatter_fields collection_name, document
      end
    end

    @@first_build = true
    def assert_git_annex(site)
      url = site.config['url']
      stdout = `git annex find --not --in web`
      if stdout != '' or not $?.success? then
        puts 'Files in Git Annex not published to "web" remote:'
        puts stdout
        puts "Add them with:\n\n"
        stdout.strip.split("\n").each do |file|
          puts "git annex addurl --file #{file} #{url}/#{file}"
        end
        msg = "\nBuild again after files above are added"
        if @@first_build then
          @@first_build = false
          puts msg
        else
          raise msg
        end
      end
    end

    def assert_media_metadata(site)
      site.collections.each do |name, collection|
        if ['podcasts', 'screencasts'].include? name then
          collection.docs.each do |document|
            date = document.data['date'].strftime('%Y-%m-%d')
            slug = document.data['slug']
            file =  "resources/#{name}/#{date}-#{slug}.flac"
            if name == 'podcasts'
              stdout = `metaflac --export-tags-to=- #{file}`
              file_metadata = stdout.strip.split("\n")
              expected = [
                "COMMENTS=#{site.config['url']}/#{file}",
                'ARTIST=EuAndreh',
                "DATE=#{date}",
                "TITLE=#{document.data['title']}",
                "ALBUM=#{site.config['t']['podcasts']['feed']['title'][document.data['lang']]}"
              ]
              expected.each do |metadata|
                unless file_metadata.include? metadata
                  tags = expected.join('\\n').gsub(/'/, "'\"'\"'")
                  add_metadata_cmd = "metaflac --remove-all #{file}\nprintf '#{tags}\\n' | metaflac --import-tags-from=- #{file}"
                  check_metadata_cmd = "metaflac --export-tags-to=- #{file}"
                  raise "Missing metadata entry '#{metadata}' in '#{file}'.\nAdd it with:\n\n#{add_metadata_cmd}\n\nCheck with:\n  #{check_metadata_cmd}"
                end
              end

              check_cover_cmd = "metaflac #{file} --export-picture-to=- | diff - static/favicon.svg"
              `#{check_cover_cmd}`
              unless $?.success? then
                add_cover_cmd = "metaflac #{file} --import-picture-from='3|image/svg+xml||420x420x24|static/favicon.svg'"
                raise "Cover art from '#{file}' doesn't match 'static/favicon.svg'.\nFix it with:\n\n#{add_cover_cmd}\n\nCheck with:\n  #{check_cover_cmd}"
              end
            elsif name == 'screencasts' then
              p document
            end
          end
        end
      end
    end

    def generate(site)
      assert_unique_ids(site)
      assert_frontmatter(site)
      assert_media_metadata(site)
      assert_git_annex(site)
    end

    def all_documents(site)
      site.collections.each do |name, collection|
        collection.docs.each do |document|
          unless document.data['generated']
            yield name, document
          end
        end
      end

      site.pages.each do |page|
        unless IGNORED_PAGES.include? page.path
          unless page.data['generated']
            yield 'page', page
          end
        end
      end
    end
  end
end