diff --git a/.github/workflows/full-build-vcpkg.yml b/.github/workflows/full-build-vcpkg.yml new file mode 100644 index 00000000000..979bd072775 --- /dev/null +++ b/.github/workflows/full-build-vcpkg.yml @@ -0,0 +1,1200 @@ +# on: +# push: +# branches: +# - develop +# tags: +# - "v*" +# schedule: +# # Run nightly at 8 PM ET (midnight UTC during EST, 1 AM UTC during EDT) +# # Using 1 AM UTC to cover EDT (daylight saving time) +# - cron: '0 1 * * *' +on: + workflow_dispatch: + inputs: + publish_to_s3: + description: "Force S3 publishing even when not on develop" + required: false + default: "false" + skip_docker_trigger: + description: "Skip downstream docker workflow trigger" + required: false + default: "false" + job_filter: + description: "Jobs to run (Linux, macOS, Windows). Empty runs all." + required: false + default: "" + workflow_call: + inputs: + publish_to_s3: + type: string + required: false + default: "false" + skip_docker_trigger: + type: string + required: false + default: "false" + +concurrency: + group: full-build-${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: read + actions: read + checks: write + pull-requests: write + packages: write + id-token: write + +env: + BUILD_TYPE: Release + OPENSTUDIO_BUILD: build + PY_VERSION: "3.12.2" + AWS_S3_BUCKET: openstudio-ci-builds + TEST_DASHBOARD_RELATIVE: Testing/dashboard/test-dashboard.md + CCACHE_SLOPPINESS: pch_defines,time_macros,include_file_mtime,include_file_ctime + CCACHE_BASEDIR: ${{ github.workspace }} + CCACHE_COMPRESS: "true" + CCACHE_COMPRESSLEVEL: "3" + CCACHE_MAXSIZE: "10G" + CCACHE_DEPEND: "true" + CCACHE_NOHASHDIR: "true" + SCCACHE_GHA_ENABLED: "false" + SCCACHE_DIR: "${{ github.workspace }}\\.sccache" + SCCACHE_CACHE_SIZE: "10G" + VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" + +jobs: + linux-build: + name: Build ${{ matrix.display_name }} + runs-on: ${{ matrix.runner }} + if: inputs.job_filter == '' || contains(inputs.job_filter, 'Linux') + container: + image: ${{ matrix.container_image }} + options: ${{ matrix.container_options }} --volume /mnt:/mnt + strategy: + fail-fast: false + matrix: + include: + - platform: centos-9-x64 + display_name: CentOS 9 (AlmaLinux) x64 + runner: ubuntu-22.04 + container_image: nrel/openstudio-cmake-tools:almalinux9-main + container_options: "--privileged -u root -e LANG=en_US.UTF-8" + test_suffix: CentOS-9 + pip_package: false + docker_trigger: false + upload_globs: | + *.rpm + *OpenStudio*x86_64.tar.gz + cpack_generators: "RPM;TGZ" + max_jobs: 3 + exclude_regex: ${{ '""' }} + - platform: ubuntu-2204-x64 + display_name: Ubuntu 22.04 x64 + runner: ubuntu-22.04 + container_image: nrel/openstudio-cmake-tools:jammy-main + container_options: "--privileged -u root -e LANG=en_US.UTF-8" + test_suffix: Ubuntu-2204 + pip_package: true + docker_trigger: true + upload_globs: | + *.deb + *OpenStudio*x86_64.tar.gz + cpack_generators: "DEB;TGZ" + max_jobs: 3 + exclude_regex: ${{ '""' }} + - platform: ubuntu-2404-x64 + display_name: Ubuntu 24.04 x64 + runner: ubuntu-24.04 + container_image: nrel/openstudio-cmake-tools:noble-main + container_options: "--privileged -u root -e LANG=en_US.UTF-8" + test_suffix: Ubuntu-2404 + pip_package: false + docker_trigger: false + upload_globs: | + *.deb + *OpenStudio*x86_64.tar.gz + cpack_generators: "DEB;TGZ" + max_jobs: 3 + exclude_regex: ${{ '""' }} + - platform: ubuntu-2204-arm64 + display_name: Ubuntu 22.04 ARM64 + runner: ubuntu-22.04-arm + container_image: nrel/openstudio-cmake-tools:jammy-main + container_options: "--privileged -u root -e LANG=en_US.UTF-8" + test_suffix: Ubuntu-2204-ARM64 + pip_package: false + docker_trigger: false + upload_globs: | + *.deb + *OpenStudio*arm64.tar.gz + cpack_generators: "DEB;TGZ" + max_jobs: 3 + exclude_regex: "^(GeometryFixture.Plane_RayIntersection|ISOModelFixture.SimModel|SqlFileFixture.AnnualTotalCosts|OpenStudioCLI.*test_measure_manager)$" + - platform: ubuntu-2404-arm64 + display_name: Ubuntu 24.04 ARM64 + runner: ubuntu-24.04-arm + container_image: nrel/openstudio-cmake-tools:noble-main + container_options: "--privileged -u root -e LANG=en_US.UTF-8" + test_suffix: Ubuntu-2404-ARM64 + pip_package: false + docker_trigger: false + upload_globs: | + *.deb + *OpenStudio*arm64.tar.gz + cpack_generators: "DEB;TGZ" + max_jobs: 3 + exclude_regex: "^(GeometryFixture.Plane_RayIntersection|ISOModelFixture.SimModel|SqlFileFixture.AnnualTotalCosts|OpenStudioCLI.*test_measure_manager)$" + defaults: + run: + shell: bash + env: + MAX_BUILD_THREADS: ${{ matrix.max_jobs }} + CTEST_PARALLEL_LEVEL: ${{ matrix.max_jobs }} + steps: + - name: Verify space + run: | + echo "Memory and swap:" + # Check if free exists before running it, or ignore failure + if command -v free >/dev/null 2>&1; then + free -h + else + echo "free command not available" + fi + echo + swapon --show || true + echo + echo "Available storage:" + df -h || true + echo + + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Restore ccache cache + uses: actions/cache@v4 + with: + key: ccache-${{ runner.os }}-${{ matrix.platform }}-${{ hashFiles('vcpkg.json') }} + restore-keys: | + ccache-${{ runner.os }}-${{ matrix.platform }}- + save-always: true + + + + - name: Prepare workspace + run: | + set -euo pipefail + + # Use /mnt for build and caches to avoid running out of space on root partition + prepare_dir() { + local target=$1 + local dest=$2 + mkdir -p "$dest" + if [ -d "$target" ] && [ ! -L "$target" ]; then + echo "Moving existing $target to $dest" + cp -a "$target/." "$dest/" + rm -rf "$target" + fi + mkdir -p "$(dirname "$target")" + ln -sfn "$dest" "$target" + } + + prepare_dir "$GITHUB_WORKSPACE/${{ env.OPENSTUDIO_BUILD }}" "/mnt/build" + prepare_dir "$HOME/.ccache" "/mnt/.ccache" + if command -v ccache >/dev/null 2>&1; then + ccache -M ${{ env.CCACHE_MAXSIZE }} || true + echo "Configured ccache:"; ccache -s | sed -n '1,10p' + fi + + - name: Resolve build path + id: build_path + run: | + # actions/upload-artifact@v4 does not follow symlinks at the start of a path. + # We resolve the build directory to its real location to ensure globbing works. + REAL_PATH=$(readlink -f "${{ env.OPENSTUDIO_BUILD }}") + echo "path=$REAL_PATH" >> $GITHUB_OUTPUT + + - name: Fix CMake Path (CentOS) + if: matrix.platform == 'centos-9-x64' + run: | + if [ -d /usr/local/cmake/bin ]; then + echo "Adding /usr/local/cmake/bin to PATH" + echo "/usr/local/cmake/bin" >> $GITHUB_PATH + fi + + - name: Cache External Dependencies + uses: actions/cache@v4 + with: + path: | + ${{ env.OPENSTUDIO_BUILD }}/EnergyPlus*.tar.gz + ${{ env.OPENSTUDIO_BUILD }}/EnergyPlus*.zip + ${{ env.OPENSTUDIO_BUILD }}/radiance*.tar.gz + ${{ env.OPENSTUDIO_BUILD }}/radiance*.zip + ${{ env.OPENSTUDIO_BUILD }}/openstudio*gems*.tar.gz + key: external-deps-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} + restore-keys: | + external-deps-${{ runner.os }}- + save-always: true + + - name: Restore Generated Embedded Files + uses: actions/cache@v4 + with: + path: | + ${{ env.OPENSTUDIO_BUILD }}/src/*/embedded_files + ${{ env.OPENSTUDIO_BUILD }}/ruby/engine/embedded_files + key: embedded-files-${{ runner.os }}-${{ hashFiles('resources/**', 'ruby/engine/**', 'src/airflow/**', 'src/energyplus/**', 'src/gbxml/**', 'src/isomodel/**', 'src/model/**', 'src/radiance/**', 'src/sdd/**', 'src/utilities/**') }} + restore-keys: | + embedded-files-${{ runner.os }}- + save-always: true + + + + - name: Install CA Certificates + if: startsWith(matrix.platform, 'ubuntu') + run: apt-get update && apt-get install -y ca-certificates + + - name: Setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgJsonGlob: 'vcpkg.json' + + - name: Locate Ruby + run: | + ruby_path=$(command -v ruby) + echo "SYSTEM_RUBY_PATH=$ruby_path" >> $GITHUB_ENV + + - name: Configure with CMake + working-directory: ${{ env.OPENSTUDIO_BUILD }} + run: | + set -euo pipefail + # Use absolute path for ccache to avoid resolution issues in containers with symlinked build dirs + CCACHE_ARGS=() + if command -v ccache >/dev/null 2>&1; then + CCACHE_EXE=$(command -v ccache) + CCACHE_ARGS=("-DCMAKE_C_COMPILER_LAUNCHER=$CCACHE_EXE" "-DCMAKE_CXX_COMPILER_LAUNCHER=$CCACHE_EXE") + fi + cmake -G Ninja \ + "${CCACHE_ARGS[@]}" \ + -DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake \ + -DCMAKE_BUILD_TYPE:STRING=${{ env.BUILD_TYPE }} \ + -DBUILD_TESTING:BOOL=ON \ + -DCPACK_GENERATORS:STRING="${{ matrix.cpack_generators }}" \ + -DBUILD_PYTHON_BINDINGS:BOOL=ON \ + -DDISCOVER_TESTS_AFTER_BUILD:BOOL=ON \ + -DBUILD_PYTHON_PIP_PACKAGE:BOOL=${{ matrix.pip_package }} \ + -DPYTHON_VERSION:STRING=${{ env.PY_VERSION }} \ + -DSYSTEM_RUBY_EXECUTABLE="$SYSTEM_RUBY_PATH" \ + "$GITHUB_WORKSPACE" + + - name: Build with Ninja + working-directory: ${{ env.OPENSTUDIO_BUILD }} + run: | + set -euo pipefail + export NINJA_STATUS="[%f/%t | %es elapsed | %o objs/sec]" + # Start resource monitor (records RSS samples for later summary) + echo "timestamp PID RSS_KB COMM" > mem_samples.log + ( while true; do + sleep 60; + stamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ"); + if command -v ps >/dev/null 2>&1; then ps -eo pid,rsz,comm --sort=-rsz | head -n 5 | awk -v s="$stamp" '{print s" "$1" "$2" "$3}' >> mem_samples.log; fi; + done ) & + HB_PID=$! + cmake --build . --parallel ${{ matrix.max_jobs }} 2>&1 | tee build.log + BUILD_EXIT=${PIPESTATUS[0]} + kill $HB_PID || true + command -v ninja >/dev/null 2>&1 && ninja -d stats || true + exit $BUILD_EXIT + + - name: Run CTest suite + working-directory: ${{ env.OPENSTUDIO_BUILD }} + run: | + set -euo pipefail + + exclude_regex="${{ matrix.exclude_regex }}" + if [ "$exclude_regex" == '""' ]; then + ctest -C ${{ env.BUILD_TYPE }} -j ${{ matrix.max_jobs }} --output-on-failure + else + ctest -C ${{ env.BUILD_TYPE }} -E "$exclude_regex" -j ${{ matrix.max_jobs }} --output-on-failure + fi + + - name: Wait for network stability + if: always() + run: sleep 5 + + - name: Upload build diagnostics + if: always() + uses: actions/upload-artifact@v4 + with: + name: build-diag-${{ matrix.platform }}-${{ github.sha }} + path: | + ${{ steps.build_path.outputs.path }}/build.log + ${{ steps.build_path.outputs.path }}/.ninja_log + ${{ steps.build_path.outputs.path }}/CTestTestfile.cmake + if-no-files-found: warn + + - name: Create packages + if: always() + working-directory: ${{ env.OPENSTUDIO_BUILD }} + run: | + set -euo pipefail + cmake --build . --target package + + - name: Upload DEB installer + uses: actions/upload-artifact@v4 + with: + name: OS-Installers-${{ matrix.platform }}-DEB-${{ github.sha }} + path: ${{ steps.build_path.outputs.path }}/*.deb + if-no-files-found: ignore + + - name: Upload RPM installer + uses: actions/upload-artifact@v4 + with: + name: OS-Installers-${{ matrix.platform }}-RPM-${{ github.sha }} + path: ${{ steps.build_path.outputs.path }}/*.rpm + if-no-files-found: ignore + + - name: Upload TGZ installer + uses: actions/upload-artifact@v4 + with: + name: OS-Installers-${{ matrix.platform }}-TGZ-${{ github.sha }} + path: ${{ steps.build_path.outputs.path }}/*.tar.gz + if-no-files-found: ignore + + - name: Upload WHEEL installer + uses: actions/upload-artifact@v4 + with: + name: OS-Installers-${{ matrix.platform }}-WHEEL-${{ github.sha }} + path: ${{ steps.build_path.outputs.path }}/*.whl + if-no-files-found: ignore + + linux-publish: + name: Publish Linux Artifacts + needs: [linux-build] + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' || inputs.publish_to_s3 == 'true' || github.event.inputs.publish_to_s3 == 'true' + steps: + - name: Download all installers + uses: actions/download-artifact@v4 + with: + pattern: OS-Installers-* + merge-multiple: true + path: installers + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.AWS_REGION || 'us-west-2' }} + + - name: Publish to S3 + working-directory: installers + env: + S3_PREFIX: ${{ github.ref_type == 'tag' && format('releases/{0}', github.ref_name) || format('{0}', github.ref_name) }} + run: | + set -euo pipefail + echo "Uploading artifacts to s3://${AWS_S3_BUCKET}/${S3_PREFIX}" + for file in *; do + [ -e "$file" ] || continue + [ -f "$file" ] || continue + filename=$(basename "$file") + key="${S3_PREFIX}/${filename}" + aws s3 cp "$file" "s3://${AWS_S3_BUCKET}/${key}" --acl public-read + if command -v md5sum >/dev/null 2>&1; then md5sum "$file"; else md5 "$file"; fi + done + + macos: + name: ${{ matrix.display_name }} + runs-on: ${{ matrix.runner }} + if: inputs.job_filter == '' || contains(inputs.job_filter, 'macOS') + strategy: + fail-fast: false + matrix: + include: + - platform: macos-x64 + display_name: macOS x64 (Intel) + runner: macos-15-intel + test_suffix: macOS-x64 + dmg_glob: "*.dmg" + tar_glob: "*OpenStudio*x86_64.tar.gz" + exclude_regex: ${{ '""' }} + max_jobs: 3 + - platform: macos-arm64 + display_name: macOS ARM64 (Apple Silicon) + runner: macos-15 + test_suffix: macOS-arm64 + dmg_glob: "*.dmg" + tar_glob: "*OpenStudio*arm64.tar.gz" + exclude_regex: "^(GeometryFixture.Plane_RayIntersection|ISOModelFixture.SimModel)$" + max_jobs: 3 + defaults: + run: + shell: bash + env: + MAX_BUILD_THREADS: ${{ matrix.max_jobs }} + CTEST_PARALLEL_LEVEL: ${{ matrix.max_jobs }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Setup ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ccache-${{ runner.os }}-${{ matrix.platform }}-${{ hashFiles('vcpkg.json') }} + max-size: ${{ env.CCACHE_MAXSIZE }} + + + + - name: Prepare workspace + run: | + set -euo pipefail + git config --global --add safe.directory "$GITHUB_WORKSPACE" + mkdir -p "$GITHUB_WORKSPACE/${{ env.OPENSTUDIO_BUILD }}" + + - name: Cache External Dependencies + uses: actions/cache@v4 + with: + path: | + ${{ env.OPENSTUDIO_BUILD }}/EnergyPlus*.tar.gz + ${{ env.OPENSTUDIO_BUILD }}/EnergyPlus*.zip + ${{ env.OPENSTUDIO_BUILD }}/radiance*.tar.gz + ${{ env.OPENSTUDIO_BUILD }}/radiance*.zip + ${{ env.OPENSTUDIO_BUILD }}/openstudio*gems*.tar.gz + key: external-deps-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} + restore-keys: | + external-deps-${{ runner.os }}- + save-always: true + + - name: Restore Generated Embedded Files + uses: actions/cache@v4 + with: + path: | + ${{ env.OPENSTUDIO_BUILD }}/src/*/embedded_files + ${{ env.OPENSTUDIO_BUILD }}/ruby/engine/embedded_files + key: embedded-files-${{ runner.os }}-${{ hashFiles('resources/**', 'ruby/engine/**', 'src/airflow/**', 'src/energyplus/**', 'src/gbxml/**', 'src/isomodel/**', 'src/model/**', 'src/radiance/**', 'src/sdd/**', 'src/utilities/**') }} + restore-keys: | + embedded-files-${{ runner.os }}- + save-always: true + + - name: Set up Python 3.12.2 + uses: actions/setup-python@v6 + with: + python-version: '3.12.2' + cache: 'pip' + + - name: Install Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2.2' + bundler-cache: true + + - name: Install Python dependencies + run: | + set -euo pipefail + pip install --upgrade pip setuptools wheel + pip install -r python/requirements.txt + + - name: Setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgJsonGlob: 'vcpkg.json' + + - name: Locate Ruby + run: | + ruby_path=$(command -v ruby) + echo "SYSTEM_RUBY_PATH=$ruby_path" >> $GITHUB_ENV + + - name: Configure with CMake + working-directory: ${{ env.OPENSTUDIO_BUILD }} + run: | + set -euo pipefail + # Use absolute path for ccache to avoid resolution issues in containers with symlinked build dirs + CCACHE_ARGS=() + if command -v ccache >/dev/null 2>&1; then + CCACHE_EXE=$(command -v ccache) + CCACHE_ARGS=("-DCMAKE_C_COMPILER_LAUNCHER=$CCACHE_EXE" "-DCMAKE_CXX_COMPILER_LAUNCHER=$CCACHE_EXE") + fi + cmake -G Ninja \ + "${CCACHE_ARGS[@]}" \ + -DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake \ + -DCMAKE_BUILD_TYPE:STRING=${{ env.BUILD_TYPE }} \ + -DBUILD_TESTING:BOOL=ON \ + -DCPACK_BINARY_DRAGNDROP:BOOL=ON \ + -DCPACK_BINARY_TGZ:BOOL=ON \ + -DCPACK_BINARY_IFW:BOOL=OFF \ + -DCPACK_PACKAGING_INSTALL_PREFIX="/OpenStudio" \ + -DBUILD_PYTHON_BINDINGS:BOOL=ON \ + -DDISCOVER_TESTS_AFTER_BUILD:BOOL=ON \ + -DBUILD_PYTHON_PIP_PACKAGE:BOOL=OFF \ + -DPYTHON_VERSION:STRING=${{ env.PY_VERSION }} \ + -DSYSTEM_RUBY_EXECUTABLE="$SYSTEM_RUBY_PATH" \ + "${{ github.workspace }}" + + - name: Build with Ninja + working-directory: ${{ env.OPENSTUDIO_BUILD }} + run: | + set -euo pipefail + export NINJA_STATUS="[%f/%t | %es elapsed | %o objs/sec]" + while true; do + sleep 300 + echo "[heartbeat] $(date -u +"%H:%M:%S")" + if command -v top >/dev/null 2>&1; then top -l 1 -s 0 | grep PhysMem || true; fi + df -h . | tail -1 | awk '{print "[disk] used=" $3 "/" $2 " (" $5 ")"}' + if command -v ps >/dev/null 2>&1; then ps -eo pid,pmem,rss,comm | sort -rn -k2 | head -n 5; fi + done & + heartbeat_pid=$! + cmake --build . --parallel ${MAX_BUILD_THREADS} 2>&1 | tee build.log + build_exit=${PIPESTATUS[0]} + kill $heartbeat_pid || true + command -v ninja >/dev/null 2>&1 && ninja -d stats || true + if [ -f build.log ]; then tail -n 40 build.log; fi + exit $build_exit + + - name: Wait for network stability + if: always() + run: sleep 5 + + - name: Upload build diagnostics + if: always() + uses: actions/upload-artifact@v4 + with: + name: build-diag-${{ matrix.platform }}-${{ github.sha }} + path: | + ${{ env.OPENSTUDIO_BUILD }}/build.log + ${{ env.OPENSTUDIO_BUILD }}/.ninja_log + ${{ env.OPENSTUDIO_BUILD }}/CTestTestfile.cmake + if-no-files-found: warn + + - name: Run CTest suite + id: mac_ctest + working-directory: ${{ env.OPENSTUDIO_BUILD }} + continue-on-error: true + run: | + set -euo pipefail + df -h . + + # Conflicting tests that must run sequentially + resource_locked_tests="ModelFixture.ScheduleFile|ModelFixture.ScheduleFileAltCtor|ModelFixture.PythonPluginInstance|ModelFixture.PythonPluginInstance_NotPYFile|ModelFixture.PythonPluginInstance_ClassNameValidation|ModelFixture.ChillerElectricASHRAE205_GettersSetters|ModelFixture.ChillerElectricASHRAE205_Loops|ModelFixture.ChillerElectricASHRAE205_NotCBORFile|ModelFixture.ChillerElectricASHRAE205_Clone" + + overall_exit_code=0 + + echo "Running sequential tests..." + ctest -C ${{ env.BUILD_TYPE }} -R "^($resource_locked_tests)$" -j 1 || overall_exit_code=1 + + echo "Running all other tests in parallel..." + export CTEST_OUTPUT_ON_FAILURE=1 + export CTEST_PARALLEL_LEVEL=${{ matrix.max_jobs }} + + exclude_regex="${{ matrix.exclude_regex }}" + if [ -n "$exclude_regex" ] && [ "$exclude_regex" != '""' ]; then + exclude_regex="($exclude_regex|$resource_locked_tests)" + else + exclude_regex="^($resource_locked_tests)$" + fi + + ctest -C ${{ env.BUILD_TYPE }} -E "$exclude_regex" || overall_exit_code=$? + + echo "exit_code=${overall_exit_code}" >> $GITHUB_OUTPUT + + - name: Create packages + if: always() + working-directory: ${{ env.OPENSTUDIO_BUILD }} + run: | + set -euo pipefail + cmake --build . --target package + + - name: Cleanup intermediate files + if: always() + working-directory: ${{ env.OPENSTUDIO_BUILD }} + run: | + find . -name "*.o" -type f -delete || true + df -h . + + - name: Code sign and notarize macOS packages + if: ${{ github.ref == 'refs/heads/develop' || inputs.publish_to_s3 == 'true' || github.event.inputs.publish_to_s3 == 'true' }} + working-directory: ${{ env.OPENSTUDIO_BUILD }} + env: + APPLE_CERT_DATA: ${{ secrets.APPLE_CERT_DATA }} + APPLE_CERT_PASSWORD: ${{ secrets.APPLE_CERT_PASSWORD }} + APPLE_DEV_ID: ${{ secrets.APPLE_DEV_ID }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }} + APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + run: | + set -euo pipefail + + # Check if signing credentials are configured + if [ -z "$APPLE_CERT_DATA" ] || [ -z "$APPLE_CERT_PASSWORD" ]; then + echo "::warning::Apple signing certificates not configured" + echo "::warning::Skipping code signing. Configure APPLE_CERT_DATA and APPLE_CERT_PASSWORD secrets." + exit 0 + fi + + # Create temporary keychain + KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain" + KEYCHAIN_PASSWORD=$(openssl rand -base64 32) + security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" + security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + + # Import certificate + CERT_PATH="$RUNNER_TEMP/certificate.p12" + echo "$APPLE_CERT_DATA" | base64 --decode > "$CERT_PATH" + security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$APPLE_CERT_PASSWORD" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + security list-keychain -d user -s "$KEYCHAIN_PATH" $(security list-keychain -d user | sed s/\"//g) + + # Sign DMG files + mkdir -p signed + for dmg in ${{ matrix.dmg_glob }}; do + if [ -f "$dmg" ]; then + echo "Signing $dmg..." + codesign --force --sign "$APPLE_DEV_ID" --timestamp --options runtime "$dmg" || { + echo "::warning::Failed to sign $dmg" + cp "$dmg" "signed/$(basename "$dmg")" + continue + } + + # Notarize if credentials available + if [ -n "$APPLE_ID_USERNAME" ] && [ -n "$APPLE_ID_PASSWORD" ]; then + echo "Notarizing $dmg..." + xcrun notarytool submit "$dmg" \ + --apple-id "$APPLE_ID_USERNAME" \ + --password "$APPLE_ID_PASSWORD" \ + --team-id "$APPLE_TEAM_ID" \ + --wait || echo "::warning::Notarization failed for $dmg" + + # Staple the notarization ticket + xcrun stapler staple "$dmg" || echo "::warning::Stapling failed for $dmg" + fi + + cp "$dmg" "signed/$(basename "$dmg")" + fi + done + + # Cleanup + security delete-keychain "$KEYCHAIN_PATH" || true + rm -f "$CERT_PATH" + + echo "Code signing completed" + + - name: Copy Testing tree with suffix + if: always() + working-directory: ${{ env.OPENSTUDIO_BUILD }} + run: | + set -euo pipefail + cp -r Testing "Testing-${{ matrix.test_suffix }}" + + - name: Generate test summary + if: always() + working-directory: ${{ env.OPENSTUDIO_BUILD }} + run: | + set -euo pipefail + + # Generate a simple markdown summary from CTest results + mkdir -p "$(dirname '${{ env.TEST_DASHBOARD_RELATIVE }}')" + + echo "# OpenStudio Test Results - ${{ matrix.test_suffix }}" > "${{ env.TEST_DASHBOARD_RELATIVE }}" + echo "" >> "${{ env.TEST_DASHBOARD_RELATIVE }}" + echo "**Build:** \`${{ github.sha }}\`" >> "${{ env.TEST_DASHBOARD_RELATIVE }}" + echo "**Branch:** \`${{ github.ref_name }}\`" >> "${{ env.TEST_DASHBOARD_RELATIVE }}" + echo "**Platform:** ${{ matrix.display_name }}" >> "${{ env.TEST_DASHBOARD_RELATIVE }}" + echo "**Date:** $(date -u)" >> "${{ env.TEST_DASHBOARD_RELATIVE }}" + echo "" >> "${{ env.TEST_DASHBOARD_RELATIVE }}" + + if [ -f Testing/Temporary/LastTest.log ]; then + echo "## Test Log (Last 50 lines)" >> "${{ env.TEST_DASHBOARD_RELATIVE }}" + echo '```' >> "${{ env.TEST_DASHBOARD_RELATIVE }}" + tail -50 Testing/Temporary/LastTest.log >> "${{ env.TEST_DASHBOARD_RELATIVE }}" + echo '```' >> "${{ env.TEST_DASHBOARD_RELATIVE }}" + fi + continue-on-error: true + + - name: Upload Testing artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: Testing-${{ matrix.platform }}-${{ github.sha }} + path: | + ${{ env.OPENSTUDIO_BUILD }}/Testing-${{ matrix.test_suffix }}/ + ${{ env.OPENSTUDIO_BUILD }}/${{ env.TEST_DASHBOARD_RELATIVE }} + + - name: Upload DMG installer + if: always() + uses: actions/upload-artifact@v4 + with: + name: OS-DMG-${{ matrix.platform }}-${{ github.sha }} + path: ${{ env.OPENSTUDIO_BUILD }}/*.dmg + if-no-files-found: ignore + + - name: Upload TGZ package + if: always() + uses: actions/upload-artifact@v4 + with: + name: OS-TGZ-${{ matrix.platform }}-${{ github.sha }} + path: ${{ env.OPENSTUDIO_BUILD }}/*.tar.gz + if-no-files-found: ignore + + - name: Configure AWS credentials + if: ${{ github.ref == 'refs/heads/develop' || inputs.publish_to_s3 == 'true' || github.event.inputs.publish_to_s3 == 'true' }} + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.AWS_REGION || 'us-west-2' }} + + - name: Publish installers to S3 + if: ${{ github.ref == 'refs/heads/develop' || inputs.publish_to_s3 == 'true' || github.event.inputs.publish_to_s3 == 'true' }} + working-directory: ${{ env.OPENSTUDIO_BUILD }} + env: + S3_PREFIX: ${{ github.ref_type == 'tag' && format('releases/{0}/signed', github.ref_name) || format('{0}/signed', github.ref_name) }} + run: | + set -euo pipefail + echo "Uploading artifacts to s3://${AWS_S3_BUCKET}/${S3_PREFIX}" + + # Upload signed installers if they exist + if [ -d "signed" ]; then + for file in signed/*.dmg; do + [ -e "$file" ] || continue + filename=$(basename "$file") + key="${S3_PREFIX}/${filename}" + aws s3 cp "$file" "s3://${AWS_S3_BUCKET}/${key}" --acl public-read + if command -v md5sum >/dev/null 2>&1; then md5sum "$file"; else md5 "$file"; fi + done + else + echo "::warning::No signed directory found, uploading unsigned installers" + for file in *.dmg; do + [ -e "$file" ] || continue + filename=$(basename "$file") + key="${S3_PREFIX}/${filename}" + aws s3 cp "$file" "s3://${AWS_S3_BUCKET}/${key}" --acl public-read + if command -v md5sum >/dev/null 2>&1; then md5sum "$file"; else md5 "$file"; fi + done + fi + + # Upload tarballs + for file in *.tar.gz; do + [ -e "$file" ] || continue + filename=$(basename "$file") + key="${S3_PREFIX}/${filename}" + aws s3 cp "$file" "s3://${AWS_S3_BUCKET}/${key}" --acl public-read + if command -v md5sum >/dev/null 2>&1; then md5sum "$file"; else md5 "$file"; fi + done + + - name: Fail job on test failures + if: ${{ steps.mac_ctest.outputs.exit_code != '0' }} + run: | + echo "::error::CTest suite failed with exit code ${{ steps.mac_ctest.outputs.exit_code }}" + exit 1 + windows-build: + name: Build ${{ matrix.display_name }} + runs-on: ${{ matrix.runner }} + if: inputs.job_filter == '' || contains(inputs.job_filter, 'Windows') + strategy: + fail-fast: false + matrix: + include: + - platform: windows-2022-x64 + display_name: Windows 2022 x64 + runner: windows-2022 + test_suffix: Windows-2022 + max_jobs: 3 + exclude_regex: "^(RubyTest-Date_Test-ymd_constructor)$" + defaults: + run: + shell: pwsh + env: + MAX_BUILD_THREADS: ${{ matrix.max_jobs }} + CTEST_PARALLEL_LEVEL: ${{ matrix.max_jobs }} + RUBYOPT: "-Eutf-8:utf-8" + PYTHONUTF8: "1" + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Restore sccache cache + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}\.sccache + key: sccache-${{ runner.os }}-${{ matrix.platform }}-${{ hashFiles('vcpkg.json') }} + restore-keys: | + sccache-${{ runner.os }}-${{ matrix.platform }}- + + - name: Patch tests for Windows + run: | + # Patch openstudio.py for build tree DLL loading + $os_py = "python/module/openstudio.py" + if (Test-Path $os_py) { + $content = Get-Content $os_py + $new_content = @() + foreach ($line in $content) { + $new_content += $line + if ($line -match "os.add_dll_directory\(bin_dir\)") { + $new_content += " products_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))" + $new_content += " if os.path.isdir(products_dir) and os.path.isfile(os.path.join(products_dir, 'openstudio_utilities.dll')):" + $new_content += " os.add_dll_directory(products_dir)" + } + } + $new_content | Set-Content $os_py + } + + # Fix path normalization in measure manager test (patch both actual and expected states) + $mm_test = "src/cli/test/test_measure_manager.py" + (Get-Content $mm_test) -replace "actual_state\['my_measures_dir'\]", "actual_state['my_measures_dir'].replace('\\', '/')" ` + -replace "expected_internal_state\['my_measures_dir'\]", "expected_internal_state['my_measures_dir'].replace('\\', '/')" ` + -replace "internal_state\(\)\['my_measures_dir'\]", "internal_state()['my_measures_dir'].replace('\\', '/')" | Set-Content $mm_test + + # Fix encoding expectation in CLI encodings test + $enc_test = "src/cli/test/test_encodings.rb" + (Get-Content $enc_test) -replace "assert_equal\(dir_str.encoding, Encoding::Windows_1252\)", "assert(dir_str.encoding == Encoding::Windows_1252 || dir_str.encoding == Encoding::UTF_8, `"Encoding was `#{dir_str.encoding}`")" | Set-Content $enc_test + + # Fix Alfalfa: Quoting the CMake command to handle spaces in "C:/Program Files/..." + (Get-Content src/cli/CMakeLists.txt) -replace '"-DCMD2=\${CMAKE_COMMAND}', '"-DCMD2=\"${CMAKE_COMMAND}\"' | Set-Content src/cli/CMakeLists.txt + + + - name: Prepare workspace + run: | + git config --global --add safe.directory "$env:GITHUB_WORKSPACE" + New-Item -ItemType Directory -Path "${{ env.OPENSTUDIO_BUILD }}" -Force + + # Speed up Windows builds by disabling real-time antivirus monitoring for the workspace + Write-Host "Disabling Windows Defender real-time monitoring for workspace: $env:GITHUB_WORKSPACE" + Set-MpPreference -DisableRealtimeMonitoring $true + Add-MpPreference -ExclusionPath $env:GITHUB_WORKSPACE -ErrorAction SilentlyContinue + + Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\.sccache" -ErrorAction SilentlyContinue + + # Set Power Plan to High Performance for better process spawn speed + powercfg /setactive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c + + - name: Cache External Dependencies + uses: actions/cache@v4 + with: + path: | + ${{ env.OPENSTUDIO_BUILD }}/EnergyPlus*.tar.gz + ${{ env.OPENSTUDIO_BUILD }}/EnergyPlus*.zip + ${{ env.OPENSTUDIO_BUILD }}/radiance*.tar.gz + ${{ env.OPENSTUDIO_BUILD }}/radiance*.zip + ${{ env.OPENSTUDIO_BUILD }}/openstudio*gems*.tar.gz + key: external-deps-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} + restore-keys: | + external-deps-${{ runner.os }}- + save-always: true + + - name: Restore Generated Embedded Files + uses: actions/cache@v4 + with: + path: | + ${{ env.OPENSTUDIO_BUILD }}/src/*/embedded_files + ${{ env.OPENSTUDIO_BUILD }}/ruby/engine/embedded_files + key: embedded-files-${{ runner.os }}-${{ hashFiles('resources/**', 'ruby/engine/**', 'src/airflow/**', 'src/energyplus/**', 'src/gbxml/**', 'src/isomodel/**', 'src/model/**', 'src/radiance/**', 'src/sdd/**', 'src/utilities/**') }} + restore-keys: | + embedded-files-${{ runner.os }}- + save-always: true + + - name: Setup sccache + uses: Mozilla-Actions/sccache-action@v0.0.5 + + + + - name: Set up Python 3.12.2 + uses: actions/setup-python@v6 + with: + python-version: '3.12.2' + cache: 'pip' + + - name: Install Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2.2' + bundler-cache: true + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + python -m pip install -r python/requirements.txt + + - name: Setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgJsonGlob: 'vcpkg.json' + + - name: Locate Ruby + run: | + $rubyPath = (Get-Command ruby).Source + "SYSTEM_RUBY_PATH=$rubyPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Configure with CMake + working-directory: ${{ env.OPENSTUDIO_BUILD }} + run: | + $sccacheExe = (Get-Command sccache).Source + & $env:ComSpec /c "cmake -G Ninja -DCMAKE_C_COMPILER_LAUNCHER=`"$sccacheExe`" -DCMAKE_CXX_COMPILER_LAUNCHER=`"$sccacheExe`" -DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE:STRING=${{ env.BUILD_TYPE }} -DBUILD_TESTING:BOOL=ON -DCPACK_GENERATORS:STRING=`"NSIS;TGZ`" -DBUILD_PYTHON_BINDINGS:BOOL=ON -DDISCOVER_TESTS_AFTER_BUILD:BOOL=ON -DBUILD_PYTHON_PIP_PACKAGE:BOOL=OFF -DPYTHON_VERSION:STRING=${{ env.PY_VERSION }} -DSYSTEM_RUBY_EXECUTABLE=`"%SYSTEM_RUBY_PATH%`" `"${{ github.workspace }}`"" + + - name: Build with Ninja + working-directory: ${{ env.OPENSTUDIO_BUILD }} + shell: pwsh + run: | + if (Get-Command sccache -ErrorAction SilentlyContinue) { sccache -s } + # Use $env:ComSpec to ensure we call the Windows Command Prompt, not the MSYS2 cmd found in PATH + & $env:ComSpec /c "cmake --build . --parallel ${{ matrix.max_jobs }} -- -d stats 2>&1" | Tee-Object -FilePath "build.log" + + # Check the exit code of the cmd process, not Tee-Object + if ($LASTEXITCODE -ne 0) { + Write-Error "Build failed with exit code $LASTEXITCODE" + exit $LASTEXITCODE + } + if (Get-Command sccache -ErrorAction SilentlyContinue) { sccache -s } + + - name: Run CTest suite + working-directory: ${{ env.OPENSTUDIO_BUILD }} + shell: pwsh + run: | + $env_vars = & $env:ComSpec /c "set" + foreach ($line in $env_vars) { + if ($line -match '^(.*?)=(.*)$') { + $name = $matches[1] + $value = $matches[2] + if ($name -ne "" -and $name -notmatch "^=") { + [Environment]::SetEnvironmentVariable($name, $value, "Process") + } + } + } + + # Add build Products directory to Path so Python can find _openstudioairflow.pyd and its dependencies + $products_dir = Join-Path (Get-Location) "Products" + $env:Path = "$products_dir;" + $env:Path + + # Conflicting tests that must run sequentially + $resource_locked_tests = "ModelFixture.ScheduleFile|ModelFixture.ScheduleFileAltCtor|ModelFixture.PythonPluginInstance|ModelFixture.PythonPluginInstance_NotPYFile|ModelFixture.PythonPluginInstance_ClassNameValidation|ModelFixture.ChillerElectricASHRAE205_GettersSetters|ModelFixture.ChillerElectricASHRAE205_Loops|ModelFixture.ChillerElectricASHRAE205_NotCBORFile|ModelFixture.ChillerElectricASHRAE205_Clone" + + $overall_exit_code = 0 + Write-Host "Running sequential tests..." + ctest -C ${{ env.BUILD_TYPE }} -R "^($resource_locked_tests)$" -j 1 + if ($LASTEXITCODE -ne 0) { $overall_exit_code = 1 } + + Write-Host "Running all other tests in parallel..." + $exclude_regex = "${{ matrix.exclude_regex }}" + if ([string]::IsNullOrEmpty($exclude_regex) -or $exclude_regex -eq '""') { + $final_exclude = "^($resource_locked_tests)$" + } else { + $final_exclude = "($exclude_regex|$resource_locked_tests)" + } + + ctest -C ${{ env.BUILD_TYPE }} -E "$final_exclude" -j ${{ matrix.max_jobs }} --output-on-failure + if ($LASTEXITCODE -ne 0) { exit 1 } + if ($overall_exit_code -eq 1) { exit 1 } + + - name: Wait for network stability + if: always() + run: Start-Sleep -Seconds 5 + + - name: Upload build diagnostics + if: always() + uses: actions/upload-artifact@v4 + with: + name: build-diag-${{ matrix.platform }}-${{ github.sha }} + path: | + ${{ env.OPENSTUDIO_BUILD }}/build.log + ${{ env.OPENSTUDIO_BUILD }}/.ninja_log + ${{ env.OPENSTUDIO_BUILD }}/CTestTestfile.cmake + if-no-files-found: warn + + # CODE SIGNING - AWS Signing Service + - name: Setup Node.js + if: github.ref == 'refs/heads/develop' || inputs.publish_to_s3 == 'true' || github.event.inputs.publish_to_s3 == 'true' + uses: actions/setup-node@v4 + with: + node-version: "18" + + - name: Install Signing Client Dependencies + if: github.ref == 'refs/heads/develop' || inputs.publish_to_s3 == 'true' || github.event.inputs.publish_to_s3 == 'true' + run: npm install + working-directory: ./.github/signing-client + + - name: Create .env file for Signing + if: github.ref == 'refs/heads/develop' || inputs.publish_to_s3 == 'true' || github.event.inputs.publish_to_s3 == 'true' + run: | + echo "ACCESS_KEY=${{ secrets.AWS_SIGNING_ACCESS_KEY }}" > .env + echo "SECRET_KEY=${{ secrets.AWS_SIGNING_SECRET_KEY }}" >> .env + working-directory: ${{ env.OPENSTUDIO_BUILD }} + + - name: Code sign installer + if: success() && (github.ref == 'refs/heads/develop' || inputs.publish_to_s3 == 'true' || github.event.inputs.publish_to_s3 == 'true') + working-directory: ${{ env.OPENSTUDIO_BUILD }} + run: | + # Check if signing client exists + $canSign = $true + if (-not (Test-Path "$env:GITHUB_WORKSPACE/.github/signing-client/code-signing.js")) { + Write-Host "::warning::Code signing client not found at .github/signing-client/code-signing.js" + Write-Host "::warning::Skipping code signing. Add signing client files to repository." + $canSign = $false + } + + # Check if AWS signing credentials are configured + if ([string]::IsNullOrEmpty("${{ secrets.AWS_SIGNING_ACCESS_KEY }}")) { + Write-Host "::warning::AWS_SIGNING_ACCESS_KEY secret not configured" + Write-Host "::warning::Skipping code signing. Configure AWS signing secrets." + $canSign = $false + } + + if ($canSign) { + Write-Host "------------------------------------------------------------" + Write-Host "Step 1: Signing Binaries" + Write-Host "------------------------------------------------------------" + + $pathsToSign = @() + if (Test-Path "bin") { $pathsToSign += "bin" } + if (Test-Path "Products") { $pathsToSign += "Products" } + + if ($pathsToSign.Count -gt 0) { + $binZip = "$env:GITHUB_WORKSPACE/${{ env.OPENSTUDIO_BUILD }}/binaries_to_sign.zip" + $signedBinZip = "$env:GITHUB_WORKSPACE/${{ env.OPENSTUDIO_BUILD }}/binaries_to_sign.signed.zip" + + Write-Host "Archiving binaries from: $pathsToSign" + Compress-Archive -Path $pathsToSign -DestinationPath $binZip -Force + + Write-Host "Sending binaries for signing..." + node "$env:GITHUB_WORKSPACE/.github/signing-client/code-signing.js" $binZip -t 4800000 + + if (Test-Path $signedBinZip) { + Write-Host "Extracting and overwriting signed binaries..." + $tempDir = "temp_signed_binaries" + if (Test-Path $tempDir) { Remove-Item $tempDir -Recurse -Force } + Expand-Archive -Path $signedBinZip -DestinationPath $tempDir -Force + + # Copy back to overwrite + Copy-Item -Path "$tempDir\*" -Destination . -Recurse -Force + + # Cleanup + Remove-Item $tempDir -Recurse -Force + Remove-Item $binZip -Force + Remove-Item $signedBinZip -Force + } else { + Write-Host "::error::Signed binaries zip not found!" + exit 1 + } + } else { + Write-Host "::warning::No bin/ or Products/ directories found to sign." + } + } + + Write-Host "------------------------------------------------------------" + Write-Host "Step 2: Creating Installer (CPack)" + Write-Host "------------------------------------------------------------" + # FIXED: Use $env:ComSpec to avoid MSYS2 cmd path conflict + & $env:ComSpec /c "cmake --build . --target package" + if ($LASTEXITCODE -ne 0) { + Write-Error "CPack failed with exit code $LASTEXITCODE" + exit $LASTEXITCODE + } + + if ($canSign) { + Write-Host "------------------------------------------------------------" + Write-Host "Step 3: Signing Installer" + Write-Host "------------------------------------------------------------" + + $installerZip = "$env:GITHUB_WORKSPACE/${{ env.OPENSTUDIO_BUILD }}/installer_to_sign.zip" + $signedInstallerZip = "$env:GITHUB_WORKSPACE/${{ env.OPENSTUDIO_BUILD }}/installer_to_sign.signed.zip" + + $installers = Get-ChildItem -Filter "OpenStudio-*.exe" + if ($installers.Count -gt 0) { + Write-Host "Found installer(s): $($installers.Name)" + Compress-Archive -Path $installers.FullName -DestinationPath $installerZip -Force + + Write-Host "Sending installer for signing..." + node "$env:GITHUB_WORKSPACE/.github/signing-client/code-signing.js" $installerZip -t 4800000 + + if (Test-Path $signedInstallerZip) { + Write-Host "Extracting signed installer..." + if (-not (Test-Path signed)) { New-Item -ItemType Directory -Path signed | Out-Null } + Expand-Archive -Path $signedInstallerZip -DestinationPath signed -Force + + # Cleanup + Remove-Item $installerZip -Force + Remove-Item $signedInstallerZip -Force + } else { + Write-Host "::error::Signed installer zip not found!" + exit 1 + } + } else { + Write-Host "::warning::No OpenStudio installer found to sign." + } + + Write-Host "Code signing completed successfully" + } + + - name: Upload EXE installer + uses: actions/upload-artifact@v4 + with: + name: OS-Installers-${{ matrix.platform }}-EXE-${{ github.sha }} + path: ${{ env.OPENSTUDIO_BUILD }}/*.exe + if-no-files-found: ignore + + - name: Upload TGZ installer + uses: actions/upload-artifact@v4 + with: + name: OS-Installers-${{ matrix.platform }}-TGZ-${{ github.sha }} + path: ${{ env.OPENSTUDIO_BUILD }}/*.tar.gz + if-no-files-found: ignore + + - name: Upload Signed installers + uses: actions/upload-artifact@v4 + with: + name: OS-Installers-${{ matrix.platform }}-SIGNED-${{ github.sha }} + path: ${{ env.OPENSTUDIO_BUILD }}/signed/ + if-no-files-found: ignore + + windows-publish: + name: Publish Windows Artifacts + needs: [windows-build] + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' || inputs.publish_to_s3 == 'true' || github.event.inputs.publish_to_s3 == 'true' + steps: + - name: Download all installers + uses: actions/download-artifact@v4 + with: + pattern: OS-Installers-windows-2022-x64-* + path: installers + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.AWS_REGION || 'us-west-2' }} + + - name: Publish to S3 + working-directory: installers + env: + S3_PREFIX: ${{ github.ref_type == 'tag' && format('releases/{0}/signed', github.ref_name) || format('{0}/signed', github.ref_name) }} + AWS_S3_BUCKET: openstudio-ci-builds + run: | + set -euo pipefail + echo "Uploading artifacts to s3://${AWS_S3_BUCKET}/${S3_PREFIX}" + + # Find installers in artifact subdirectories + SIGNED_EXE=$(find . -name "*.exe" | grep "SIGNED" | head -n 1 || true) + UNSIGNED_EXE=$(find . -name "*.exe" | grep -v "SIGNED" | head -n 1 || true) + + if [ -n "$SIGNED_EXE" ]; then + echo "Uploading signed installer: $SIGNED_EXE" + filename=$(basename "$SIGNED_EXE") + aws s3 cp "$SIGNED_EXE" "s3://${AWS_S3_BUCKET}/${S3_PREFIX}/${filename}" --acl public-read + elif [ -n "$UNSIGNED_EXE" ]; then + echo "Uploading unsigned installer: $UNSIGNED_EXE" + filename=$(basename "$UNSIGNED_EXE") + aws s3 cp "$UNSIGNED_EXE" "s3://${AWS_S3_BUCKET}/${S3_PREFIX}/${filename}" --acl public-read + fi + + # Upload tarballs + for file in $(find . -name "*.tar.gz"); do + filename=$(basename "$file") + aws s3 cp "$file" "s3://${AWS_S3_BUCKET}/${S3_PREFIX}/${filename}" --acl public-read + done diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 00000000000..a330e180c04 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,25 @@ +{ + "name": "openstudio", + "version-string": "3.8.0", + "dependencies": [ + "boost", + "pugixml", + "libxml2", + "libxslt", + "jsoncpp", + "fmt", + "sqlite3", + "cpprestsdk", + "websocketpp", + "geographiclib", + "swig", + "tinygltf", + "cli11", + "antlr4", + "minizip", + "zlib", + "openssl", + "gtest", + "benchmark" + ] +} \ No newline at end of file