diff options
Diffstat (limited to '.github/actions')
| -rw-r--r-- | .github/actions/capiext/action.yml | 86 | ||||
| -rw-r--r-- | .github/actions/compilers/action.yml | 164 | ||||
| -rwxr-xr-x | .github/actions/compilers/entrypoint.sh | 90 | ||||
| -rw-r--r-- | .github/actions/launchable/setup/action.yml | 337 | ||||
| -rw-r--r-- | .github/actions/make-snapshot/action.yml | 77 | ||||
| -rw-r--r-- | .github/actions/setup/baseruby/action.yml | 73 | ||||
| -rw-r--r-- | .github/actions/setup/directories/action.yml | 205 | ||||
| -rw-r--r-- | .github/actions/setup/macos/action.yml | 29 | ||||
| -rw-r--r-- | .github/actions/setup/ubuntu/action.yml | 72 | ||||
| -rw-r--r-- | .github/actions/slack/action.yml | 51 |
10 files changed, 1184 insertions, 0 deletions
diff --git a/.github/actions/capiext/action.yml b/.github/actions/capiext/action.yml new file mode 100644 index 0000000000..ed69c8ac5e --- /dev/null +++ b/.github/actions/capiext/action.yml @@ -0,0 +1,86 @@ +name: rubyspec C-API extensions + +inputs: + builddir: + required: false + default: '.' + make: + required: false + default: 'make -s' + +outputs: + key: + value: >- + ${{ + !steps.restore.outputs.cache-hit && + github.ref == 'refs/heads/master' && + steps.config.outputs.key + }} + +runs: + using: composite + + steps: + - id: config + shell: bash + run: | + eval $(grep -e '^arch *=' -e '^ruby_version *=' -e '^DLEXT *=' Makefile | + sed 's/ *= */=/') + case "${ruby_version}" in + *+*) key=capiexts-${arch}-${ruby_version}-${{ hashFiles('src/spec/ruby/optional/capi/ext/*.[ch]') }};; + *) key=;; + esac + echo version=$ruby_version >> $GITHUB_OUTPUT + echo key="$key" >> $GITHUB_OUTPUT + echo DLEXT=$DLEXT >> $GITHUB_OUTPUT + working-directory: ${{ inputs.builddir }} + + - name: Restore previous CAPI extensions + uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 + id: cache + with: + path: ${{ inputs.builddir }}/spec/ruby/optional/capi/ext/ + key: ${{ steps.config.outputs.key }} + if: ${{ steps.config.outputs.key }} + + - name: Run test-spec with previous CAPI extension binaries + id: check + shell: bash + run: | # zizmor: ignore[template-injection] + touch spec/ruby/optional/capi/ext/*.$DLEXT + [ ! -f spec/ruby/optional/capi/ext/\*.$DLEXT ] + ${{ inputs.make }} SPECOPTS=optional/capi test-spec + env: + DLEXT: ${{ steps.config.outputs.DLEXT }} + working-directory: ${{ inputs.builddir }} + if: ${{ steps.cache.outputs.cache-hit }} + + - name: Strip CAPI extensions + id: strip + shell: bash + run: | + rm -f spec/ruby/optional/capi/ext/*.c + [ "$DLEXT" = bundle ] || # separated to .dSYM directories + strip spec/ruby/optional/capi/ext/*.$DLEXT + env: + DLEXT: ${{ steps.config.outputs.DLEXT }} + working-directory: ${{ inputs.builddir }} + if: >- + ${{true + && ! steps.cache.outputs.cache-hit + && github.ref_name == 'master' + }} + + - name: Save CAPI extensions + uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 + with: + path: ${{ inputs.builddir }}/spec/ruby/optional/capi/ext/ + key: ${{ steps.config.outputs.key }} + if: ${{ steps.strip.outcome == 'success' }} + + - shell: bash + run: | + echo "::error::Change from ${prev} detected; bump up ABI version" + env: + prev: ${{ steps.config.outputs.version }} + if: ${{ always() && steps.check.outcome == 'failure' }} diff --git a/.github/actions/compilers/action.yml b/.github/actions/compilers/action.yml new file mode 100644 index 0000000000..c700bbfe9e --- /dev/null +++ b/.github/actions/compilers/action.yml @@ -0,0 +1,164 @@ +name: Compiles ruby in a container +description: >- + Makes ruby using a dedicated container + +inputs: + tag: + required: false + default: clang-20 + description: >- + container image tag to use in this run. + + with_gcc: + required: false + description: >- + override compiler path & flags. + + CFLAGS: + required: false + description: >- + C compiler flags to override. + + CXXFLAGS: + required: false + description: >- + C++ compiler flags to override. + + optflags: + required: false + # -O1 is faster than -O3 in our tests... Majority of time are consumed trying + # to optimize binaries. Also GitHub Actions run on relatively modern CPUs + # compared to, say, GCC 4 or Clang 3. We don't specify `-march=native` + # because compilers tend not understand what the CPU is. + default: '-O1' + description: >- + Compiler flags for optimisations. + + cppflags: + required: false + description: >- + Additional preprocessor flags. + + append_configure: + required: false + default: >- + --without-valgrind + --without-jemalloc + --without-gmp + description: >- + flags to append to configure. + + enable_shared: + required: false + default: true + description: >- + Whether to build libruby.so. + + check: + required: false + default: '' + description: >- + Whether to run `make check` + + test_all: + required: false + default: '' + description: >- + Whether to run `make test-all` with options for test-all. + + test_spec: + required: false + default: '' + description: >- + Whether to run `make test-spec` with options for mspec. + + static_exts: + required: false + description: >- + whitespace separated list of extensions that need be linked statically. + +runs: + using: composite + steps: + - shell: bash + run: docker pull --quiet "ghcr.io/ruby/ruby-ci-image:${INPUT_TAG}" + env: + INPUT_TAG: ${{ inputs.tag }} + + - name: Enable Launchable conditionally + id: enable-launchable + run: echo "enable-launchable=true" >> $GITHUB_OUTPUT + shell: bash + if: >- + ${{ + github.repository == 'ruby/ruby' || + (github.repository != 'ruby/ruby' && env.LAUNCHABLE_TOKEN) + }} + + - name: compile + shell: bash + run: >- + docker run + --rm + --user=root + --volume "${GITHUB_WORKSPACE}:/github/workspace:ro" + --workdir=/github/workspace + --entrypoint=/github/workspace/.github/actions/compilers/entrypoint.sh + --env CI + --env GITHUB_ACTION + --env INPUT_WITH_GCC + --env INPUT_CFLAGS + --env INPUT_CXXFLAGS + --env INPUT_OPTFLAGS + --env INPUT_CPPFLAGS + --env INPUT_APPEND_CONFIGURE + --env INPUT_CHECK + --env INPUT_TEST_ALL + --env INPUT_TEST_SPEC + --env INPUT_ENABLE_SHARED + --env INPUT_STATIC_EXTS + --env LAUNCHABLE_ORGANIZATION + --env LAUNCHABLE_WORKSPACE + --env LAUNCHABLE_ENABLED + --env GITHUB_PR_HEAD_SHA + --env GITHUB_PULL_REQUEST_URL + --env GITHUB_REF + --env GITHUB_ACTIONS + --env GITHUB_RUN_ID + --env GITHUB_REPOSITORY + --env GITHUB_WORKFLOW + --env GITHUB_RUN_NUMBER + --env GITHUB_EVENT_NAME + --env GITHUB_SHA + --env GITHUB_HEAD_REF + --env GITHUB_SERVER_URL + "ghcr.io/ruby/ruby-ci-image:${INPUT_TAG}" + env: + INPUT_TAG: ${{ inputs.tag }} + INPUT_WITH_GCC: ${{ inputs.with_gcc || inputs.tag }} + INPUT_CFLAGS: ${{ inputs.CFLAGS }} + INPUT_CXXFLAGS: ${{ inputs.CXXFLAGS }} + INPUT_OPTFLAGS: ${{ inputs.OPTFLAGS }} + INPUT_CPPFLAGS: ${{ inputs.cppflags }} + INPUT_APPEND_CONFIGURE: ${{ inputs.append_configure }} + INPUT_CHECK: ${{ inputs.check }} + INPUT_TEST_ALL: ${{ inputs.test_all }} + INPUT_TEST_SPEC: ${{ inputs.test_spec }} + INPUT_ENABLE_SHARED: ${{ inputs.enable_shared }} + INPUT_STATIC_EXTS: ${{ inputs.static_exts }} + LAUNCHABLE_ORGANIZATION: ${{ github.repository_owner }} + LAUNCHABLE_WORKSPACE: ${{ github.event.repository.name }} + LAUNCHABLE_ENABLED: ${{ steps.enable-launchable.outputs.enable-launchable || false }} + GITHUB_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + GITHUB_PULL_REQUEST_URL: ${{ github.event.pull_request.html_url }} + GITHUB_REF: ${{ github.ref }} + + # Clean up non-default docker images to save disk space. + # The default image (clang-20) is reused across multiple steps + # within the same job, so we keep it to avoid redundant pulls. + - name: clean up docker image + shell: bash + run: docker rmi "ghcr.io/ruby/ruby-ci-image:${INPUT_TAG}" || true + if: ${{ always() && inputs.tag != 'clang-20' }} + env: + INPUT_TAG: ${{ inputs.tag }} diff --git a/.github/actions/compilers/entrypoint.sh b/.github/actions/compilers/entrypoint.sh new file mode 100755 index 0000000000..b554151091 --- /dev/null +++ b/.github/actions/compilers/entrypoint.sh @@ -0,0 +1,90 @@ +#! /bin/bash + +# Copyright (c) 2024 Ruby developers. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +grouped() +{ + echo "::group::${@}" + "${@}" + echo "::endgroup::" +} + +set -e +set -u +set -o pipefail + +srcdir="/github/workspace/src" +builddir="$(mktemp -dt)" + +export GITHUB_WORKFLOW='Compilations' +export CONFIGURE_TTY='never' +export RUBY_DEBUG='ci rgengc' +export RUBY_TESTOPTS='-q --color=always --tty=no' +export RUBY_DEBUG_COUNTER_DISABLE='1' +export GNUMAKEFLAGS="-j$((1 + $(nproc)))" + +case "x${INPUT_ENABLE_SHARED}" in +x | xno | xfalse ) + enable_shared='--disable-shared' + ;; +*) + enable_shared='--enable-shared' + ;; +esac + +pushd ${builddir} + +grouped git config --global --add safe.directory ${srcdir} + +grouped ${srcdir}/configure \ + -C \ + --with-gcc="${INPUT_WITH_GCC}" \ + --enable-debug-env \ + --disable-install-doc \ + --with-ext=-test-/cxxanyargs,+ \ + --without-git \ + ${enable_shared} \ + ${INPUT_APPEND_CONFIGURE} \ + CFLAGS="${INPUT_CFLAGS}" \ + CXXFLAGS="${INPUT_CXXFLAGS}" \ + optflags="${INPUT_OPTFLAGS}" \ + cppflags="${INPUT_CPPFLAGS}" \ + debugflags='-ggdb3' # -g0 disables backtraces when SEGV. Do not set that. + +popd + +if [[ -n "${INPUT_STATIC_EXTS}" ]]; then + echo "::group::ext/Setup" + set -x + mkdir ${builddir}/ext + ( + for ext in ${INPUT_STATIC_EXTS}; do + echo "${ext}" + done + ) >> ${builddir}/ext/Setup + set +x + echo "::endgroup::" +fi + +if [ -n "$INPUT_TEST_ALL" ]; then + tests=" -- $INPUT_TEST_ALL" +else + tests=" -- ruby -ext-" +fi + +pushd ${builddir} + +grouped make showflags +grouped make all +# grouped make install + +# Run only `make test` by default. Run other tests if specified. +grouped make test +if [[ -n "$INPUT_CHECK" ]]; then grouped make test-tool; fi +if [[ -n "$INPUT_CHECK" || -n "$INPUT_TEST_ALL" ]]; then grouped make test-all TESTS="$tests"; fi +if [[ -n "$INPUT_CHECK" || -n "$INPUT_TEST_SPEC" ]]; then grouped env CHECK_LEAKS=true make test-spec MSPECOPT="$INPUT_TEST_SPEC"; fi diff --git a/.github/actions/launchable/setup/action.yml b/.github/actions/launchable/setup/action.yml new file mode 100644 index 0000000000..305878492c --- /dev/null +++ b/.github/actions/launchable/setup/action.yml @@ -0,0 +1,337 @@ +name: Set up Launchable +description: >- + Install the required dependencies and execute the necessary Launchable commands for test recording + +inputs: + os: + required: true + description: The operating system that CI runs on. This value is used in Launchable flavor. + + test-opts: + default: none + required: false + description: >- + Test options that determine how tests are run. + This value is used in the Launchable flavor. + + launchable-token: + required: false + description: >- + Launchable token is needed if you want to run Launchable on your forked repository. + See https://github.com/ruby/ruby/wiki/CI-Servers#launchable-ci for details. + + builddir: + required: false + default: ${{ github.workspace }} + description: >- + Directory to create Launchable report file. + + srcdir: + required: false + default: ${{ github.workspace }} + description: >- + Directory to (re-)checkout source codes. Launchable retrieves the commit information + from the directory. + + test-task: + required: false + default: ${{ matrix.test_task }} + description: >- + Specifies a single test task to be executed. + This value is used in the Launchable flavor. + Either 'test-task' or 'multi-test-tasks' must be configured. + + test-tasks: + required: false + default: '[]' + description: >- + Specifies an array of multiple test tasks to be executed. + For example: '["test", "test-all"]'. + If you want to run a single test task, use the 'test-task' input instead. + + is-yjit: + required: false + default: 'false' + description: >- + Whether this workflow is executed on YJIT. + + is-zjit: + required: false + default: 'false' + description: >- + Whether this workflow is executed on ZJIT. + +outputs: + stdout_report_path: + value: ${{ steps.global.outputs.stdout_report_path }} + description: >- + Report file path for standard output. + + stderr_report_path: + value: ${{ steps.global.outputs.stderr_report_path }} + description: >- + Report file path for standard error. + +runs: + using: composite + + steps: + - name: Enable Launchable conditionally + id: enable-launchable + run: echo "enable-launchable=true" >> $GITHUB_OUTPUT + shell: bash + if: >- + ${{ + (github.repository == 'ruby/ruby' + || (github.repository != 'ruby/ruby' + && env.LAUNCHABLE_TOKEN)) + && (inputs.test-task == 'check' + || inputs.test-task == 'test-all' + || inputs.test-task == 'test' + || contains(fromJSON(inputs.test-tasks), 'test-all') + || contains(fromJSON(inputs.test-tasks), 'test')) + }} + + # Launchable CLI requires Python and Java. + # https://www.launchableinc.com/docs/resources/cli-reference/ + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.x" + if: >- + ${{ steps.enable-launchable.outputs.enable-launchable + && !endsWith(inputs.os, 'ppc64le') && !endsWith(inputs.os, 's390x') }} + + - name: Set up Java + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0 + with: + distribution: 'temurin' + java-version: '17' + if: >- + ${{ steps.enable-launchable.outputs.enable-launchable + && !endsWith(inputs.os, 'ppc64le') && !endsWith(inputs.os, 's390x') }} + + - name: Set up Java ppc64le + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0 + with: + distribution: 'semeru' + architecture: 'ppc64le' + java-version: '17' + if: >- + ${{ steps.enable-launchable.outputs.enable-launchable + && endsWith(inputs.os, 'ppc64le') }} + + - name: Set up Java s390x + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0 + with: + distribution: 'semeru' + architecture: 's390x' + java-version: '17' + if: >- + ${{ steps.enable-launchable.outputs.enable-launchable + && endsWith(inputs.os, 's390x') }} + + - name: Set global vars + id: global + shell: bash + run: | + test_all_enabled="${{ inputs.test-task == 'check' || inputs.test-task == 'test-all' || contains(fromJSON(inputs.test-tasks), 'test-all') }}" + btest_enabled="${{ inputs.test-task == 'check' || inputs.test-task == 'test' || contains(fromJSON(inputs.test-tasks), 'test') }}" + test_spec_enabled="${{ inputs.test-task == 'check' || inputs.test-task == 'test-spec' || contains(fromJSON(inputs.test-tasks), 'test-spec') }}" + echo test_all_enabled="${test_all_enabled}" >> $GITHUB_OUTPUT + echo btest_enabled="${btest_enabled}" >> $GITHUB_OUTPUT + echo test_spec_enabled="${test_spec_enabled}" >> $GITHUB_OUTPUT + echo test_all_report_file='launchable_test_all_report.json' >> $GITHUB_OUTPUT + echo btest_report_file='launchable_btest_report.json' >> $GITHUB_OUTPUT + echo test_spec_report_dir='launchable_test_spec_report' >> $GITHUB_OUTPUT + echo stdout_report_path="launchable_stdout.log" >> $GITHUB_OUTPUT + echo stderr_report_path="launchable_stderr.log" >> $GITHUB_OUTPUT + if: steps.enable-launchable.outputs.enable-launchable + + - name: Set environment variables for Launchable + shell: bash + run: | # zizmor: ignore[github-env] + : # GITHUB_PULL_REQUEST_URL are used for commenting test reports in Launchable Github App. + : # https://github.com/launchableinc/cli/blob/v1.80.1/launchable/utils/link.py#L42 + echo "GITHUB_PULL_REQUEST_URL=${INPUT_PR_HTML_URL}" >> $GITHUB_ENV + : # The following envs are necessary in Launchable tokenless authentication. + : # https://github.com/launchableinc/cli/blob/v1.80.1/launchable/utils/authentication.py#L20 + echo "LAUNCHABLE_ORGANIZATION=${INPUT_REPOSITORY_OWNER}" >> $GITHUB_ENV + echo "LAUNCHABLE_WORKSPACE=${INPUT_REPOSITORY_NAME}" >> $GITHUB_ENV + : # https://github.com/launchableinc/cli/blob/v1.80.1/launchable/utils/authentication.py#L71 + echo "GITHUB_PR_HEAD_SHA=${INPUT_PR_HEAD_SHA}" >> $GITHUB_ENV + echo "LAUNCHABLE_TOKEN=${INPUT_LAUNCHABLE_TOKEN}" >> $GITHUB_ENV + : # To prevent a slowdown in CI, disable request retries when the Launchable server is unstable. + echo "LAUNCHABLE_SKIP_TIMEOUT_RETRY=1" >> $GITHUB_ENV + echo "LAUNCHABLE_COMMIT_TIMEOUT=1" >> $GITHUB_ENV + env: + INPUT_PR_HTML_URL: ${{ github.event.pull_request.html_url }} + INPUT_REPOSITORY_OWNER: ${{ github.repository_owner }} + INPUT_REPOSITORY_NAME: ${{ github.event.repository.name }} + INPUT_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + INPUT_LAUNCHABLE_TOKEN: ${{ inputs.launchable-token }} + if: steps.enable-launchable.outputs.enable-launchable + + - name: Set up path + shell: bash + working-directory: ${{ inputs.srcdir }} + # Since updated PATH variable will be available in only subsequent actions, we need to add the path beforehand. + # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path + run: echo "$(python -msite --user-base)/bin" >> $GITHUB_PATH # zizmor: ignore[github-env] + if: >- + ${{ + steps.enable-launchable.outputs.enable-launchable + && (startsWith(inputs.os, 'macos') + || endsWith(inputs.os, 'ppc64le') + || endsWith(inputs.os, 's390x')) + }} + + - name: Set up Launchable + id: setup-launchable + shell: bash + working-directory: ${{ inputs.srcdir }} + run: | # zizmor: ignore[github-env] + set -x + pip install --user launchable + : # The build name cannot include a slash, so we replace the string here. + github_ref="${INPUT_GITHUB_REF}" + github_ref="${github_ref//\//_}" + : # With the --name option, we need to configure a unique identifier for this build. + : # To avoid setting the same build name as the CI which runs on other branches, we use the branch name here. + build_name="${github_ref}_${GITHUB_PR_HEAD_SHA}" + test_opts="${INPUT_TEST_OPTS}" + test_opts="${test_opts// /}" + test_opts="${test_opts//=/:}" + test_all_test_suite='test-all' + btest_test_suite='btest' + test_spec_test_suite='test-spec' + if [ "${INPUT_IS_YJIT}" = "true" ]; then + test_all_test_suite="yjit-${test_all_test_suite}" + btest_test_suite="yjit-${btest_test_suite}" + test_spec_test_suite="yjit-${test_spec_test_suite}" + fi + if [ "${INPUT_IS_ZJIT}" = "true" ]; then + test_all_test_suite="zjit-${test_all_test_suite}" + btest_test_suite="zjit-${btest_test_suite}" + test_spec_test_suite="zjit-${test_spec_test_suite}" + fi + # launchable_setup target var -- refers ${target} prefixed variables + launchable_setup() { + local target=$1 session + eval [ "\${${target}_enabled}" = "true" ] || return + eval local suite=\${${target}_test_suite} + session=$(launchable record session \ + --build "${build_name}" \ + --observation \ + --flavor os="${INPUT_OS}" \ + --flavor test_task="${INPUT_TEST_TASK}" \ + --flavor test_opts="${test_opts}" \ + --flavor workflow="${INPUT_WORKFLOW}" \ + --test-suite ${suite} \ + ) + echo "${target}_session=${session}" >> $GITHUB_OUTPUT + } + + launchable record build --name "${build_name}" + if launchable_setup test_all; then + echo "TESTS=${TESTS:+$TESTS }--launchable-test-reports=${test_all_report_file}" >> $GITHUB_ENV + fi + if launchable_setup btest; then + echo "BTESTS=${BTESTS:+$BTESTS }--launchable-test-reports=${btest_report_file}" >> $GITHUB_ENV + fi + if launchable_setup test_spec; then + echo "SPECOPTS=${SPECOPTS:$SPECOPTS }--launchable-test-reports=${test_spec_report_dir}" >> $GITHUB_ENV + echo test_spec_enabled=true >> $GITHUB_OUTPUT + fi + + echo launchable_setup_dir=$(pwd) >> $GITHUB_OUTPUT + if: steps.enable-launchable.outputs.enable-launchable + env: + INPUT_GITHUB_REF: ${{ github.ref }} + INPUT_TEST_OPTS: ${{ inputs.test-opts }} + INPUT_IS_YJIT: ${{ inputs.is-yjit }} + INPUT_IS_ZJIT: ${{ inputs.is-zjit }} + INPUT_OS: ${{ inputs.os }} + INPUT_TEST_TASK: ${{ inputs.test-task }} + INPUT_WORKFLOW: ${{ github.workflow }} + test_all_enabled: ${{ steps.global.outputs.test_all_enabled }} + btest_enabled: ${{ steps.global.outputs.btest_enabled }} + test_spec_enabled: ${{ steps.global.outputs.test_spec_enabled }} + test_all_report_file: ${{ steps.global.outputs.test_all_report_file }} + btest_report_file: ${{ steps.global.outputs.btest_report_file }} + test_spec_report_dir: ${{ steps.global.outputs.test_spec_report_dir }} + + - name: make test-spec report directory in build directory + shell: bash + working-directory: ${{ inputs.builddir }} + run: mkdir "${test_spec_report_dir}" + if: ${{ steps.setup-launchable.outputs.test_spec_enabled == 'true' }} + env: + test_spec_report_dir: ${{ steps.global.outputs.test_spec_report_dir }} + + - name: Clean up test results in Launchable + uses: gacts/run-and-post-run@81b6ce503cde93862cec047c54652e45c5dca991 # v1.4.3 + with: + shell: bash + working-directory: ${{ inputs.builddir }} + post: | + rm -f "${test_all_report_file}" + rm -f "${btest_report_file}" + rm -fr "${test_spec_report_dir}" + rm -f launchable_stdout.log + rm -f launchable_stderr.log + if: always() && steps.setup-launchable.outcome == 'success' + env: + test_all_report_file: ${{ steps.global.outputs.test_all_report_file }} + btest_report_file: ${{ steps.global.outputs.btest_report_file }} + test_spec_report_dir: ${{ steps.global.outputs.test_spec_report_dir }} + + - name: Record test results in Launchable + uses: gacts/run-and-post-run@81b6ce503cde93862cec047c54652e45c5dca991 # v1.4.3 + with: + shell: bash + working-directory: ${{ inputs.builddir }} + post: | + if [[ "${test_all_enabled}" = "true" ]]; then \ + launchable record attachment \ + --session "${test_all_session}" \ + "${stdout_report_path}" \ + "${stderr_report_path}"; \ + launchable record tests \ + --session "${test_all_session}" \ + raw "${test_all_report_file}" || true; \ + fi + + if [[ "${btest_enabled}" = "true" ]]; then \ + launchable record attachment \ + --session "${btest_session}" \ + "${stdout_report_path}" \ + "${stderr_report_path}"; \ + launchable record tests \ + --session "${btest_session}" \ + raw "${btest_report_file}" || true; \ + fi + + if [[ "${test_spec_enabled}" = "true" ]]; then \ + launchable record attachment \ + --session "${test_spec_session}" \ + "${stdout_report_path}" \ + "${stderr_report_path}"; \ + launchable record tests \ + --session "${test_spec_session}" \ + raw ${test_spec_report_dir}/* || true; \ + fi + if: ${{ always() && steps.setup-launchable.outcome == 'success' }} + env: + test_all_report_file: ${{ steps.global.outputs.test_all_report_file }} + btest_report_file: ${{ steps.global.outputs.btest_report_file }} + test_spec_report_dir: ${{ steps.global.outputs.test_spec_report_dir }} + test_all_enabled: ${{ steps.global.outputs.test_all_enabled }} + btest_enabled: ${{ steps.global.outputs.btest_enabled }} + test_spec_enabled: ${{ steps.global.outputs.test_spec_enabled }} + test_all_session: ${{ steps.setup-launchable.outputs.test_all_session }} + btest_session: ${{ steps.setup-launchable.outputs.btest_session }} + test_spec_session: ${{ steps.setup-launchable.outputs.test_spec_session }} + stdout_report_path: ${{ steps.global.outputs.stdout_report_path }} + stderr_report_path: ${{ steps.global.outputs.stderr_report_path }} + LAUNCHABLE_SETUP_DIR: ${{ steps.setup-launchable.outputs.launchable_setup_dir }} diff --git a/.github/actions/make-snapshot/action.yml b/.github/actions/make-snapshot/action.yml new file mode 100644 index 0000000000..4552f0e067 --- /dev/null +++ b/.github/actions/make-snapshot/action.yml @@ -0,0 +1,77 @@ +name: 'make-snapshot' +description: 'Make snapshot tarballs' +inputs: + archname: + description: 'archname passed to tool/make-snapshot (e.g. snapshot-master)' + required: true + version: + description: 'Target Version' + required: false + shallow-since: + description: 'git fetch --shallow-since' + required: true + default: '2018-12-25 00:00:00' + fetch-branch: + description: 'fetch branch' + required: false + srcdir: + description: 'srcdir for tool/make-snapshot. Empty = clone ruby/ruby into ./ruby.' + required: false + default: '' + upload-artifact: + description: 'Upload Packages and Info as workflow artifacts. Pass "false" when callers run in a matrix that would collide on artifact names.' + required: false + default: 'true' + +runs: + using: "composite" + steps: + - name: Install libraries + run: | + set -x + sudo apt-get update -q || : + sudo apt-get install --no-install-recommends -q -y build-essential git bison autoconf ruby p7zip-full curl + shell: bash + - name: Checkout ruby/ruby for tool/make-snapshot + if: inputs.srcdir == '' + run: git clone --single-branch --depth=1 https://github.com/ruby/ruby ruby + shell: bash + - name: Fetch branches and notes (clone mode) + if: inputs.srcdir == '' + env: + SHALLOW_SINCE: ${{ inputs.shallow-since }} + FETCH_BRANCH: ${{ inputs.fetch-branch }} + run: | + set -x + cd ruby + git fetch --shallow-since="$SHALLOW_SINCE" + [ -n "$FETCH_BRANCH" ] && git fetch origin "+$FETCH_BRANCH:$FETCH_BRANCH" + git fetch origin '+refs/notes/commits:refs/notes/commits' + git fetch origin '+refs/notes/log-fix:refs/notes/log-fix' + shell: bash + - name: Fetch notes (local srcdir mode) + if: inputs.srcdir != '' + working-directory: ${{ inputs.srcdir }} + run: | + git fetch origin '+refs/notes/commits:refs/notes/commits' || : + git fetch origin '+refs/notes/log-fix:refs/notes/log-fix' || : + shell: bash + - name: Make snapshot + env: + ARCHNAME: ${{ inputs.archname }} + SRCDIR: ${{ inputs.srcdir }} + VERSION: ${{ inputs.version }} + run: | + [ -z "$SRCDIR" ] && SRCDIR=ruby + ruby "$SRCDIR/tool/make-snapshot" "-archname=$ARCHNAME" -srcdir="$SRCDIR" -packages=gzip,xz,zip pkg $VERSION + shell: bash + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: Packages + path: pkg + if: ${{ inputs.upload-artifact == 'true' }} + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: Info + path: pkg/info + if: ${{ inputs.upload-artifact == 'true' }} diff --git a/.github/actions/setup/baseruby/action.yml b/.github/actions/setup/baseruby/action.yml new file mode 100644 index 0000000000..76fe068897 --- /dev/null +++ b/.github/actions/setup/baseruby/action.yml @@ -0,0 +1,73 @@ +name: Setup directories etc. +description: >- + Build baseruby for cross-compiling + +inputs: + srcdir: + required: true + default: ${{ github.workspace }} + description: >- + Directory of source codes. + + builddir: + required: false + default: ${{ github.workspace }}/baseruby + description: >- + Where baseruby will be built. + + installdir: + required: false + default: install + description: >- + The path where the baseruby will be installed to. + This is relative from the workspace. + +outputs: + ruby: + value: ${{ steps.build.outputs.installdir }}/bin/ruby + description: >- + The path of the executable baseruby. + dump_ast: + value: ${{ steps.build.outputs.installdir }}/bin/dump_ast + description: >- + The path of the executable dump_ast. + +runs: + using: composite + + steps: + - name: Build baseruby + shell: bash + id: build + run: | + case "$installdir" in /*) ;; *) installdir="$PWD/$installdir";; esac + mkdir "$builddir" + ln -sr "$srcdir" "$builddir/.src" + pushd "$builddir" + .src/configure "--prefix=${installdir}" --disable-install-doc + CONFIGURE_ARGS=--with-out-ext=-test- make install + install dump_ast "${installdir}/bin" + { + echo "${installdir}/bin/dump_ast" + echo "${installdir}/.installed.list" + echo "${installdir}/" + } >> .installed.list + cp .installed.list "${installdir}/" + make distclean + rm .src + popd + rmdir "$builddir" + { + echo "installdir=${installdir}" + } | tee -a "$GITHUB_OUTPUT" + env: + srcdir: ${{ inputs.srcdir }} + builddir: ${{ inputs.builddir }} + installdir: ${{ inputs.installdir }} + + - name: clean + uses: gacts/run-and-post-run@598d7a875d5620e0457490555b5e18e46082aa47 # v1.4.4 + with: + working-directory: ${{ inputs.srcdir }} + post: | + ruby tool/rbuninstall.rb "${{ steps.build.outputs.installdir }}/.installed.list" > /dev/null diff --git a/.github/actions/setup/directories/action.yml b/.github/actions/setup/directories/action.yml new file mode 100644 index 0000000000..589049a4b8 --- /dev/null +++ b/.github/actions/setup/directories/action.yml @@ -0,0 +1,205 @@ +name: Setup directories etc. +description: >- + Set up the source code and build directories (plus some + environmental tweaks) + +inputs: + srcdir: + required: false + default: ${{ github.workspace }} + description: >- + Directory to (re-)checkout source codes. This will be created + if absent. If there is no `configure` file that is also + generated inside. + + builddir: + required: false + default: ${{ github.workspace }} + description: >- + Where binaries and other generated contents go. This will be + created if absent. + + make-command: + required: false + type: string + default: 'make' + description: >- + The command of `make`. + + makeup: + required: false + type: boolean + # Note that `default: false` evaluates to a string constant + # `'false'`, which is a truthy value :sigh: + # https://github.com/actions/runner/issues/2238 + default: '' + description: >- + If set to true, additionally runs `make up`. + + checkout: + required: false + type: boolean + default: true + description: >- + If set to '' (false), skip running actions/checkout. This is useful when + you don't want to overwrite a GitHub token that is already set up. + + dummy-files: + required: false + type: boolean + default: '' + description: >- + If set to true, creates dummy files in build dir. + + fetch-depth: + required: false + default: '1' + description: The depth of commit history fetched from the remote repository + + clean: + required: false + type: boolean + default: '' + description: >- + If set to true, clean build directory. + +outputs: {} # nothing? + +runs: + using: composite + + steps: + # Note that `shell: bash` works on both Windows and Linux, but not + # `shell: sh`. This is because GitHub hosted Windows runners have + # their bash manually installed. + - shell: bash + run: | + mkdir -p "${INPUT_SRCDIR}" + mkdir -p "${INPUT_BUILDDIR}" + env: + INPUT_SRCDIR: ${{ inputs.srcdir }} + INPUT_BUILDDIR: ${{ inputs.builddir }} + + # Did you know that actions/checkout works without git(1)? We are + # checking that here. + - id: which + shell: bash + run: | + echo "git=`command -v git`" >> "$GITHUB_OUTPUT" + echo "sudo=`sudo true && command -v sudo`" >> "$GITHUB_OUTPUT" + echo "autoreconf=`command -v autoreconf`" >> "$GITHUB_OUTPUT" + + - if: steps.which.outputs.git + shell: bash + run: | + git config --global core.autocrlf false + git config --global core.eol lf + git config --global advice.detachedHead 0 + git config --global init.defaultBranch garbage + + - if: inputs.checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + path: ${{ inputs.srcdir }} + fetch-depth: ${{ inputs.fetch-depth }} + persist-credentials: false + + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ${{ inputs.srcdir }}/.downloaded-cache + key: ${{ runner.os }}-${{ runner.arch }}-downloaded-cache + + - if: steps.which.outputs.autoreconf + shell: bash + working-directory: ${{ inputs.srcdir }} + run: ./autogen.sh --install + + # This is for MinGW. + - if: runner.os == 'Windows' + shell: bash + run: echo "GNUMAKEFLAGS=-j$((2 * NUMBER_OF_PROCESSORS))" >> $GITHUB_ENV # zizmor: ignore[github-env] + + - if: runner.os == 'Linux' + shell: bash + run: echo "GNUMAKEFLAGS=-sj$((1 + $(nproc)))" >> "$GITHUB_ENV" # zizmor: ignore[github-env] + + # macOS' GNU make is so old that they doesn't understand `GNUMAKEFLAGS`. + - if: runner.os == 'macOS' + shell: bash + run: echo "MAKEFLAGS=-j$((1 + $(sysctl -n hw.activecpu)))" >> "$GITHUB_ENV" # zizmor: ignore[github-env] + + - if: inputs.makeup + shell: bash + working-directory: ${{ inputs.srcdir }} + run: | + touch config.status .rbconfig.time + for mk in Makefile GNUmakefile; do + sed -f tool/prereq.status template/$mk.in > $mk + done + make up + + # Cleanup, runs even on failure + - if: always() && inputs.makeup + shell: bash + working-directory: ${{ inputs.srcdir }} + run: | + rm -f config.status .rbconfig.time \ + Makefile GNUmakefile uncommon.mk enc.mk noarch-fake.rb + rm -f prism/.time prism/util/.time + + - if: steps.which.outputs.sudo + shell: bash + run: | + sudo chmod -R go-w /usr/share + chmod -v go-w $HOME $HOME/.config || : + declare -a dirs # -A is not supported by old bash, e.g. macos + SAVE_IFS="$IFS" IFS=:; set $PATH + for d do + while [ -d "$d" ]; do + case "$IFS${dirs[*]}$IFS" in *"$IFS$d$IFS"*) ;; *) dirs+=("$d");; esac + d="${d%/*}" + done + done + IFS="$SAVE_IFS" + sudo chmod -v go-w "${dirs[@]}" || : + + - if: inputs.dummy-files == 'true' + shell: bash + id: dummy-files + working-directory: ${{ inputs.builddir }} + run: | + : Create dummy files in build dir + set {{a..z},{A..Z},{0..9},foo,bar,test,zzz}.rb + for file; do \ + echo > $file "raise 'do not load $file'"; \ + done + # drop {a..z}.rb if case-insensitive filesystem + grep -F A.rb a.rb > /dev/null && set "${@:27}" + echo clean="cd ${INPUT_BUILDDIR} && rm $*" >> $GITHUB_OUTPUT + env: + INPUT_BUILDDIR: ${{ inputs.builddir }} + + - if: inputs.clean == 'true' + shell: bash + id: clean + run: | + echo distclean="cd ${INPUT_BUILDDIR} && ${INPUT_MAKE_COMMAND} distclean" >> $GITHUB_OUTPUT + echo remained-files="find ${INPUT_BUILDDIR} -ls" >> $GITHUB_OUTPUT + [ "${INPUT_BUILDDIR}" = "${INPUT_SRCDIR}" ] || + echo final="rmdir ${INPUT_BUILDDIR}" >> $GITHUB_OUTPUT + env: + INPUT_BUILDDIR: ${{ inputs.builddir }} + INPUT_SRCDIR: ${{ inputs.srcdir }} + INPUT_MAKE_COMMAND: ${{ inputs.make-command }} + + - name: clean + uses: gacts/run-and-post-run@598d7a875d5620e0457490555b5e18e46082aa47 # v1.4.4 + with: + working-directory: + post: | + ${{ steps.dummy-files.outputs.clean }} + ${{ steps.clean.outputs.distclean }} + ${{ steps.clean.outputs.remained-files }} + ${{ steps.clean.outputs.final }} + # rmdir randomly fails due to launchable files + continue-on-error: true diff --git a/.github/actions/setup/macos/action.yml b/.github/actions/setup/macos/action.yml new file mode 100644 index 0000000000..9cd37a9b12 --- /dev/null +++ b/.github/actions/setup/macos/action.yml @@ -0,0 +1,29 @@ +name: Setup macOS environment +description: >- + Installs necessary packages via Homebrew. + +inputs: {} # nothing? + +outputs: {} # nothing? + +runs: + using: composite + + steps: + - name: brew + shell: bash + run: | + brew install --quiet jemalloc gmp libffi openssl@3 zlib autoconf automake libtool + + - name: Set ENV + shell: bash + run: | # zizmor: ignore[github-env] + dir_config() { + local args=() lib var="$1"; shift + for lib in "$@"; do + args+=("--with-${lib%@*}-dir=$(brew --prefix $lib)") + done + echo "$var=${args[*]}" >> $GITHUB_ENV + } + dir_config ruby_configure_args gmp + dir_config CONFIGURE_ARGS openssl@3 diff --git a/.github/actions/setup/ubuntu/action.yml b/.github/actions/setup/ubuntu/action.yml new file mode 100644 index 0000000000..5209ccc03f --- /dev/null +++ b/.github/actions/setup/ubuntu/action.yml @@ -0,0 +1,72 @@ +name: Setup ubuntu environment +description: >- + At the beginning there was no way but to copy & paste `apt-get` + everywhere. But now that we have composite actions, it seems better + merge them into one. + +inputs: + arch: + required: false + default: '' + description: >- + Architecture. Because we run this on a GitHub-hosted runner + acceptable value for this input is very limited. + +outputs: + arch: + value: ${{ steps.uname.outputs.uname }} + description: >- + Actual architecture. This could be different from the one + passed to the `inputs.arch`. For instance giving `i386` to this + action yields `i686`. + +runs: + using: composite + + steps: + - id: uname + name: uname + shell: bash + env: + arch: ${{ inputs.arch }} + run: | + setarch="${arch:+setarch $arch --}" + # normalize `uname` + if uname=$(${setarch} uname -m 2> /dev/null); then + # `setarch` works, `$arch` is a valid architecture name. + echo "setarch=${setarch}" >> "$GITHUB_OUTPUT" + else + # if `setarch` failed, take the given `arch` as-is. + uname="${arch}" + setarch="" + fi + echo "uname=$uname" >> "$GITHUB_OUTPUT" + echo "dpkg=${uname/686/386}" >> "$GITHUB_OUTPUT" + + - name: set SETARCH + shell: bash + run: echo "SETARCH=${setarch}" >> "$GITHUB_ENV" # zizmor: ignore[github-env] + env: + setarch: ${{ steps.uname.outputs.setarch }} # validated + + - name: dpkg setup + shell: bash + run: sudo dpkg --add-architecture "${dpkg}" + # `dpkg` is valid, also `uname`. + if: ${{ inputs.arch }} + env: + dpkg: ${{ steps.uname.outputs.dpkg }} + + - name: apt-get + shell: bash + env: + arch: ${{ inputs.arch && format(':{0}', steps.uname.outputs.dpkg) || '' }} + run: | + set -x + sudo apt-get update -qq || : + sudo apt-get install --no-install-recommends -qq -y -o=Dpkg::Use-Pty=0 \ + ${arch:+cross}build-essential${arch/:/-} \ + libssl-dev${arch} libyaml-dev${arch} libreadline6-dev${arch} \ + zlib1g-dev${arch} libncurses5-dev${arch} libffi-dev${arch} \ + autoconf ruby + sudo apt-get install -qq -y pkg-config${arch} || : diff --git a/.github/actions/slack/action.yml b/.github/actions/slack/action.yml new file mode 100644 index 0000000000..6f89bef11a --- /dev/null +++ b/.github/actions/slack/action.yml @@ -0,0 +1,51 @@ +name: Post a message to slack +description: >- + We have our ruby/action-slack webhook. However its arguments are + bit verbose to be listed in every workflow files. Better merge them + into one. + +inputs: + SLACK_WEBHOOK_URL: + required: true + description: >- + The URL to post the payload. This is an input because it tends + to be stored in a secrets vault and a composite action cannot + look into one. + + label: + required: false + description: >- + Human-readable description of the run, something like "DEBUG=1". + This need not be unique among runs. + + event_name: + required: false + default: 'push' + description: >- + Target event to trigger notification. Notify only push by default. + + extra_channel_id: + required: false + description: >- + Slack channel ID to notify besides #alerts and #alerts-emoji. + +outputs: {} # Nothing? + +runs: + using: composite + + steps: + - uses: ruby/action-slack@d260b61aa817726d5bedd22dd6cc305787fa4cdd # v4.0.0 + with: + payload: | + { + "ci": "GitHub Actions", + "env": "${{ github.workflow }}${{ inputs.label && format(' / {0}', inputs.label) }}", + "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", + "commit": "${{ github.sha }}", + "branch": "${{ github.ref_name }}" + ${{ inputs.extra_channel_id && format(', "extra_channel_id": "{0}"', inputs.extra_channel_id) }} + } + env: + SLACK_WEBHOOK_URL: ${{ inputs.SLACK_WEBHOOK_URL }} + if: ${{ github.event_name == inputs.event_name && startsWith(github.repository, 'ruby/') }} |
