diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..3c9c02d3 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,30 @@ +name: Release Gem + +on: + push: + branches: ["master"] + paths: ["lib/**/version.rb"] + +jobs: + release: + if: "github.repository_owner == 'jekyll'" + name: "Release Gem (Ruby ${{ matrix.ruby_version }})" + runs-on: "ubuntu-latest" + strategy: + fail-fast: true + matrix: + ruby_version: ["2.7"] + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + - name: "Set up Ruby ${{ matrix.ruby_version }}" + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby_version }} + bundler-cache: true + - name: Build and Publish Gem + uses: ashmaroli/release-gem@dist + with: + gemspec_name: "jekyll-feed" + env: + GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_GEM_PUSH_API_KEY }} diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 79c634c2..3a8a1d19 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -8,12 +8,11 @@ on: jobs: test: - if: "!contains(github.event.commits[0].message, '[ci skip]')" name: 'Ruby ${{ matrix.ruby-version }}' runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['2.6', '2.7', '3.0'] + ruby-version: ['2.6', '2.7', '3.0', '3.1'] steps: - uses: actions/checkout@v2 @@ -22,12 +21,5 @@ jobs: with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true - - name: Cache dependencies - uses: actions/cache@v2.1.3 - with: - path: vendor/bundle - key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} - restore-keys: | - ${{ runner.os }}-gems- - name: Run tests run: bash script/cibuild diff --git a/.rubocop.yml b/.rubocop.yml index 08ae1f2b..80a91934 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,27 +1,42 @@ +inherit_from: .rubocop_todo.yml + require: rubocop-jekyll inherit_gem: rubocop-jekyll: .rubocop.yml AllCops: - TargetRubyVersion: 2.4 - Include: - - lib/**/*.rb - + TargetRubyVersion: 2.5 + SuggestExtensions: false Exclude: - - .gitignore - - .rspec - - .rubocop.yml - - .travis.yml + - vendor/**/* - - Gemfile.lock - - History.markdown - - LICENSE.txt - - README.md +Layout/LineEndStringConcatenationIndentation: + Enabled: true - - script/**/* - - vendor/**/* +Lint/EmptyInPattern: + Enabled: false + +Metrics/AbcSize: + IgnoredMethods: + - generate # in generator.rb +Naming/InclusiveLanguage: + Enabled: false Naming/MemoizedInstanceVariableName: Exclude: - lib/jekyll-feed/page-without-a-file.rb + +Performance/MapCompact: + Enabled: true +Performance/RedundantEqualityComparisonBlock: + Enabled: true +Performance/RedundantSplitRegexpArgument: + Enabled: true + +Style/InPatternThen: + Enabled: false +Style/MultilineInPatternThen: + Enabled: false +Style/QuotedSymbols: + Enabled: true diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 00000000..500786af --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,65 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2021-09-17 11:44:51 UTC using RuboCop version 1.18.4. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/EmptyLines: + Exclude: + - 'spec/jekyll-feed_spec.rb' + +# Offense count: 15 +# Cop supports --auto-correct. +# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. +# URISchemes: http, https +Layout/LineLength: + Max: 144 + +# Offense count: 3 +# Configuration parameters: AllowedMethods. +# AllowedMethods: enums +Lint/ConstantDefinitionInBlock: + Exclude: + - 'spec/jekyll-feed_spec.rb' + - 'spec/spec_helper.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +Performance/RegexpMatch: + Exclude: + - 'spec/jekyll-feed_spec.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: AutoCorrect. +Performance/StringInclude: + Exclude: + - 'spec/jekyll-feed_spec.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. +# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys +Style/HashSyntax: + Exclude: + - 'spec/jekyll-feed_spec.rb' + +# Offense count: 7 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiterals: + Exclude: + - 'spec/jekyll-feed_spec.rb' + +# Offense count: 5 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyleForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, no_comma +Style/TrailingCommaInHashLiteral: + Exclude: + - 'spec/jekyll-feed_spec.rb' diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2f9e4dbf..00000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: ruby -cache: bundler -rvm: - - 3.0 - - 2.7 - - 2.5 -env: - matrix: - - JEKYLL_VERSION="~> 3.9" - - JEKYLL_VERSION="~> 4.2" -matrix: - exclude: - - rvm: 2.5 - env: JEKYLL_VERSION="~> 4.2" -before_install: -- gem update --system -- gem install bundler -before_script: bundle update -script: script/cibuild -notifications: - email: false -deploy: - provider: rubygems - api_key: - secure: qz0q6ur0kGo03jjLdoAak6WcEAwxX2r9LG3DVrhOrcfoFipkuW+uwR0et4tpK8uFrz0P9y7eTIKOb0XCXeIsIXWj6R5benpRGr2U8m9A+tE/jxviBFUaxaokte0lqWiX1fEyhRmW3zvcdLQ47Vd2EwTNaq6ZmPulmEe9gS0rBQghyclakGlZ17LI7oGgiNL9SQ335Yqa1qJklTHYHbodWQ3Z07v7VN2jxqi3WH6NacT5gUGp5iCNCLLa8+jpKr4uONNIoy6/geAWdqtvgGUE8oTjIWDoJarrknJpqfx9Rd0KLDzkyneAigHDYPW60QtrE6GGpK/+TF1pF4DzdK2EgTWqGFnZf8ehfnxmtHVl2Xq/DPr6hS8Q/f+ut4ioMzBQxPD0hfh8/EOMYKsO8mOuOlYTiZXC7iuGyvFUOl2hnBgWA99t+I0NNB06qFp3ZxIjolEc3zjzc9f1a5HUXlEut5V8nqvCwbctNiTVpT8ZEWlsQlyRUnr9cIMUTEfLgQ+v6DnvAJBMO1EILq6liB5qfutjNhzhlREt7P/ZdppGsAzWpgt0q2PafqVoPe62WR3+/8Lj2ErMr034xSSqZVNcBS0mbdvW6k3jaABo1VJ4XuHm6/yDuemWzWb7kdG9/14+IIJMW1VuaWcmnCnB6gxjkCW3Dm2ftYiN7Rfn3AUz/nU= - gem: jekyll-feed - on: - tags: true - repo: jekyll/jekyll-feed diff --git a/Gemfile b/Gemfile index e1c7ddf3..1a27d6d2 100644 --- a/Gemfile +++ b/Gemfile @@ -3,9 +3,9 @@ source "https://rubygems.org" gemspec -gem "rss" if RUBY_VERSION >= "3.0.0" gem "jekyll", ENV["JEKYLL_VERSION"] if ENV["JEKYLL_VERSION"] gem "kramdown-parser-gfm" if ENV["JEKYLL_VERSION"] == "~> 3.9" +gem "rss" if RUBY_VERSION >= "3.0.0" install_if -> { Gem.win_platform? } do gem "tzinfo", "~> 1.2" diff --git a/History.markdown b/History.markdown index f3a36217..87dca6f6 100644 --- a/History.markdown +++ b/History.markdown @@ -1,8 +1,48 @@ ## HEAD +### Development Fixes + + * Upgrade to Rake 13 to fix master (#381) + +### Documentation + + * Fix typos in Readme (#384) + +## 0.17.0 / 2022-10-14 + +### Documentation + + * Update CI status badge (#363) + +### Development Fixes + + * Add Ruby 3.1 to the CI matrix (#365) + +### Minor Enhancements + + * Allow disabling of jekyll-feed while in development (#370) + +## 0.16.0 / 2022-01-03 + +### Minor Enhancements + + * Add support for `page.description` in front matter to become entry `` (#297) + +### Bug Fixes + + * Fold private methods into the `:render` method as local variables (#327) + * Check `post.categories` instead of `post.category` (#357) + * Switched xml_escape for `` for post content (#332) + ### Development Fixes * Add Ruby 3.0 to CI (#337) + * Lock RuboCop to v1.18.x (#348) + * Add workflow to release gem via GH Action (#355) + +### Documentation + + * Use `.atom` extension in documented examples since we write an Atom feed (#359) ## 0.15.1 / 2020-10-04 @@ -109,8 +149,6 @@ * fix template for posts with post.lang defined (#168) -## 0.9.3 / 2017-03-28 - ## 0.9.1 / 2017-02-17 ### Minor Enhancements diff --git a/README.md b/README.md index 3159782b..a0f9d1f2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A Jekyll plugin to generate an Atom (RSS-like) feed of your Jekyll posts -[![Build Status](https://travis-ci.org/jekyll/jekyll-feed.svg)](https://travis-ci.org/jekyll/jekyll-feed) [![Gem Version](https://badge.fury.io/rb/jekyll-feed.svg)](https://badge.fury.io/rb/jekyll-feed) +[![Continuous Integration](https://github.com/jekyll/jekyll-feed/actions/workflows/ruby.yml/badge.svg)](https://github.com/jekyll/jekyll-feed/actions/workflows/ruby.yml) [![Gem Version](https://badge.fury.io/rb/jekyll-feed.svg)](https://badge.fury.io/rb/jekyll-feed) ## Installation @@ -40,7 +40,7 @@ Do you already have an existing feed someplace other than `/feed.xml`, but are o ```yml feed: - path: atom.xml + path: /blog/feed.atom ``` To note, you shouldn't have to do this unless you already have a feed you're using, and you can't or wish not to redirect existing subscribers. @@ -62,13 +62,15 @@ Additionally, the plugin will use the following values, if present in a post's Y * `author` - The author of the post, e.g., "Dr. Jekyll". If none is given, feed readers will look to the feed author as defined in `_config.yml`. Like the feed author, this can also be an object or a reference to an author in `_data/authors.yml` (see below). +* `description` - A short description of the post. + ### Author information *TL;DR: In most cases, put `author: [your name]` in the document's front matter, for sites with multiple authors. If you need something more complicated, read on.* There are several ways to convey author-specific information. Author information is found in the following order of priority: -1. An `author` object, in the documents's front matter, e.g.: +1. An `author` object, in the document's front matter, e.g.: ```yml author: @@ -176,16 +178,16 @@ By default, collection feeds will be outputted to `/feed/.xml`. If y feed: collections: changes: - path: "/changes.xml" + path: "/changes.atom" ``` -Finally, collections can also have category feeds which are outputted as `/feed//.xml`. Specify categories like so: +Finally, collections can also have category feeds that are outputted as `/feed//.xml`. Specify categories like so: ```yml feed: collections: changes: - path: "/changes.xml" + path: "/changes.atom" categories: - news - updates @@ -195,15 +197,15 @@ feed: Optional flag `excerpt_only` allows you to exclude post content from the Atom feed. Default value is `false` for backward compatibility. -When in `config.yml` is `true` than all posts in feed will be without `` tags. +When in `config.yml` is `true` then all posts in feed will be without `` tags. ```yml feed: excerpt_only: true ``` -The same flag can be used directly in post file. It will be disable `` tag for selected post. -Settings in post file has higher priority than in config file. +The same flag can be used directly in post file. It will disable `` tag for selected post. +Settings in post file have higher priority than in config file. ## Tags @@ -244,6 +246,16 @@ feed: Note that if you include a tag that is excluded a feed will not be generated for it. +## Skip development + +Use `disable_in_development: true` if you want to turn off feed generation when `jekyll.environment == "development"`, +but don't want to remove the plugin (so you don't accidentally commit the removal). Default value is `false`. + +```yml +feed: + disable_in_development: true +``` + ## Contributing 1. Fork it (https://github.com/jekyll/jekyll-feed/fork) diff --git a/appveyor.yml b/appveyor.yml index e6716814..d9776686 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,6 @@ environment: - RUBY_FOLDER_VER: "26" JEKYLL_VERSION : ">= 4.0.0.pre.alpha1" - RUBY_FOLDER_VER: "26" - - RUBY_FOLDER_VER: "24" install: - SET PATH=C:\Ruby%RUBY_FOLDER_VER%-x64\bin;%PATH% diff --git a/jekyll-feed.gemspec b/jekyll-feed.gemspec index f96b9a32..4ffbc6af 100644 --- a/jekyll-feed.gemspec +++ b/jekyll-feed.gemspec @@ -16,14 +16,14 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r!^spec/!) spec.require_paths = ["lib"] - spec.required_ruby_version = ">= 2.4.0" + spec.required_ruby_version = ">= 2.5.0" spec.add_dependency "jekyll", ">= 3.7", "< 5.0" spec.add_development_dependency "bundler" spec.add_development_dependency "nokogiri", "~> 1.6" - spec.add_development_dependency "rake", "~> 12.0" + spec.add_development_dependency "rake", "~> 13.0" spec.add_development_dependency "rspec", "~> 3.0" - spec.add_development_dependency "rubocop-jekyll", "~> 0.5" + spec.add_development_dependency "rubocop-jekyll", "~> 0.12.0" spec.add_development_dependency "typhoeus", ">= 0.7", "< 2.0" end diff --git a/lib/jekyll-feed/feed.xml b/lib/jekyll-feed/feed.xml index a7e23f41..38046994 100644 --- a/lib/jekyll-feed/feed.xml +++ b/lib/jekyll-feed/feed.xml @@ -45,7 +45,7 @@ {% assign posts = site[page.collection] %} {% endif %} {% if page.category %} - {% assign posts = posts | where: "category", page.category %} + {% assign posts = posts | where: "categories", page.category %} {% endif %} {% unless site.show_drafts %} {% assign posts = posts | where_exp: "post", "post.draft != true" %} @@ -63,7 +63,7 @@ {{ post.id | absolute_url | xml_escape }} {% assign excerpt_only = post.feed.excerpt_only | default: site.feed.excerpt_only %} {% unless excerpt_only %} - {{ post.content | strip | xml_escape }} + {% endunless %} {% assign post_author = post.author | default: post.authors[0] | default: site.author %} @@ -94,8 +94,9 @@ {% endfor %} - {% if post.excerpt and post.excerpt != empty %} - {{ post.excerpt | strip_html | normalize_whitespace | xml_escape }} + {% assign post_summary = post.description | default: post.excerpt %} + {% if post_summary and post_summary != empty %} + {% endif %} {% assign post_image = post.image.path | default: post.image %} diff --git a/lib/jekyll-feed/generator.rb b/lib/jekyll-feed/generator.rb index 02b81450..aa229829 100644 --- a/lib/jekyll-feed/generator.rb +++ b/lib/jekyll-feed/generator.rb @@ -8,6 +8,10 @@ class Generator < Jekyll::Generator # Main plugin action, called by Jekyll-core def generate(site) @site = site + if disabled_in_development? + Jekyll.logger.info "Jekyll Feed:", "Skipping feed generation in development" + return + end collections.each do |name, meta| Jekyll.logger.info "Jekyll Feed:", "Generating feed for #{name}" (meta["categories"] + [nil]).each do |category| @@ -54,9 +58,10 @@ def feed_path(collection: "posts", category: nil) def collections return @collections if defined?(@collections) - @collections = if config["collections"].is_a?(Array) + @collections = case config["collections"] + when Array config["collections"].map { |c| [c, {}] }.to_h - elsif config["collections"].is_a?(Hash) + when Hash config["collections"] else {} @@ -140,5 +145,9 @@ def normalize_posts_meta(hash) config["path"] ||= hash["posts"]["path"] hash end + + def disabled_in_development? + config && config["disable_in_development"] && Jekyll.env == "development" + end end end diff --git a/lib/jekyll-feed/meta-tag.rb b/lib/jekyll-feed/meta-tag.rb index 54759297..d143c728 100644 --- a/lib/jekyll-feed/meta-tag.rb +++ b/lib/jekyll-feed/meta-tag.rb @@ -6,35 +6,22 @@ class MetaTag < Liquid::Tag include Jekyll::Filters::URLFilters def render(context) + # Jekyll::Filters::URLFilters requires `@context` to be set in the environment. @context = context - attrs = attributes.map do |k, v| - v = v.to_s unless v.respond_to?(:encode) - %(#{k}=#{v.encode(:xml => :attr)}) - end - "" - end - - private - - def config - @config ||= @context.registers[:site].config - end - def attributes - { - :type => "application/atom+xml", - :rel => "alternate", - :href => absolute_url(path), - :title => title, - }.keep_if { |_, v| v } - end + config = context.registers[:site].config + path = config.dig("feed", "path") || "feed.xml" + title = config["title"] || config["name"] - def path - config.dig("feed", "path") || "feed.xml" - end + attributes = { + :type => "application/atom+xml", + :rel => "alternate", + :href => absolute_url(path), + } + attributes[:title] = title if title - def title - config["title"] || config["name"] + attrs = attributes.map { |k, v| "#{k}=#{v.to_s.encode(:xml => :attr)}" }.join(" ") + "" end end end diff --git a/lib/jekyll-feed/version.rb b/lib/jekyll-feed/version.rb index 474050b7..dbaf1b22 100644 --- a/lib/jekyll-feed/version.rb +++ b/lib/jekyll-feed/version.rb @@ -2,6 +2,6 @@ module Jekyll module Feed - VERSION = "0.15.1" + VERSION = "0.17.0" end end diff --git a/script/fmt b/script/fmt index c5351ecf..913591ad 100755 --- a/script/fmt +++ b/script/fmt @@ -1,7 +1,7 @@ #!/bin/bash set -e -echo "Rubocop $(bundle exec rubocop --version)" +echo "RuboCop $(bundle exec rubocop --version)" bundle exec rubocop -D -E $@ success=$? if ((success != 0)); then diff --git a/spec/fixtures/_posts/2014-03-02-march-the-second.md b/spec/fixtures/_posts/2014-03-02-march-the-second.md index e33a699d..3a6e5b4a 100644 --- a/spec/fixtures/_posts/2014-03-02-march-the-second.md +++ b/spec/fixtures/_posts/2014-03-02-march-the-second.md @@ -1,6 +1,8 @@ --- image: https://cdn.example.org/absolute.png?h=188&w=250 category: news +excerpt: "ignore me" +description: cool post --- March the second! diff --git a/spec/jekyll-feed_spec.rb b/spec/jekyll-feed_spec.rb index af95ea67..41514d1f 100644 --- a/spec/jekyll-feed_spec.rb +++ b/spec/jekyll-feed_spec.rb @@ -25,7 +25,9 @@ let(:contents) { File.read(dest_dir("feed.xml")) } let(:context) { make_context(:site => site) } let(:feed_meta) { Liquid::Template.parse("{% feed_meta %}").render!(context, {}) } + let(:jekyll_env) { "development" } before(:each) do + allow(Jekyll).to receive(:env).and_return(jekyll_env) site.process end @@ -76,7 +78,7 @@ end it "converts markdown posts to HTML" do - expect(contents).to match %r!<p>March the second\!</p>! + expect(contents).to match %r!<\!\[CDATA\[

March the second\!

\]\]! end it "uses last_modified_at where available" do @@ -150,6 +152,11 @@ expect(post.summary.content).to eql("Foo") end + it "includes the item's description" do + post = feed.items[-2] + expect(post.summary.content).to eql("cool post") + end + it "doesn't include the item's excerpt if blank" do post = feed.items.first expect(post.summary).to be_nil @@ -392,10 +399,11 @@ def to_s context "with top-level post categories" do let(:overrides) do { - "feed" => { "categories" => ["news"] }, + "feed" => { "categories" => %w(news jekyll) }, } end - let(:news_feed) { File.read(dest_dir("feed/news.xml")) } + let(:singular_category_feed) { File.read(dest_dir("feed/news.xml")) } + let(:plural_categories_feed) { File.read(dest_dir("feed/jekyll.xml")) } it "outputs the primary feed" do expect(contents).to match "http://example.org/updates/jekyll/2014/03/04/march-the-fourth.html" @@ -405,12 +413,13 @@ def to_s expect(contents).to_not match "http://example.org/2016/02/09/a-draft.html" end - it "outputs the category feed" do - expect(news_feed).to match 'My awesome site | News' - expect(news_feed).to match "http://example.org/news/2014/03/02/march-the-second.html" - expect(news_feed).to match "http://example.org/news/2013/12/12/dec-the-second.html" - expect(news_feed).to_not match "http://example.org/updates/jekyll/2014/03/04/march-the-fourth.html" - expect(news_feed).to_not match "http://example.org/2015/08/08/stuck-in-the-middle.html" + it "outputs the category feeds" do + expect(singular_category_feed).to match 'My awesome site | News' + expect(singular_category_feed).to match "http://example.org/news/2014/03/02/march-the-second.html" + expect(singular_category_feed).to match "March the second!" + expect(plural_categories_feed).to match 'My awesome site | News' + expect(plural_categories_feed).to match "http://example.org/updates/jekyll/2014/03/04/march-the-fourth.html" + expect(plural_categories_feed).to match "March the fourth!" end end @@ -738,4 +747,30 @@ def to_s end end end + + context "with skip_development" do + let(:overrides) do + { + "feed" => { + "disable_in_development" => true + }, + } + end + + context "in production environment" do + let(:jekyll_env) { "production" } + + it "generates a feed as normal" do + expect(Pathname.new(dest_dir("feed.xml"))).to exist + end + end + + context "in development environment" do + let(:jekyll_env) { "development" } + + it "does not generate a feed" do + expect(Pathname.new(dest_dir("feed.xml"))).not_to exist + end + end + end end