diff --git a/.github/workflows/sf_cli_integration.yml b/.github/workflows/sf_cli_integration.yml index f3c5fb3..c320f8b 100644 --- a/.github/workflows/sf_cli_integration.yml +++ b/.github/workflows/sf_cli_integration.yml @@ -17,6 +17,18 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Set mock server TLS cert paths + run: | + echo "MOCK_SF_CERT_FILE=$RUNNER_TEMP/mock_sf_cert.pem" >> "$GITHUB_ENV" + echo "MOCK_SF_KEY_FILE=$RUNNER_TEMP/mock_sf_key.pem" >> "$GITHUB_ENV" + + - name: Generate mock server TLS cert + run: | + openssl req -x509 -newkey rsa:2048 -nodes \ + -keyout "$MOCK_SF_KEY_FILE" -out "$MOCK_SF_CERT_FILE" \ + -days 1 -subj "/CN=localhost" \ + -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" + - name: Set up Python 3.11 uses: actions/setup-python@v5 with: @@ -73,7 +85,7 @@ jobs: sfdx_dir.mkdir(exist_ok=True) auth = { "accessToken": "00D000000000001AAA!fakeTokenForCITesting", - "instanceUrl": "http://localhost:8888", + "instanceUrl": "https://localhost:8888", "loginUrl": "https://login.salesforce.com", "orgId": "00D000000000001AAA", "userId": "005000000000001AAA", @@ -164,6 +176,9 @@ jobs: # ── Script: run ─────────────────────────────────────────────────────────── - name: '[script] run — sf data-code-extension script run --entrypoint testScript/payload/entrypoint.py -o dev1' + env: + NODE_EXTRA_CA_CERTS: ${{ env.MOCK_SF_CERT_FILE }} + REQUESTS_CA_BUNDLE: ${{ env.MOCK_SF_CERT_FILE }} run: | sf data-code-extension script run \ --entrypoint testScript/payload/entrypoint.py \ @@ -175,6 +190,9 @@ jobs: # ── Script: deploy ─────────────────────────────────────────────────────── - name: '[script] deploy — sf data-code-extension script deploy' + env: + NODE_EXTRA_CA_CERTS: ${{ env.MOCK_SF_CERT_FILE }} + REQUESTS_CA_BUNDLE: ${{ env.MOCK_SF_CERT_FILE }} run: | sf data-code-extension script deploy \ --name test-script-deploy \ @@ -262,6 +280,9 @@ jobs: # ── Function: run ───────────────────────────────────────────────────────── - name: '[function] run — sf data-code-extension function run --entrypoint testFunction/payload/entrypoint.py --test-with testFunction/payload/tests/test.json -o dev1' + env: + NODE_EXTRA_CA_CERTS: ${{ env.MOCK_SF_CERT_FILE }} + REQUESTS_CA_BUNDLE: ${{ env.MOCK_SF_CERT_FILE }} run: | sf data-code-extension function run \ --entrypoint testFunction/payload/entrypoint.py \ @@ -273,6 +294,9 @@ jobs: # ── Function: deploy ───────────────────────────────────────────────────── - name: '[function] deploy — sf data-code-extension function deploy' + env: + NODE_EXTRA_CA_CERTS: ${{ env.MOCK_SF_CERT_FILE }} + REQUESTS_CA_BUNDLE: ${{ env.MOCK_SF_CERT_FILE }} run: | sf data-code-extension function deploy \ --name test-function-deploy \ diff --git a/scripts/mock_sf_server.py b/scripts/mock_sf_server.py index b296577..8012f89 100644 --- a/scripts/mock_sf_server.py +++ b/scripts/mock_sf_server.py @@ -40,6 +40,17 @@ python scripts/mock_sf_server.py # listens on port 8888 MOCK_SF_PORT=9000 python scripts/mock_sf_server.py python scripts/mock_sf_server.py 9000 + +Serves TLS (the deploy path requires an HTTPS upload URL) using a pre-generated +cert/key pair — this script does not generate one. Set ``MOCK_SF_CERT_FILE`` / +``MOCK_SF_KEY_FILE`` to the pair's paths; generate a throwaway one with: + + openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem \\ + -days 1 -subj "/CN=localhost" \\ + -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" + +Point clients at the cert so they trust it: ``NODE_EXTRA_CA_CERTS`` (CLI) and +``REQUESTS_CA_BUNDLE`` (SDK). """ from __future__ import annotations @@ -47,6 +58,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer import json import os +import ssl import sys PORT = ( @@ -68,7 +80,7 @@ _TOKEN_RESPONSE = { "access_token": "00D000000000001AAA!fakeAccessTokenForCITesting", - "instance_url": f"http://localhost:{PORT}", + "instance_url": f"https://localhost:{PORT}", "token_type": "Bearer", "scope": "api", } @@ -135,7 +147,9 @@ def do_POST(self) -> None: elif path == _DATA_CUSTOM_CODE_PATH: # create_deployment() — return a presigned upload URL self._send_json( - {"fileUploadUrl": f"http://localhost:{PORT}/upload/fake-deployment.zip"} + { + "fileUploadUrl": f"https://localhost:{PORT}/upload/fake-deployment.zip" + } ) elif path == _DATA_TRANSFORMS_PATH: # create_data_transform() — script packages only @@ -150,7 +164,18 @@ def do_PUT(self) -> None: if __name__ == "__main__": + cert_path = os.environ.get("MOCK_SF_CERT_FILE") + key_path = os.environ.get("MOCK_SF_KEY_FILE") + if not cert_path or not key_path: + sys.exit( + "MOCK_SF_CERT_FILE and MOCK_SF_KEY_FILE must both be set to an " + "existing TLS cert/key pair — see the module docstring." + ) + server = HTTPServer(("localhost", PORT), MockSFHandler) server.allow_reuse_address = True - print(f"[MOCK SF] Listening on http://localhost:{PORT}", flush=True) + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + ctx.load_cert_chain(cert_path, key_path) + server.socket = ctx.wrap_socket(server.socket, server_side=True) + print(f"[MOCK SF] Listening on https://localhost:{PORT}", flush=True) server.serve_forever()