From 4e4b637a5dccf01f030ebe7b5457661e3f7ce469 Mon Sep 17 00:00:00 2001 From: digitalMoksha Date: Thu, 18 May 2023 21:16:48 -0500 Subject: [PATCH 1/6] Add the AssetProxyFilter --- README.md | 1 + .../node_filter/asset_proxy_filter.rb | 97 +++++++++++++ .../node_filter/asset_proxy_filter_test.rb | 136 ++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 lib/html_pipeline/node_filter/asset_proxy_filter.rb create mode 100644 test/html_pipeline/node_filter/asset_proxy_filter_test.rb diff --git a/README.md b/README.md index 706caff3..9f550173 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,7 @@ end For more information on how to write effective `NodeFilter`s, refer to the provided filters, and see the underlying lib, [Selma](https://www.github.com/gjtorikian/selma) for more information. - `AbsoluteSourceFilter`: replace relative image urls with fully qualified versions +- `AssetProxyFilter`: replace image links with an encoding link to an asset server - `EmojiFilter`: converts `::` to [emoji](http://www.emoji-cheat-sheet.com/) - (Note: the included `MarkdownFilter` will already convert emoji) - `HttpsFilter`: Replacing http urls with https versions diff --git a/lib/html_pipeline/node_filter/asset_proxy_filter.rb b/lib/html_pipeline/node_filter/asset_proxy_filter.rb new file mode 100644 index 00000000..d6b610f4 --- /dev/null +++ b/lib/html_pipeline/node_filter/asset_proxy_filter.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +require "openssl" + +class HTMLPipeline + class NodeFilter + # Proxy images/assets to another server, such as + # [cactus/go-camo](https://github.com/cactus/go-camo#). + # Reduces mixed content warnings as well as hiding the customer's + # IP address when requesting images. + # Copies the original img `src` to `data-canonical-src` then replaces the + # `src` with a new url to the proxy server. + # + # Based on https://github.com/gjtorikian/html-pipeline/blob/v2.14.3/lib/html/pipeline/camo_filter.rb + class AssetProxyFilter < NodeFilter + SELECTOR = Selma::Selector.new(match_element: "img") + + def selector + SELECTOR + end + + def handle_element(element) + return unless asset_proxy_enabled? + + original_src = element["src"] + return unless original_src + + begin + uri = URI.parse(original_src) + rescue StandardError + return + end + + return if uri.host.nil? && !original_src.start_with?("///") + return if asset_host_allowed?(uri.host) + + element["src"] = asset_proxy_url(original_src) + element["data-canonical-src"] = original_src + end + + def validate + needs(:asset_proxy, :asset_proxy_secret_key) if asset_proxy_enabled? + end + + def asset_host_allowed?(host) + context[:asset_proxy_domain_regexp] ? context[:asset_proxy_domain_regexp].match?(host) : false + end + + class << self + # This helps setup the context. It's not needed if you're always providing + # all the necessary keys in the context. One example would be to override + # this and pull the settings from a set of global application settings. + def transform_context(context, proxy_settings = {}) + context[:asset_proxy_enabled] = proxy_settings.fetch(:enabled, true) + return context unless context[:asset_proxy_enabled] + + context[:asset_proxy] = proxy_settings[:url] if proxy_settings[:url] + context[:asset_proxy_secret_key] = proxy_settings[:secret_key] if proxy_settings[:secret_key] + + allowlist = determine_allowlist(proxy_settings) + context[:asset_proxy_domain_regexp] ||= compile_allowlist(allowlist) + + context + end + + def compile_allowlist(domain_list) + return if domain_list.empty? + + escaped = domain_list.map { |domain| Regexp.escape(domain).gsub("\\*", ".*?") } + Regexp.new("^(#{escaped.join("|")})$", Regexp::IGNORECASE) + end + + def determine_allowlist(proxy_settings) + proxy_settings[:allowlist] || [] + end + end + + private + + def asset_proxy_enabled? + context[:asset_proxy_enabled] + end + + def asset_proxy_url(url) + "#{context[:asset_proxy]}/#{asset_url_hash(url)}/#{hexencode(url)}" + end + + def asset_url_hash(url) + OpenSSL::HMAC.hexdigest("sha1", context[:asset_proxy_secret_key], url) + end + + def hexencode(str) + str.unpack1("H*") + end + end + end +end diff --git a/test/html_pipeline/node_filter/asset_proxy_filter_test.rb b/test/html_pipeline/node_filter/asset_proxy_filter_test.rb new file mode 100644 index 00000000..968add49 --- /dev/null +++ b/test/html_pipeline/node_filter/asset_proxy_filter_test.rb @@ -0,0 +1,136 @@ +# frozen_string_literal: true + +require "test_helper" + +class HTMLPipeline + class AssetProxyFilterTest < Minitest::Spec + DESCRIBED_CLASS = HTMLPipeline::NodeFilter::AssetProxyFilter + + def setup + @filter = DESCRIBED_CLASS + end + + def image(path) + %() + end + + it "does not replace if disabled" do + settings = { + enabled: false, + secret_key: "shared-secret", + url: "https://assets.example.com", + allowlist: ["somewhere.com", "*.mydomain.com"], + } + + context = DESCRIBED_CLASS.transform_context({}, settings) + src = "http://example.com/test.png" + res = @filter.call(image(src), context: context) + + assert_equal(image(src), res) + end + + describe "setting up the context" do + it "#transform_context" do + settings = { + enabled: true, + secret_key: "shared-secret", + url: "https://assets.example.com", + allowlist: ["somewhere.com", "*.mydomain.com"], + } + + context = DESCRIBED_CLASS.transform_context({}, settings) + + assert(context[:asset_proxy_enabled]) + assert_equal("shared-secret", context[:asset_proxy_secret_key]) + assert_equal("https://assets.example.com", context[:asset_proxy]) + assert_equal(/^(somewhere\.com|.*?\.mydomain\.com)$/i, context[:asset_proxy_domain_regexp]) + end + + it "requires :asset_proxy" do + settings = { enabled: true } + context = DESCRIBED_CLASS.transform_context({}, settings) + + exception = assert_raises(ArgumentError) do + @filter.call("", context: context) + end + + assert_match(/:asset_proxy/, exception.message) + end + + it "requires :asset_proxy_secret_key" do + settings = { enabled: true, url: "example.com" } + context = DESCRIBED_CLASS.transform_context({}, settings) + + exception = assert_raises(ArgumentError) do + @filter.call("", context: context) + end + + assert_match(/:asset_proxy_secret_key/, exception.message) + end + end + + describe "when properly configured" do + before do + settings = { + enabled: true, + secret_key: "shared-secret", + url: "https://assets.mydomain.com", + allowlist: ["somewhere.com", "*.mydomain.com"], + } + + @context = DESCRIBED_CLASS.transform_context({}, settings) + end + + it "replaces img src" do + src = "http://example.com/test.png" + new_image = '' + res = @filter.call(image(src), context: @context) + + assert_equal(new_image, res) + end + + it "replaces invalid URLs" do + src = "///example.com/test.png" + new_image = '' + res = @filter.call(image(src), context: @context) + + assert_equal(new_image, res) + end + + it "skips internal images" do + src = "http://images.mydomain.com/test.png" + res = @filter.call(image(src), context: @context) + + assert_equal(image(src), res) + end + + it "skip relative urls" do + src = "/test.png" + res = @filter.call(image(src), context: @context) + + assert_equal(image(src), res) + end + + it "skips single domain" do + src = "http://somewhere.com/test.png" + res = @filter.call(image(src), context: @context) + + assert_equal(image(src), res) + end + + it "skips single domain and ignores url in query string" do + src = "http://somewhere.com/test.png?url=http://example.com/test.png" + res = @filter.call(image(src), context: @context) + + assert_equal(image(src), res) + end + + it "skips wildcarded domain" do + src = "http://images.mydomain.com/test.png" + res = @filter.call(image(src), context: @context) + + assert_equal(image(src), res) + end + end + end +end From feb4c60fef120a0eb33275f62a4fe0b40b59bddb Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Sun, 21 May 2023 13:47:27 +0200 Subject: [PATCH 2/6] Slight README change --- .vscode/settings.json | 8 ++++++++ README.md | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..59e1f3e3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[ruby]": { + "editor.defaultFormatter": "Shopify.ruby-lsp" + } +} diff --git a/README.md b/README.md index 9f550173..f5453a3c 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,7 @@ end For more information on how to write effective `NodeFilter`s, refer to the provided filters, and see the underlying lib, [Selma](https://www.github.com/gjtorikian/selma) for more information. - `AbsoluteSourceFilter`: replace relative image urls with fully qualified versions -- `AssetProxyFilter`: replace image links with an encoding link to an asset server +- `AssetProxyFilter`: replace image links with an encoded link to an asset server - `EmojiFilter`: converts `::` to [emoji](http://www.emoji-cheat-sheet.com/) - (Note: the included `MarkdownFilter` will already convert emoji) - `HttpsFilter`: Replacing http urls with https versions From 6a401845c30b38d639936fc219df82700a9d284a Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Sun, 21 May 2023 13:47:33 +0200 Subject: [PATCH 3/6] Move doc comment --- .../node_filter/absolute_source_filter.rb | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/html_pipeline/node_filter/absolute_source_filter.rb b/lib/html_pipeline/node_filter/absolute_source_filter.rb index f253fd6e..72205806 100644 --- a/lib/html_pipeline/node_filter/absolute_source_filter.rb +++ b/lib/html_pipeline/node_filter/absolute_source_filter.rb @@ -4,6 +4,19 @@ class HTMLPipeline class NodeFilter + # HTML Filter for replacing relative and root relative image URLs with + # fully qualified URLs + # + # This is useful if an image is root relative but should really be going + # through a cdn, or if the content for the page assumes the host is known + # i.e. scraped webpages and some RSS feeds. + # + # Context options: + # :image_base_url - Base URL for image host for root relative src. + # :image_subpage_url - For relative src. + # + # This filter does not write additional information to the context. + # Note: This filter would need to be run before AssetProxyFilter. class AbsoluteSourceFilter < NodeFilter SELECTOR = Selma::Selector.new(match_element: "img") @@ -11,19 +24,6 @@ def selector SELECTOR end - # HTML Filter for replacing relative and root relative image URLs with - # fully qualified URLs - # - # This is useful if an image is root relative but should really be going - # through a cdn, or if the content for the page assumes the host is known - # i.e. scraped webpages and some RSS feeds. - # - # Context options: - # :image_base_url - Base URL for image host for root relative src. - # :image_subpage_url - For relative src. - # - # This filter does not write additional information to the context. - # This filter would need to be run before CamoFilter. def handle_element(element) src = element["src"] return if src.nil? || src.empty? From 45c60cb62305ad2af26798111956737ec1e80ae6 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 5 Jun 2023 11:12:22 +0200 Subject: [PATCH 4/6] Removing the ability to disable Just don't include it in the pipeline if you don't want it --- .../node_filter/asset_proxy_filter.rb | 13 +------------ .../node_filter/asset_proxy_filter_test.rb | 16 ---------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/lib/html_pipeline/node_filter/asset_proxy_filter.rb b/lib/html_pipeline/node_filter/asset_proxy_filter.rb index d6b610f4..accdaca8 100644 --- a/lib/html_pipeline/node_filter/asset_proxy_filter.rb +++ b/lib/html_pipeline/node_filter/asset_proxy_filter.rb @@ -20,8 +20,6 @@ def selector end def handle_element(element) - return unless asset_proxy_enabled? - original_src = element["src"] return unless original_src @@ -51,9 +49,6 @@ class << self # all the necessary keys in the context. One example would be to override # this and pull the settings from a set of global application settings. def transform_context(context, proxy_settings = {}) - context[:asset_proxy_enabled] = proxy_settings.fetch(:enabled, true) - return context unless context[:asset_proxy_enabled] - context[:asset_proxy] = proxy_settings[:url] if proxy_settings[:url] context[:asset_proxy_secret_key] = proxy_settings[:secret_key] if proxy_settings[:secret_key] @@ -75,13 +70,7 @@ def determine_allowlist(proxy_settings) end end - private - - def asset_proxy_enabled? - context[:asset_proxy_enabled] - end - - def asset_proxy_url(url) + private def asset_proxy_url(url) "#{context[:asset_proxy]}/#{asset_url_hash(url)}/#{hexencode(url)}" end diff --git a/test/html_pipeline/node_filter/asset_proxy_filter_test.rb b/test/html_pipeline/node_filter/asset_proxy_filter_test.rb index 968add49..6569db8d 100644 --- a/test/html_pipeline/node_filter/asset_proxy_filter_test.rb +++ b/test/html_pipeline/node_filter/asset_proxy_filter_test.rb @@ -14,21 +14,6 @@ def image(path) %() end - it "does not replace if disabled" do - settings = { - enabled: false, - secret_key: "shared-secret", - url: "https://assets.example.com", - allowlist: ["somewhere.com", "*.mydomain.com"], - } - - context = DESCRIBED_CLASS.transform_context({}, settings) - src = "http://example.com/test.png" - res = @filter.call(image(src), context: context) - - assert_equal(image(src), res) - end - describe "setting up the context" do it "#transform_context" do settings = { @@ -40,7 +25,6 @@ def image(path) context = DESCRIBED_CLASS.transform_context({}, settings) - assert(context[:asset_proxy_enabled]) assert_equal("shared-secret", context[:asset_proxy_secret_key]) assert_equal("https://assets.example.com", context[:asset_proxy]) assert_equal(/^(somewhere\.com|.*?\.mydomain\.com)$/i, context[:asset_proxy_domain_regexp]) From 19511758bd47c69026bc06801fa3f48cfb55c692 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 5 Jun 2023 11:12:29 +0200 Subject: [PATCH 5/6] lint --- lib/html_pipeline.rb | 2 +- lib/html_pipeline/convert_filter.rb | 2 +- lib/html_pipeline/filter.rb | 2 +- lib/html_pipeline/node_filter/asset_proxy_filter.rb | 6 +++--- lib/html_pipeline/node_filter/mention_filter.rb | 2 +- lib/html_pipeline/node_filter/team_mention_filter.rb | 2 +- lib/html_pipeline/sanitization_filter.rb | 2 +- lib/html_pipeline/text_filter.rb | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/html_pipeline.rb b/lib/html_pipeline.rb index 9aa912b7..c7861484 100644 --- a/lib/html_pipeline.rb +++ b/lib/html_pipeline.rb @@ -81,7 +81,7 @@ def require_dependencies(names, requirer) def define_dependency_loaded_method(name, value) self.class.define_method(:"#{name}_loaded?", -> { value }) end -end + end # Public: Returns an Array of Filter objects for this Pipeline. attr_reader :text_filters, :node_filters diff --git a/lib/html_pipeline/convert_filter.rb b/lib/html_pipeline/convert_filter.rb index 4bacd563..a8c08ca7 100644 --- a/lib/html_pipeline/convert_filter.rb +++ b/lib/html_pipeline/convert_filter.rb @@ -12,6 +12,6 @@ class << self def call(text, context: {}, result: {}) new(context: context, result: result).call(text) end - end + end end end diff --git a/lib/html_pipeline/filter.rb b/lib/html_pipeline/filter.rb index 900688d4..5baf57cd 100644 --- a/lib/html_pipeline/filter.rb +++ b/lib/html_pipeline/filter.rb @@ -49,7 +49,7 @@ class << self def call(input, context: {}) raise NoMethodError end - end + end # Make sure the context has everything we need. Noop: Subclasses can override. def validate; end diff --git a/lib/html_pipeline/node_filter/asset_proxy_filter.rb b/lib/html_pipeline/node_filter/asset_proxy_filter.rb index accdaca8..f07d25d5 100644 --- a/lib/html_pipeline/node_filter/asset_proxy_filter.rb +++ b/lib/html_pipeline/node_filter/asset_proxy_filter.rb @@ -37,7 +37,7 @@ def handle_element(element) end def validate - needs(:asset_proxy, :asset_proxy_secret_key) if asset_proxy_enabled? + needs(:asset_proxy, :asset_proxy_secret_key) end def asset_host_allowed?(host) @@ -74,11 +74,11 @@ def determine_allowlist(proxy_settings) "#{context[:asset_proxy]}/#{asset_url_hash(url)}/#{hexencode(url)}" end - def asset_url_hash(url) + private def asset_url_hash(url) OpenSSL::HMAC.hexdigest("sha1", context[:asset_proxy_secret_key], url) end - def hexencode(str) + private def hexencode(str) str.unpack1("H*") end end diff --git a/lib/html_pipeline/node_filter/mention_filter.rb b/lib/html_pipeline/node_filter/mention_filter.rb index f98a598c..b7cc7e53 100644 --- a/lib/html_pipeline/node_filter/mention_filter.rb +++ b/lib/html_pipeline/node_filter/mention_filter.rb @@ -38,7 +38,7 @@ def mentioned_logins_in(text, username_pattern = USERNAME_PATTERN) yield match, login end end - end + end # Hash that contains all of the mention patterns used by the pipeline MENTION_PATTERNS = Hash.new do |hash, key| hash[key] = %r{ diff --git a/lib/html_pipeline/node_filter/team_mention_filter.rb b/lib/html_pipeline/node_filter/team_mention_filter.rb index eb3efb0f..1d15e8b4 100644 --- a/lib/html_pipeline/node_filter/team_mention_filter.rb +++ b/lib/html_pipeline/node_filter/team_mention_filter.rb @@ -35,7 +35,7 @@ def mentioned_teams_in(text, team_pattern = TEAM_PATTERN) yield match, org, team end end - end + end # Default pattern used to extract team names from text. The value can be # overridden by providing the team_pattern variable in the context. To diff --git a/lib/html_pipeline/sanitization_filter.rb b/lib/html_pipeline/sanitization_filter.rb index 020c5487..eaddc2cb 100644 --- a/lib/html_pipeline/sanitization_filter.rb +++ b/lib/html_pipeline/sanitization_filter.rb @@ -183,6 +183,6 @@ def call(html, config) sanitization_config = Selma::Sanitizer.new(config) Selma::Rewriter.new(sanitizer: sanitization_config).rewrite(html) end - end + end end end diff --git a/lib/html_pipeline/text_filter.rb b/lib/html_pipeline/text_filter.rb index 98d13030..bfcc8d1a 100644 --- a/lib/html_pipeline/text_filter.rb +++ b/lib/html_pipeline/text_filter.rb @@ -16,6 +16,6 @@ class << self def call(input, context: {}, result: {}) new(input, context: context, result: result).call end - end + end end end From dfba42fdebc358555da0170b6592d5c083f778ab Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 5 Jun 2023 11:17:43 +0200 Subject: [PATCH 6/6] Workflows --- .github/dependabot.yml | 3 -- .github/workflows/automerge.yml | 25 +--------- .github/workflows/ci.yml | 22 +++++++++ .github/workflows/lint.yml | 10 ++-- .github/workflows/publish.yml | 19 ++++++++ .github/workflows/tag_and_release.yml | 70 --------------------------- .github/workflows/test.yml | 33 ------------- .ruby-version | 1 + 8 files changed, 49 insertions(+), 134 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/publish.yml delete mode 100644 .github/workflows/tag_and_release.yml delete mode 100644 .github/workflows/test.yml create mode 100644 .ruby-version diff --git a/.github/dependabot.yml b/.github/dependabot.yml index be466a31..703fc7a1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,6 +15,3 @@ updates: time: "09:00" timezone: "Etc/UTC" open-pull-requests-limit: 10 - allow: - - dependency-name: "*" - dependency-type: "production" diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 4b61205e..61eef5f2 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -9,26 +9,5 @@ permissions: jobs: dependabot: - name: Dependabot - runs-on: ubuntu-latest - - if: ${{ github.actor == 'dependabot[bot]' }} - steps: - - name: Fetch Dependabot metadata - id: dependabot-metadata - uses: dependabot/fetch-metadata@v1 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" - - - name: Approve Dependabot PR - if: ${{steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major'}} - run: gh pr review --approve "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Merge Dependabot PR - run: gh pr merge --auto --squash "$PR_URL" - env: - PR_URL: ${{ github.event.pull_request.html_url }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: yettoapp/actions/.github/workflows/automerge_dependabot.yml@main + secrets: inherit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..b0dbaf7a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,22 @@ +name: CI + +on: + pull_request: + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Ruby + uses: yettoapp/actions/setup-languages@main + with: + ruby: true + + - name: Run tests + run: bundle exec rake test diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2a5663ae..c45d78b2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,11 +13,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: ruby/setup-ruby@v1 + + - name: Set up Ruby + uses: yettoapp/actions/setup-languages@main with: - ruby-version: 3.1.0 - rubygems: latest - bundler-cache: true - - run: bundle install + ruby: true + - name: Rubocop run: bundle exec rake rubocop diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..95808655 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,19 @@ +name: Release + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - "lib/html_pipeline/version.rb" + +jobs: + ruby: + uses: yettoapp/actions/.github/workflows/ruby_gem_release.yml@main + secrets: + rubygems_api_key: ${{ secrets.RUBYGEMS_API_BOT_KEY }} + gh_token: ${{ secrets.GITHUB_TOKEN }} + with: + gem_name: html-pipeline + version_filepath: lib/html_pipeline/version.rb diff --git a/.github/workflows/tag_and_release.yml b/.github/workflows/tag_and_release.yml deleted file mode 100644 index b44a7225..00000000 --- a/.github/workflows/tag_and_release.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: Tag and Release - -on: - workflow_dispatch: - push: - branches: - - main - paths: - - "lib/html_pipeline/version.rb" - -jobs: - release: - env: - GEM_NAME: html-pipeline - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_BOT_KEY }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up Ruby 3.1 - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.1 - bundler-cache: true - - - name: Configure Git - run: | - git config --local user.email "actions@github.com" - git config --local user.name "Actions Auto Build" - - - name: Get current version - id: version-label - run: | - VERSION=$(grep VERSION lib/html_pipeline/version.rb | head -n 1 | cut -d'"' -f2) - echo "version=${VERSION}" >> $GITHUB_OUTPUT - - - name: Create tag - run: | - git tag -a v${{ steps.version-label.outputs.version }} -m "Release v${{ steps.version-label.outputs.version }}" - git push origin --tags - - - name: Generate CHANGELOG.md - id: changelog - run: script/generate_changelog - - - name: Commit & Push Changelog - run: | - git config --local user.email "actions@github.com" - git config --local user.name "Actions Auto Build" - git add -f CHANGELOG.md - git commit -m "docs: update changelog" || true - git push - - - name: Publish release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh release create v${{ steps.version-label.outputs.version }} --generate-notes - - - name: Publish to RubyGems - run: | - mkdir -p $HOME/.gem - touch $HOME/.gem/credentials - chmod 0600 $HOME/.gem/credentials - printf -- "---\n:rubygems_api_key: ${RUBYGEMS_API_BOT_KEY}\n" > $HOME/.gem/credentials - bundle exec rake package - for gem in pkg/html-pipeline-${{ steps.version-label.outputs.version }}*.gem ; do - gem push "$gem" --host https://rubygems.org - done diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index ba94ccbf..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Tests - -on: - pull_request: - -permissions: - contents: read - -jobs: - test: - runs-on: ubuntu-latest - - strategy: - fail-fast: true - matrix: - ruby-version: - - 3.1.0 - - steps: - - uses: actions/checkout@v3 - - - name: Set up Ruby ${{ matrix.ruby-version }} - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby-version }} - rubygems: latest - bundler-cache: true - - - name: Install dependencies - run: bundle install - - - name: Run tests - run: bundle exec rake test diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..e4604e3a --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.2.1