Skip to content
Merged
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
76 changes: 58 additions & 18 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,26 @@ permissions:
pull-requests: write

jobs:
test:
test-python-versions:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
include:
- os: macos-latest
python-version: '3.9'
python-version: '3.13'
- os: windows-latest
python-version: '3.9'
python-version: '3.13'
runs-on: ${{ matrix.os }}
name: test (py${{ matrix.python-version }} ${{ matrix.os }})
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-python@v4
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- run: pip install '.[test]'
Expand All @@ -52,12 +52,12 @@ jobs:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-python@v4
- uses: actions/setup-python@v6
with:
python-version: 3.8.x
- run: pip install --pre '.[test]'
Expand All @@ -67,18 +67,18 @@ jobs:
- run: make test-3.8

distributions:
needs: test
needs: test-python-versions
strategy:
matrix:
package_name: ["rsconnect_python", "rsconnect"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-python@v4
- uses: actions/setup-python@v6
with:
python-version: 3.8.x
- name: Install uv # see scripts/temporary-rename
Expand All @@ -103,10 +103,10 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1

docs:
needs: test
needs: test-python-versions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
env:
Expand All @@ -117,7 +117,7 @@ jobs:
python-version: 3.12
- name: build docs
run: make docs
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v5
with:
name: docs
path: site/
Expand All @@ -136,12 +136,52 @@ jobs:
- if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
run: make promote-docs-in-s3

test-rsconnect:
name: "Integration tests against latest Connect"
test-connect-versions:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
version:
- "release" # special value that always points to the latest Connect release
- "2025.09.0" # jammy
Comment on lines +145 to +146
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing 2025.10.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentionally so. I only have it testing every 6 months, same as on connectapi. I don't think it's worth testing every single release in there.

- "2025.03.0" # jammy
- "2024.09.0" # jammy
- "2024.03.0" # jammy
- "2023.09.0" # jammy
- "2023.03.0" # bionic
- "2022.10.0" # bionic
name: Integration tests against Connect ${{ matrix.version }}
env:
python-version: 3.13
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe worth checking the last 2 or 3 versions of python here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a separate matrix that tests across python versions. I'm not sure we need python versions x Connect versions here, these are just testing that we haven't done anything that breaks the Connect API contract.

steps:
- uses: actions/checkout@v6

- uses: actions/setup-python@v6
with:
python-version: ${{ env.python-version }}

- name: Install dependencies
run: pip install '.[test]'

- run: pip freeze

- run: rsconnect version

- name: Run integration tests
uses: posit-dev/with-connect@main
with:
version: ${{ matrix.version }}
# License file valid until 2026-12-05
license: ${{ secrets.CONNECT_LICENSE_FILE }}
command: |
make test-${{ env.python-version }}

test-dev-connect:
name: "Integration tests against dev Connect"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.12.4
- name: Install dependencies
Expand Down
4 changes: 2 additions & 2 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class TestAPI(TestCase):
def test_executor_init(self):
connect_server = require_connect()
api_key = require_api_key()
ce = RSConnectExecutor(None, None, connect_server, api_key, True, None)
ce = RSConnectExecutor(url=connect_server, api_key=api_key, insecure=True)
self.assertEqual(ce.remote_server.url, connect_server)

def test_output_task_log(self):
Expand All @@ -52,7 +52,7 @@ def test_output_task_log(self):
def test_make_deployment_name(self):
connect_server = require_connect()
api_key = require_api_key()
ce = RSConnectExecutor(None, None, connect_server, api_key, True, None)
ce = RSConnectExecutor(url=connect_server, api_key=api_key, insecure=True)
self.assertEqual(ce.make_deployment_name("title", False), "title")
self.assertEqual(ce.make_deployment_name("Title", False), "title")
self.assertEqual(ce.make_deployment_name("My Title", False), "my_title")
Expand Down
5 changes: 5 additions & 0 deletions tests/test_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ def test_make_notebook_source_bundle1(self):
"package_file": "requirements.txt",
},
},
"environment": {
"python": {
"requires": ">=3.8",
},
},
"files": {
"dummy.ipynb": {
"checksum": ipynb_hash,
Expand Down
5 changes: 3 additions & 2 deletions tests/test_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def test_file(self):
source="file",
),
python_interpreter=sys.executable,
python_version_requirement=">=3.8",
)
self.assertEqual(expected, result)

Expand Down Expand Up @@ -276,12 +277,12 @@ def fake_inspect_environment(
class TestEnvironmentDeprecations:
def test_override_python_version(self):
with mock.patch.object(rsconnect.environment.logger, "warning") as mock_warning:
result = Environment.create_python_environment(get_dir("pip1"), override_python_version=None)
result = Environment.create_python_environment(get_dir("pip1-no-version"), override_python_version=None)
assert mock_warning.call_count == 0
assert result.python_version_requirement is None

with mock.patch.object(rsconnect.environment.logger, "warning") as mock_warning:
result = Environment.create_python_environment(get_dir("pip1"), override_python_version="3.8")
result = Environment.create_python_environment(get_dir("pip1-no-version"), override_python_version="3.8")
assert mock_warning.call_count == 1
mock_warning.assert_called_once_with(
"The --override-python-version option is deprecated, "
Expand Down
11 changes: 9 additions & 2 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
optional_target,
require_api_key,
require_connect,
require_connect_version,
)


Expand Down Expand Up @@ -88,6 +89,7 @@ def test_ping_api_key(self):
assert "OK" in result.output

def test_deploy(self):
require_connect_version("2025.03.0")
target = optional_target(get_dir(join("pip1", "dummy.ipynb")))
runner = CliRunner()
args = self.create_deploy_args("notebook", target)
Expand Down Expand Up @@ -290,6 +292,7 @@ def post_application_deploy_callback(request, uri, response_headers):
os.environ["CONNECT_SERVER"] = original_server_value

# noinspection SpellCheckingInspection
@pytest.mark.skip(reason="Skipping R manifest test (requires R 3.5, docker containers have moved on).")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this test just be removed?

Copy link
Contributor Author

@nealrichardson nealrichardson Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed or deleted, yeah. I made #741 to look into that.

def test_deploy_manifest(self):
target = optional_target(get_manifest_path("shinyapp"))
runner = CliRunner()
Expand All @@ -299,7 +302,8 @@ def test_deploy_manifest(self):

# noinspection SpellCheckingInspection
@httpretty.activate(verbose=True, allow_net_connect=False)
def test_deploy_manifest_shinyapps(self):
@mock.patch("rsconnect.api.webbrowser.open_new")
def test_deploy_manifest_shinyapps(self, mock_open_browser):
original_api_key_value = os.environ.pop("CONNECT_API_KEY", None)
original_server_value = os.environ.pop("CONNECT_SERVER", None)

Expand Down Expand Up @@ -474,7 +478,8 @@ def post_deploy_callback(request, uri, response_headers):
os.environ["CONNECT_SERVER"] = original_server_value

@httpretty.activate(verbose=True, allow_net_connect=False)
def test_redeploy_manifest_shinyapps(self):
@mock.patch("rsconnect.api.webbrowser.open_new")
def test_redeploy_manifest_shinyapps(self, mock_open_browser):
original_api_key_value = os.environ.pop("CONNECT_API_KEY", None)
original_server_value = os.environ.pop("CONNECT_SERVER", None)

Expand Down Expand Up @@ -641,6 +646,7 @@ def post_deploy_callback(request, uri, response_headers):
os.environ["CONNECT_SERVER"] = original_server_value

def test_deploy_api(self):
require_connect_version("2025.03.0")
target = optional_target(get_api_path("flask"))
runner = CliRunner()
args = self.create_deploy_args("api", target)
Expand All @@ -656,6 +662,7 @@ def test_deploy_api_fail_verify(self):
assert result.exit_code == 1, result.output

def test_deploy_api_fail_no_verify(self):
require_connect_version("2025.03.0")
target = optional_target(get_api_path("flask-bad"))
runner = CliRunner()
args = self.create_deploy_args("api", target)
Expand Down
1 change: 1 addition & 0 deletions tests/testdata/api/flask-bad/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
>=3.8
1 change: 1 addition & 0 deletions tests/testdata/api/flask/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
>=3.8
52 changes: 52 additions & 0 deletions tests/testdata/py3/pip1-no-version/dummy.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'this is a notebook'"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\"this is a notebook\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.6"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
3 changes: 3 additions & 0 deletions tests/testdata/py3/pip1-no-version/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
numpy
pandas
matplotlib
1 change: 1 addition & 0 deletions tests/testdata/py3/pip1/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
>=3.8
25 changes: 25 additions & 0 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import jwt
import re
from os.path import join, dirname, exists
from packaging import version

import pytest
from rsconnect.api import RSConnectServer, RSConnectClient


def apply_common_args(args: list, server=None, key=None, cacert=None, insecure=False):
Expand Down Expand Up @@ -42,6 +44,29 @@ def require_api_key():
return connect_api_key


def require_connect_version(min_version: str):
"""
Skip test if Connect server version is less than min_version.

Args:
min_version: Minimum required version (e.g., "2025.03.0")
"""
connect_server = require_connect()
api_key = require_api_key()

server = RSConnectServer(connect_server, api_key)
client = RSConnectClient(server)

try:
settings = client.server_settings()
server_version = settings["version"]

if version.parse(server_version) < version.parse(min_version):
pytest.skip(f"Connect server {server_version} < {min_version}")
except Exception as e:
pytest.skip(f"Could not determine Connect server version: {e}")


def get_dir(name):
py_version = "py%d" % sys.version_info[0]
# noinspection SpellCheckingInspection
Expand Down
Loading