Skip to content

Product Release

Product Release #33

name: Product Release
on:
workflow_dispatch:
inputs:
product:
description: "Product name"
required: true
type: choice
options:
- connect
- workbench
- package-manager
version:
description: "Product version to set as appVersion (e.g. 2026.03.0)"
required: true
type: string
python_version:
description: "Python version baked into the server image (connect only, e.g. 3.14.6)"
required: false
type: string
default: ""
defaults:
run:
shell: bash
jobs:
update:
name: Update ${{ inputs.product }} to ${{ inputs.version }}
runs-on: ubuntu-latest
steps:
- name: Map product to chart
id: chart
env:
PRODUCT: ${{ inputs.product }}
run: |
case "$PRODUCT" in
connect)
echo "name=rstudio-connect" >> "$GITHUB_OUTPUT"
echo "image=posit/connect" >> "$GITHUB_OUTPUT"
echo "init-image=posit/connect-content-init" >> "$GITHUB_OUTPUT"
echo "os-tag=ubuntu-24.04" >> "$GITHUB_OUTPUT"
;;
workbench)
echo "name=rstudio-workbench" >> "$GITHUB_OUTPUT"
echo "image=posit/workbench" >> "$GITHUB_OUTPUT"
echo "session-image=posit/workbench-session-init" >> "$GITHUB_OUTPUT"
echo "os-tag=ubuntu-24.04" >> "$GITHUB_OUTPUT"
;;
package-manager)
echo "name=rstudio-pm" >> "$GITHUB_OUTPUT"
echo "image=posit/package-manager" >> "$GITHUB_OUTPUT"
echo "os-tag=ubuntu-24.04" >> "$GITHUB_OUTPUT"
;;
*)
echo "::error::Unknown product: $PRODUCT"
exit 1
;;
esac
- name: Generate GitHub App Token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ secrets.POSIT_PLATFORM_CLIENT_ID }}
private-key: ${{ secrets.POSIT_PLATFORM_PEM }}
permission-contents: write
permission-pull-requests: write
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
token: ${{ steps.app-token.outputs.token }}
- name: Validate and normalize app version
id: app-version
env:
APP_VERSION: ${{ inputs.version }}
run: |
# Strip pre-release/build metadata
APP_VERSION="${APP_VERSION%%[+-]*}"
if [[ ! "$APP_VERSION" =~ ^[0-9]{4}\.[0-9]{2}\.[0-9]+$ ]]; then
echo "::error::Invalid version format: $APP_VERSION (expected YYYY.MM.PATCH)"
exit 1
fi
echo "value=$APP_VERSION" >> "$GITHUB_OUTPUT"
- name: Check current appVersion
id: current
env:
CHART_NAME: ${{ steps.chart.outputs.name }}
run: |
CURRENT=$(yq '.appVersion' "charts/${CHART_NAME}/Chart.yaml")
echo "app-version=$CURRENT" >> "$GITHUB_OUTPUT"
- name: Skip (already up to date)
if: steps.current.outputs.app-version == steps.app-version.outputs.value
env:
APP_VERSION: ${{ steps.app-version.outputs.value }}
run: echo "::notice::appVersion is already ${APP_VERSION}, nothing to do"
- name: Update Chart.yaml
if: steps.current.outputs.app-version != steps.app-version.outputs.value
id: update
env:
APP_VERSION: ${{ steps.app-version.outputs.value }}
CHART_NAME: ${{ steps.chart.outputs.name }}
IMAGE: ${{ steps.chart.outputs.image }}
INIT_IMAGE: ${{ steps.chart.outputs.init-image }}
SESSION_IMAGE: ${{ steps.chart.outputs.session-image }}
OS_TAG: ${{ steps.chart.outputs.os-tag }}
run: |
CHART="charts/${CHART_NAME}/Chart.yaml"
# Bump appVersion
yq -i ".appVersion = \"$APP_VERSION\"" "$CHART"
# Bump chart patch version
CURRENT_VERSION=$(yq '.version' "$CHART")
MAJOR=$(echo "$CURRENT_VERSION" | cut -d. -f1)
MINOR=$(echo "$CURRENT_VERSION" | cut -d. -f2)
PATCH=$(echo "$CURRENT_VERSION" | cut -d. -f3)
NEW_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))"
yq -i ".version = \"$NEW_VERSION\"" "$CHART"
# Update main image annotation ({version}-{os-tag} format)
yq -i "(.annotations.\"artifacthub.io/images\" | select(. != null)) |= sub(\"${IMAGE}:[^\n]*\", \"${IMAGE}:${APP_VERSION}-${OS_TAG}\")" "$CHART"
# connect-content-init is OS-agnostic; tag is just the version
if [ -n "$INIT_IMAGE" ]; then
yq -i "(.annotations.\"artifacthub.io/images\" | select(. != null)) |= sub(\"${INIT_IMAGE}:[^\n]*\", \"${INIT_IMAGE}:${APP_VERSION}\")" "$CHART"
fi
# Workbench session-init uses the same OS-suffixed format as the main image
if [ -n "$SESSION_IMAGE" ]; then
yq -i "(.annotations.\"artifacthub.io/images\" | select(. != null)) |= sub(\"${SESSION_IMAGE}:[^\n]*\", \"${SESSION_IMAGE}:${APP_VERSION}-${OS_TAG}\")" "$CHART"
fi
echo "chart-version=$NEW_VERSION" >> "$GITHUB_OUTPUT"
- name: Sync Connect interpreter paths
if: >-
steps.current.outputs.app-version != steps.app-version.outputs.value &&
inputs.product == 'connect' &&
inputs.python_version != ''
env:
CHART_NAME: ${{ steps.chart.outputs.name }}
PYTHON_VERSION: ${{ inputs.python_version }}
run: |
yq -i \
".config.Python.Executable = [\"/opt/python/${PYTHON_VERSION}/bin/python\"]" \
"charts/${CHART_NAME}/values.yaml"
- name: Add NEWS.md entry
if: steps.current.outputs.app-version != steps.app-version.outputs.value
env:
APP_VERSION: ${{ steps.app-version.outputs.value }}
PRODUCT: ${{ inputs.product }}
CHART_NAME: ${{ steps.chart.outputs.name }}
CHART_VERSION: ${{ steps.update.outputs.chart-version }}
run: |
NEWS="charts/${CHART_NAME}/NEWS.md"
# Capitalize product name for changelog
DISPLAY_NAME=$(echo "$PRODUCT" | sed 's/connect/Connect/;s/workbench/Workbench/;s/package-manager/Package Manager/')
# Build the new entry and prepend it after the heading line
{
head -1 "$NEWS"
echo ""
echo "## ${CHART_VERSION}"
echo ""
echo "- Bump ${DISPLAY_NAME} version to ${APP_VERSION}"
echo ""
tail -n +3 "$NEWS"
} > "${NEWS}.tmp" && mv "${NEWS}.tmp" "$NEWS"
- name: Install Just
if: steps.current.outputs.app-version != steps.app-version.outputs.value
uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
- name: Generate docs
if: steps.current.outputs.app-version != steps.app-version.outputs.value
run: |
just setup
just docs
- name: Create Pull Request
if: steps.current.outputs.app-version != steps.app-version.outputs.value
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ steps.app-token.outputs.token }}
branch: update-${{ inputs.product }}-${{ steps.app-version.outputs.value }}
delete-branch: true
title: "Update ${{ inputs.product }} to ${{ steps.app-version.outputs.value }}"
body: |
Automated update from `posit-dev/images-${{ inputs.product }}` production build.
- Chart: `${{ steps.chart.outputs.name }}`
- appVersion: `${{ steps.current.outputs.app-version }}` → `${{ steps.app-version.outputs.value }}`
${{ inputs.python_version != '' && format('- Python: `{0}`', inputs.python_version) || '' }}
commit-message: "Update ${{ inputs.product }} appVersion to ${{ steps.app-version.outputs.value }}"
base: main