diff --git a/changelog/2025-08-12-ruby-support/index.md b/changelog/2025-08-12-ruby-support/index.md new file mode 100644 index 000000000..d66f7e6e0 --- /dev/null +++ b/changelog/2025-08-12-ruby-support/index.md @@ -0,0 +1,17 @@ +--- +slug: ruby-support +version: v1.XXX.0 +title: Ruby support +tags: ['Ruby', 'Script editor'] +video: ./videos/ruby.mp4 +description: Windmill now supports Ruby scripts with bundler-compatible dependency management. +features: + [ + 'Write your Windmill script in Ruby.', + 'Flexible and simple syntax.', + 'Fast dependency installer with caching.', + 'Bundler-compatible gemfile syntax for dependency management.', + 'Private gem repository support.', + ] +docs: /docs/getting_started/scripts_quickstart/ruby +--- diff --git a/docs/core_concepts/9_worker_groups/index.mdx b/docs/core_concepts/9_worker_groups/index.mdx index 4483043ac..8eae8f237 100644 --- a/docs/core_concepts/9_worker_groups/index.mdx +++ b/docs/core_concepts/9_worker_groups/index.mdx @@ -131,6 +131,7 @@ The tags of _default_ worker group are: - `csharp`: The default worker group for [C#](../../getting_started/0_scripts_quickstart/11_csharp_quickstart/index.mdx) scripts. - `java`: The default worker group for [Java](../../getting_started/0_scripts_quickstart/13_java_quickstart/index.mdx) scripts. - `nu`: The default worker group for [Nu scripts](../../getting_started/0_scripts_quickstart/4_bash_quickstart/index.mdx#nu). +- `ruby`: The default worker group for [Ruby](../../getting_started/0_scripts_quickstart/ruby_quickstart/index.mdx) scripts. - `other`: Everything else (other than the [native](#native-worker-group) tags). #### Native worker group diff --git a/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/customize-ui.png b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/customize-ui.png new file mode 100644 index 000000000..341377a1a Binary files /dev/null and b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/customize-ui.png differ diff --git a/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/index.mdx b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/index.mdx new file mode 100644 index 000000000..bc10b51fa --- /dev/null +++ b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/index.mdx @@ -0,0 +1,429 @@ +--- +title: 'Ruby quickstart' +slug: '/getting_started/scripts_quickstart/ruby' +--- + +import DocCard from '@site/src/components/DocCard'; + +# Ruby quickstart + +In this quick start guide, we will write our first script in [Ruby](https://www.ruby-lang.org/). + +
+
+ + + +
+ + +
+ +Scripts are the basic building blocks in Windmill. They can be [run and scheduled](../../8_triggers/index.mdx) as standalone, chained together to create [Flows](../../../flows/1_flow_editor.mdx) or displayed with a personalized User Interface as [Apps](../../7_apps_quickstart/index.mdx). + +
+ + +
+ +Scripts consist of 2 parts: + +- [Code](#code): for Ruby scripts, they can optionally have a main function. Scripts without a main function will execute the entire file. +- [Settings](#settings): settings & metadata about the Script such as its path, summary, description, [jsonschema](../../../core_concepts/13_json_schema_and_parsing/index.mdx) of its inputs (inferred from its signature). + +When stored in a code repository, these 2 parts are stored separately at `.rb` and `.script.yaml` + +Windmill automatically manages [dependencies](/docs/getting_started/scripts_quickstart/ruby#dependencies-management) for you. +When you use gems in your Ruby script through `gemfile` blocks (compatible with bundler/inline syntax), +Windmill parses these dependencies upon saving the script and automatically generates a Gemfile.lock, +ensuring that the same version of the script is always executed with the same versions of its dependencies. +More to it, to remove vendor lock-in barrier you have ability to extract the lockfile and use it outside Windmill if you want. + +This is a simple example of a script built in Ruby with Windmill: + +```ruby +require 'windmill/inline' + +gemfile do + source 'https://rubygems.org' + gem 'httparty' + gem 'json' +end + +def main(url: "https://httpbin.org/get", message: "Hello from Windmill!") + response = HTTParty.get(url, query: { message: message }) + return { + status: response.code, + body: JSON.parse(response.body), + message: "Request completed successfully" + } +end +``` + +In this quick start guide, we'll create a script that greets the operator running it. + +From the Home page, click `+Script`. This will take you to the first step of script creation: Metadata. + +## Settings + +![Ruby Settings](./ruby-settings.png "Ruby Settings") + +As part of the [settings](../../../script_editor/settings.mdx) menu, each script has metadata associated with it, enabling it to be defined and configured in depth. + +- **Path** is the Script's unique identifier that consists of the + [script's owner](../../../core_concepts/16_roles_and_permissions/index.mdx), and the script's name. + The owner can be either a user, or a group ([folder](../../../core_concepts/8_groups_and_folders/index.mdx#folders)). +- **Summary** (optional) is a short, human-readable summary of the Script. It + will be displayed as a title across Windmill. If omitted, the UI will use the `path` by + default. +- **Language** of the script. +- **Description** is where you can give instructions through the [auto-generated UI](../../../core_concepts/6_auto_generated_uis/index.mdx) + to users on how to run your Script. It supports markdown. +- **Script kind**: Action (by default), [Trigger](../../../flows/10_flow_trigger.mdx), [Approval](../../../flows/11_flow_approval.mdx) or [Error handler](../../../flows/7_flow_error_handler.md). This acts as a tag to filter appropriate scripts from the [flow editor](../../6_flows_quickstart/index.mdx). + +This menu also has additional settings on [Runtime](../../../script_editor/settings.mdx#runtime), [Generated UI](#generated-ui) and [Triggers](../../../script_editor/settings.mdx#triggers). + +
+ +
+ +Now click on the code editor on the left side, and let's build our Hello World! + +## Code + +Windmill provides an online editor to work on your Scripts. The left-side is +the editor itself. The right-side [previews the UI](../../../core_concepts/6_auto_generated_uis/index.mdx) that Windmill will +generate from the Script's signature - this will be visible to the users of the +Script. You can preview that UI, provide input values, and [test your script](#instant-preview--testing) there. + +![Ruby Editor](./ruby-startpage.png "Ruby Editor") + +
+ + +
+ +As we picked `ruby` for this example, Windmill provided some Ruby +boilerplate. Let's take a look: + +```ruby +# Builtin mini windmill client +require 'windmill/mini' +require 'windmill/inline' + +# Add your gem dependencies here using gemfile syntax +gemfile do + source 'https://rubygems.org' + gem 'httparty', '~> 0.21' + gem 'json', '~> 2.6' +end + +# You can import any gem from RubyGems. +# See here for more info: https://www.windmill.dev/docs/advanced/dependencies_in_ruby + +def main( + name = "Nicolas Bourbaki", + age = 42, + obj = { "even" => "hashes" }, + l = ["or", "arrays!"] +) + puts "Hello World and a warm welcome especially to #{name}" + puts "and its acolytes.. #{age} #{obj} #{l}" + + # retrieve variables, resources using built-in methods + begin + # Imported from windmill mini client + secret = get_variable("f/examples/secret") + rescue => e + secret = "No secret yet at f/examples/secret!" + end + puts "The variable at `f/examples/secret`: #{secret}" + + # fetch context variables + user = ENV['WM_USERNAME'] + + # return value is converted to JSON + return { + "splitted" => name.split, + "user" => user, + "message" => "Hello from Ruby!" + } +end +``` + +In Windmill, scripts can optionally have a `main` function that will be the script's +entrypoint. If no main function is defined, the entire script will be executed. There are a few important things to note about the `main` function: + +- The main arguments are used for generating + 1. the [input spec](../../../core_concepts/13_json_schema_and_parsing/index.mdx) of the Script + 2. the [frontend](../../../core_concepts/6_auto_generated_uis/index.mdx) that you see when running the Script as a standalone app. +- Default values are used to infer argument types and generate the UI form. String defaults create string inputs, numeric defaults create number inputs, hash/array defaults create appropriate JSON inputs, etc. +- You can customize the UI in later steps (but not change the input type!). + +
+ +
+ +The first import line imports the Windmill Ruby client, which provides access to built-in methods for accessing +[variables](../../../core_concepts/2_variables_and_secrets/index.mdx) and +[resources](../../../core_concepts/3_resources_and_types/index.mdx). + + + + + + + + + + + + + + +Back to our Hello World. We can clean up the boilerplate, change the +main to take in the user's name. Let's also return the `name`, maybe we can use +this later if we use this Script within a [flow](../../../flows/1_flow_editor.mdx) or [app](../../../apps/0_app_editor/index.mdx) and need to pass its result on. + +```ruby +def main(name = "World") + puts "Hello #{name}! Greetings from Ruby!" + return name +end +``` + +## Instant preview & testing + +Look at the UI preview on the right: it was updated to match the input +signature. Run a test (`Ctrl` + `Enter`) to verify everything works. + +You can change how the UI behaves by changing the main signature. For example, +if you remove the default for the `name` argument, the UI will consider this field +as required. + +```ruby +def main(name) +``` + +
+ +
+ +Now let's go to the last step: the "Generated UI" settings. + +## Generated UI + +From the Settings menu, the "Generated UI" tab lets you customize the script's arguments. + +The UI is generated from the Script's main function signature, but you can add additional constraints here. For example, we could use the `Customize property`: add a regex by clicking on `Pattern` to make sure users are providing a name with only alphanumeric characters: `^[A-Za-z0-9]+$`. Let's still allow numbers in case you are some tech billionaire's kid. + +![Generated UI](./customize-ui.png "Generated UI") + +
+ + +
+ +## Run! + +We're done! Now let's look at what users of the script will do. Click on the [Deploy](../../../core_concepts/0_draft_and_deploy/index.mdx) button +to load the script. You'll see the user input form we defined earlier. + +Note that Scripts are [versioned](../../../core_concepts/34_versioning/index.mdx#script-versioning) in Windmill, and +each script version is uniquely identified by a hash. + +Fill in the input field, then hit "Run". You should see a run view, as well as +your logs. All script runs are also available in the [Runs](../../../core_concepts/5_monitor_past_and_future_runs/index.mdx) menu on +the left. + + + +You can also choose to [run the script from the CLI](../../../advanced/3_cli/index.mdx) with the pre-made Command-line interface call. + +
+ +
+ +## Dependencies management + +Ruby dependencies are managed using a `gemfile` block that is fully compatible with bundler/inline syntax. The gemfile block must include a single global source: + +```ruby +require 'windmill/inline' + +gemfile do + source 'https://rubygems.org' + gem 'httparty', '~> 0.21' + gem 'redis', '>= 4.0' + gem 'activerecord', '7.0.0' + gem 'pg', require: 'pg' + gem 'dotenv', require: false +end +``` + +### Private gem sources + +You can use private gem repositories using different syntax options: + +**Option 1: Per-gem source specification** +```ruby +require 'windmill/inline' + +gemfile do + source 'https://rubygems.org' + gem 'httparty' + gem 'private-gem', source: 'https://gems.example.com' +end +``` + +**Option 2: Source block syntax** +```ruby +require 'windmill/inline' + +gemfile do + source 'https://rubygems.org' + + source 'https://gems.example.com' do + gem 'private-gem-1' + gem 'private-gem-2' + end +end +``` + +For authentication with private sources, specify the source URL without credentials in your script. For [Enterprise Edition](/pricing) users, add the authenticated URL to Ruby repositories in instance settings. Navigate to **Instance Settings > Registries > Ruby Repos** and add: + +``` +https://admin:123@gems.example.com/ +``` + +![Ruby Private repos Instance Settings](./ruby-gems-instance-settings.png "Ruby Private repos Instance Settings") + +Windmill will automatically match the source URL from your script with the authenticated URL from settings and handle authentication seamlessly. + +### Network configuration + +- **TLS/SSL**: Automatically handled as long as the remote certificate is trusted by the system +- **Proxy**: Proxy environment variables are automatically handled during lockfile generation, gem installation, and runtime stages + +Windmill will automatically: +- Parse your gemfile block when you save the script +- Generate a Gemfile and Gemfile.lock +- Install dependencies in an isolated environment +- Cache dependencies for faster execution + + + + + + + + + +## Caching + +Every gem dependency in Ruby is cached on disk by default. Furthermore if you use the [Distributed cache storage](../../../misc/13_s3_cache/index.mdx), it will be available to every other worker, allowing fast startup for every worker. + +## What's next? + +This script is a minimal working example, but there's a few more steps that can be useful in a real-world use case: + +- Pass [variables and secrets](../../../core_concepts/2_variables_and_secrets/index.mdx) + to a script. +- Connect to [resources](../../../core_concepts/3_resources_and_types/index.mdx). +- [Trigger that script](../../8_triggers/index.mdx) in many ways. +- Compose scripts in [Flows](../../../flows/1_flow_editor.mdx) or [Apps](../../../apps/0_app_editor/index.mdx). +- You can [share your scripts](../../../misc/1_share_on_hub/index.md) with the community on [Windmill Hub](https://hub.windmill.dev). Once + submitted, they will be verified by moderators before becoming available to + everyone right within Windmill. + +Scripts are immutable and there is a hash for each deployment of a given script. Scripts are never overwritten and referring to a script by path is referring to the latest deployed hash at that path. + +
+ +
+ +For each script, a UI is autogenerated from the jsonschema inferred from the script signature, and can be customized further as standalone or embedded into rich UIs using the [App builder](../../7_apps_quickstart/index.mdx). + +
+ + +
+ +In addition to the UI, sync and async [webhooks](../../../core_concepts/4_webhooks/index.mdx) are generated for each deployment. + +
+ +
diff --git a/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-gems-instance-settings.png b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-gems-instance-settings.png new file mode 100644 index 000000000..3231fbcb3 Binary files /dev/null and b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-gems-instance-settings.png differ diff --git a/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-settings.png b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-settings.png new file mode 100644 index 000000000..f49fc1ca9 Binary files /dev/null and b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-settings.png differ diff --git a/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-startpage.png b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-startpage.png new file mode 100644 index 000000000..9c36cdbd3 Binary files /dev/null and b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-startpage.png differ diff --git a/docs/getting_started/0_scripts_quickstart/index.mdx b/docs/getting_started/0_scripts_quickstart/index.mdx index 40980e550..04cc227c8 100644 --- a/docs/getting_started/0_scripts_quickstart/index.mdx +++ b/docs/getting_started/0_scripts_quickstart/index.mdx @@ -11,13 +11,14 @@ import { SiAnsible, SiRust, SiCplusplus, + SiRuby, } from 'react-icons/si'; import { LiaJava } from "react-icons/lia"; # Scripts quickstart -Windmill supports scripts in TypeScript, Python, Go, PHP, Bash, C#, SQL and Rust. +Windmill supports scripts in TypeScript, Python, Go, PHP, Bash, C#, SQL and more.
+
diff --git a/docs/misc/17_windows_workers/index.mdx b/docs/misc/17_windows_workers/index.mdx index e97a4067f..b12304885 100644 --- a/docs/misc/17_windows_workers/index.mdx +++ b/docs/misc/17_windows_workers/index.mdx @@ -149,3 +149,11 @@ You can also check other installation methods in uv`s [official documentation](h # Make sure you provide **full** path! $env:COURSIER_PATH="C:\..\..\coursier" ``` + +### Ruby executor + +1. **Install Ruby**: For Ruby you will need to have `ruby.exe`, `bundler.bat` and `gem.cmd` in `PATH`. You can use [RubyInstaller](https://rubyinstaller.org/) for this. + - Start PowerShell and verify you have working executables: + ```powershell + ruby --version && bundler --version && gem --version + ``` diff --git a/sidebars.js b/sidebars.js index b2964445b..4cccf5ea0 100644 --- a/sidebars.js +++ b/sidebars.js @@ -84,6 +84,11 @@ const sidebars = { type: 'doc', id: 'getting_started/scripts_quickstart/java_quickstart/index', label: 'Java' + }, + { + type: 'doc', + id: 'getting_started/scripts_quickstart/ruby_quickstart/index', + label: 'Ruby' } ] }, diff --git a/static/images/ruby-startpage.png b/static/images/ruby-startpage.png new file mode 100644 index 000000000..dc4352856 Binary files /dev/null and b/static/images/ruby-startpage.png differ diff --git a/static/videos/ruby.mp4 b/static/videos/ruby.mp4 new file mode 100644 index 000000000..67d7d2f02 Binary files /dev/null and b/static/videos/ruby.mp4 differ