From 2934cc522e7c791c972d103c69fd1d8c7f77bfdc Mon Sep 17 00:00:00 2001 From: Largo Date: Wed, 28 Oct 2026 14:43:39 +0100 Subject: [PATCH 1/3] IronRuby 4.0.2 The version, the release notes and the README for IronRuby 4.0.2. Pushing the tag v4.0.2 then builds, attests and publishes it. --- CurrentVersion.props | 2 +- Docs/releases/v4.0.2.md | 87 ++++++++++++++++++++++++++++++++++++++ README.md | 32 +++++++------- Src/Ruby/CurrentVersion.cs | 10 ++--- 4 files changed, 109 insertions(+), 22 deletions(-) create mode 100644 Docs/releases/v4.0.2.md diff --git a/CurrentVersion.props b/CurrentVersion.props index e77d0c287..78225f21d 100644 --- a/CurrentVersion.props +++ b/CurrentVersion.props @@ -3,7 +3,7 @@ 4 0 - 1 + 2 final 0 diff --git a/Docs/releases/v4.0.2.md b/Docs/releases/v4.0.2.md new file mode 100644 index 000000000..82545a40a --- /dev/null +++ b/Docs/releases/v4.0.2.md @@ -0,0 +1,87 @@ +**IronRuby 4.0.2** runs a Rails 8.1 app with SQLite on Windows, is about 1.2 times as fast +across IronRuby's micro benchmarks and starts in 1.25 s instead of 1.86 s, serves TLS through +net/http and Puma, and calls .NET the way Ruby 4 code is written. The legacy parser and the +Ruby 1.9 library are gone. The interpreter is now called `ironruby`, as JRuby's is `jruby`, +and every download comes with a signed build provenance attestation. + +### Download + +Self-contained: unpack one and run `ironruby` / `ironruby.cmd`, `irb`, `igem`. No .NET to +install. `ir.sh` and `ir.cmd` still work. + +- **Windows x64**: `ironruby-4.0.2-win-x64.zip`, with Windows Forms, WPF, `ironrubyw.exe` + and `Samples\Notepad\notepad.cmd` +- **Linux x64**: `ironruby-4.0.2-linux-x64.tar.gz` +- **macOS arm64**: `ironruby-4.0.2-osx-arm64.tar.gz`, built in CI, not tested there + +`SHA256SUMS` lists the checksums. Each archive and `SHA256SUMS` has a signed build +provenance attestation, which says which workflow built it, from which commit, in which run: + +``` +gh attestation verify ironruby-4.0.2-linux-x64.tar.gz --repo Largo/ironruby +``` + +### New + +- **Rails 8.1 with SQLite on Windows** (#34). `gem install rails`, `rails new`, + `bundle install`, a scaffold, migrations, importmap with Turbo and Stimulus, and + `bin/rails server` on Puma and sqlite3, as a Windows user would run them. It took + `File#flock` on Windows, IO readiness without libc, Bundler's `platforms:` for IronRuby, + binstubs that start IronRuby, and a dozen more fixes, listed in + [Docs/rails-on-windows.md](https://github.com/Largo/ironruby/blob/v4.0.2/Docs/rails-on-windows.md). + In a sweep of popular gems on Windows, 81 give the same output as CRuby byte for byte, + among them rubocop, standard, rspec, pry, prawn, sprockets and listen. +- **Faster** (#22). IronRuby's 50 micro benchmarks run in 11.2 s instead of 26.1 s (geometric + mean 1.21x): switching fibers is 7.1x as fast, `raise`/`rescue` 5.5x, keyword arguments + about 9x, near CRuby's time. `ironruby -e 1` starts in about 1.25 s instead of 1.86 s. +- **TLS** (#28, #32, #33). `OpenSSL::SSL::SSLSocket` is non-blocking, so net/http's open, + read and write timeouts work unmodified, for RubyGems too; certificates are verified + against the context's CAs, with `verify_callback`. Puma serves `ssl://`. +- **Calling .NET** (#26, #31, #25). Keyword arguments bind to parameters by name, a block + goes to the trailing delegate parameter (`list.Sort { |a, b| b <=> a }`, `Task.Run { }`), + and the split .NET libraries load on demand, so `System::Console` just works. `super` in + `initialize` runs the .NET base constructor. Stores into a boxed struct's field stick, + zlib works on Windows, and so do `Process.kill` and `File::NULL`. +- **Ruby 4.0** (#23, #30). Many core classes now behave as CRuby 4.0's, found by running + IronRuby and CRuby side by side and by ruby/spec. `Rational` and `Complex` are ports of + CRuby 4.0's, and `Pathname` is CRuby 4.0's own. +- **prism 2.0** (#27). IronRuby follows ruby/prism's new serialization, and deeply nested + code runs instead of overflowing the stack. + +### Changed + +- **The interpreter is `ironruby`** (#35), and so is RbConfig's `ruby_install_name`: the + binstubs Bundler and Rails write start `#!/usr/bin/env ironruby`, a line Ruby runs as + Ruby. `ir`, `ir.sh` and `ir.cmd` stay as its short name. On Windows, `ironrubyw.exe` is + the same interpreter without a console window, as `rubyw.exe` is CRuby's; the Notepad + sample starts with it. +- **`ironruby` with no script reads the program from stdin** (#35), as `ruby` does, at a terminal + too, and `$0` is `"-"`. It used to start the DLR's interactive console; irb is `irb` or + `iirb`. +- **Prism is the only parser** (#34). `-X:UsePrism` is still accepted and does nothing. +- **Gems written for prism 1.x or bigdecimal 2.x and 3.x activate** with the built-in + prism 2.0 and bigdecimal 4.0 (#34): rubocop, standard, minitest 6 and prawn among them. +- **Windows**: file names, `ENV`, `ARGV` and the default external encoding are UTF-8, as in + CRuby; `Dir.pwd` keeps a short 8.3 path as it was given; `Dir.home` uses `/`; local time + follows daylight saving time (#19). +- **The Ruby 1.9 library is removed** (#29, #30), including its `cgi` code with known + vulnerabilities and a `win32/sspi` that wrote past its buffers on 64-bit Windows. + +### Security and auditing + +- Signed build provenance for every published file, made by GitHub's `actions/attest` in + the job that built it (#36). +- The repository holds no programs any more: 18 test fixtures and sample DLLs from 2016, + which nothing built or shipped, are gone, and CI fails if one comes back (#36). The + upstream checkouts the build uses are pinned (#29). + +### Thanks + +[@khasinski](https://github.com/khasinski) for `File#flock` on macOS (#17) and for +`ir -S ` finding RubyGems' binstubs (#18). + +### Known limits + +- The release targets .NET 8, whose support ends on 10 November 2026. +- macOS is built, not tested. The full status is in the + [README](https://github.com/Largo/ironruby#status). diff --git a/README.md b/README.md index ba7225134..6e81e171f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ brought back to life: it **builds and runs on .NET 8 and 10**, and it parses Rub **[prism](https://github.com/ruby/prism), CRuby's own parser**, instead of the hand-ported Ruby 1.9 grammar it shipped with in 2011. -**IronRuby 4.0 is the first stable release since 1.1.3 in 2011**, and 4.0.1 is its latest. +**IronRuby 4.0 is the first stable release since 1.1.3 in 2011**, and 4.0.2 is its latest. The downloads are self-contained: unpack one and run it, there is no .NET to install. **The compatibility target is Ruby 4.0.** IronRuby aims to run what CRuby 4.0 runs, with @@ -22,12 +22,12 @@ not kept. | | Download | | |---|---|---| -| **Windows** x64 | [ironruby-4.0.1-win-x64.zip](https://github.com/Largo/ironruby/releases/download/v4.0.1/ironruby-4.0.1-win-x64.zip) | with Windows Forms, WPF and the Notepad sample | -| **Linux** x64 | [ironruby-4.0.1-linux-x64.tar.gz](https://github.com/Largo/ironruby/releases/download/v4.0.1/ironruby-4.0.1-linux-x64.tar.gz) | | -| **macOS** arm64 | [ironruby-4.0.1-osx-arm64.tar.gz](https://github.com/Largo/ironruby/releases/download/v4.0.1/ironruby-4.0.1-osx-arm64.tar.gz) | built in CI, not tested there | +| **Windows** x64 | [ironruby-4.0.2-win-x64.zip](https://github.com/Largo/ironruby/releases/download/v4.0.2/ironruby-4.0.2-win-x64.zip) | with Windows Forms, WPF and the Notepad sample | +| **Linux** x64 | [ironruby-4.0.2-linux-x64.tar.gz](https://github.com/Largo/ironruby/releases/download/v4.0.2/ironruby-4.0.2-linux-x64.tar.gz) | | +| **macOS** arm64 | [ironruby-4.0.2-osx-arm64.tar.gz](https://github.com/Largo/ironruby/releases/download/v4.0.2/ironruby-4.0.2-osx-arm64.tar.gz) | built in CI, not tested there | -[SHA256SUMS](https://github.com/Largo/ironruby/releases/download/v4.0.1/SHA256SUMS) · -[release notes](https://github.com/Largo/ironruby/releases/tag/v4.0.1) · +[SHA256SUMS](https://github.com/Largo/ironruby/releases/download/v4.0.2/SHA256SUMS) · +[release notes](https://github.com/Largo/ironruby/releases/tag/v4.0.2) · [all releases](https://github.com/Largo/ironruby/releases) · [getting started](#binary-releases) [![IronRuby 4.0 launch video: the title, an irb session, Windows Forms and the Notepad sample](media/launch/teaser.webp)](https://github.com/Largo/ironruby/releases/download/v4.0.0/ironruby-4-launch.mp4) @@ -64,7 +64,7 @@ drives it in CI with nobody at the keyboard.* ```console $ ./irb.sh irb(main):001> RUBY_DESCRIPTION -=> "IronRuby 4.0.1 (4.0.0) on .NET 8.0.31 [x86_64-linux]" +=> "IronRuby 4.0.2 (4.0.0) on .NET 8.0.31 [x86_64-linux]" irb(main):002> def fib(n) = n < 2 ? n : fib(n - 1) + fib(n - 2) => :fib irb(main):003> (1..10).map { fib(_1) } @@ -177,7 +177,7 @@ releases for that Ruby version (4.0.0, 4.0.1, ...) and is not CRuby's patch leve release notes say which CRuby release the specs were checked against. The last release of the original project was 1.1.3, for Ruby 1.9.2, in 2011. -`IRONRUBY_VERSION` is the full version (`4.0.1`, or `4.0.0-preview1` for a pre-release), +`IRONRUBY_VERSION` is the full version (`4.0.2`, or `4.0.0-preview1` for a pre-release), `RUBY_ENGINE_VERSION` the same without the pre-release suffix, and `RUBY_VERSION` the Ruby version (`4.0.0`). @@ -500,19 +500,19 @@ processes, socket addresses and Puma were fixed there in 4.0.1 (#12, #13). [Releases](https://github.com/Largo/ironruby/releases) carry a ready-to-run IronRuby for Linux x64, Windows x64 and macOS arm64 (not tested in CI); the [table at the top](#ironruby-modernized) -links the ones for 4.0.1. They are self-contained, so no .NET has to be installed. Unpack +links the ones for 4.0.2. They are self-contained, so no .NET has to be installed. Unpack one and run the launchers in it: ```console -$ tar xzf ironruby-4.0.1-linux-x64.tar.gz && cd ironruby-4.0.1-linux-x64 -$ ./ir.sh -e 'puts RUBY_DESCRIPTION' +$ tar xzf ironruby-4.0.2-linux-x64.tar.gz && cd ironruby-4.0.2-linux-x64 +$ ./ironruby -e 'puts RUBY_DESCRIPTION' $ ./irb.sh $ ./igem.sh install rack ``` -4.0.1 has `ir.sh` and `ir.cmd`; releases after it also have `ironruby` and `ironruby.cmd` -(see above), and `ir.sh` / `ir.cmd` keep working there. -On Windows, unzip it and use `ir.cmd`, `irb.cmd` and `igem.cmd`, or double-click +From 4.0.2 on, the archives have `ironruby` and `ironruby.cmd` (see above), and `ir.sh` / +`ir.cmd` keep working; 4.0.1 and earlier have only those. +On Windows, unzip it and use `ironruby.cmd`, `irb.cmd` and `igem.cmd`, or double-click `Samples\Notepad\notepad.cmd`. The archive has the same layout as a checkout, with the Release build in `Src/Console/bin/Release/net8.0`, so everything above applies to it unchanged. `irubyc` works too, but it still needs the .NET SDK to compile the application. @@ -524,7 +524,7 @@ bundles, Microsoft's license and third-party notices. `.github/workflows/release.yml` builds all three and publishes them when a `v*` tag is pushed; the [launch video](media/launch) is rendered and added by hand. -Releases after 4.0.1 carry a signed [build provenance attestation](https://docs.github.com/en/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds) +Releases from 4.0.2 on carry a signed [build provenance attestation](https://docs.github.com/en/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds) for each archive and for `SHA256SUMS`, made by GitHub's `actions/attest` in the job that built the file. It says which workflow built the file, from which commit, in which run, and that the file is @@ -604,7 +604,7 @@ Windows machine, keeping the same relative layout (`ir.cmd` finds the binaries u ``` C:\ir> set IR_CONFIG=Release C:\ir> ir.cmd -e "puts RUBY_DESCRIPTION" -IronRuby 4.0.1 (4.0.0) on .NET 8.0.31 [x64-mswin64] +IronRuby 4.0.2 (4.0.0) on .NET 8.0.31 [x64-mswin64] C:\ir> irb.cmd C:\ir> set RUBY_EXE=C:\ir\ir.cmd C:\ir> ir.cmd -Imspec/lib mspec/bin/mspec-run spec/language diff --git a/Src/Ruby/CurrentVersion.cs b/Src/Ruby/CurrentVersion.cs index 7906e7ad3..42750bb84 100644 --- a/Src/Ruby/CurrentVersion.cs +++ b/Src/Ruby/CurrentVersion.cs @@ -10,22 +10,22 @@ namespace IronRuby { public static class CurrentVersion { public const int Major = 4; public const int Minor = 0; - public const int Micro = 1; + public const int Micro = 2; public const string ReleaseLevel = "final"; public const int ReleaseSerial = 0; public const string ShortReleaseLevel = "final"; public const string Series = "4.0"; - public const string DisplayVersion = "4.0.1"; - public const string DisplayName = "IronRuby 4.0.1"; + public const string DisplayVersion = "4.0.2"; + public const string DisplayName = "IronRuby 4.0.2"; // Stays 4.0.0.0 across 4.0.x, so that what was built against one 4.0 release binds // to the next; the file version and the display version say which release it is. public const string AssemblyVersion = "4.0.0.0"; - public const string AssemblyFileVersion = "4.0.1.0"; - public const string AssemblyInformationalVersion = "IronRuby 4.0.1"; + public const string AssemblyFileVersion = "4.0.2.0"; + public const string AssemblyInformationalVersion = "IronRuby 4.0.2"; public static readonly Version Version = new Version(Major, Minor, Micro); } From 4c47c7ccf65836617d0595e8df9dabb2033870e8 Mon Sep 17 00:00:00 2001 From: Largo Date: Wed, 28 Oct 2026 14:55:05 +0100 Subject: [PATCH 2/3] Fix the three compiler warnings CI annotates - FileOps.cs (CA1416): flock tells Windows by OperatingSystem.IsWindows(), which the platform analyzer understands, and WindowsFileLock is marked SupportedOSPlatform("windows"), so its NativeOverlapped is no warning. - MiniSsl.cs (CS0728): the PFX round trip no longer assigns to the variable of its using statement; the ephemeral certificate has a name of its own, and is still the one disposed. - Range.Subclass.cs (CS0628): the serialization constructor of the sealed type is private, as for any sealed ISerializable type. The Debug build of the console has no warnings now on net8.0 and net10.0; the full C# suite passes. --- Src/Libraries/Builtins/FileOps.cs | 3 ++- Src/Libraries/Puma/MiniSsl.cs | 7 ++++--- Src/Ruby/Builtins/Range.Subclass.cs | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Src/Libraries/Builtins/FileOps.cs b/Src/Libraries/Builtins/FileOps.cs index 5b851bd6f..6a558263f 100644 --- a/Src/Libraries/Builtins/FileOps.cs +++ b/Src/Libraries/Builtins/FileOps.cs @@ -1141,7 +1141,7 @@ internal static int GetNativeFileDescriptor(RubyIO/*!*/ io) { [RubyMethod("flock", BuildConfig = "FEATURE_FILESYSTEM")] public static object FileLock(RubyFile/*!*/ self, [DefaultProtocol]int operation) { self.RequireInitialized(); - if (Path.DirectorySeparatorChar == '\\') { + if (OperatingSystem.IsWindows()) { return WindowsFileLock(self, operation); } if (!Posix.IsFlockAvailable) { @@ -1195,6 +1195,7 @@ private static extern bool UnlockFileEx(Microsoft.Win32.SafeHandles.SafeFileHand /// the reason the Posix path above does. RubyGems takes one for every binstub it /// writes, so without it `gem install` of any gem with an executable failed. /// + [System.Runtime.Versioning.SupportedOSPlatform("windows")] private static object WindowsFileLock(RubyFile/*!*/ self, int operation) { var fs = self.GetStream().BaseStream as FileStream; if (fs == null) { diff --git a/Src/Libraries/Puma/MiniSsl.cs b/Src/Libraries/Puma/MiniSsl.cs index 9e64aefba..16df9aa21 100644 --- a/Src/Libraries/Puma/MiniSsl.cs +++ b/Src/Libraries/Puma/MiniSsl.cs @@ -194,11 +194,12 @@ private static string FileText(RubyContext/*!*/ context, object path, string/*!* // SChannel cannot serve with an ephemeral key: round-trip the certificate through PFX. if (OperatingSystem.IsWindows()) { - using (leaf) { + // the ephemeral one is disposed once the PFX copy has replaced it + using (X509Certificate2 ephemeral = leaf) { #if NET9_0_OR_GREATER - leaf = X509CertificateLoader.LoadPkcs12(leaf.Export(X509ContentType.Pfx), null); + leaf = X509CertificateLoader.LoadPkcs12(ephemeral.Export(X509ContentType.Pfx), null); #else - leaf = new X509Certificate2(leaf.Export(X509ContentType.Pfx)); + leaf = new X509Certificate2(ephemeral.Export(X509ContentType.Pfx)); #endif } } diff --git a/Src/Ruby/Builtins/Range.Subclass.cs b/Src/Ruby/Builtins/Range.Subclass.cs index bdc9ad80c..b8cdc745a 100644 --- a/Src/Ruby/Builtins/Range.Subclass.cs +++ b/Src/Ruby/Builtins/Range.Subclass.cs @@ -33,8 +33,9 @@ private Subclass(Range.Subclass/*!*/ range) ImmediateClass = range.ImmediateClass.NominalClass; } - // Marshal.load of a Range subclass: the class travels in the SerializationInfo - protected Subclass(System.Runtime.Serialization.SerializationInfo/*!*/ info, System.Runtime.Serialization.StreamingContext context) + // Marshal.load of a Range subclass: the class travels in the SerializationInfo. + // Private, as for any sealed ISerializable type: serialization finds it anyway. + private Subclass(System.Runtime.Serialization.SerializationInfo/*!*/ info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { ImmediateClass = (RubyClass)info.GetValue(RubyUtils.SerializationInfoClassKey, typeof(RubyClass)); } From c68c047b5a8d997390328baa598220866701d8a2 Mon Sep 17 00:00:00 2001 From: Largo Date: Wed, 28 Oct 2026 15:05:32 +0100 Subject: [PATCH 3/3] Release notes: the launchers by the names the archives have The Linux and macOS archives have irb.sh and igem.sh, the Windows one irb.cmd and igem.cmd; a bare irb could start another Ruby. --- Docs/releases/v4.0.2.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Docs/releases/v4.0.2.md b/Docs/releases/v4.0.2.md index 82545a40a..627346869 100644 --- a/Docs/releases/v4.0.2.md +++ b/Docs/releases/v4.0.2.md @@ -6,8 +6,9 @@ and every download comes with a signed build provenance attestation. ### Download -Self-contained: unpack one and run `ironruby` / `ironruby.cmd`, `irb`, `igem`. No .NET to -install. `ir.sh` and `ir.cmd` still work. +Self-contained: unpack one and run `./ironruby`, `./irb.sh` and `./igem.sh` - on Windows +`ironruby.cmd`, `irb.cmd` and `igem.cmd`. No .NET to install. `ir.sh` and `ir.cmd` still +work. - **Windows x64**: `ironruby-4.0.2-win-x64.zip`, with Windows Forms, WPF, `ironrubyw.exe` and `Samples\Notepad\notepad.cmd`