-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathaction.yml
More file actions
1369 lines (1202 loc) · 56.9 KB
/
action.yml
File metadata and controls
1369 lines (1202 loc) · 56.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
name: "Setup Ruby with rv and ore"
description: "Fast Ruby environment setup using rv for Ruby installation and ore for gem management"
author: "appraisal-rb"
branding:
icon: "zap"
color: "red"
inputs:
ruby-version:
description: |
Ruby version to install. Reads from .ruby-version, .tool-versions, or mise.toml if set to 'default'.
Use 'ruby' for latest stable MRI Ruby version.
Supported versions: 3.2, 3.3, 3.4, 4.0
required: false
default: "default"
rubygems:
description: |
The version of RubyGems to use. Either 'default' (the default), 'latest', or a version number (e.g., 3.3.5).
For 'default', no action is taken and the version of RubyGems that comes with Ruby by default is used.
For 'latest', `gem update --system` is run to update to the latest compatible RubyGems version.
If a version number is given, `gem update --system <version>` is run to update to that version.
required: false
default: "default"
bundler:
description: |
The version of Bundler to install. Either 'Gemfile.lock' (the default), 'default', 'latest', 'none', or a version number.
For 'Gemfile.lock', the version of the BUNDLED WITH section from the Gemfile.lock if it exists.
For 'default', uses the Bundler version shipped with Ruby if it's 2.2+, otherwise installs 'latest'.
For 'latest', installs the latest compatible Bundler version (Bundler 2 on Ruby 3.2+).
For 'none', nothing is done.
required: false
default: "Gemfile.lock"
ore-setup:
description: |
Install ore (the gem manager) in the environment.
Either 'true', 'false', or 'auto' (default).
When 'auto', ore is installed if ore-install or bundler-cache is true.
When 'true', ore is always installed even if ore-install is false.
When 'false', ore is never installed.
Use 'true' when you want ore available but will run ore commands manually.
required: false
default: "auto"
ore-install:
description: |
Run "ore install" command to install gems from lockfile.
Either 'true' or 'false'.
Note: Requires ore-setup to be 'true' or 'auto' (with bundler-cache enabled).
When false, gems are not installed automatically, but ore will still be available if ore-setup is true.
required: false
default: "false"
bundler-cache:
description: |
Enable Bundler caching (alias for ore-install for ruby/setup-ruby compatibility).
When true, acts the same as ore-install: true.
Either 'true' or 'false'.
Note: When using fallback to ruby/setup-ruby, this is passed through directly.
required: false
default: "false"
working-directory:
description: |
Working directory for resolving version files (.ruby-version, etc.) and Gemfile.
required: false
default: "."
cache-version:
description: |
Cache version string. Change this to invalidate caches when needed.
required: false
default: "v1"
rv-version:
description: |
Version of rv to install. Either 'latest' or a specific version (e.g., '0.4.0').
Ignored if rv-git-ref is set.
required: false
default: "latest"
rv-git-ref:
description: |
Git branch, tag, or commit SHA to build rv from source.
When set, rv will be built from the specified git ref instead of using a release.
Supports fork syntax: Use 'owner:ref' to build from a fork (e.g., 'pboling:feat/my-fix').
Examples: 'main', 'feat/new-feature', 'v0.5.0-beta', 'pboling:feat/github-token-authenticated-requests'
Rust toolchain is automatically installed if needed.
required: false
default: ""
ore-version:
description: |
Version of ore to install. Either 'latest' or a specific version (e.g., '0.1.0').
Ignored if ore-git-ref is set.
required: false
default: "latest"
ore-git-ref:
description: |
Git branch, tag, or commit SHA to build ore from source.
When set, ore will be built from the specified git ref instead of using a release.
Supports fork syntax: Use 'owner:ref' to build from a fork (e.g., 'yourname:feat/my-fix').
Examples: 'main', 'feat/a-cool-feature', 'v0.19.0-alpha', 'contriboss:feat/bundle-support'
Go toolchain is automatically installed if needed.
required: false
default: ""
gfgo-git-ref:
description: |
Git branch, tag, or commit SHA to build gemfile-go from source when building ore from source.
Only takes effect when ore-git-ref is also set. Creates a go.work workspace to use local gemfile-go.
Supports fork syntax: Use 'owner:ref' to build from a fork (e.g., 'yourname:feat/my-fix').
Examples: 'main', 'feat/new-feature', 'v1.2.3', 'contriboss:feat/enhancement'
required: false
default: ""
skip-extensions:
description: |
Skip building native extensions during gem installation.
Either 'true' or 'false'.
required: false
default: "false"
without-groups:
description: |
Gem groups to exclude from installation (comma-separated).
Example: 'development,test'
required: false
default: ""
ruby-install-retries:
description: |
Number of retry attempts for Ruby installation if it fails (e.g., due to GitHub API rate limiting).
Uses exponential backoff between retries.
required: false
default: "3"
no-document:
description: |
Skip generating documentation for installed gems (ri/rdoc).
Either 'true' or 'false'. Speeds up gem installation significantly.
Applies to 'gem install' and 'gem update --system' commands.
Creates .gemrc (if it doesn't exist) to affect Bundler/ore gem installations.
Preserves existing .gemrc files without modification.
required: false
default: "true"
token:
description: |
GitHub token for API calls and downloading releases.
The default token is usually sufficient.
required: false
default: ${{ github.token }}
use-setup-ruby:
description: |
Force fallback to ruby/setup-ruby for specific Ruby versions.
Use this to benchmark setup-ruby-flash vs setup-ruby for the same version.
Accepts the same format as ruby-version in a matrix: ['3.4', '4.0'] or just '3.4'
Takes precedence over automatic detection.
required: false
default: ""
use-setup-ruby-flash:
description: |
Force use of setup-ruby-flash for specific Ruby versions.
Use this for forward compatibility when rv adds support for new versions.
Accepts the same format as ruby-version in a matrix: ['head', 'jruby'] or just 'head'
Takes precedence over automatic detection and use-setup-ruby.
Warning: Will fail if rv doesn't actually support the version.
required: false
default: ""
outputs:
ruby-version:
description: "The installed Ruby version"
value: ${{ steps.setup.outputs.ruby-version }}
ruby-prefix:
description: "The prefix/path of the installed Ruby"
value: ${{ steps.setup.outputs.ruby-prefix }}
rv-version:
description: "The installed rv version"
value: ${{ steps.setup.outputs.rv-version }}
rubygems-version:
description: "The installed RubyGems version"
value: ${{ steps.configure-rubygems.outputs.version || steps.setup.outputs.gem-version }}
bundler-version:
description: "The installed Bundler version"
value: ${{ steps.install-bundler.outputs.version || 'default' }}
ore-version:
description: "The installed ore version (if ore-install is true)"
value: ${{ steps.setup.outputs.ore-version }}
cache-hit:
description: "Whether gems were restored from cache"
value: ${{ steps.setup.outputs.cache-hit }}
runs:
using: "composite"
steps:
- name: Check Ruby version support
id: check-support
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
# Helper function to normalize Ruby version from any source
normalize_ruby_version() {
local version="$1"
# Remove all leading/trailing whitespace
version=$(echo "$version" | tr -d '[:space:]')
# Strip only the MRI ruby- prefix (not jruby-, truffleruby-, etc.)
# MRI ruby versions can be specified as "ruby-3.4" or just "3.4"
# Alternative implementations keep their prefix (jruby-10.0.3.0, truffleruby-33.0, etc.)
if [[ "$version" =~ ^ruby-[0-9] ]]; then
version=$(echo "$version" | sed 's/^ruby-//')
fi
echo "$version"
}
RUBY_VERSION="${{ inputs.ruby-version }}"
USE_FALLBACK="false"
OVERRIDE_REASON=""
# Resolve version if 'default'
if [ "$RUBY_VERSION" = "default" ]; then
if [ -f ".ruby-version" ]; then
RUBY_VERSION=$(cat .ruby-version | head -1)
elif [ -f ".tool-versions" ]; then
RUBY_VERSION=$(grep '^ruby ' .tool-versions | awk '{print $2}')
elif [ -f "mise.toml" ]; then
RUBY_VERSION=$(grep 'ruby' mise.toml | sed 's/.*= *"\?\([^"]*\)"\?.*/\1/')
fi
fi
# Normalize the version string
RUBY_VERSION=$(normalize_ruby_version "$RUBY_VERSION")
# Handle special "ruby" keyword (latest stable)
if [ "$RUBY_VERSION" = "ruby" ]; then
RUBY_VERSION="4.0"
fi
# Parse use-setup-ruby-flash override (highest priority)
USE_FLASH_LIST='${{ inputs.use-setup-ruby-flash }}'
if [ -n "$USE_FLASH_LIST" ]; then
# Handle single value (e.g., "3.4") or array (e.g., ['3.4', '4.0'])
# Check if current version matches (with quotes or without)
if [ "$USE_FLASH_LIST" = "$RUBY_VERSION" ] || \
echo "$USE_FLASH_LIST" | grep -qE "(^|[^a-z0-9.])$RUBY_VERSION([^a-z0-9.]|$)"; then
echo "::notice::Ruby version '$RUBY_VERSION' forced to use setup-ruby-flash via use-setup-ruby-flash input."
USE_FALLBACK="false"
OVERRIDE_REASON="forced-flash"
fi
fi
# Parse use-setup-ruby override (second priority, only if not forced to flash)
if [ "$OVERRIDE_REASON" != "forced-flash" ]; then
USE_RUBY_LIST='${{ inputs.use-setup-ruby }}'
if [ -n "$USE_RUBY_LIST" ]; then
# Handle single value or array format
if [ "$USE_RUBY_LIST" = "$RUBY_VERSION" ] || \
echo "$USE_RUBY_LIST" | grep -qE "(^|[^a-z0-9.])$RUBY_VERSION([^a-z0-9.]|$)"; then
echo "::notice::Ruby version '$RUBY_VERSION' forced to use ruby/setup-ruby via use-setup-ruby input."
USE_FALLBACK="true"
OVERRIDE_REASON="forced-ruby"
fi
fi
fi
# Automatic detection (only if no override)
if [ -z "$OVERRIDE_REASON" ]; then
# Allowlists of supported Ruby versions (update these as rv adds support)
# Numeric MRI versions (major.minor format)
SUPPORTED_NUMERIC_VERSIONS="3.2 3.3 3.4 4.0"
# Special versions and alternative implementations (e.g., head, jruby, truffleruby)
SUPPORTED_SPECIAL_VERSIONS="" # Empty for now, add versions as rv supports them (e.g., "head jruby truffleruby")
# Default to fallback unless version is in allowlist
USE_FALLBACK="true"
# First check if it's a prefix match in special versions (head, jruby-*, truffleruby-*, etc.)
if [ -n "$SUPPORTED_SPECIAL_VERSIONS" ]; then
for allowed_version in $SUPPORTED_SPECIAL_VERSIONS; do
if [[ "$RUBY_VERSION" == $allowed_version* ]]; then
echo "Ruby version '$RUBY_VERSION' is supported by setup-ruby-flash."
USE_FALLBACK="false"
break
fi
done
fi
# If not found in special versions, check numeric versions
if [ "$USE_FALLBACK" = "true" ]; then
# Extract major.minor version (e.g., "3.4.1" -> "3.4")
# Use || true to prevent exit on no match
VERSION_PREFIX=$(echo "$RUBY_VERSION" | grep -oE '^[0-9]+\.[0-9]+' || true)
if [ -n "$VERSION_PREFIX" ]; then
for allowed_version in $SUPPORTED_NUMERIC_VERSIONS; do
if [ "$VERSION_PREFIX" = "$allowed_version" ]; then
echo "Ruby version '$RUBY_VERSION' is supported by setup-ruby-flash."
USE_FALLBACK="false"
break
fi
done
fi
fi
# If still using fallback, show appropriate message
if [ "$USE_FALLBACK" = "true" ]; then
ALL_SUPPORTED="$SUPPORTED_NUMERIC_VERSIONS"
if [ -n "$SUPPORTED_SPECIAL_VERSIONS" ]; then
ALL_SUPPORTED="$ALL_SUPPORTED $SUPPORTED_SPECIAL_VERSIONS"
fi
echo "::notice::Ruby version '$RUBY_VERSION' is not supported by setup-ruby-flash (only$ALL_SUPPORTED supported). Falling back to ruby/setup-ruby."
fi
fi
echo "use-fallback=$USE_FALLBACK" >> $GITHUB_OUTPUT
echo "ruby-version=$RUBY_VERSION" >> $GITHUB_OUTPUT
- name: Start fallback timer
if: steps.check-support.outputs.use-fallback == 'true'
id: fallback-start
shell: bash
run: |
echo "start-time=$(date +%s)" >> $GITHUB_OUTPUT
- name: Setup Ruby with ruby/setup-ruby (fallback)
if: steps.check-support.outputs.use-fallback == 'true'
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ inputs.ruby-version }}
rubygems: ${{ inputs.rubygems }}
bundler: ${{ inputs.bundler }}
bundler-cache: ${{ inputs.bundler-cache == 'true' || inputs.ore-install == 'true' }}
working-directory: ${{ inputs.working-directory }}
cache-version: ${{ inputs.cache-version }}
- name: End fallback timer
if: steps.check-support.outputs.use-fallback == 'true'
id: fallback-end
shell: bash
run: |
END_TIME=$(date +%s)
START_TIME=${{ steps.fallback-start.outputs.start-time }}
ELAPSED=$((END_TIME - START_TIME))
echo "elapsed=$ELAPSED" >> $GITHUB_OUTPUT
echo "setup-ruby fallback completed in ${ELAPSED}s"
- name: Validate platform
if: steps.check-support.outputs.use-fallback != 'true'
id: platform
shell: bash
run: |
# Detect OS
case "$RUNNER_OS" in
Linux) OS="linux" ;;
macOS) OS="darwin" ;;
*)
echo "::error::Unsupported OS: $RUNNER_OS. setup-ruby-flash only supports Linux and macOS."
exit 1
;;
esac
# Detect architecture
ARCH="$(uname -m)"
case "$ARCH" in
x86_64) ARCH="amd64" ;;
aarch64) ARCH="arm64" ;;
arm64) ARCH="arm64" ;;
*)
echo "::error::Unsupported architecture: $ARCH. setup-ruby-flash only supports x86_64 and arm64."
exit 1
;;
esac
echo "os=$OS" >> $GITHUB_OUTPUT
echo "arch=$ARCH" >> $GITHUB_OUTPUT
echo "platform=$OS-$ARCH" >> $GITHUB_OUTPUT
echo "Detected platform: $OS-$ARCH"
- name: Resolve rv version
if: steps.check-support.outputs.use-fallback != 'true'
id: rv-version
shell: bash
env:
GH_TOKEN: ${{ inputs.token }}
run: |
# If git ref is specified, resolve to commit SHA for cache key
if [ -n "${{ inputs.rv-git-ref }}" ]; then
GIT_REF="${{ inputs.rv-git-ref }}"
echo "build-from-source=true" >> $GITHUB_OUTPUT
# Parse fork syntax: owner:ref or just ref
if [[ "$GIT_REF" == *":"* ]]; then
FORK_OWNER="${GIT_REF%%:*}"
REF="${GIT_REF#*:}"
REPO="$FORK_OWNER/rv"
else
REPO="spinel-coop/rv"
REF="$GIT_REF"
fi
# Resolve to commit SHA using GitHub API
COMMIT_SHA=$(gh api "repos/$REPO/commits/$REF" --jq '.sha' 2>/dev/null || echo "$REF")
VERSION="${COMMIT_SHA:0:12}" # Use first 12 chars of SHA
echo "Using rv git ref: $GIT_REF (commit: $VERSION)"
elif [ "${{ inputs.rv-version }}" = "latest" ]; then
VERSION=$(gh api repos/spinel-coop/rv/releases/latest --jq '.tag_name' 2>/dev/null || echo "v0.4.1")
echo "build-from-source=false" >> $GITHUB_OUTPUT
else
VERSION="${{ inputs.rv-version }}"
echo "build-from-source=false" >> $GITHUB_OUTPUT
fi
VERSION="${VERSION#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Resolved rv version: $VERSION"
- name: Cache rv binary
if: steps.check-support.outputs.use-fallback != 'true'
id: cache-rv
uses: actions/cache@v4
with:
path: ~/.local/bin/rv
key: setup-ruby-flash-rv-${{ steps.platform.outputs.platform }}-${{ steps.rv-version.outputs.version }}-${{ steps.rv-version.outputs.build-from-source }}
- name: Setup Rust toolchain for rv
if: steps.check-support.outputs.use-fallback != 'true' && steps.cache-rv.outputs.cache-hit != 'true' && steps.rv-version.outputs.build-from-source == 'true'
uses: dtolnay/rust-toolchain@stable
- name: Build rv from source
if: steps.check-support.outputs.use-fallback != 'true' && steps.cache-rv.outputs.cache-hit != 'true' && steps.rv-version.outputs.build-from-source == 'true'
id: build-rv
shell: bash
run: |
GIT_REF="${{ inputs.rv-git-ref }}"
echo "Building rv from source (git ref: $GIT_REF)..."
START_TIME=$(date +%s)
# Parse fork syntax: owner:ref or just ref
if [[ "$GIT_REF" == *":"* ]]; then
FORK_OWNER="${GIT_REF%%:*}"
REF="${GIT_REF#*:}"
REPO_URL="https://github.com/${FORK_OWNER}/rv.git"
echo "Using fork: $FORK_OWNER/rv (ref: $REF)"
else
REPO_URL="https://github.com/spinel-coop/rv.git"
REF="$GIT_REF"
echo "Using upstream: spinel-coop/rv (ref: $REF)"
fi
# Clone repository (shallow clone for speed)
git clone --depth 1 "$REPO_URL" /tmp/rv-build
cd /tmp/rv-build
# Fetch the specific ref if shallow clone doesn't have it
if ! git checkout "$REF" 2>/dev/null; then
git fetch --depth 1 origin "$REF"
if ! git checkout FETCH_HEAD; then
echo "::error::Failed to checkout rv git ref: $REF from $REPO_URL"
exit 1
fi
fi
# Build rv using cargo
cargo build --release
# Install to ~/.local/bin
mkdir -p ~/.local/bin
cp target/release/rv ~/.local/bin/rv
chmod +x ~/.local/bin/rv
END_TIME=$(date +%s)
ELAPSED=$((END_TIME - START_TIME))
echo "build-time=$ELAPSED" >> $GITHUB_OUTPUT
echo "Built and installed rv from $GIT_REF in ${ELAPSED}s"
~/.local/bin/rv --version
# Cleanup build directory
rm -rf /tmp/rv-build
- name: Install rv from release
if: steps.check-support.outputs.use-fallback != 'true' && steps.cache-rv.outputs.cache-hit != 'true' && steps.rv-version.outputs.build-from-source == 'false'
id: install-rv
shell: bash
run: |
VERSION="${{ steps.rv-version.outputs.version }}"
OS="${{ steps.platform.outputs.os }}"
ARCH="${{ steps.platform.outputs.arch }}"
START_TIME=$(date +%s)
# Map to rv platform names
case "$OS-$ARCH" in
linux-amd64) RV_PLATFORM="x86_64-unknown-linux-gnu" ;;
linux-arm64) RV_PLATFORM="aarch64-unknown-linux-gnu" ;;
darwin-amd64) RV_PLATFORM="x86_64-apple-darwin" ;;
darwin-arm64) RV_PLATFORM="aarch64-apple-darwin" ;;
esac
# rv releases use .tar.xz format with files in a subdirectory
DOWNLOAD_URL="https://github.com/spinel-coop/rv/releases/download/v${VERSION}/rv-${RV_PLATFORM}.tar.xz"
echo "Downloading rv from: $DOWNLOAD_URL"
mkdir -p ~/.local/bin
curl -fsSL "$DOWNLOAD_URL" | tar -xJ --strip-components=1 -C ~/.local/bin "rv-${RV_PLATFORM}/rv"
chmod +x ~/.local/bin/rv
END_TIME=$(date +%s)
ELAPSED=$((END_TIME - START_TIME))
echo "install-time=$ELAPSED" >> $GITHUB_OUTPUT
echo "Installed rv $VERSION in ${ELAPSED}s"
- name: Add rv to PATH
if: steps.check-support.outputs.use-fallback != 'true'
shell: bash
run: |
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Detect Ruby version
if: steps.check-support.outputs.use-fallback != 'true'
id: detect-ruby
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
# Helper function to normalize Ruby version from any source
normalize_ruby_version() {
local version="$1"
# Remove all leading/trailing whitespace
version=$(echo "$version" | tr -d '[:space:]')
# Strip only the MRI ruby- prefix (not jruby-, truffleruby-, etc.)
# MRI ruby versions can be specified as "ruby-3.4" or just "3.4"
# Alternative implementations keep their prefix (jruby-10.0.3.0, truffleruby-33.0, etc.)
if [[ "$version" =~ ^ruby-[0-9] ]]; then
version=$(echo "$version" | sed 's/^ruby-//')
fi
echo "$version"
}
RUBY_VERSION="${{ inputs.ruby-version }}"
# Resolve version if 'default'
if [ "$RUBY_VERSION" = "default" ]; then
if [ -f ".ruby-version" ]; then
RUBY_VERSION=$(cat .ruby-version | head -1)
echo "Found Ruby version in .ruby-version: $RUBY_VERSION"
elif [ -f ".tool-versions" ]; then
RUBY_VERSION=$(grep '^ruby ' .tool-versions | awk '{print $2}')
echo "Found Ruby version in .tool-versions: $RUBY_VERSION"
elif [ -f "mise.toml" ]; then
RUBY_VERSION=$(grep 'ruby' mise.toml | sed 's/.*= *"\?\([^"]*\)"\?.*/\1/')
echo "Found Ruby version in mise.toml: $RUBY_VERSION"
else
echo "::error::No Ruby version specified and no version file found (.ruby-version, .tool-versions, mise.toml)"
exit 1
fi
fi
# Normalize the version string
RUBY_VERSION=$(normalize_ruby_version "$RUBY_VERSION")
# Handle special "ruby" keyword (latest stable)
if [ "$RUBY_VERSION" = "ruby" ]; then
RUBY_VERSION="4.0"
echo "Using latest stable Ruby version: $RUBY_VERSION"
fi
echo "version=$RUBY_VERSION" >> $GITHUB_OUTPUT
echo "Using Ruby version: $RUBY_VERSION"
- name: Cache Ruby installation
if: steps.check-support.outputs.use-fallback != 'true'
id: cache-ruby
uses: actions/cache@v4
with:
path: ~/.local/share/rv/rubies
key: setup-ruby-flash-ruby-${{ inputs.cache-version }}-${{ steps.platform.outputs.platform }}-${{ steps.detect-ruby.outputs.version }}
- name: Install Ruby
if: steps.check-support.outputs.use-fallback != 'true' && steps.cache-ruby.outputs.cache-hit != 'true'
id: install-ruby
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.token }}
run: |
RUBY_VERSION="${{ steps.detect-ruby.outputs.version }}"
MAX_RETRIES=${{ inputs.ruby-install-retries }}
RETRY_COUNT=0
BACKOFF=5
START_TIME=$(date +%s)
# Pre-seed rv's releases cache with authenticated API call to avoid rate limiting
# ...existing code...
echo "Installing Ruby $RUBY_VERSION via rv (max retries: $MAX_RETRIES)..."
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "Attempt $RETRY_COUNT of $MAX_RETRIES..."
if ~/.local/bin/rv ruby install "$RUBY_VERSION"; then
END_TIME=$(date +%s)
ELAPSED=$((END_TIME - START_TIME))
echo "install-time=$ELAPSED" >> $GITHUB_OUTPUT
echo "Ruby $RUBY_VERSION installed successfully on attempt $RETRY_COUNT in ${ELAPSED}s"
exit 0
fi
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
echo "::warning::rv ruby install failed (attempt $RETRY_COUNT/$MAX_RETRIES). Retrying in ${BACKOFF}s..."
sleep $BACKOFF
BACKOFF=$((BACKOFF * 2))
fi
done
echo "::error::Failed to install Ruby $RUBY_VERSION after $MAX_RETRIES attempts"
exit 1
- name: Configure Ruby environment with rv shell integration
if: steps.check-support.outputs.use-fallback != 'true'
id: setup
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
RUBY_VERSION="${{ steps.detect-ruby.outputs.version }}"
# Always overwrite .ruby-version to ensure it matches the installed Ruby version
# In CI, we expect to use the specified Ruby version regardless of local .ruby-version
echo "$RUBY_VERSION" > .ruby-version
echo "Created .ruby-version with $RUBY_VERSION"
# Activate rv shell integration to set up PATH and environment
# This will now use the .ruby-version file we just created
eval "$(~/.local/bin/rv shell env bash)"
# Verify Ruby is now active
RUBY_EXECUTABLE=$(which ruby)
if [ -z "$RUBY_EXECUTABLE" ]; then
echo "::error::Ruby not found in PATH after rv activation"
~/.local/bin/rv ruby list || true
exit 1
fi
# Get Ruby installation details
RUBY_BIN_DIR=$(dirname "$RUBY_EXECUTABLE")
RUBY_PREFIX=$(dirname "$RUBY_BIN_DIR")
echo "Found Ruby at: $RUBY_PREFIX"
# Determine GEM_HOME based on rv's Ruby installation structure
# rv installs gems to: $RUBY_PREFIX/lib/ruby/gems/$MAJOR.$MINOR.0
FULL_RUBY_VERSION=$(ruby -e 'print RUBY_VERSION')
MAJOR_MINOR=$(echo "$FULL_RUBY_VERSION" | cut -d. -f1,2)
ACTUAL_GEM_HOME="$RUBY_PREFIX/lib/ruby/gems/${MAJOR_MINOR}.0"
ACTUAL_GEM_PATH="$ACTUAL_GEM_HOME"
echo "Setting GEM_HOME to: $ACTUAL_GEM_HOME"
# Export environment variables to GITHUB_ENV for subsequent steps
echo "GEM_HOME=$ACTUAL_GEM_HOME" >> $GITHUB_ENV
echo "GEM_PATH=$ACTUAL_GEM_PATH" >> $GITHUB_ENV
# Export other Ruby-related variables if they're set
[ -n "$RUBY_ROOT" ] && echo "RUBY_ROOT=$RUBY_ROOT" >> $GITHUB_ENV
[ -n "$RUBY_ENGINE" ] && echo "RUBY_ENGINE=$RUBY_ENGINE" >> $GITHUB_ENV
[ -n "$RUBY_VERSION" ] && echo "RUBY_VERSION=$RUBY_VERSION" >> $GITHUB_ENV
[ -n "$RUBYOPT" ] && echo "RUBYOPT=$RUBYOPT" >> $GITHUB_ENV
# Add Ruby to GITHUB_PATH for subsequent steps
echo "$RUBY_BIN_DIR" >> $GITHUB_PATH
# Get versions for outputs
GEM_VERSION=$(gem --version)
# Set outputs
echo "ruby-version=$RUBY_VERSION" >> $GITHUB_OUTPUT
echo "ruby-full-version=$FULL_RUBY_VERSION" >> $GITHUB_OUTPUT
echo "ruby-prefix=$RUBY_PREFIX" >> $GITHUB_OUTPUT
echo "rv-version=${{ steps.rv-version.outputs.version }}" >> $GITHUB_OUTPUT
echo "gem-version=$GEM_VERSION" >> $GITHUB_OUTPUT
# Verify Ruby installation
ruby --version
gem --version
- name: Configure RubyGems
if: steps.check-support.outputs.use-fallback != 'true' && inputs.rubygems != 'default'
id: configure-rubygems
shell: bash
run: |
RUBYGEMS_INPUT="${{ inputs.rubygems }}"
CURRENT_VERSION=$(gem --version)
echo "Current RubyGems version: $CURRENT_VERSION"
# Set silent flag when no-document is enabled
SILENT_FLAG=""
if [ "${{ inputs.no-document }}" = "true" ]; then
SILENT_FLAG="--silent"
fi
if [ "$RUBYGEMS_INPUT" = "latest" ]; then
echo "Updating RubyGems to latest version..."
gem update --system $SILENT_FLAG
NEW_VERSION=$(gem --version)
echo "Updated RubyGems to: $NEW_VERSION"
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
elif [ -n "$RUBYGEMS_INPUT" ]; then
# Check if requested version is newer than current
if ruby -e "exit(Gem::Version.new('$RUBYGEMS_INPUT') > Gem::Version.new('$CURRENT_VERSION') ? 0 : 1)" 2>/dev/null; then
echo "Updating RubyGems to version $RUBYGEMS_INPUT..."
gem update --system "$RUBYGEMS_INPUT" $SILENT_FLAG
echo "version=$RUBYGEMS_INPUT" >> $GITHUB_OUTPUT
else
echo "Requested RubyGems version ($RUBYGEMS_INPUT) is not newer than current ($CURRENT_VERSION), skipping update"
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
fi
fi
- name: Install Bundler
if: steps.check-support.outputs.use-fallback != 'true' && inputs.bundler != 'none'
id: install-bundler
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
BUNDLER_INPUT="${{ inputs.bundler }}"
RUBY_VERSION="${{ steps.setup.outputs.ruby-full-version }}"
# Set documentation flag based on input
DOC_FLAG=""
if [ "${{ inputs.no-document }}" = "true" ]; then
DOC_FLAG="--no-document"
fi
# Helper function to check if a specific bundler version is installed
is_bundler_installed() {
local version="$1"
gem list bundler --installed --version "$version" >/dev/null 2>&1
}
# Helper function to check if bundler satisfies a version requirement
bundler_satisfies() {
local required="$1"
local current="$2"
# If we just need major version match (e.g., "2" or "4"), check major only
if [[ "$required" =~ ^[0-9]+$ ]]; then
local required_major="${required}"
local current_major="${current%%.*}"
[ "$required_major" = "$current_major" ]
else
# For specific version, check exact match or compatible
[ "$required" = "$current" ]
fi
}
# Get currently installed bundler version
CURRENT_BUNDLER=$(bundle --version 2>/dev/null | awk '{print $NF}' || echo "")
if [ -n "$CURRENT_BUNDLER" ]; then
echo "Current Bundler version: $CURRENT_BUNDLER"
fi
# Determine Bundler version to install
if [ "$BUNDLER_INPUT" = "Gemfile.lock" ]; then
if [ -f "Gemfile.lock" ]; then
# Extract BUNDLED WITH version from Gemfile.lock
BUNDLER_VERSION=$(awk '/^BUNDLED WITH$/{getline; print $1}' Gemfile.lock 2>/dev/null || echo "")
if [ -n "$BUNDLER_VERSION" ]; then
echo "Found Bundler version in Gemfile.lock: $BUNDLER_VERSION"
# Check if this version is already installed
if is_bundler_installed "$BUNDLER_VERSION"; then
echo "Bundler $BUNDLER_VERSION is already installed"
echo "version=$BUNDLER_VERSION" >> $GITHUB_OUTPUT
exit 0
fi
else
echo "No BUNDLED WITH found in Gemfile.lock, using default"
BUNDLER_INPUT="default"
fi
else
echo "No Gemfile.lock found, using default Bundler"
BUNDLER_INPUT="default"
fi
fi
if [ "$BUNDLER_INPUT" = "default" ]; then
# Ruby 3.2+ ships with Bundler 2.2+, use it
if [ -n "$CURRENT_BUNDLER" ]; then
echo "Using default Bundler: $CURRENT_BUNDLER"
echo "version=$CURRENT_BUNDLER" >> $GITHUB_OUTPUT
exit 0
fi
BUNDLER_INPUT="latest"
fi
if [ "$BUNDLER_INPUT" = "latest" ]; then
# If RubyGems was updated to latest, it already installed the latest Bundler
# (RubyGems and Bundler are always released together)
if [ "${{ inputs.rubygems }}" = "latest" ] && [ -n "${{ steps.configure-rubygems.outputs.version }}" ]; then
CURRENT_BUNDLER=$(bundle --version 2>/dev/null | awk '{print $NF}' || echo "")
if [ -n "$CURRENT_BUNDLER" ]; then
echo "Using Bundler $CURRENT_BUNDLER (installed with latest RubyGems)"
echo "version=$CURRENT_BUNDLER" >> $GITHUB_OUTPUT
exit 0
fi
fi
# Check if we already have a recent bundler
if [ -n "$CURRENT_BUNDLER" ]; then
CURRENT_MAJOR="${CURRENT_BUNDLER%%.*}"
# If we have Bundler 2+, that's fine for "latest" on most Rubies
if [ "$CURRENT_MAJOR" -ge 2 ]; then
echo "Using existing Bundler $CURRENT_BUNDLER (satisfies 'latest')"
echo "version=$CURRENT_BUNDLER" >> $GITHUB_OUTPUT
exit 0
fi
fi
echo "Installing latest Bundler..."
gem install bundler $DOC_FLAG --force 2>/dev/null || gem install bundler $DOC_FLAG
INSTALLED_VERSION=$(bundle --version | awk '{print $NF}')
echo "Installed Bundler: $INSTALLED_VERSION"
echo "version=$INSTALLED_VERSION" >> $GITHUB_OUTPUT
elif [ -n "$BUNDLER_VERSION" ]; then
# Install specific version from Gemfile.lock
echo "Installing Bundler version $BUNDLER_VERSION..."
gem install bundler -v "$BUNDLER_VERSION" $DOC_FLAG --force 2>/dev/null || \
gem install bundler -v "$BUNDLER_VERSION" $DOC_FLAG
echo "version=$BUNDLER_VERSION" >> $GITHUB_OUTPUT
elif [[ "$BUNDLER_INPUT" =~ ^[0-9] ]]; then
# Version number provided - check if already satisfied
if [ -n "$CURRENT_BUNDLER" ] && bundler_satisfies "$BUNDLER_INPUT" "$CURRENT_BUNDLER"; then
echo "Using existing Bundler $CURRENT_BUNDLER (satisfies '$BUNDLER_INPUT')"
echo "version=$CURRENT_BUNDLER" >> $GITHUB_OUTPUT
exit 0
fi
echo "Installing Bundler version $BUNDLER_INPUT..."
if [[ "$BUNDLER_INPUT" =~ ^[0-9]+$ ]]; then
# Just major version, use ~> constraint
gem install bundler -v "~> $BUNDLER_INPUT.0" $DOC_FLAG --force 2>/dev/null || \
gem install bundler -v "~> $BUNDLER_INPUT.0" $DOC_FLAG
else
gem install bundler -v "$BUNDLER_INPUT" $DOC_FLAG --force 2>/dev/null || \
gem install bundler -v "$BUNDLER_INPUT" $DOC_FLAG
fi
INSTALLED_VERSION=$(bundle --version | awk '{print $NF}')
echo "version=$INSTALLED_VERSION" >> $GITHUB_OUTPUT
fi
- name: Determine if ore should be installed
if: steps.check-support.outputs.use-fallback != 'true'
id: should-install-ore
shell: bash
run: |
ORE_SETUP="${{ inputs.ore-setup }}"
ORE_INSTALL="${{ inputs.ore-install }}"
BUNDLER_CACHE="${{ inputs.bundler-cache }}"
# Determine if ore should be installed
if [ "$ORE_SETUP" = "true" ]; then
echo "install-ore=true" >> $GITHUB_OUTPUT
echo "Ore will be installed (ore-setup: true)"
elif [ "$ORE_SETUP" = "false" ]; then
echo "install-ore=false" >> $GITHUB_OUTPUT
echo "Ore will NOT be installed (ore-setup: false)"
else
# auto mode: install if ore-install or bundler-cache is true
if [ "$ORE_INSTALL" = "true" ] || [ "$BUNDLER_CACHE" = "true" ]; then
echo "install-ore=true" >> $GITHUB_OUTPUT
echo "Ore will be installed (ore-setup: auto, ore-install or bundler-cache enabled)"
else
echo "install-ore=false" >> $GITHUB_OUTPUT
echo "Ore will NOT be installed (ore-setup: auto, no gem installation requested)"
fi
fi
- name: Generate summary (Ruby only)
if: steps.check-support.outputs.use-fallback != 'true' && steps.should-install-ore.outputs.install-ore != 'true'
shell: bash
run: |
RUBYGEMS_VERSION="${{ steps.configure-rubygems.outputs.version }}"
if [ -z "$RUBYGEMS_VERSION" ]; then
RUBYGEMS_VERSION="${{ steps.setup.outputs.gem-version }}"
fi
BUNDLER_VERSION="${{ steps.install-bundler.outputs.version }}"
if [ -z "$BUNDLER_VERSION" ]; then
BUNDLER_VERSION=$(bundle --version 2>/dev/null | awk '{print $NF}' || echo "default")
fi
echo "### setup-ruby-flash Summary ⚡" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Component | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Ruby Version | ${{ steps.detect-ruby.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
echo "| RubyGems Version | $RUBYGEMS_VERSION |" >> $GITHUB_STEP_SUMMARY
echo "| Bundler Version | $BUNDLER_VERSION |" >> $GITHUB_STEP_SUMMARY
echo "| rv Version | ${{ steps.rv-version.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
echo "| Platform | ${{ steps.platform.outputs.platform }} |" >> $GITHUB_STEP_SUMMARY
echo "| Ruby Cache Hit | ${{ steps.cache-ruby.outputs.cache-hit }} |" >> $GITHUB_STEP_SUMMARY
# Add timing information if available
if [ -n "${{ steps.build-rv.outputs.build-time }}" ]; then
echo "| rv Build Time | ${{ steps.build-rv.outputs.build-time }}s |" >> $GITHUB_STEP_SUMMARY
fi
if [ -n "${{ steps.install-rv.outputs.install-time }}" ]; then
echo "| rv Install Time | ${{ steps.install-rv.outputs.install-time }}s |" >> $GITHUB_STEP_SUMMARY
fi
if [ -n "${{ steps.install-ruby.outputs.install-time }}" ]; then
echo "| Ruby Install Time | ${{ steps.install-ruby.outputs.install-time }}s |" >> $GITHUB_STEP_SUMMARY
fi
- name: Resolve ore version
if: steps.check-support.outputs.use-fallback != 'true' && steps.should-install-ore.outputs.install-ore == 'true'
id: ore-version
shell: bash
env:
GH_TOKEN: ${{ inputs.token }}
run: |
# If git ref is specified, resolve to commit SHA for cache key
if [ -n "${{ inputs.ore-git-ref }}" ]; then
GIT_REF="${{ inputs.ore-git-ref }}"
echo "build-from-source=true" >> $GITHUB_OUTPUT
# Parse fork syntax: owner:ref or just ref
if [[ "$GIT_REF" == *":"* ]]; then
FORK_OWNER="${GIT_REF%%:*}"
REF="${GIT_REF#*:}"
REPO="$FORK_OWNER/ore-light"
else
REPO="contriboss/ore-light"
REF="$GIT_REF"
fi
# Resolve to commit SHA using GitHub API
COMMIT_SHA=$(gh api "repos/$REPO/commits/$REF" --jq '.sha' 2>/dev/null || echo "$REF")
VERSION="${COMMIT_SHA:0:12}" # Use first 12 chars of SHA
echo "Using ore git ref: $GIT_REF (commit: $VERSION)"
elif [ "${{ inputs.ore-version }}" = "latest" ]; then
VERSION=$(gh api repos/contriboss/ore-light/releases/latest --jq '.tag_name' 2>/dev/null || echo "v0.18.0")
echo "build-from-source=false" >> $GITHUB_OUTPUT
else
VERSION="${{ inputs.ore-version }}"
echo "build-from-source=false" >> $GITHUB_OUTPUT
fi
VERSION="${VERSION#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Resolved ore version: $VERSION"
# Also resolve gfgo-git-ref if specified (affects ore cache key)
if [ -n "${{ inputs.gfgo-git-ref }}" ]; then
GFGO_REF="${{ inputs.gfgo-git-ref }}"
# Parse fork syntax: owner:ref or just ref
if [[ "$GFGO_REF" == *":"* ]]; then
GFGO_FORK_OWNER="${GFGO_REF%%:*}"
GFGO_REF_NAME="${GFGO_REF#*:}"
GFGO_REPO="$GFGO_FORK_OWNER/gemfile-go"
else
GFGO_REPO="contriboss/gemfile-go"
GFGO_REF_NAME="$GFGO_REF"
fi
# Resolve to commit SHA
GFGO_SHA=$(gh api "repos/$GFGO_REPO/commits/$GFGO_REF_NAME" --jq '.sha' 2>/dev/null || echo "$GFGO_REF")
GFGO_VERSION="${GFGO_SHA:0:12}"
echo "gfgo-version=$GFGO_VERSION" >> $GITHUB_OUTPUT
echo "Using gemfile-go git ref: $GFGO_REF (commit: $GFGO_VERSION)"
else
echo "gfgo-version=" >> $GITHUB_OUTPUT
fi
- name: Cache ore binary
if: steps.check-support.outputs.use-fallback != 'true' && steps.should-install-ore.outputs.install-ore == 'true'
id: cache-ore
uses: actions/cache@v4
with:
path: ~/.local/bin/ore
key: setup-ruby-flash-ore-${{ steps.platform.outputs.platform }}-ruby-${{ steps.detect-ruby.outputs.version }}-${{ steps.ore-version.outputs.version }}-${{ steps.ore-version.outputs.build-from-source }}-gfgo-${{ steps.ore-version.outputs.gfgo-version }}
- name: Setup Go toolchain for ore
if: steps.check-support.outputs.use-fallback != 'true' && steps.should-install-ore.outputs.install-ore == 'true' && steps.cache-ore.outputs.cache-hit != 'true' && steps.ore-version.outputs.build-from-source == 'true'
uses: actions/setup-go@v5
with:
go-version: "stable"
- name: Build ore from source
if: steps.check-support.outputs.use-fallback != 'true' && steps.should-install-ore.outputs.install-ore == 'true' && steps.cache-ore.outputs.cache-hit != 'true' && steps.ore-version.outputs.build-from-source == 'true'
id: build-ore
shell: bash