Skip to content

Commit b0613aa

Browse files
committed
Merge branch 'master' into vetiver-testing
2 parents 0f0b084 + d4d328f commit b0613aa

21 files changed

+366
-279
lines changed

.github/workflows/main.yml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ on:
55
tags: ['*']
66
pull_request:
77
branches: [master]
8+
workflow_dispatch:
9+
810
permissions:
911
id-token: write
1012
contents: write
@@ -14,7 +16,7 @@ jobs:
1416
matrix:
1517
# TODO: work on windows-latest compatibility?
1618
os: [ubuntu-latest]
17-
python-version: ['3.5', '3.6', '3.7', '3.8', '3.9', '3.10']
19+
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
1820
include:
1921
- os: macos-latest
2022
python-version: '3.9'
@@ -28,10 +30,8 @@ jobs:
2830
python-version: ${{ matrix.python-version }}
2931
- run: pip install -r requirements.txt
3032
- run: pip freeze
31-
- if: matrix.python-version > '3.5'
32-
run: make fmt
33-
- if: matrix.python-version >= '3.6'
34-
run: make lint
33+
- run: make fmt
34+
- run: make lint
3535
- run: python setup.py --version
3636
- run: make test-${{ matrix.python-version }}
3737
prerelease-test:
@@ -89,10 +89,10 @@ jobs:
8989
asset_name: ${{ steps.create_dist.outputs.whl_basename }}
9090
asset_content_type: application/x-wheel+zip
9191
- uses: aws-actions/configure-aws-credentials@v1
92+
id: creds
9293
with:
93-
aws-access-key-id: ${{ secrets.AWS_ID }}
94-
aws-secret-access-key: ${{ secrets.AWS_SECRET }}
95-
aws-region: us-east-1
94+
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
95+
aws-region: ${{ secrets.AWS_REGION }}
9696
- if: github.event_name == 'push' && github.ref == 'refs/heads/master'
9797
run: make sync-latest-to-s3
9898
- if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
@@ -119,10 +119,10 @@ jobs:
119119
name: docs
120120
path: docs/site/
121121
- uses: aws-actions/configure-aws-credentials@v1
122+
id: creds
122123
with:
123-
aws-access-key-id: ${{ secrets.AWS_ID }}
124-
aws-secret-access-key: ${{ secrets.AWS_SECRET }}
125-
aws-region: us-east-1
124+
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
125+
aws-region: ${{ secrets.AWS_REGION }}
126126
- if: github.event_name == 'push' && github.ref == 'refs/heads/master'
127127
run: make sync-latest-docs-to-s3
128128
- if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')

.github/workflows/snyk.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: snyk
2+
on:
3+
schedule:
4+
- cron: "0 10 * * 1" # Monday @ 10am UTC
5+
workflow_dispatch:
6+
7+
env:
8+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
9+
SNYK_ORG: rstudio-connect
10+
SNYK_PROJECT: rsconnect-python
11+
12+
jobs:
13+
python-dependencies:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@master
17+
- name: Run Snyk on dependencies
18+
uses: snyk/actions/python@master
19+
with:
20+
command: monitor
21+
args: --file=setup.py --print-deps --project-name=${{ env.SNYK_PROJECT }} --org=${{ env.SNYK_ORG }}
22+
python-code:
23+
runs-on: ubuntu-latest
24+
steps:
25+
- uses: actions/checkout@master
26+
- name: Run Snyk static analysis
27+
uses: snyk/actions/python@master
28+
with:
29+
command: code test
30+
args: --project-name=${{ env.SNYK_PROJECT }} --org=${{ env.SNYK_ORG }} rsconnect/

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [1.13.0] - 2022-12-02
8+
9+
### Added
10+
- When running rsconnect bootstrap, you can now specify the jwt secret using the CONNECT_BOOTSTRAP_SECRETKEY environment variable.
11+
12+
### Changed
13+
- Update pip_freeze to use `pip freeze` since Connect filters for valid package paths in the backend and it no longer depends on the undocumented behavior of `pip list --format=freeze`. This reverts the change made in 1.5.2.
14+
15+
- Renamed the deploy_html `excludes` flag to `exclude` for consistency with other deploy commands.
16+
717
## [1.12.1] - 2022-11-07
818

919
### Changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ tooling](https://packaging.python.org/guides/tool-recommendations/).
55

66
To get started, you'll want to:
77
- clone the repo into a project directory
8-
- setup a virtual 3.5+ python environment in the project directory
8+
- setup a virtual 3.7+ python environment in the project directory
99
- activate that virtual environment
1010
- install the dependencies
1111
- validate your build environment with some sample commands

Makefile

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ SOURCE_DATE_EPOCH := $(shell date +%s)
3737
export SOURCE_DATE_EPOCH
3838

3939
.PHONY: all-tests
40-
all-tests: all-images test-3.5 test-3.6 test-3.7 test-3.8 test-3.9 test-3.10
40+
all-tests: all-images test-3.7 test-3.8 test-3.9 test-3.10
4141

4242
.PHONY: all-images
43-
all-images: image-3.5 image-3.6 image-3.7 image-3.8 image-3.9 image-3.10
43+
all-images: image-3.7 image-3.8 image-3.9 image-3.10
4444

4545
image-%:
4646
docker build -t rsconnect-python:$* --build-arg BASE_IMAGE=python:$*-slim .
@@ -64,9 +64,6 @@ mock-test-%: clean-stores
6464
fmt-%:
6565
$(RUNNER) 'black .'
6666

67-
.PHONY: fmt-3.5
68-
fmt-3.5: .fmt-unsupported
69-
7067
.PHONY: .fmt-unsupported
7168
.fmt-unsupported:
7269
@echo ERROR: This python version cannot run the fmting tools
@@ -85,9 +82,6 @@ lint-%:
8582
$(RUNNER) 'flake8 tests/'
8683
$(RUNNER) 'mypy -p rsconnect'
8784

88-
.PHONY: lint-3.5
89-
lint-3.5: .lint-unsupported
90-
9185
.PHONY: .lint-unsupported
9286
.lint-unsupported:
9387
@echo ERROR: This python version cannot run the linting tools

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
!!! warning
44

5-
As of version 1.7.0, rsconnect-python requires Python version 3.5 or higher. Please see the
6-
[official announcement](https://www.rstudio.com/blog/rstudio-connect-2021-08-python-updates/)
7-
for details about this decision.
5+
As of version 1.14.0, rsconnect-python requires Python version 3.7 or higher.
86

97
This package provides a CLI (command-line interface) for interacting
108
with and deploying to Posit Connect. This is also used by the

requirements.txt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
black==22.3.0; python_version >= '3.6'
1+
black==22.3.0
22
click>=7.0.0
33
coverage
44
flake8
@@ -8,13 +8,12 @@ importlib-metadata
88
ipykernel
99
ipython
1010
jupyter_client
11-
mypy; python_version >= '3.6'
11+
mypy
1212
nbconvert
13-
pyjwt>=2.4.0; python_version >= '3.6'
14-
pyjwt; python_version < '3.6'
13+
pyjwt>=2.4.0
1514
pytest
1615
pytest-cov
17-
pytest-mypy; python_version >= '3.5'
16+
pytest-mypy
1817
semver>=2.0.0,<3.0.0
1918
setuptools_scm
2019
six>=1.14.0

rsconnect/actions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,8 @@ def test_server(connect_server):
225225
raise RSConnectException("\n".join(failures))
226226

227227

228-
def test_rstudio_server(server: api.RStudioServer):
229-
with api.RStudioClient(server) as client:
228+
def test_rstudio_server(server: api.PositServer):
229+
with api.PositClient(server) as client:
230230
try:
231231
result = client.get_current_user()
232232
server.handle_bad_response(result)

rsconnect/api.py

Lines changed: 25 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ def handle_bad_response(self, response):
6464
)
6565

6666

67-
class RStudioServer(AbstractRemoteServer):
67+
class PositServer(AbstractRemoteServer):
6868
"""
69-
A class used to represent the server of the shinyapps.io and RStudio Cloud APIs.
69+
A class used to represent the server of the shinyapps.io and Posit Cloud APIs.
7070
"""
7171

7272
def __init__(self, remote_name: str, url: str, account_name: str, token: str, secret: str):
@@ -76,7 +76,7 @@ def __init__(self, remote_name: str, url: str, account_name: str, token: str, se
7676
self.secret = secret
7777

7878

79-
class ShinyappsServer(RStudioServer):
79+
class ShinyappsServer(PositServer):
8080
"""
8181
A class to encapsulate the information needed to interact with an
8282
instance of the shinyapps.io server.
@@ -89,16 +89,16 @@ def __init__(self, url: str, account_name: str, token: str, secret: str):
8989
super().__init__(remote_name=remote_name, url=url, account_name=account_name, token=token, secret=secret)
9090

9191

92-
class CloudServer(RStudioServer):
92+
class CloudServer(PositServer):
9393
"""
9494
A class to encapsulate the information needed to interact with an
95-
instance of the RStudio Cloud server.
95+
instance of the Posit Cloud server.
9696
"""
9797

9898
def __init__(self, url: str, account_name: str, token: str, secret: str):
99-
remote_name = "RStudio Cloud"
100-
if url == "rstudio.cloud" or url is None:
101-
url = "https://api.rstudio.cloud"
99+
remote_name = "Posit Cloud"
100+
if url in {"posit.cloud", "rstudio.cloud", None}:
101+
url = "https://api.posit.cloud"
102102
super().__init__(remote_name=remote_name, url=url, account_name=account_name, token=token, secret=secret)
103103

104104

@@ -475,7 +475,7 @@ def setup_remote_server(
475475
if api_key:
476476
self.remote_server = RSConnectServer(url, api_key, insecure, ca_data)
477477
elif token and secret:
478-
if url and "rstudio.cloud" in url:
478+
if url and ("rstudio.cloud" in url or "posit.cloud" in url):
479479
self.remote_server = CloudServer(url, account_name, token, secret)
480480
else:
481481
self.remote_server = ShinyappsServer(url, account_name, token, secret)
@@ -485,8 +485,8 @@ def setup_remote_server(
485485
def setup_client(self, cookies=None, timeout=30, **kwargs):
486486
if isinstance(self.remote_server, RSConnectServer):
487487
self.client = RSConnectClient(self.remote_server, cookies, timeout)
488-
elif isinstance(self.remote_server, RStudioServer):
489-
self.client = RStudioClient(self.remote_server, timeout)
488+
elif isinstance(self.remote_server, PositServer):
489+
self.client = PositClient(self.remote_server, timeout)
490490
else:
491491
raise RSConnectException("Unable to infer Connect client.")
492492

@@ -515,7 +515,7 @@ def validate_server(
515515
):
516516
if (url and api_key) or isinstance(self.remote_server, RSConnectServer):
517517
self.validate_connect_server(name, url, api_key, insecure, cacert, api_key_is_required)
518-
elif (url and token and secret) or isinstance(self.remote_server, RStudioServer):
518+
elif (url and token and secret) or isinstance(self.remote_server, PositServer):
519519
self.validate_rstudio_server(url, account_name, token, secret)
520520
else:
521521
raise RSConnectException("Unable to validate server from information provided.")
@@ -596,11 +596,11 @@ def validate_rstudio_server(
596596
secret = secret or self.remote_server.secret
597597
server = (
598598
CloudServer(url, account_name, token, secret)
599-
if "rstudio.cloud" in url
599+
if "rstudio.cloud" in url or "posit.cloud" in url
600600
else ShinyappsServer(url, account_name, token, secret)
601601
)
602602

603-
with RStudioClient(server) as client:
603+
with PositClient(server) as client:
604604
try:
605605
result = client.get_current_user()
606606
server.handle_bad_response(result)
@@ -657,7 +657,7 @@ def check_server_capabilities(self, capability_functions):
657657
:param details_source: the source for obtaining server details, gather_server_details(),
658658
by default.
659659
"""
660-
if isinstance(self.remote_server, RStudioServer):
660+
if isinstance(self.remote_server, PositServer):
661661
return self
662662

663663
details = self.server_details
@@ -847,12 +847,12 @@ def validate_app_mode(self, *args, **kwargs):
847847
if isinstance(self.remote_server, RSConnectServer):
848848
app = get_app_info(self.remote_server, app_id)
849849
existing_app_mode = AppModes.get_by_ordinal(app.get("app_mode", 0), True)
850-
elif isinstance(self.remote_server, RStudioServer):
850+
elif isinstance(self.remote_server, PositServer):
851851
app = get_rstudio_app_info(self.remote_server, app_id)
852852
existing_app_mode = AppModes.get_by_cloud_name(app.json_data["mode"])
853853
else:
854854
raise RSConnectException("Unable to infer Connect client.")
855-
if existing_app_mode and app_mode != existing_app_mode:
855+
if existing_app_mode and existing_app_mode not in (None, AppModes.UNKNOWN, app_mode):
856856
msg = (
857857
"Deploying with mode '%s',\n"
858858
+ "but the existing deployment has mode '%s'.\n"
@@ -1008,14 +1008,14 @@ def __init__(
10081008
self.output_id = output_id
10091009

10101010

1011-
class RStudioClient(HTTPServer):
1011+
class PositClient(HTTPServer):
10121012
"""
1013-
An HTTP client to call the RStudio Cloud and shinyapps.io APIs.
1013+
An HTTP client to call the Posit Cloud and shinyapps.io APIs.
10141014
"""
10151015

10161016
_TERMINAL_STATUSES = {"success", "failed", "error"}
10171017

1018-
def __init__(self, rstudio_server: RStudioServer, timeout: int = 30):
1018+
def __init__(self, rstudio_server: PositServer, timeout: int = 30):
10191019
self._token = rstudio_server.token
10201020
try:
10211021
self._key = base64.b64decode(rstudio_server.secret)
@@ -1156,7 +1156,7 @@ class ShinyappsService:
11561156
Encapsulates operations involving multiple API calls to shinyapps.io.
11571157
"""
11581158

1159-
def __init__(self, rstudio_client: RStudioClient, server: ShinyappsServer):
1159+
def __init__(self, rstudio_client: PositClient, server: ShinyappsServer):
11601160
self._rstudio_client = rstudio_client
11611161
self._server = server
11621162

@@ -1202,10 +1202,10 @@ def do_deploy(self, bundle_id, app_id):
12021202

12031203
class CloudService:
12041204
"""
1205-
Encapsulates operations involving multiple API calls to RStudio Cloud.
1205+
Encapsulates operations involving multiple API calls to Posit Cloud.
12061206
"""
12071207

1208-
def __init__(self, rstudio_client: RStudioClient, server: CloudServer):
1208+
def __init__(self, rstudio_client: PositClient, server: CloudServer):
12091209
self._rstudio_client = rstudio_client
12101210
self._server = server
12111211

@@ -1216,17 +1216,6 @@ def prepare_deploy(
12161216
bundle_size: int,
12171217
bundle_hash: str,
12181218
):
1219-
accounts = self._rstudio_client.get_accounts()
1220-
self._server.handle_bad_response(accounts)
1221-
account = next(
1222-
filter(lambda acct: acct["name"] == self._server.account_name, accounts.json_data["accounts"]), None
1223-
)
1224-
# TODO: also check this during `add` command
1225-
if account is None:
1226-
raise RSConnectException(
1227-
"No account found by name : %s for given user credential" % self._server.account_name
1228-
)
1229-
12301219
if app_id is None:
12311220
project_application_id = os.getenv("LUCID_APPLICATION_ID")
12321221
if project_application_id is not None:
@@ -1343,7 +1332,7 @@ def get_app_info(connect_server, app_id):
13431332

13441333

13451334
def get_rstudio_app_info(server, app_id):
1346-
with RStudioClient(server) as client:
1335+
with PositClient(server) as client:
13471336
result = client.get_application(app_id)
13481337
server.handle_bad_response(result)
13491338
return result
@@ -1552,7 +1541,7 @@ def find_unique_name(remote_server: TargetableServer, name: str):
15521541
mapping_function=lambda client, app: app["name"],
15531542
)
15541543
elif isinstance(remote_server, ShinyappsServer):
1555-
client = RStudioClient(remote_server)
1544+
client = PositClient(remote_server)
15561545
existing_names = client.get_applications_like_name(name)
15571546
else:
15581547
# non-unique names are permitted in cloud

0 commit comments

Comments
 (0)