Publish with GitHub Actions

These instructions help you publish programmatically via GitHub Actions into a Posit Connect installation hosted in Snowflake Snowpark Container Services (SPCS).

CI/CD with GitHub Actions

GitHub Actions lets you build workflows for continuous integration (CI) and continuous deployment (CD). These instructions use a workflow to publish updates from GitHub to Connect. The workflow drives publishing with rsconnect-python, which interacts with Connect through its API.

Use the Connect Server API to fully automate most actions, such as deploying content, adding users to content, or customizing settings. API endpoints can be called directly or teams can integrate client packages like rsconnect-python into their CI/CD systems.

Code Promotion Example Flow

  1. Develop your app in Posit Workbench running in your Snowflake development account.
  2. Test in development by deploying to Posit Connect running in your Snowflake development account using the publish-inside-snowflake workflow.
  3. Promote to production - by deploying to Posit Connect running in your Snowflake production account. Authenticate across accounts using profiles created in a connections.toml file.

Configure credentials and secrets

You must have write access to your GitHub repository and the ability to configure GitHub Actions secrets.

Connect API Key

Visit your Posit Connect installation and provision an API key. You will later use this key to create the CONNECT_SERVER_API_KEY GitHub secret.

Snowflake credentials

You need a Snowflake service user with:

  • Username: e.g., GITHUB_ACTIONS
  • Private Key: RSA private key for JWT authentication (PEM format)
  • Private Key Passphrase: Password for the private key
  • Account: Your Snowflake account identifier (e.g., <orgname>-<account_name>)
Note

Create a Snowflake service user within each account targeted for publishing.

The below code generates an encrypted private key when configuring key-pair authentication for your account.

Terminal
# Create public/private key files with encryption
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8
openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub
chmod 600 rsa_key.p8
# Copy your public key contents (macOS)
grep -v '\-' rsa_key.pub | pbcopy
# Or manually copy the output (Linux/Windows)
grep -v '\-' rsa_key.pub

Configure GitHub Secrets

GitHub secrets can be configured using the GitHub CLI or via the GitHub user interface.

Secret Name Description Example Value
CONNECT_SERVER External Connect URL where you are publishing to https://abcde-<orgname>-<account_name>.snowflakecomputing.app
CONNECT_SERVER_API_KEY Connect API key abcd1234...
SNOWFLAKE_PRIVATE_KEY Snowflake RSA private key (PEM format) -----BEGIN ENCRYPTED PRIVATE KEY-----\n...
SNOWFLAKE_PRIVATE_KEY_PASSPHRASE Private key passphrase your-passphrase

Create your GitHub setup-snowflake action

Create .github/actions/setup-snowflake/action.yml in your repository to set up account authentication:

name: Setup Snowflake Environment

inputs:
  username:
    description: Snowflake Username
    required: true
  private-key:
    description: Snowflake Private Key
    required: true
  private-key-passphrase:
    description: Snowflake Private Key Passphrase
    required: true
  connection:
    description: Snowflake Connection Name
    default: "default"

runs:
  using: "composite"
  steps:
    - name: Write Snowflake Configuration
      shell: bash
      run: |
        mkdir -p ~/.snowflake
        chmod 700 ~/.snowflake

        echo "${{ inputs.private-key }}" > ~/.snowflake/rsa_key.p8
        chmod 600 ~/.snowflake/rsa_key.p8

        cat << EOF > ~/.snowflake/connections.toml
        [${{ inputs.connection }}]
        account = "<orgname>-<account_name>"
        user = "${{ inputs.username }}"
        authenticator = "SNOWFLAKE_JWT"
        private_key_file = "${HOME}/.snowflake/rsa_key.p8"
        private_key_file_pwd = "${{ inputs.private-key-passphrase }}"
        EOF

        chmod 600 ~/.snowflake/connections.toml

To publish across multiple Snowflake environments, configure your setup-snowflake action above to create multiple connection profiles in the connections.toml file.

Create your GitHub workflow file

Create .github/workflows/publish-connect-app.yml in your repository:

name: Publish Shiny Python App to Connect

on:
    push:
        branches:
        - main

jobs:
  publish:
    name: Publish to Staging Connect
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python and uv
        uses: astral-sh/setup-uv@v8
        with:
          enable-cache: true

      - name: Setup Snowflake
        uses: ./.github/actions/setup-snowflake
        with:
          username: "GITHUB_ACTIONS"
          private-key: "${{ secrets.SNOWFLAKE_PRIVATE_KEY }}"
          private-key-passphrase: "${{ secrets.SNOWFLAKE_PRIVATE_KEY_PASSPHRASE }}"
          connection: "<orgname>-<account_name>"

      - name: Install rsconnect-python with Snowflake support
        run: |
          # Install rsconnect-python with snowflake extras
          uv tool install rsconnect-python --with snowflake-cli-labs

      - name: Generate manifest and publish to target Connect server
        env:
          CONNECT_SERVER: ${{ secrets.CONNECT_SERVER }}
          CONNECT_API_KEY: ${{ secrets.CONNECT_SERVER_API_KEY }}
        run: |
          # Add uv tool bin directory to PATH
          export PATH="$HOME/.local/bin:$PATH"

          # Generate manifest
          rsconnect write-manifest shiny \
            --overwrite \
            --entrypoint app:app \
            .

          # Deploy using rsconnect CLI with Snowflake connection
          rsconnect deploy manifest manifest.json \
            --server "${CONNECT_SERVER}" \
            --api-key "${CONNECT_SERVER_API_KEY}" \
            --snowflake-connection-name "<orgname>-<account_name>"

This workflow runs on any push request to main. The GitHub documentation about workflow triggers explains other types of events you can use.

Back to top