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
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ env:
MSPEC_REF: b79c65ed28b00f0869ad82b0e3dceef0d943cd58

jobs:
# The repository holds no programs - no .exe, .dll, .so or other binary that runs - so all
# of it can be read; Util/check-no-executables.rb says why and how it tells them apart.
no-executables:
name: no programs in the repository
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Check every tracked file
run: ruby Util/check-no-executables.rb

build-and-test:
name: ${{ matrix.os }} ${{ matrix.tfm }}
runs-on: ${{ matrix.os }}
Expand Down
60 changes: 58 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ name: Release
# 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.
# Each published file gets a signed build provenance attestation (actions/attest) in the
# job that built it; `gh attestation verify <file> --repo Largo/ironruby` checks it.
# The launch video, rendered from media/launch, is only rendered when asked for by hand -
# it takes about 7 minutes of every run otherwise: with render_video it is kept as an
# artifact, and with video_for set to a tag it alone is rendered and replaces the video,
Expand Down Expand Up @@ -50,6 +52,12 @@ jobs:
runs-on: ${{ matrix.os }}
# macOS is untested (README, Platforms): a failure there does not hold the release back.
continue-on-error: ${{ matrix.experimental }}
# id-token, attestations and artifact-metadata: for the build provenance below.
permissions:
contents: read
id-token: write
attestations: write
artifact-metadata: write
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -151,6 +159,17 @@ jobs:
working-directory: dist
run: cd ironruby-*-win-x64 && cmd //c ir.cmd Samples/Notepad/test_notepad.rb

# 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.
- 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 }}.*

- uses: actions/upload-artifact@v7
with:
name: ironruby-${{ matrix.rid }}
Expand All @@ -168,6 +187,11 @@ jobs:
continue-on-error: true
# A render takes about 7 minutes; a failed one can leave hyperframes waiting.
timeout-minutes: 20
permissions:
contents: read
id-token: write
attestations: write
artifact-metadata: write
steps:
- uses: actions/checkout@v7

Expand Down Expand Up @@ -198,6 +222,14 @@ jobs:
$hf render --fps 60 --quality high --output "$GITHUB_WORKSPACE/dist/ironruby-4-launch.mp4"
$hf snapshot --frames 12

# As the archives: the film that video_for puts on a release gets its provenance here,
# in the job that rendered it.
- name: Attest build provenance
if: inputs.video_for != ''
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-path: dist/ironruby-4-launch.mp4

- uses: actions/upload-artifact@v7
with:
name: ironruby-launch-video
Expand All @@ -220,6 +252,9 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
attestations: write
artifact-metadata: write
steps:
- uses: actions/checkout@v7
with:
Expand All @@ -236,6 +271,12 @@ jobs:
working-directory: dist
run: sha256sum * | tee SHA256SUMS

# The archives were attested where they were built; SHA256SUMS is made here.
- name: Attest SHA256SUMS
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-path: dist/SHA256SUMS

- name: Publish
env:
GH_TOKEN: ${{ github.token }}
Expand All @@ -259,13 +300,16 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
attestations: write
artifact-metadata: write
steps:
- uses: actions/download-artifact@v8
with:
name: ironruby-launch-video
path: dist

- name: Upload
- name: Checksums
working-directory: dist
env:
GH_TOKEN: ${{ github.token }}
Expand All @@ -276,4 +320,16 @@ jobs:
{ grep -v ' ironruby-4-launch.mp4$' SHA256SUMS; echo "$sum"; } > SHA256SUMS.new
mv SHA256SUMS.new SHA256SUMS
cat SHA256SUMS
gh release upload "$TAG" ironruby-4-launch.mp4 SHA256SUMS --clobber --repo "$GITHUB_REPOSITORY"

# The video was attested where it was rendered; this SHA256SUMS is made here.
- name: Attest SHA256SUMS
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-path: dist/SHA256SUMS

- name: Upload
working-directory: dist
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.video_for }}
run: gh release upload "$TAG" ironruby-4-launch.mp4 SHA256SUMS --clobber --repo "$GITHUB_REPOSITORY"
Binary file removed Build/Key.snk
Binary file not shown.
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,30 @@ The Windows archive also carries the Windows Desktop runtime, for Windows Forms
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, renders the [launch video](media/launch)
and publishes them when a `v*` tag is pushed.
`.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)
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
the one it built. Check one with the GitHub CLI:

```console
$ gh attestation verify ironruby-<version>-linux-x64.tar.gz --repo Largo/ironruby
```

What is in an archive comes from these places, none of them a binary in this repository:
IronRuby, the DLR and prism (libprism too) are compiled in the job from source, at the
revisions `release.yml` pins; the NuGet packages the projects name, at the versions they
pin - AngleSharp, AngleSharp.Xml, Microsoft.Data.Sqlite, System.Text.Encoding.CodePages
and SQLitePCLRaw.bundle_e_sqlite3, whose native `e_sqlite3` is SQLite as that package
builds it on Windows and macOS; on Linux, SQLite's own amalgamation source, downloaded
from sqlite.org and checked against the SHA-256 `Util/build-e_sqlite3.sh` pins, which
that script compiles in the job instead (the package's library crashes beside another
SQLite); and the .NET runtime. The repository itself holds no programs - no `.exe`,
`.dll`, `.so` or any other binary that runs - and CI keeps it so with
`Util/check-no-executables.rb`, which looks at what each tracked file is, not at its name.

## Building

Expand Down
Binary file not shown.
Binary file removed Src/Samples/Tutorial/wpf/SplashScreen.dll
Binary file not shown.
Binary file modified Tests/Experiments/BuiltinsAll.txt
Binary file not shown.
Binary file modified Tests/Experiments/Loader/Extensions/fcntl.so
Binary file not shown.
Binary file modified Tests/Experiments/Loader/Extensions2/fcntl.so
Binary file not shown.
Binary file removed Tests/Experiments/Process/X/A B/x.exe
Binary file not shown.
Binary file removed Tests/Experiments/Process/X/a.exe
Binary file not shown.
Binary file removed Tests/Experiments/Process/X/attrib.exe
Binary file not shown.
Binary file removed Tests/Experiments/Process/X/b.exe
Binary file not shown.
Binary file removed Tests/Experiments/Process/X/inspect.exe
Binary file not shown.
Binary file removed Tests/Experiments/Process/X/notepad.exe
Binary file not shown.
Binary file removed Tests/Experiments/Process/X/q.com
Binary file not shown.
Binary file removed Tests/Experiments/Process/X/w.exe.exe
Binary file not shown.
Binary file removed Tests/Experiments/Process/X/w.t.exe
Binary file not shown.
Binary file removed Tests/Experiments/Process/X/w.tx.exe
Binary file not shown.
Binary file removed Tests/Experiments/Process/X/z.txt.exe
Binary file not shown.
Binary file removed Tests/Experiments/Process/X/z.txtt.exe
Binary file not shown.
Binary file removed Tests/Interop/com/fixtures.generated.dll
Binary file not shown.
Binary file removed Tests/Interop/net/A/a.generated.dll
Binary file not shown.
Binary file removed Tests/Interop/net/B/b.generated.dll
Binary file not shown.
Binary file removed Tests/Interop/net/fixtures.generated.dll
Binary file not shown.
51 changes: 51 additions & 0 deletions Util/check-no-executables.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true
#
# Fails when a file in the repository is a program: a Windows PE (.exe, .dll, .NET
# assemblies included), an ELF or Mach-O binary, a Java class or WebAssembly. Everything
# IronRuby ships is compiled from the sources here, the NuGet packages the projects pin and
# the .NET runtime, so the tree itself holds nothing that runs but cannot be read. Images,
# fonts and documents are not programs and are let through. CI runs it on every change:
#
# ruby Util/check-no-executables.rb # every file git tracks
# ruby Util/check-no-executables.rb FILE... # only these
#
# A file is recognised by its first bytes, not by its name, so a program cannot pass by
# being called notes.txt, and an extension-only placeholder such as
# Tests/Experiments/Loader/Extensions/fcntl.so - which is text - is not flagged.

def kind_of(file, size)
head = file.read(4096) || "".b
return "ELF" if head.start_with?("\x7FELF".b)
return "WebAssembly" if head.start_with?("\0asm".b)
if head.start_with?("MZ".b) && head.bytesize >= 0x40
# The PE signature is where e_lfanew says, which may be past what was read.
pe = head.byteslice(0x3c, 4).unpack1("V")
if pe + 4 <= size
signature = pe + 4 <= head.bytesize ? head.byteslice(pe, 4) : (file.seek(pe) && file.read(4))
return "PE (Windows program or .NET assembly)" if signature == "PE\0\0".b
end
end
case head.byteslice(0, 4)&.unpack1("N")
when 0xFEEDFACE, 0xFEEDFACF, 0xCEFAEDFE, 0xCFFAEDFE then "Mach-O"
when 0xCAFEBABE then "Mach-O universal binary or Java class"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
when 0xCAFEBABF then "Mach-O universal binary (64-bit offsets)"
end
end

paths = ARGV.empty? ? `git ls-files -z`.split("\0") : ARGV
abort "check-no-executables: git ls-files failed" if ARGV.empty? && !$?.success?

found = paths.filter_map do |path|
next unless File.file?(path)
size = File.size(path)
kind = File.open(path, "rb") { |file| kind_of(file, size) }
"#{path}: #{kind}" if kind
end

if found.empty?
puts "check-no-executables: #{paths.size} files, no programs among them"
else
warn "check-no-executables: #{found.size} program(s) in the repository - build them from source instead:"
found.each { |line| warn " #{line}" }
exit 1
end
Binary file modified Util/numeric-conversion-matrix.rb
Binary file not shown.
Loading