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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 93 additions & 20 deletions .github/workflows/docker-openstudio.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ on:
workflow_dispatch:
inputs:
openstudio_version:
description: 'OpenStudio version (e.g. 3.10.0)'
description: 'OpenStudio version (e.g. 3.11.0)'
required: true
default: '3.11.0'
openstudio_sha:
description: 'OpenStudio git SHA'
description: 'OpenStudio git SHA (first 10 chars are used in GitHub release asset names)'
required: true
default: 'dee62bf9dd'
default: '241b8abb4d'
openstudio_version_ext:
description: 'Version extension (e.g. -rc1). Leave blank for a final release. Default: -rc1'
description: 'Version extension (e.g. -rc1). Leave blank for a final release.'
required: false
default: ''
apptainer_only:
description: 'Skip docker build/test and only build the .sif from an existing Docker Hub image'
required: false
default: 'false'
default: false
type: boolean

# example of how to restrict to one branch and push event
Expand All @@ -39,9 +39,8 @@ concurrency:
env:
USE_TESTING_TIMEOUTS: "true"
OPENSTUDIO_VERSION: 3.11.0
OPENSTUDIO_SHA: dee62bf9dd
OPENSTUDIO_VERSION_EXT: "-rc1"
OPENSTUDIO_DOWNLOAD_URL: "https://openstudio-ci-builds.s3-us-west-2.amazonaws.com/develop/OpenStudio-3.11.0-rc1%2Bdee62bf9dd-Ubuntu-24.04-x86_64.deb"
OPENSTUDIO_SHA: 241b8abb4d
OPENSTUDIO_VERSION_EXT: ""

permissions:
contents: read
Expand All @@ -50,9 +49,11 @@ jobs:
setup:
runs-on: ubuntu-24.04
outputs:
openstudio_version: ${{ steps.resolve.outputs.openstudio_version }}
openstudio_sha: ${{ steps.resolve.outputs.openstudio_sha }}
openstudio_version_ext: ${{ steps.resolve.outputs.openstudio_version_ext }}
openstudio_version: ${{ steps.resolve.outputs.openstudio_version }}
openstudio_sha: ${{ steps.resolve.outputs.openstudio_sha }}
openstudio_version_ext: ${{ steps.resolve.outputs.openstudio_version_ext }}
openstudio_download_url: ${{ steps.resolve.outputs.openstudio_download_url }}
openstudio_download_url_arm64: ${{ steps.resolve.outputs.openstudio_download_url_arm64 }}
steps:
- name: Resolve version
id: resolve
Expand All @@ -72,18 +73,48 @@ jobs:
EXT="${{ inputs.openstudio_version_ext }}"
fi

SHA10="${SHA:0:10}"
if [ -n "${EXT}" ]; then
ESC_VERSION=$(echo "${VERSION}${EXT}" | sed 's/+/%2B/g')
# Pre-releases: only x86_64 is published to the CI S3 bucket.
# arm64 assets are only published for stable GitHub releases, so the
# arm64 URL below will 404 and the docker job's pre-flight check
# fails fast with a clear message.
DOWNLOAD_URL="https://openstudio-ci-builds.s3-us-west-2.amazonaws.com/develop/OpenStudio-${ESC_VERSION}%2B${SHA}-Ubuntu-24.04-x86_64.deb"
DOWNLOAD_URL_ARM64="https://github.com/NatLabRockies/OpenStudio/releases/download/v${VERSION}/OpenStudio-${ESC_VERSION}%2B${SHA10}-Ubuntu-24.04-arm64.deb"
else
# Stable releases: GitHub release assets are the canonical source and
# publish both amd64 and arm64 (Ubuntu 24.04).
DOWNLOAD_URL="https://github.com/NatLabRockies/OpenStudio/releases/download/v${VERSION}/OpenStudio-${VERSION}%2B${SHA10}-Ubuntu-24.04-x86_64.deb"
DOWNLOAD_URL_ARM64="https://github.com/NatLabRockies/OpenStudio/releases/download/v${VERSION}/OpenStudio-${VERSION}%2B${SHA10}-Ubuntu-24.04-arm64.deb"
fi

echo "openstudio_version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "openstudio_sha=${SHA}" >> "$GITHUB_OUTPUT"
echo "openstudio_version_ext=${EXT}" >> "$GITHUB_OUTPUT"
echo "openstudio_download_url=${DOWNLOAD_URL}" >> "$GITHUB_OUTPUT"
echo "openstudio_download_url_arm64=${DOWNLOAD_URL_ARM64}" >> "$GITHUB_OUTPUT"
echo "Resolved: OpenStudio ${VERSION}${EXT} (${SHA})"
echo "Resolved amd64 URL: ${DOWNLOAD_URL}"
echo "Resolved arm64 URL: ${DOWNLOAD_URL_ARM64}"

docker:
needs: setup
runs-on: ubuntu-24.04
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
os: ubuntu-24.04
- arch: arm64
os: ubuntu-24.04-arm
env:
OPENSTUDIO_VERSION: ${{ needs.setup.outputs.openstudio_version }}
OPENSTUDIO_SHA: ${{ needs.setup.outputs.openstudio_sha }}
OPENSTUDIO_VERSION_EXT: ${{ needs.setup.outputs.openstudio_version_ext }}
DEPLOY_ARCH: ${{ matrix.arch }}
OPENSTUDIO_VERSION: ${{ needs.setup.outputs.openstudio_version }}
OPENSTUDIO_SHA: ${{ needs.setup.outputs.openstudio_sha }}
OPENSTUDIO_VERSION_EXT: ${{ needs.setup.outputs.openstudio_version_ext }}
OPENSTUDIO_DOWNLOAD_URL: ${{ matrix.arch == 'arm64' && needs.setup.outputs.openstudio_download_url_arm64 || needs.setup.outputs.openstudio_download_url }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
Expand All @@ -95,17 +126,36 @@ jobs:
shell: bash
run: |
set -euo pipefail
# Fail fast with a clear message if the OpenStudio package URL is missing
# or 404s. Pre-releases only publish x86_64 artifacts, so an arm64 build
# of a pre-release cannot succeed — report that instead of a cryptic curl/gdebi error.
if [ -z "${OPENSTUDIO_DOWNLOAD_URL}" ]; then
echo "ERROR: No OpenStudio package URL resolved for arch=${DEPLOY_ARCH} version=${OPENSTUDIO_VERSION}${OPENSTUDIO_VERSION_EXT}" >&2
echo "Multi-arch builds require assets for BOTH amd64 and arm64. Stable GitHub releases provide both." >&2
exit 1
fi
if ! curl -sfI --max-time 20 "${OPENSTUDIO_DOWNLOAD_URL}" > /dev/null; then
echo "ERROR: OpenStudio package not found for arch=${DEPLOY_ARCH}:" >&2
echo " ${OPENSTUDIO_DOWNLOAD_URL}" >&2
echo "For pre-releases only x86_64 artifacts are published. Use a stable release for multi-arch builds." >&2
exit 1
fi
docker build -t openstudio:latest \
--build-arg OPENSTUDIO_VERSION=$OPENSTUDIO_VERSION \
--build-arg OPENSTUDIO_SHA=$OPENSTUDIO_SHA \
--build-arg OPENSTUDIO_DOWNLOAD_URL=$OPENSTUDIO_DOWNLOAD_URL \
--build-arg OPENSTUDIO_VERSION_EXT=$OPENSTUDIO_VERSION_EXT .
docker run openstudio:latest openstudio openstudio_version
docker run openstudio:latest /usr/local/openstudio-$OPENSTUDIO_VERSION/Radiance/bin/rtrace -version
if [ "$DEPLOY_ARCH" = "amd64" ]; then
docker run openstudio:latest /usr/local/openstudio-$OPENSTUDIO_VERSION/Radiance/bin/rtrace -version
else
echo "Skipping Radiance rtrace test on $DEPLOY_ARCH: packaged binary is not executable on arm64"
fi
docker run -v "$(pwd)":/var/simdata/openstudio openstudio:latest ruby /var/simdata/openstudio/test/test_run.rb
docker run -v "$(pwd)/test":/var/simdata/openstudio openstudio:latest ./test_gemfile.sh

- name: deploy docker
if: ${{ success() && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/custom_branch_name') }}
if: ${{ success() && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/multi-arch-combined') }}
shell: bash
run: |
set -euo pipefail
Expand All @@ -114,10 +164,33 @@ jobs:
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
DOCKER_USER: ${{ secrets.DOCKER_USER }}

docker-manifest:
needs: [setup, docker]
runs-on: ubuntu-24.04
if: ${{ github.event_name != 'pull_request' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/multi-arch-combined') && !(github.event_name == 'workflow_dispatch' && inputs.apptainer_only) }}
env:
OPENSTUDIO_VERSION: ${{ needs.setup.outputs.openstudio_version }}
OPENSTUDIO_SHA: ${{ needs.setup.outputs.openstudio_sha }}
OPENSTUDIO_VERSION_EXT: ${{ needs.setup.outputs.openstudio_version_ext }}
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: merge multi-arch manifests
shell: bash
run: |
set -euo pipefail
./merge_manifests.sh
env:
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
DOCKER_USER: ${{ secrets.DOCKER_USER }}

apptainer:
runs-on: ubuntu-24.04
needs: [setup, docker]
if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/custom_branch_name' }}
needs: [setup, docker, docker-manifest]
if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/multi-arch-combined' }}
env:
OPENSTUDIO_VERSION: ${{ needs.setup.outputs.openstudio_version }}
OPENSTUDIO_SHA: ${{ needs.setup.outputs.openstudio_sha }}
Expand Down Expand Up @@ -145,7 +218,7 @@ jobs:
set -euo pipefail
apptainer build \
OpenStudio-$OPENSTUDIO_VERSION$OPENSTUDIO_VERSION_EXT.$OPENSTUDIO_SHA-Apptainer.sif \
docker://nrel/openstudio:$OPENSTUDIO_VERSION$OPENSTUDIO_VERSION_EXT
docker://nrel/openstudio:$OPENSTUDIO_VERSION$OPENSTUDIO_VERSION_EXT$OPENSTUDIO_SHA

- uses: actions/upload-artifact@v4
with:
Expand Down
15 changes: 13 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ LABEL maintainer="Nicholas Long nicholas.long@nrel.gov"

# Set the version of OpenStudio when building the container. For example `docker build --build-arg
ARG OPENSTUDIO_VERSION=3.11.0
ARG OPENSTUDIO_VERSION_EXT="-rc1"
ARG OPENSTUDIO_SHA="dee62bf9dd"
ARG OPENSTUDIO_VERSION_EXT=""
ARG OPENSTUDIO_SHA="241b8abb4d"
# Automatically populated by BuildKit during multi-arch builds (amd64 / arm64).
# Used to select the correct OpenStudio .deb package when OPENSTUDIO_DOWNLOAD_URL is not provided.
ARG TARGETARCH
# If OPENSTUDIO_DOWNLOAD_URL is not provided, construct a reasonable default using the
# OpenStudio CI S3 pattern. Users can override by passing --build-arg OPENSTUDIO_DOWNLOAD_URL=...
ARG OPENSTUDIO_DOWNLOAD_URL=""
Expand Down Expand Up @@ -33,11 +36,19 @@ RUN apt-get update && apt-get install -y \
sudo \
&& if [ -z "${OPENSTUDIO_DOWNLOAD_URL}" ]; then \
ESC_VERSION=$(echo "${OPENSTUDIO_VERSION}${OPENSTUDIO_VERSION_EXT}" | sed 's/+/%2B/g'); \
if [ "${TARGETARCH}" = "arm64" ]; then \
# S3 does not publish arm64 builds; use the GitHub release asset (named -arm64).
# Mirror the workflow's setup job: GitHub URLs use the 10-char SHA.
SHA10=$(echo "${OPENSTUDIO_SHA}" | cut -c1-10); \
OPENSTUDIO_DOWNLOAD_URL="https://github.com/NatLabRockies/OpenStudio/releases/download/v${OPENSTUDIO_VERSION}/OpenStudio-${ESC_VERSION}%2B${SHA10}-Ubuntu-24.04-arm64.deb"; \
else \
# amd64: BuildKit TARGETARCH is "amd64" but the S3 asset is named -x86_64.
if [ -n "${OPENSTUDIO_SHA}" ]; then \
OPENSTUDIO_DOWNLOAD_URL="https://openstudio-ci-builds.s3.amazonaws.com/develop/OpenStudio-${ESC_VERSION}%2B${OPENSTUDIO_SHA}-Ubuntu-24.04-x86_64.deb"; \
else \
OPENSTUDIO_DOWNLOAD_URL="https://openstudio-ci-builds.s3.amazonaws.com/develop/OpenStudio-${ESC_VERSION}-Ubuntu-24.04-x86_64.deb"; \
fi; \
fi; \
fi \
&& echo "OpenStudio Package Download URL is ${OPENSTUDIO_DOWNLOAD_URL}" \
&& curl -SLO "$OPENSTUDIO_DOWNLOAD_URL" \
Expand Down
90 changes: 36 additions & 54 deletions deploy_docker.sh
Original file line number Diff line number Diff line change
@@ -1,61 +1,43 @@
#!/usr/bin/env bash
IMAGETAG=${OPENSTUDIO_VERSION}${OPENSTUDIO_VERSION_EXT}
echo "default image tag would be $IMAGETAG"
IMAGETAG=skip
DOCKER_REPO=${DOCKER_REPO:-nrel/openstudio}

# Check branch name for correct tagging
if [ "${GITHUB_REF}" == "refs/heads/develop" ]; then
IMAGETAG="develop"
elif [ "${GITHUB_REF}" == "refs/heads/2.9.X-LTS" ]; then
IMAGETAG="2.9.X-LTS"
elif [ "${GITHUB_REF}" == "refs/heads/master" ]; then
# Retrieve the version number from rails
IMAGETAG=${OPENSTUDIO_VERSION}${OPENSTUDIO_VERSION_EXT}
# Uncomment and set branch name for custom builds.
elif [ "${GITHUB_REF}" == "refs/heads/custom_branch_name" ]; then
IMAGETAG="experimental"
elif [ "${DOCKER_MANUAL_IMAGE_TAG}" == "develop" ]; then
IMAGETAG="develop"
# Builds the locally-built image, tags it with an arch suffix, and pushes it.
# The canonical (multi-arch) tags are created later by merge_manifests.sh.
#
# Required env: DOCKER_USER, DOCKER_PASS, OPENSTUDIO_VERSION, OPENSTUDIO_VERSION_EXT
# Optional env: DEPLOY_ARCH (amd64|arm64, default amd64), DOCKER_MANUAL_IMAGE_TAG
set -euo pipefail

source "$(dirname "$0")/get_image_tags.sh"
DEPLOY_ARCH=${DEPLOY_ARCH:-amd64}

# GITHUB_BASE_REF is only set on Pull Request events. Do not build those
if [ "${IMAGETAG}" == "skip" ] || [ -n "${GITHUB_BASE_REF:-}" ]; then
echo "Not on a deployable branch, this is a pull request or has been explicitly skipped"
exit 0
fi

# Check if this is a manual installer GH action
if [ ! -z "${DOCKER_MANUAL_IMAGE_TAG}" ]; then
if [ "${DOCKER_MANUAL_IMAGE_TAG}" == "develop" ]; then
IMAGETAG="develop"
elif [[ "${DOCKER_MANUAL_IMAGE_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then
IMAGETAG="${DOCKER_MANUAL_IMAGE_TAG}"
else
IMAGETAG="dev-${DOCKER_MANUAL_IMAGE_TAG}"
fi
echo "Tagging image as ${IMAGETAG}-${DEPLOY_ARCH} and pushing to ${DOCKER_REPO}"

echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin

# Push the arch-suffixed image. The canonical tag is assembled later by the
# manifest job so that parallel amd64/arm64 pushes never clobber each other.
ARCH_TAG="${DOCKER_REPO}:${IMAGETAG}-${DEPLOY_ARCH}"
docker tag openstudio:latest "${ARCH_TAG}"
docker push "${ARCH_TAG}"

# If on develop branch, also push the develop tag pointing to this image
if [ "${IMAGETAG}" == "develop" ]; then
docker tag openstudio:latest "${DOCKER_REPO}:develop-${DEPLOY_ARCH}"
docker push "${DOCKER_REPO}:develop-${DEPLOY_ARCH}"
fi

# GITHUB_BASE_REF is only set on Pull Request events. Do not build those
if [ "${IMAGETAG}" != "skip" ] && [[ -z "${GITHUB_BASE_REF}" ]]; then
echo "Tagging image as $IMAGETAG and pushing to ${DOCKER_REPO}"

echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin
# Tag versioned image
docker tag openstudio:latest ${DOCKER_REPO}:$IMAGETAG; (( exit_status = exit_status || $? ))

# Only update and push 'latest' if this is a stable release (no extension)
if [ -z "${OPENSTUDIO_VERSION_EXT}" ]; then
echo "Stable release detected. Updating and pushing '${DOCKER_REPO}:latest'"
docker tag openstudio:latest ${DOCKER_REPO}:latest; (( exit_status = exit_status || $? ))
docker push ${DOCKER_REPO}:latest; (( exit_status = exit_status || $? ))
else
echo "Pre-release detected (extension: '${OPENSTUDIO_VERSION_EXT}'). Skipping 'latest' tag update."
fi

# Push versioned tag
docker push ${DOCKER_REPO}:$IMAGETAG; (( exit_status = exit_status || $? ))
# If on develop branch, also push the develop tag pointing to this image
if [ "${IMAGETAG}" == "develop" ] || [ "${GITHUB_REF}" == "refs/heads/develop" ]; then
docker tag openstudio:latest ${DOCKER_REPO}:develop; (( exit_status = exit_status || $? ))
docker push ${DOCKER_REPO}:develop; (( exit_status = exit_status || $? ))
fi

exit $exit_status
# Only update and push 'latest' if this is a stable release (no extension)
if [ -z "${OPENSTUDIO_VERSION_EXT}" ]; then
echo "Stable release detected. Updating and pushing '${DOCKER_REPO}:latest-${DEPLOY_ARCH}'"
docker tag openstudio:latest "${DOCKER_REPO}:latest-${DEPLOY_ARCH}"
docker push "${DOCKER_REPO}:latest-${DEPLOY_ARCH}"
else
echo "Not on a deployable branch, this is a pull request or has been explicity skipped"
echo "Pre-release detected (extension: '${OPENSTUDIO_VERSION_EXT}'). Skipping 'latest' tag update."
fi

echo "Done pushing ${DEPLOY_ARCH} artifacts for ${IMAGETAG}"
36 changes: 36 additions & 0 deletions get_image_tags.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
# Determines the image tag to use based on the branch and manual-tag inputs.
# This file is sourced by deploy_docker.sh and merge_manifests.sh; it sets
# IMAGETAG (or "skip" when nothing should be deployed/merged) and DOCKER_REPO.
#
# Usage: source "$(dirname "$0")/get_image_tags.sh"
set -euo pipefail

IMAGETAG=skip
DOCKER_REPO=${DOCKER_REPO:-nrel/openstudio}

# Check branch name for correct tagging
if [ "${GITHUB_REF}" == "refs/heads/develop" ]; then
IMAGETAG="develop"
elif [ "${GITHUB_REF}" == "refs/heads/2.9.X-LTS" ]; then
IMAGETAG="2.9.X-LTS"
elif [ "${GITHUB_REF}" == "refs/heads/master" ]; then
# Retrieve the version number from the workflow env
IMAGETAG=${OPENSTUDIO_VERSION}${OPENSTUDIO_VERSION_EXT}
# Uncomment and set branch name for custom builds.
elif [ "${GITHUB_REF}" == "refs/heads/custom_branch_name" ]; then
IMAGETAG="experimental"
elif [ "${DOCKER_MANUAL_IMAGE_TAG:-}" == "develop" ]; then
IMAGETAG="develop"
fi

# Check if this is a manual installer GH action
if [ -n "${DOCKER_MANUAL_IMAGE_TAG:-}" ]; then
if [ "${DOCKER_MANUAL_IMAGE_TAG}" == "develop" ]; then
IMAGETAG="develop"
elif [[ "${DOCKER_MANUAL_IMAGE_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then
IMAGETAG="${DOCKER_MANUAL_IMAGE_TAG}"
else
IMAGETAG="dev-${DOCKER_MANUAL_IMAGE_TAG}"
fi
fi
Loading
Loading