discourse/lib/tasks/themes.rake

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

220 lines
6.3 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
require "yaml"
#
# 2 different formats are accepted:
#
# == JSON format
#
# bin/rake themes:install -- '--{"discourse-something": "https://github.com/discourse/discourse-something"}'
# OR
# bin/rake themes:install -- '--{"discourse-something": {"url": "https://github.com/discourse/discourse-something", default: true}}'
#
# == YAML file formats
#
# theme_name: https://github.com/example/theme.git
# OR
# theme_name:
# url: https://github.com/example/theme_name.git
# branch: "master"
# private_key: ""
# default: false
# add_to_all_themes: false # only for components - install on every theme
#
# In the first form, only the url is required.
#
desc "Install themes & theme components"
task "themes:install" => :environment do |task, args|
theme_args = (STDIN.tty?) ? "" : STDIN.read
use_json = theme_args == ""
theme_args =
begin
2021-10-27 04:39:28 -04:00
use_json ? JSON.parse(ARGV.last.gsub("--", "")) : YAML.safe_load(theme_args)
rescue StandardError
puts use_json ? "Invalid JSON input. \n#{ARGV.last}" : "Invalid YML: \n#{theme_args}"
exit 1
end
log, counts = ThemesInstallTask.install(theme_args)
puts log
puts
puts "Results:"
puts " Installed: #{counts[:installed]}"
puts " Updated: #{counts[:updated]}"
puts " Errors: #{counts[:errors]}"
puts " Skipped: #{counts[:skipped]}"
exit 1 if counts[:errors] > 0
end
desc "Install themes & theme components from an archive"
task "themes:install:archive" => :environment do |task, args|
filename = ENV["THEME_ARCHIVE"]
RemoteTheme.update_zipped_theme(filename, File.basename(filename))
end
def update_themes
Theme
.includes(:remote_theme)
.where(enabled: true, auto_update: true)
.find_each do |theme|
theme.transaction do
remote_theme = theme.remote_theme
next if remote_theme.blank? || remote_theme.remote_url.blank?
print "Checking '#{theme.name}' for '#{RailsMultisite::ConnectionManagement.current_db}'... "
remote_theme.update_remote_version
if remote_theme.out_of_date?
puts "updating from #{remote_theme.local_version[0..7]} to #{remote_theme.remote_version[0..7]}"
FEATURE: Theme settings migrations (#24071) This commit introduces a new feature that allows theme developers to manage the transformation of theme settings over time. Similar to Rails migrations, the theme settings migration system enables developers to write and execute migrations for theme settings, ensuring a smooth transition when changes are required in the format or structure of setting values. Example use cases for the theme settings migration system: 1. Renaming a theme setting. 2. Changing the data type of a theme setting (e.g., transforming a string setting containing comma-separated values into a proper list setting). 3. Altering the format of data stored in a theme setting. All of these use cases and more are now possible while preserving theme setting values for sites that have already modified their theme settings. Usage: 1. Create a top-level directory called `migrations` in your theme/component, and then within the `migrations` directory create another directory called `settings`. 2. Inside the `migrations/settings` directory, create a JavaScript file using the format `XXXX-some-name.js`, where `XXXX` is a unique 4-digit number, and `some-name` is a descriptor of your choice that describes the migration. 3. Within the JavaScript file, define and export (as the default) a function called `migrate`. This function will receive a `Map` object and must also return a `Map` object (it's acceptable to return the same `Map` object that the function received). 4. The `Map` object received by the `migrate` function will include settings that have been overridden or changed by site administrators. Settings that have never been changed from the default will not be included. 5. The keys and values contained in the `Map` object that the `migrate` function returns will replace all the currently changed settings of the theme. 6. Migrations are executed in numerical order based on the XXXX segment in the migration filenames. For instance, `0001-some-migration.js` will be executed before `0002-another-migration.js`. Here's a complete example migration script that renames a setting from `setting_with_old_name` to `setting_with_new_name`: ```js // File name: 0001-rename-setting.js export default function migrate(settings) { if (settings.has("setting_with_old_name")) { settings.set("setting_with_new_name", settings.get("setting_with_old_name")); } return settings; } ``` Internal topic: t/109980
2023-11-02 01:10:15 -04:00
remote_theme.update_from_remote(already_in_transaction: true)
else
puts "up to date"
end
if remote_theme.last_error_text.present?
raise RemoteTheme::ImportError.new(remote_theme.last_error_text)
end
rescue => e
STDERR.puts "Failed to update '#{theme.name}': #{e}"
raise if ENV["RAISE_THEME_ERRORS"] == "1"
end
end
true
end
desc "Update themes & theme components"
task "themes:update": %w[environment assets:precompile:theme_transpiler] do
if ENV["RAILS_DB"].present?
update_themes
else
RailsMultisite::ConnectionManagement.each_connection { update_themes }
end
end
desc "List all the installed themes on the site"
task "themes:audit" => :environment do
components = Set.new
puts "Selectable themes"
puts "-----------------"
Theme
.where("(enabled OR user_selectable) AND NOT component")
.each do |theme|
puts theme.remote_theme&.remote_url || theme.name
theme.child_themes.each do |child|
if child.enabled
repo = child.remote_theme&.remote_url || child.name
components << repo
end
end
end
puts
puts "Selectable components"
puts "---------------------"
components.each { |repo| puts repo }
end
FEATURE: Introduce theme/component QUnit tests (take 2) (#12661) This commit allows themes and theme components to have QUnit tests. To add tests to your theme/component, create a top-level directory in your theme and name it `test`, and Discourse will save all the files in that directory (and its sub-directories) as "tests files" in the database. While tests files/directories are not required to be organized in a specific way, we recommend that you follow Discourse core's tests [structure](https://github.com/discourse/discourse/tree/master/app/assets/javascripts/discourse/tests). Writing theme tests should be identical to writing plugins or core tests; all the `import` statements and APIs that you see in core (or plugins) to define/setup tests should just work in themes. You do need a working Discourse install to run theme tests, and you have 2 ways to run theme tests: * In the browser at the `/qunit` route. `/qunit` will run tests of all active themes/components as well as core and plugins. The `/qunit` now accepts a `theme_name` or `theme_url` params that you can use to run tests of a specific theme/component like so: `/qunit?theme_name=<your_theme_name>`. * In the command line using the `themes:qunit` rake task. This take is meant to run tests of a single theme/component so you need to provide it with a theme name or URL like so: `bundle exec rake themes:qunit[name=<theme_name>]` or `bundle exec rake themes:qunit[url=<theme_url>]`. There are some refactors to how Discourse processes JavaScript that comes with themes/components, and these refactors may break your JS customizations; see https://meta.discourse.org/t/upcoming-core-changes-that-may-break-some-themes-components-april-12/186252?u=osama for details on how you can check if your themes/components are affected and what you need to do to fix them. This commit also improves theme error handling in Discourse. We will now be able to catch errors that occur when theme initializers are run and prevent them from breaking the site and other themes/components.
2021-04-12 08:02:58 -04:00
desc "Run QUnit tests of a theme/component"
task "themes:qunit", :type, :value do |t, args|
type = args[:type]
value = args[:value]
raise <<~TEXT if !%w[name url id].include?(type) || value.blank?
Wrong arguments type:#{type.inspect}, value:#{value.inspect}"
FEATURE: Introduce theme/component QUnit tests (take 2) (#12661) This commit allows themes and theme components to have QUnit tests. To add tests to your theme/component, create a top-level directory in your theme and name it `test`, and Discourse will save all the files in that directory (and its sub-directories) as "tests files" in the database. While tests files/directories are not required to be organized in a specific way, we recommend that you follow Discourse core's tests [structure](https://github.com/discourse/discourse/tree/master/app/assets/javascripts/discourse/tests). Writing theme tests should be identical to writing plugins or core tests; all the `import` statements and APIs that you see in core (or plugins) to define/setup tests should just work in themes. You do need a working Discourse install to run theme tests, and you have 2 ways to run theme tests: * In the browser at the `/qunit` route. `/qunit` will run tests of all active themes/components as well as core and plugins. The `/qunit` now accepts a `theme_name` or `theme_url` params that you can use to run tests of a specific theme/component like so: `/qunit?theme_name=<your_theme_name>`. * In the command line using the `themes:qunit` rake task. This take is meant to run tests of a single theme/component so you need to provide it with a theme name or URL like so: `bundle exec rake themes:qunit[name=<theme_name>]` or `bundle exec rake themes:qunit[url=<theme_url>]`. There are some refactors to how Discourse processes JavaScript that comes with themes/components, and these refactors may break your JS customizations; see https://meta.discourse.org/t/upcoming-core-changes-that-may-break-some-themes-components-april-12/186252?u=osama for details on how you can check if your themes/components are affected and what you need to do to fix them. This commit also improves theme error handling in Discourse. We will now be able to catch errors that occur when theme initializers are run and prevent them from breaking the site and other themes/components.
2021-04-12 08:02:58 -04:00
Usage:
`bundle exec rake "themes:qunit[url,<theme_url>]"`
OR
`bundle exec rake "themes:qunit[name,<theme_name>]"`
FEATURE: Introduce theme/component QUnit tests (take 2) (#12661) This commit allows themes and theme components to have QUnit tests. To add tests to your theme/component, create a top-level directory in your theme and name it `test`, and Discourse will save all the files in that directory (and its sub-directories) as "tests files" in the database. While tests files/directories are not required to be organized in a specific way, we recommend that you follow Discourse core's tests [structure](https://github.com/discourse/discourse/tree/master/app/assets/javascripts/discourse/tests). Writing theme tests should be identical to writing plugins or core tests; all the `import` statements and APIs that you see in core (or plugins) to define/setup tests should just work in themes. You do need a working Discourse install to run theme tests, and you have 2 ways to run theme tests: * In the browser at the `/qunit` route. `/qunit` will run tests of all active themes/components as well as core and plugins. The `/qunit` now accepts a `theme_name` or `theme_url` params that you can use to run tests of a specific theme/component like so: `/qunit?theme_name=<your_theme_name>`. * In the command line using the `themes:qunit` rake task. This take is meant to run tests of a single theme/component so you need to provide it with a theme name or URL like so: `bundle exec rake themes:qunit[name=<theme_name>]` or `bundle exec rake themes:qunit[url=<theme_url>]`. There are some refactors to how Discourse processes JavaScript that comes with themes/components, and these refactors may break your JS customizations; see https://meta.discourse.org/t/upcoming-core-changes-that-may-break-some-themes-components-april-12/186252?u=osama for details on how you can check if your themes/components are affected and what you need to do to fix them. This commit also improves theme error handling in Discourse. We will now be able to catch errors that occur when theme initializers are run and prevent them from breaking the site and other themes/components.
2021-04-12 08:02:58 -04:00
OR
`bundle exec rake "themes:qunit[id,<theme_id>]"`
TEXT
ENV["THEME_#{type.upcase}"] = value.to_s
ENV["QUNIT_RAILS_ENV"] ||= "development" # qunit:test will switch to `test` by default
Rake::Task["qunit:test"].reenable
Rake::Task["qunit:test"].invoke(1_200_000, "/theme-qunit")
end
desc "Install a theme/component on a temporary DB and run QUnit tests"
task "themes:isolated_test" => :environment do |t, args|
# This task can be called in a production environment that likely has a bunch
# of DISCOURSE_* env vars that we don't want to be picked up by the Unicorn
# server that will be spawned for the tests. So we need to unset them all
# before we proceed.
# Make this behavior opt-in to make it very obvious.
if ENV["UNSET_DISCOURSE_ENV_VARS"] == "1"
ENV.keys.each do |key|
next if !key.start_with?("DISCOURSE_")
next if ENV["DONT_UNSET_#{key}"] == "1"
ENV[key] = nil
end
end
redis = TemporaryRedis.new
redis.start
Discourse.redis = redis.instance
db = TemporaryDb.new
db.start
db.migrate
ActiveRecord::Base.establish_connection(
adapter: "postgresql",
database: "discourse",
port: db.pg_port,
host: "localhost",
)
seeded_themes = Theme.pluck(:id)
Rake::Task["themes:install"].invoke
themes = Theme.pluck(:name, :id)
ENV["PGPORT"] = db.pg_port.to_s
ENV["PGHOST"] = "localhost"
ENV["QUNIT_RAILS_ENV"] = "development"
ENV["DISCOURSE_DEV_DB"] = "discourse"
ENV["DISCOURSE_REDIS_PORT"] = redis.port.to_s
count = 0
themes.each do |(name, id)|
if seeded_themes.include?(id)
puts "Skipping seeded theme #{name} (id: #{id})"
next
end
puts "Running tests for theme #{name} (id: #{id})..."
Rake::Task["themes:qunit"].reenable
Rake::Task["themes:qunit"].invoke("id", id)
count += 1
end
raise "Error: No themes were installed" if count == 0
ensure
db&.stop
db&.remove
redis&.remove
FEATURE: Introduce theme/component QUnit tests (take 2) (#12661) This commit allows themes and theme components to have QUnit tests. To add tests to your theme/component, create a top-level directory in your theme and name it `test`, and Discourse will save all the files in that directory (and its sub-directories) as "tests files" in the database. While tests files/directories are not required to be organized in a specific way, we recommend that you follow Discourse core's tests [structure](https://github.com/discourse/discourse/tree/master/app/assets/javascripts/discourse/tests). Writing theme tests should be identical to writing plugins or core tests; all the `import` statements and APIs that you see in core (or plugins) to define/setup tests should just work in themes. You do need a working Discourse install to run theme tests, and you have 2 ways to run theme tests: * In the browser at the `/qunit` route. `/qunit` will run tests of all active themes/components as well as core and plugins. The `/qunit` now accepts a `theme_name` or `theme_url` params that you can use to run tests of a specific theme/component like so: `/qunit?theme_name=<your_theme_name>`. * In the command line using the `themes:qunit` rake task. This take is meant to run tests of a single theme/component so you need to provide it with a theme name or URL like so: `bundle exec rake themes:qunit[name=<theme_name>]` or `bundle exec rake themes:qunit[url=<theme_url>]`. There are some refactors to how Discourse processes JavaScript that comes with themes/components, and these refactors may break your JS customizations; see https://meta.discourse.org/t/upcoming-core-changes-that-may-break-some-themes-components-april-12/186252?u=osama for details on how you can check if your themes/components are affected and what you need to do to fix them. This commit also improves theme error handling in Discourse. We will now be able to catch errors that occur when theme initializers are run and prevent them from breaking the site and other themes/components.
2021-04-12 08:02:58 -04:00
end
desc "Clones all official themes."
task "themes:clone_all_official" do |task, args|
require "theme_metadata"
FileUtils.rm_rf("tmp/themes")
official_themes =
ThemeMetadata::OFFICIAL_THEMES.each do |theme_name|
repo = "https://github.com/discourse/#{theme_name}"
path = File.expand_path("tmp/themes/#{theme_name}")
attempts = 0
begin
attempts += 1
system("git clone #{repo} #{path}", exception: true)
rescue StandardError
abort("Failed to clone #{repo}") if attempts >= 3
STDERR.puts "Failed to clone #{repo}... trying again..."
retry
end
end
end