From 95e854a8e97ee5b132f7b164c3e80539e5cabe56 Mon Sep 17 00:00:00 2001 From: Jordan Jensen Date: Tue, 4 Aug 2026 11:12:41 -0700 Subject: [PATCH 1/5] Skip requirements generation for non-Python content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Generate requirements.txt if needed" step unconditionally required a Python dependency source (requirements.txt, uv.lock, or pyproject.toml), failing the deploy for content that legitimately has none — e.g. Node.js apps, whose dependencies come from package.json/package-lock.json. The app type is already resolved one step earlier, so thread it through: AppType gains a needs_requirements flag (true for the mapped Python/Quarto deploy types and fall-through python-*/jupyter-* app modes, false otherwise and for manifests), the CLI emits it as an output, and action.yml gates the requirements step on it — mirroring the existing needs_quarto pattern. generate-requirements.sh itself is unchanged. Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 4 +-- README.md | 4 ++- deploy/action.yml | 5 ++++ skills/setup-connect-deploy/SKILL.md | 3 +++ src/connect_actions/apptype.py | 27 +++++++++++++++++--- src/connect_actions/cli.py | 14 +++++++--- tests/test_apptype.py | 38 +++++++++++++++++++++++++--- tests/test_cli.py | 16 ++++++++++++ 8 files changed, 96 insertions(+), 15 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 60f3f73..36a1e94 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,10 +35,10 @@ Both actions authenticate once via `scripts/login.sh`, which logs the `posit` CL 1. Install `uv` and the `posit` CLI 2. Resolve config (server, GUID, entrypoint, extra_files) via `connect_actions.cli resolve-config` 3. Log in to Connect (`scripts/login.sh`) -4. Determine app type: if a `manifest.json` is present use `manifest`; otherwise query `app_mode` from the Connect content record (`posit connect api`) and run `connect_actions.cli resolve-app-type`, which maps it to a `posit connect deploy` subcommand (shiny, fastapi, flask, dash, streamlit, bokeh, quarto) and sets `needs_quarto` (true only when the resolved subcommand is `quarto`). R app modes (`shiny`, `rmd-shiny`, `rmd-static`, `api`) require a `manifest.json` and will error clearly if it is not present. +4. Determine app type: if a `manifest.json` is present use `manifest`; otherwise query `app_mode` from the Connect content record (`posit connect api`) and run `connect_actions.cli resolve-app-type`, which maps it to a `posit connect deploy` subcommand (shiny, fastapi, flask, dash, streamlit, bokeh, quarto), sets `needs_quarto` (true only when the resolved subcommand is `quarto`), and sets `needs_requirements` (true only for content with Python dependencies: the mapped subcommands plus fall-through `python-*`/`jupyter-*` app modes; false for e.g. `nodejs` and for manifests). R app modes (`shiny`, `rmd-shiny`, `rmd-static`, `api`) require a `manifest.json` and will error clearly if it is not present. 5. Set up Quarto (`quarto-dev/quarto-actions/setup`) only when `needs_quarto` is true — the `quarto` subcommand runs `quarto inspect` locally to build the manifest 6. Check Connect capabilities: read the server version (`posit connect api server_settings -q .version`) and run `connect_actions.cli check-deploy-features`, which fails fast if a draft is requested on a server older than 2025.07.0 and sets the `send_metadata` output (false on servers older than 2025.12.0, or when the version can't be read) -7. Generate `requirements.txt` from `pyproject.toml` if missing (`generate-requirements.sh`) +7. Generate `requirements.txt` if missing (`generate-requirements.sh`, from `uv.lock` or `pyproject.toml`) — only when `needs_requirements` is true; non-Python content (e.g. Node.js) skips it 8. Run `posit connect deploy` with the resolved app type, `--draft` for PRs, passing `--metadata` only when `send_metadata` is true, and appending `extra_files` as trailing positionals for `quarto` deploys 9. Extract content URL from deploy logs, set as action output 10. On PRs: comment preview URL via `actions/github-script` diff --git a/README.md b/README.md index e6158fa..dddf740 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ There are a few prerequisites to set up before you can use these actions: 1. Deploy your content to Connect for the first time by other means. The `deploy` action here will not create a new content item for you; it will only update an existing one with new code. If you use the Publisher extension for Positron, VS Code, or other Code OSS forks, check in the `.posit/` TOML files it creates---this action can detect and use them. Otherwise, you just need the content's URL. 2. Configure auth. If your Connect server is version 2026.07.0 or newer and has an Enhanced or Advanced license, we recommend using the Trusted Publishing feature, which allows you to publish from this GitHub repository automatically, no API keys needed. You can enable this in the "Access" tab of the content settings. If you are not using Trusted Publishing, you will need to get an API key with at least "publisher" privileges from your Connect account and add it as a GitHub Actions secret. -3. Make sure your requirements files are checked in. For Python content, this can either be a `uv.lock` file or a `requirements.txt`, and if you have neither, one can be generated from a `pyproject.toml` file. (We recommend that you keep both `pyproject.toml` and one of those lockfiles and use [Dependabot](https://docs.github.com/en/code-security/dependabot) to update the lockfile on a schedule so that your content stays up to date and security vulnerabilities are resolved.) For R, use the `rsconnect::writeManifest()` function to generate a `manifest.json` file. +3. Make sure your requirements files are checked in. For Python content, this can either be a `uv.lock` file or a `requirements.txt`, and if you have neither, one can be generated from a `pyproject.toml` file. (We recommend that you keep both `pyproject.toml` and one of those lockfiles and use [Dependabot](https://docs.github.com/en/code-security/dependabot) to update the lockfile on a schedule so that your content stays up to date and security vulnerabilities are resolved.) For R, use the `rsconnect::writeManifest()` function to generate a `manifest.json` file. Node.js content needs no Python requirements file; its dependencies come from its `package.json` and `package-lock.json`. Then, you can add these actions. There are examples below, or you can let an AI agent set them up for you with the bundled Agent Skill. @@ -93,6 +93,8 @@ For authentication, we recommend using Trusted Publishing if your Connect server If a `manifest.json` exists at the root of your repo, the action deploys it directly using `posit connect deploy manifest`. In this mode the manifest's declared app type, entrypoint, and dependencies are used as-is. +Requirements generation only applies to content with Python dependencies (the Python frameworks, plus Quarto, which may run Python via the jupyter engine). Content without them---such as Node.js apps, whose dependencies come from `package.json`/`package-lock.json`---skips this step entirely. + For Python content, Connect installs your app's dependencies from a `requirements.txt`. When one isn't present, the action generates it, looking for a dependency source in this order: 1. **`requirements.txt`** -- if it already exists, it is used as-is. diff --git a/deploy/action.yml b/deploy/action.yml index 6d28ef6..9d0dd5a 100644 --- a/deploy/action.yml +++ b/deploy/action.yml @@ -120,7 +120,12 @@ runs: env: DRAFT: ${{ inputs.draft }} + # Python content declares its dependencies through a requirements.txt, + # generated here from uv.lock or pyproject.toml when absent. Non-Python + # content (e.g. Node.js apps) has no Python dependency source at all, so + # skip the step rather than failing on a legitimately missing one. - name: Generate requirements.txt if needed + if: steps.apptype.outputs.needs_requirements == 'true' shell: bash working-directory: ${{ inputs.path }} run: ${{ github.action_path }}/scripts/generate-requirements.sh diff --git a/skills/setup-connect-deploy/SKILL.md b/skills/setup-connect-deploy/SKILL.md index 01a8ce4..e835240 100644 --- a/skills/setup-connect-deploy/SKILL.md +++ b/skills/setup-connect-deploy/SKILL.md @@ -121,6 +121,9 @@ Connect needs to know your app's dependencies. Check the app directory: - Recommend committing a lockfile (`uv.lock` via `uv lock`, or a pinned `requirements.txt` via `uv pip compile pyproject.toml -o requirements.txt`) for reproducible deploys, optionally kept fresh with Dependabot. +- **Node.js, no manifest** → nothing to check; dependencies come from the app's + `package.json`/`package-lock.json` and the action skips requirements + generation for non-Python content. Don't generate these files yourself unless the user asks — just report what you found and recommend. diff --git a/src/connect_actions/apptype.py b/src/connect_actions/apptype.py index 6a10689..5690408 100644 --- a/src/connect_actions/apptype.py +++ b/src/connect_actions/apptype.py @@ -56,6 +56,16 @@ } ) +# Deploy types whose bundles declare Python dependencies through a +# requirements.txt: every mapped type qualifies -- the Python frameworks +# obviously, and ``quarto`` because a Quarto doc may run Python via the jupyter +# engine. Fall-through modes are covered by prefix in :func:`resolve_app_type`: +# any ``python-*``/``jupyter-*`` app_mode is Python content even without an +# entry in ``APP_MODE_TO_TYPE``. Everything else (e.g. ``nodejs``) carries no +# Python dependencies, so the action skips requirements generation instead of +# failing on a bundle that legitimately has no dependency source. +REQUIREMENTS_DEPLOY_TYPES: frozenset[str] = frozenset(APP_MODE_TO_TYPE.values()) + class AppTypeError(Exception): """Raised when the deploy subcommand can't be determined. @@ -67,10 +77,11 @@ class AppTypeError(Exception): @dataclass class AppType: - """The resolved deploy subcommand and whether it needs a local Quarto.""" + """The resolved deploy subcommand and which optional deploy steps it needs.""" deploy_type: str needs_quarto: bool + needs_requirements: bool def resolve_app_type(*, manifest_present: bool, app_mode: str) -> AppType: @@ -82,10 +93,13 @@ def resolve_app_type(*, manifest_present: bool, app_mode: str) -> AppType: ``app_mode`` (R content has no source-deploy path here and needs a ``manifest.json``). Only the ``quarto`` subcommand runs ``quarto inspect`` locally, so ``needs_quarto`` is true exactly when the resolved type is - ``quarto``. + ``quarto``. ``needs_requirements`` is true only for content with Python + dependencies (see :data:`REQUIREMENTS_DEPLOY_TYPES`); a manifest declares + its own dependencies, and non-Python content such as Node.js apps has no + requirements.txt to generate. """ if manifest_present: - return AppType(deploy_type="manifest", needs_quarto=False) + return AppType(deploy_type="manifest", needs_quarto=False, needs_requirements=False) if not app_mode: raise AppTypeError( @@ -101,4 +115,9 @@ def resolve_app_type(*, manifest_present: bool, app_mode: str) -> AppType: ) deploy_type = APP_MODE_TO_TYPE.get(app_mode, app_mode) - return AppType(deploy_type=deploy_type, needs_quarto=deploy_type == "quarto") + return AppType( + deploy_type=deploy_type, + needs_quarto=deploy_type == "quarto", + needs_requirements=deploy_type in REQUIREMENTS_DEPLOY_TYPES + or app_mode.startswith(("python-", "jupyter-")), + ) diff --git a/src/connect_actions/cli.py b/src/connect_actions/cli.py index 7d33a50..4f7a089 100644 --- a/src/connect_actions/cli.py +++ b/src/connect_actions/cli.py @@ -71,9 +71,10 @@ def cmd_resolve_app_type(_args: argparse.Namespace) -> int: """Map the content's app_mode to a deploy subcommand and Quarto need. Reads ``MANIFEST_PRESENT`` (whether a ``manifest.json`` was found) and - ``APP_MODE`` (from ``posit connect api``), then writes ``app_type`` and - ``needs_quarto`` so the action can conditionally set up Quarto and hand the - subcommand to the deploy step. + ``APP_MODE`` (from ``posit connect api``), then writes ``app_type``, + ``needs_quarto``, and ``needs_requirements`` so the action can + conditionally set up Quarto, skip requirements generation for non-Python + content, and hand the subcommand to the deploy step. """ try: app_type = resolve_app_type( @@ -84,10 +85,15 @@ def cmd_resolve_app_type(_args: argparse.Namespace) -> int: print(f"Error: {err}", file=sys.stderr) return 1 - print(f"Resolved app type: {app_type.deploy_type} (needs_quarto={app_type.needs_quarto})") + print( + f"Resolved app type: {app_type.deploy_type} " + f"(needs_quarto={app_type.needs_quarto}, " + f"needs_requirements={app_type.needs_requirements})" + ) _write_output( app_type=app_type.deploy_type, needs_quarto="true" if app_type.needs_quarto else "false", + needs_requirements="true" if app_type.needs_requirements else "false", ) return 0 diff --git a/tests/test_apptype.py b/tests/test_apptype.py index 3c7003e..de46d42 100644 --- a/tests/test_apptype.py +++ b/tests/test_apptype.py @@ -12,7 +12,7 @@ def test_manifest_short_circuits_lookup(): # needs a local Quarto install. result = resolve_app_type(manifest_present=True, app_mode="quarto-static") - assert result == AppType(deploy_type="manifest", needs_quarto=False) + assert result == AppType(deploy_type="manifest", needs_quarto=False, needs_requirements=False) @pytest.mark.parametrize( @@ -35,13 +35,43 @@ def test_quarto_static_needs_quarto(): assert resolve_app_type(manifest_present=False, app_mode="quarto-static").needs_quarto is True +@pytest.mark.parametrize( + "app_mode", + [ + "python-shiny", + "python-fastapi", + "python-flask", + "python-dash", + "python-streamlit", + "python-bokeh", + # Quarto may run Python via the jupyter engine, so it keeps the + # requirements step. + "quarto-static", + # Fall-through modes with a Python/Jupyter prefix are Python content + # even without a mapping. + "python-gradio", + "jupyter-static", + ], +) +def test_python_content_needs_requirements(app_mode): + assert resolve_app_type(manifest_present=False, app_mode=app_mode).needs_requirements is True + + +def test_nodejs_falls_through_and_skips_requirements(): + # Node.js content has no Python dependency source, so the requirements step + # must be skipped instead of failing on a missing pyproject/uv.lock. + result = resolve_app_type(manifest_present=False, app_mode="nodejs") + + assert result == AppType(deploy_type="nodejs", needs_quarto=False, needs_requirements=False) + + def test_quarto_shiny_falls_through_unchanged(): # Connect doesn't support Python shiny-backed Quarto docs (only R), so there # is no mapping for quarto-shiny; it passes straight through. See # https://github.com/posit-dev/rsconnect-python/pull/755#issuecomment-4271245574 result = resolve_app_type(manifest_present=False, app_mode="quarto-shiny") - assert result == AppType(deploy_type="quarto-shiny", needs_quarto=False) + assert result == AppType(deploy_type="quarto-shiny", needs_quarto=False, needs_requirements=False) def test_unknown_app_mode_falls_through_unchanged(): @@ -49,7 +79,7 @@ def test_unknown_app_mode_falls_through_unchanged(): # reject it if genuinely unsupported. result = resolve_app_type(manifest_present=False, app_mode="python-gradio") - assert result == AppType(deploy_type="python-gradio", needs_quarto=False) + assert result == AppType(deploy_type="python-gradio", needs_quarto=False, needs_requirements=True) def test_empty_app_mode_without_manifest_errors(): @@ -77,4 +107,4 @@ def test_r_app_mode_with_manifest_still_deploys(): # short-circuits the R check and deploys the manifest directly. result = resolve_app_type(manifest_present=True, app_mode="shiny") - assert result == AppType(deploy_type="manifest", needs_quarto=False) + assert result == AppType(deploy_type="manifest", needs_quarto=False, needs_requirements=False) diff --git a/tests/test_cli.py b/tests/test_cli.py index 6a1d5cf..5c50eb6 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -105,6 +105,7 @@ def test_resolve_app_type_maps_app_mode(tmp_path, monkeypatch): written = output_file.read_text() assert "app_type=quarto" in written assert "needs_quarto=true" in written + assert "needs_requirements=true" in written def test_resolve_app_type_manifest(tmp_path, monkeypatch): @@ -118,6 +119,21 @@ def test_resolve_app_type_manifest(tmp_path, monkeypatch): written = output_file.read_text() assert "app_type=manifest" in written assert "needs_quarto=false" in written + assert "needs_requirements=false" in written + + +def test_resolve_app_type_nodejs_skips_requirements(tmp_path, monkeypatch): + output_file = tmp_path / "github_output" + monkeypatch.setenv("GITHUB_OUTPUT", str(output_file)) + monkeypatch.setenv("MANIFEST_PRESENT", "false") + monkeypatch.setenv("APP_MODE", "nodejs") + + assert main(["resolve-app-type"]) == 0 + + written = output_file.read_text() + assert "app_type=nodejs" in written + assert "needs_quarto=false" in written + assert "needs_requirements=false" in written def test_resolve_app_type_empty_mode_exits_nonzero(tmp_path, monkeypatch, capsys): From a303d36f1b5894a090b746f1ca0a3e382441ff6a Mon Sep 17 00:00:00 2001 From: Jordan Jensen Date: Tue, 4 Aug 2026 11:23:03 -0700 Subject: [PATCH 2/5] Split README prerequisite 3 into per-language bullets Co-Authored-By: Claude Fable 5 --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dddf740..4c5445a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,10 @@ There are a few prerequisites to set up before you can use these actions: 1. Deploy your content to Connect for the first time by other means. The `deploy` action here will not create a new content item for you; it will only update an existing one with new code. If you use the Publisher extension for Positron, VS Code, or other Code OSS forks, check in the `.posit/` TOML files it creates---this action can detect and use them. Otherwise, you just need the content's URL. 2. Configure auth. If your Connect server is version 2026.07.0 or newer and has an Enhanced or Advanced license, we recommend using the Trusted Publishing feature, which allows you to publish from this GitHub repository automatically, no API keys needed. You can enable this in the "Access" tab of the content settings. If you are not using Trusted Publishing, you will need to get an API key with at least "publisher" privileges from your Connect account and add it as a GitHub Actions secret. -3. Make sure your requirements files are checked in. For Python content, this can either be a `uv.lock` file or a `requirements.txt`, and if you have neither, one can be generated from a `pyproject.toml` file. (We recommend that you keep both `pyproject.toml` and one of those lockfiles and use [Dependabot](https://docs.github.com/en/code-security/dependabot) to update the lockfile on a schedule so that your content stays up to date and security vulnerabilities are resolved.) For R, use the `rsconnect::writeManifest()` function to generate a `manifest.json` file. Node.js content needs no Python requirements file; its dependencies come from its `package.json` and `package-lock.json`. +3. Make sure your requirements files are checked in. + * For Python content, this can either be a `uv.lock` file or a `requirements.txt`, and if you have neither, one can be generated from a `pyproject.toml` file. (We recommend that you keep both `pyproject.toml` and one of those lockfiles and use [Dependabot](https://docs.github.com/en/code-security/dependabot) to update the lockfile on a schedule so that your content stays up to date and security vulnerabilities are resolved.) + * For R content, use the `rsconnect::writeManifest()` function to generate a `manifest.json` file. + * For Node.js content, no Python requirements file is needed; dependencies come from your `package.json` and `package-lock.json`. Then, you can add these actions. There are examples below, or you can let an AI agent set them up for you with the bundled Agent Skill. From bb165113aa2369166b18641fa093e886d95ddba7 Mon Sep 17 00:00:00 2001 From: Jordan Jensen Date: Tue, 4 Aug 2026 11:25:21 -0700 Subject: [PATCH 3/5] Describe each content type's dependency files positively Co-Authored-By: Claude Fable 5 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4c5445a..04578b8 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,10 @@ There are a few prerequisites to set up before you can use these actions: 1. Deploy your content to Connect for the first time by other means. The `deploy` action here will not create a new content item for you; it will only update an existing one with new code. If you use the Publisher extension for Positron, VS Code, or other Code OSS forks, check in the `.posit/` TOML files it creates---this action can detect and use them. Otherwise, you just need the content's URL. 2. Configure auth. If your Connect server is version 2026.07.0 or newer and has an Enhanced or Advanced license, we recommend using the Trusted Publishing feature, which allows you to publish from this GitHub repository automatically, no API keys needed. You can enable this in the "Access" tab of the content settings. If you are not using Trusted Publishing, you will need to get an API key with at least "publisher" privileges from your Connect account and add it as a GitHub Actions secret. -3. Make sure your requirements files are checked in. +3. Make sure the files declaring your dependencies are checked in. * For Python content, this can either be a `uv.lock` file or a `requirements.txt`, and if you have neither, one can be generated from a `pyproject.toml` file. (We recommend that you keep both `pyproject.toml` and one of those lockfiles and use [Dependabot](https://docs.github.com/en/code-security/dependabot) to update the lockfile on a schedule so that your content stays up to date and security vulnerabilities are resolved.) * For R content, use the `rsconnect::writeManifest()` function to generate a `manifest.json` file. - * For Node.js content, no Python requirements file is needed; dependencies come from your `package.json` and `package-lock.json`. + * For Node.js content, your `package.json` and `package-lock.json` declare your dependencies. Then, you can add these actions. There are examples below, or you can let an AI agent set them up for you with the bundled Agent Skill. From a0967e437aa32ec2e6fdb39b1f79ebef03a657f9 Mon Sep 17 00:00:00 2001 From: Jordan Jensen Date: Tue, 4 Aug 2026 14:20:03 -0700 Subject: [PATCH 4/5] Explain why manifests and Node.js content skip requirements Co-Authored-By: Claude Fable 5 --- src/connect_actions/apptype.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/connect_actions/apptype.py b/src/connect_actions/apptype.py index 5690408..3762d7a 100644 --- a/src/connect_actions/apptype.py +++ b/src/connect_actions/apptype.py @@ -94,9 +94,12 @@ def resolve_app_type(*, manifest_present: bool, app_mode: str) -> AppType: ``manifest.json``). Only the ``quarto`` subcommand runs ``quarto inspect`` locally, so ``needs_quarto`` is true exactly when the resolved type is ``quarto``. ``needs_requirements`` is true only for content with Python - dependencies (see :data:`REQUIREMENTS_DEPLOY_TYPES`); a manifest declares - its own dependencies, and non-Python content such as Node.js apps has no - requirements.txt to generate. + dependencies (see :data:`REQUIREMENTS_DEPLOY_TYPES`). It is false for + manifests even when the content is Python: a manifest deploy bundles + exactly the files the manifest lists, so the dependency file it names must + already sit beside it — one generated at deploy time could never enter the + bundle. Node.js content declares its dependencies in + package.json/package-lock.json instead, so there is nothing to generate. """ if manifest_present: return AppType(deploy_type="manifest", needs_quarto=False, needs_requirements=False) From 97063e6ed911be8624bb06c8fba97675e3d7c53e Mon Sep 17 00:00:00 2001 From: Jordan Jensen Date: Tue, 4 Aug 2026 14:29:35 -0700 Subject: [PATCH 5/5] Address review: fix stale overview, dash style, and gate note CLAUDE.md's project overview still claimed only Python app types are supported; the docstring's em dash broke the file's `--` convention; and generate-requirements.sh now notes that its manifest check is redundant on the action path since the step is gated on needs_requirements. Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 2 +- deploy/scripts/generate-requirements.sh | 5 ++++- src/connect_actions/apptype.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 36a1e94..a552272 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -GitHub Actions monorepo (`posit-dev/connect-actions`) providing composite actions for deploying Python applications to [Posit Connect](https://posit.co/products/enterprise/connect/) from GitHub Actions. Currently in early beta; only Python app types supported. All Connect interaction goes through the [`posit` CLI](https://github.com/posit-dev/posit-cli), which mounts the full `rsconnect-python` command set under `posit connect` and adds a `gh api`-style `posit connect api` raw REST client. +GitHub Actions monorepo (`posit-dev/connect-actions`) providing composite actions for deploying applications to [Posit Connect](https://posit.co/products/enterprise/connect/) from GitHub Actions. Currently in early beta; Python, Quarto, and Node.js content deploys from source, and R content deploys from a pre-built `manifest.json`. All Connect interaction goes through the [`posit` CLI](https://github.com/posit-dev/posit-cli), which mounts the full `rsconnect-python` command set under `posit connect` and adds a `gh api`-style `posit connect api` raw REST client. ## Repository Structure diff --git a/deploy/scripts/generate-requirements.sh b/deploy/scripts/generate-requirements.sh index a756b8b..7c33cd5 100755 --- a/deploy/scripts/generate-requirements.sh +++ b/deploy/scripts/generate-requirements.sh @@ -1,5 +1,8 @@ #!/bin/bash -# Generate requirements.txt if it doesn't exist +# Generate requirements.txt if it doesn't exist. +# The action only runs this step when resolve-app-type reports +# needs_requirements (content with Python dependencies), so the manifest check +# below is redundant on that path; it is kept for direct invocations. set -euo pipefail diff --git a/src/connect_actions/apptype.py b/src/connect_actions/apptype.py index 3762d7a..c98e33e 100644 --- a/src/connect_actions/apptype.py +++ b/src/connect_actions/apptype.py @@ -97,7 +97,7 @@ def resolve_app_type(*, manifest_present: bool, app_mode: str) -> AppType: dependencies (see :data:`REQUIREMENTS_DEPLOY_TYPES`). It is false for manifests even when the content is Python: a manifest deploy bundles exactly the files the manifest lists, so the dependency file it names must - already sit beside it — one generated at deploy time could never enter the + already sit beside it -- one generated at deploy time could never enter the bundle. Node.js content declares its dependencies in package.json/package-lock.json instead, so there is nothing to generate. """