#!/usr/bin/env ruby require 'siteleaf' require 'fileutils' require 'open-uri' require 'digest/sha1' require 'tempfile' require 'yaml' def help %Q( Usage: siteleaf [COMMAND] [OPTIONS] Commands: auth Login in with your credentials c, config DOMAIN Configure an existing directory n, new DOMAIN Creates new site on siteleaf.net pull Pulls files for configured site from Siteleaf push Pushes all files in dir to configured site publish Publish website to hosting provider help Prints this help document version Prints the siteleaf gem version Options: -h, --help Prints this help document -v, --version Prints the siteleaf gem version -q, --quiet Suppress publish status See https://github.com/siteleaf/siteleaf-gem for additional documentation. ) end def auth(re_auth = false) Siteleaf.load_settings if !re_auth if re_auth or !Siteleaf.api_key print 'Enter your Siteleaf e-mail: ' email = $stdin.gets.chomp print 'Enter your Siteleaf password: ' system 'stty -echo' password = $stdin.gets.chomp system 'stty echo' puts "\nAuthorizing..." if (auth = Siteleaf::Client.auth(email, password)) && (auth.is_a?(Hash)) && (auth.has_key?('api_key')) Siteleaf.save_settings({api_key: auth['api_key'], api_secret: auth['api_secret']}) puts "=> Gem authorized." if re_auth return true else puts auth['error'] || "Could not authorize, check your e-mail or password." return false end end end def config(site) Siteleaf.save_settings({site_id: site.id}, '.siteleaf.yml') puts "=> Site configured." end def get_site_id # check env vars [:api_key, :api_secret, :api_base, :api_version].each do |key| if value = ENV[key.to_s.upcase] Siteleaf.send "#{key}=", value end end ENV['SITE_ID'] || if settings = Siteleaf.load_settings('.siteleaf.yml') settings[:site_id] end end def pull(site_id) # get all the things site = Siteleaf::Site.new(id: site_id) files = site.files uploads = site.uploads pages = site.pages posts = site.posts collections = site.collections documents = [] collections.each do |collection| documents += collection.documents end assets = files + uploads + pages + posts + documents updated_count = 0 assets.each do |asset| sha = ::File.exist?(asset.filename) && Digest::SHA1.hexdigest(::File.read(asset.filename)) if asset.sha == sha # file is up to date else print "Downloading #{asset.filename}..." FileUtils.mkdir_p(::File.dirname(asset.filename)) ::File.open(asset.filename, 'w:UTF-8') { |f| f.write(asset.to_file) } updated_count += 1 print "complete.\n" end end puts "=> #{updated_count} file(s) downloaded.\n" end def push(site_id) # check config config = ::File.exist?('_config.yml') ? YAML::load(::File.read('_config.yml')) : {} markdown_ext = (config['markdown_ext'] || 'markdown,mdw,mdwn,md,text').split(',') # get all the things site = Siteleaf::Site.new(id: site_id) files = site.files uploads = site.uploads pages = site.pages posts = site.posts collections = site.collections documents = [] collections.each do |collection| documents += collection.documents end assets = files + uploads + pages + posts + documents updated_count = 0 ignore_paths = ['_config.yml', 'config.ru', '.*', '_site/*', 'Gemfile', 'Gemfile.lock'] ignore_paths += ::File.read('.siteleafignore').split(/\r?\n/) if ::File.exists?('.siteleafignore') ignore_paths += config['exclude'] if config['exclude'].is_a? Array # create collections existing_collections = config['collections'] || {} existing_collections.each do |label, metadata| path = label.gsub(/[^a-z0-9_\-\.]/i, '') unless collections.find {|c| c.path == path } # create any new collections output = metadata.delete('output') permalink = metadata.delete('permalink') collections << Siteleaf::Collection.create(site_id: site.id, title: label, path: path, permalink: permalink, metadata: metadata) end end collection_dirs = collections.map{|c| c.basename } # upload files paths = Dir.glob("**/*") paths.each do |path| if !::File.directory?(path) && !ignore_paths.any?{|i| ::File.fnmatch?(i, path, File::FNM_CASEFOLD) || ::File.fnmatch?(i, ::File.basename(path), File::FNM_CASEFOLD) } asset = assets.find{|a| a.filename == path } sha = Digest::SHA1.hexdigest(::File.read(path)) if asset.nil? || asset.sha != sha print "Uploading #{path}..." static = !has_yaml_header?(path) basename = ::File.basename(path) basedir = ::File.dirname(path).split('/').first ext = ::File.extname(path).sub('.', '') response = if !static && markdown_ext.include?(ext) && basedir != '_uploads' # handle content metadata = {} unless static body, metadata = read_frontmatter(path) end clean_path = path.sub("#{basedir}/",'').sub(".#{ext}",'') title = metadata.delete('title') || ::File.basename(clean_path) attrs = {site_id: site.id, title: title, path: clean_path, static: static} attrs[:body] = body if body && body != "" attrs[:metadata] = metadata if metadata && !metadata.empty? model = if basedir == '_drafts' attrs[:visibility] = 'draft' Siteleaf::Post elsif basedir == '_posts' attrs[:visibility] = (metadata['published'].delete == false) ? 'hidden' : 'visible' Siteleaf::Post elsif collection = collections.find {|c| c.basename == basedir } attrs[:collection_id] = collection.id Siteleaf::Document else attrs[:path] = path.sub(".#{ext}",'') Siteleaf::Page end if asset && asset.is_a?(model) attrs[:id] = asset.id asset = model.new(attrs).save else asset.delete if asset asset = model.create(attrs) end else # handle assets file = path metadata = {} unless static body, metadata = read_frontmatter(path) file = Tempfile.new(basename) ::File.open(file, 'w:UTF-8') { |f| f.write(body) } end attrs = {site_id: site.id, file: ::File.new(path), path: path, static: static} model = if basedir == '_uploads' attrs[:path] = path.sub("#{basedir}/",'') Siteleaf::Upload else Siteleaf::File end asset.delete if asset asset = model.create(attrs) if metadata && !metadata.empty? asset.metadata = metadata asset.save end end if error = !response || response.error || response.message print (error) ? "error: #{error}\n" : "error.\n" break else updated_count += 1 print "complete.\n" end end end end # check for old files missing_assets = [] assets.each do |asset| missing_assets << asset if !paths.include?(asset.filename) end if missing_assets.empty? puts "=> #{updated_count} file(s) uploaded.\n" else print "=> #{updated_count} file(s) uploaded. Delete the following #{missing_assets.size} unmatched file(s)?\n" missing_assets.each do |asset| puts asset.filename end print '(y/n)? ' if $stdin.gets.chomp == 'y' missing_assets.each do |asset| print "Deleting #{asset.filename}..." asset.delete print "complete.\n" end puts "=> #{missing_assets.size} file(s) deleted.\n" end end end def publish(site_id, quiet = true) site = Siteleaf::Site.new(id: site_id) job = site.publish if quiet puts "=> Publish queued.\n" else last_msg = nil job.stream do |s| if (msg = s["message"]) && (msg != last_msg) puts msg last_msg = msg end end puts "=> Publish completed.\n" end end def has_yaml_header?(file) !!(File.open(file, 'rb') { |f| f.read(5) } =~ /\A---\r?\n/) end def read_frontmatter(file) content = ::File.read(file) metadata = {} if content =~ /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m content = $POSTMATCH metadata = YAML.load($1) end return content, metadata end case ARGV[0] when '-v', '--version', 'version' puts Siteleaf::VERSION when '-h', '--help', 'help' puts help when 'auth' auth true when 'c', 'config', 'setup' if auth != false if site = Siteleaf::Site.find_by_domain(ARGV[1]) config site else puts "No site found for `#{ARGV[1]}`, run `siteleaf new #{ARGV[1]}` to create it.\n" end end when 'n', 'new' if auth != false if (site = Siteleaf::Site.create(:title => ARGV[1], :domain => ARGV[1])) && (!site.error) dir = ARGV.size >= 3 ? ARGV[2] : ARGV[1] Dir.mkdir(dir) unless ::File.directory?(dir) Dir.chdir(dir) config site else puts "Could not create site `#{ARGV[1]}`.\n" end end when 'pull' #case ARGV[1] #when 'theme' site_id = get_site_id if auth != false if site_id pull(site_id) else puts "Site not configured, run `siteleaf config yoursite.com`.\n" end end #else # puts "`#{ARGV[0]}` command not found.\n" #end when 'push' #case ARGV[1] #when 'theme' site_id = get_site_id if auth != false if site_id push(site_id) else puts "Site not configured, run `siteleaf config yoursite.com`.\n" end end #else # puts "`#{ARGV[0]}` command not found.\n" #end when 'publish' site_id = get_site_id if auth != false quiet = %w[-q --quiet].include?(ARGV[1]) && ARGV[1] if site_id publish(site_id, quiet) else puts "Site not configured, run `siteleaf config yoursite.com`.\n" end end else puts "`#{ARGV[0]}` command not found.\n" puts help end