Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
name: Release

# Binary builds: a self-contained IronRuby for each platform, made by
# Util/package-release.sh. Pushing a tag v* publishes them as a GitHub release (a
# Util/package-release.sh, and for Windows also an installer made from that archive by
# Util/installer/build-installer.sh. Pushing a tag v* publishes them as a GitHub release (a
# pre-release while the version has a suffix such as -preview1), with
# Docs/releases/<tag>.md as the notes when there is one; running the workflow by hand, or
# a pull request that changes the packaging, only keeps them as workflow artifacts.
Expand All @@ -18,6 +19,7 @@ on:
paths:
- .github/workflows/release.yml
- Util/package-release.sh
- Util/installer/**
workflow_dispatch:
inputs:
render_video:
Expand Down Expand Up @@ -159,23 +161,63 @@ jobs:
working-directory: dist
run: cd ironruby-*-win-x64 && cmd //c ir.cmd Samples/Notepad/test_notepad.rb

# Inno Setup, for the installer: its release from GitHub, pinned by version and by the
# SHA-256 GitHub lists for that asset, installed for this job only.
- name: Inno Setup
if: runner.os == 'Windows'
env:
INNO_VERSION: 6.7.3
INNO_SHA256: 9c73c3bae7ed48d44112a0f48e66742c00090bdb5bef71d9d3c056c66e97b732
run: |
mkdir -p "$RUNNER_TEMP/innosetup-download"
setup="$RUNNER_TEMP/innosetup-download/innosetup-$INNO_VERSION.exe"
curl -fsSL -o "$setup" \
"https://github.com/jrsoftware/issrc/releases/download/is-${INNO_VERSION//./_}/innosetup-$INNO_VERSION.exe"
echo "$INNO_SHA256 $setup" | sha256sum -c
MSYS2_ARG_CONV_EXCL='*' "$setup" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- /CURRENTUSER \
"/DIR=$(cygpath -w "$RUNNER_TEMP/innosetup")"
test -f "$RUNNER_TEMP/innosetup/ISCC.exe"
echo "ISCC=$RUNNER_TEMP/innosetup/ISCC.exe" >> "$GITHUB_ENV"

# The installer carries the archive just made, unchanged.
- name: Installer, from the archive
if: runner.os == 'Windows'
working-directory: ironruby
run: Util/installer/build-installer.sh "$GITHUB_WORKSPACE"/dist/ironruby-*-win-x64.zip

# Into C:\IronRuby40-x64, for this user and then for all users: ironruby, irb and
# `igem install rack` from the PATH a new terminal gets, then the uninstaller, which
# must leave no file and the PATH as it was.
- name: Install, run and uninstall
if: runner.os == 'Windows'
timeout-minutes: 15
shell: pwsh
working-directory: dist
run: |
$test = "$env:GITHUB_WORKSPACE\ironruby\Util\installer\test-installer.ps1"
$setup = (Get-Item ironruby-*-win-x64-setup.exe).FullName
& $test $setup
if ($LASTEXITCODE) { exit $LASTEXITCODE }
& $test $setup -AllUsers

# Signed SLSA build provenance for the archive a tag publishes, with GitHub's own
# actions/attest, pinned to the commit of its v4.2.2 tag: `gh attestation verify
# <archive> --repo Largo/ironruby` shows that this workflow built it, from which
# commit and in which run. dist/ironruby-<version>-<rid>.* is the archive alone, not
# the directory it was packed from.
# commit and in which run. dist/ironruby-<version>-<rid>*.* is the archive, and on
# Windows the installer, -setup.exe; not the directory the archive was packed from.
- name: Attest build provenance
if: github.event_name == 'push' && github.ref_type == 'tag'
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-path: dist/ironruby-*-${{ matrix.rid }}.*
subject-path: dist/ironruby-*-${{ matrix.rid }}*.*

- uses: actions/upload-artifact@v7
with:
name: ironruby-${{ matrix.rid }}
path: |
dist/*.tar.gz
dist/*.zip
dist/*-setup.exe
if-no-files-found: error

# media/launch is a HyperFrames composition (media/launch/README.md). A video that
Expand Down
42 changes: 42 additions & 0 deletions Docs/releases/v4.0.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
### Download

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. Or, on Windows, run the
installer.

- **Windows x64 installer**: `ironruby-4.0.3-win-x64-setup.exe`, the zip below, installed
into `C:\IronRuby40-x64` and put on the PATH
- **Windows x64**: `ironruby-4.0.3-win-x64.zip`, with Windows Forms, WPF, `ironrubyw.exe`
and `Samples\Notepad\notepad.cmd`
- **Linux x64**: `ironruby-4.0.3-linux-x64.tar.gz`
- **macOS arm64**: `ironruby-4.0.3-osx-arm64.tar.gz`, built in CI, not tested there

`SHA256SUMS` lists the checksums. Each file 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.3-win-x64-setup.exe --repo Largo/ironruby
```

### New

- **A Windows installer**, in the style of RubyInstaller. It installs the win-x64 zip's
tree, unchanged, into `C:\IronRuby40-x64` by default, or a directory you choose, for you
alone (no admin rights) or for all users, and puts that directory first on the PATH, so
`ironruby`, `ir`, `irb` and `igem` work from a new terminal. `igem install` writes into
the install tree, which is why the default is a directory you can write to and has no
space in its path: Bundler's `RUBYOPT`, the Rails templates and mspec break on one.
Running `.rb` files with IronRuby is an option, off by default. The uninstaller removes
the files, the gems installed since, and the PATH entry. It is built in CI with Inno
Setup 6.7.3 from `Util/installer/ironruby.iss`, and installed, run and uninstalled there
before it is published.

### Fixes

- **irb starts on Windows again.** In 4.0.2 it stopped at once with `unknown symbol
"FillConsoleOutputCharacter"`. reline asks Fiddle for Win32 functions by their bare
names, and Fiddle now also looks for the `A` export, as CRuby's does. In a console, irb
then crashed: reline hands the console API its buffers as `[str].pack("p")`, and on
IronRuby that was an index, not an address. It is a real address now, of a block holding
the String's bytes, and what C writes there through Fiddle comes back into the String, as
with CRuby - so reline runs as Ruby ships it.
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -513,19 +513,30 @@ $ ./igem.sh install rack
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
`Samples\Notepad\notepad.cmd`. From 4.0.3 on there is also an installer,
`ironruby-<version>-win-x64-setup.exe`, in the style of RubyInstaller: it installs the same
tree into `C:\IronRuby40-x64` (or a directory you choose), for you or for all users, puts
that directory first on your PATH so `ironruby`, `ir`, `irb` and `igem` work from a new
terminal, and its uninstaller removes it all again, installed gems included. It needs no
admin rights for you alone, and keeps the default path free of spaces, which Bundler, the
Rails templates and mspec do not handle. IronRuby's `irb` then comes before CRuby's on the
PATH; CRuby's `ruby` and `gem` are not affected. `Util/installer/build-installer.sh` makes
it from the zip with [Inno Setup](https://jrsoftware.org/isinfo.php), from
`Util/installer/ironruby.iss`. 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.
The Windows archive also carries the Windows Desktop runtime, for Windows Forms and WPF, and
`Samples/`. `License/` holds IronRuby's licenses and, for each .NET framework an archive
bundles, Microsoft's license and third-party notices.

`Util/package-release.sh <rid>` builds such an archive from a checkout, and
`.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.
`.github/workflows/release.yml` builds all three, and the Windows installer from the zip,
installs and uninstalls it once for the current user and once for all users, and
publishes them when a `v*` tag is pushed; the [launch video](media/launch) is rendered and
added by hand.

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
for each archive (from 4.0.3 on, the installer too) 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
the one it built. Check one with the GitHub CLI:
Expand Down
55 changes: 55 additions & 0 deletions Util/installer/build-installer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# Builds IronRuby's Windows installer (ironruby.iss, Inno Setup 6) from the win-x64
# release archive that Util/package-release.sh makes:
#
# Util/installer/build-installer.sh dist/ironruby-4.0.2-win-x64.zip
# -> dist/ironruby-4.0.2-win-x64-setup.exe
# ISCC=/path/to/ISCC.exe Util/installer/build-installer.sh ZIP OUTDIR
#
# The installer carries the archive's tree as it is: the archive is unpacked into a
# directory of its own and handed to Inno Setup's compiler, and the version comes from
# the archive's name. ISCC is taken from $ISCC, from PATH or from where Inno Setup 6
# installs itself. Runs in Git Bash on Windows.
set -euo pipefail

ZIP=${1:?usage: Util/installer/build-installer.sh ironruby-VERSION-win-x64.zip [OUTDIR]}
ISS=$(cd "$(dirname "$0")" && pwd)/ironruby.iss
ZIP=$(cd "$(dirname "$ZIP")" && pwd)/$(basename "$ZIP")
OUT=$(mkdir -p "${2:-$(dirname "$ZIP")}" && cd "${2:-$(dirname "$ZIP")}" && pwd)
NAME=$(basename "$ZIP" .zip)
case "$NAME" in
ironruby-*-win-x64) ;;
*) echo "build-installer.sh: not a win-x64 release archive: $ZIP" >&2; exit 1 ;;
esac
VERSION=${NAME#ironruby-}
VERSION=${VERSION%-win-x64}
# 4.0.3-preview1 and 4.0.2+1a2b3c4 are 4.0.3 and 4.0.2 to Windows' file version.
NUMERIC=$(echo "$VERSION" | grep -oE '^[0-9]+(\.[0-9]+){0,3}')

if [ -z "${ISCC:-}" ]; then
for candidate in "$(command -v iscc || true)" \
"$(cygpath -u "${LOCALAPPDATA:-C:\\}")/Programs/Inno Setup 6/ISCC.exe" \
"/c/Program Files (x86)/Inno Setup 6/ISCC.exe" \
"/c/Program Files/Inno Setup 6/ISCC.exe"; do
if [ -n "$candidate" ] && [ -f "$candidate" ]; then ISCC=$candidate; break; fi
done
fi
if [ -z "${ISCC:-}" ]; then
echo "build-installer.sh: no Inno Setup 6 compiler (ISCC.exe); install Inno Setup 6 or set ISCC" >&2
exit 1
fi

# Unpacked into the temporary directory, not next to the output: ISCC does not read paths
# past Windows' 260 characters, and the tree's own go 100 deep.
STAGE=$(mktemp -d)
trap 'rm -rf "$STAGE"' EXIT
if command -v unzip >/dev/null; then unzip -q "$ZIP" -d "$STAGE"
else 7z x -bd -o"$STAGE" "$ZIP" >/dev/null; fi
test -f "$STAGE/$NAME/ironruby.cmd"

# Not MSYS's path conversion: it would take ISCC's /D and /O switches for paths.
MSYS2_ARG_CONV_EXCL='*' "$ISCC" /Q \
"/DAppVersion=$VERSION" "/DNumericVersion=$NUMERIC" \
"/DSourceDir=$(cygpath -w "$STAGE/$NAME")" \
"/O$(cygpath -w "$OUT")" "$(cygpath -w "$ISS")"
echo "installer: $OUT/$NAME-setup.exe"
Loading
Loading