From 6c27e4ffd29aeeb3e03fb9197657faa69780d6e6 Mon Sep 17 00:00:00 2001 From: Leechael Yim Date: Tue, 19 May 2026 10:12:51 +0800 Subject: [PATCH 1/3] feat(ci): add trusted publisher release workflows for JS and Python SDKs Add GitHub Actions workflows that publish to npm and PyPI using OIDC trusted publishers (no long-lived secrets). - js-sdk-release.yml: triggered by js-sdk-v* tags, publishes to npm with provenance. Includes npm upgrade, OIDC verification, and repository consistency checks. - python-sdk-release.yml: triggered by python-sdk-v* tags, builds with PDM and publishes via pypa/gh-action-pypi-publish. - Add repository field to sdk/js/package.json (required for npm Trusted Publishers / Sigstore provenance verification). --- .github/workflows/js-sdk-release.yml | 94 ++++++++++++++++++++++++ .github/workflows/python-sdk-release.yml | 52 +++++++++++++ sdk/js/package.json | 5 ++ 3 files changed, 151 insertions(+) create mode 100644 .github/workflows/js-sdk-release.yml create mode 100644 .github/workflows/python-sdk-release.yml diff --git a/.github/workflows/js-sdk-release.yml b/.github/workflows/js-sdk-release.yml new file mode 100644 index 000000000..4d54283d6 --- /dev/null +++ b/.github/workflows/js-sdk-release.yml @@ -0,0 +1,94 @@ +# SPDX-FileCopyrightText: © 2025 Phala Network +# +# SPDX-License-Identifier: Apache-2.0 + +name: Publish JS SDK to npm +on: + push: + tags: ['js-sdk-v*'] + workflow_dispatch: + inputs: + npm_tag: + description: 'npm dist-tag (latest, beta, canary)' + required: true + default: 'latest' + type: choice + options: + - latest + - beta + - canary + +permissions: + id-token: write + contents: read + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - name: Upgrade npm for OIDC support + run: | + npm install -g npm@latest + echo "npm version: $(npm --version)" + + - name: Verify OIDC token availability + run: | + if [ -n "${ACTIONS_ID_TOKEN_REQUEST_URL}" ] && [ -n "${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" ]; then + echo "OIDC token available" + else + echo "OIDC token NOT available" + echo "Check workflow permissions include 'id-token: write'" + exit 1 + fi + + - name: Verify repository configuration + working-directory: sdk/js + run: | + echo "Checking repository consistency..." + GIT_REPO=$(git remote get-url origin | sed 's/.*github.com[/:]//; s/.git$//') + PKG_REPO=$(node -e "console.log(require('./package.json').repository?.url || '')" | sed 's|https://github.com/||; s|git+||; s|.git$||') + echo "Git remote: $GIT_REPO" + echo "package.json: $PKG_REPO" + if [ "$GIT_REPO" != "$PKG_REPO" ]; then + echo "Repository mismatch!" + echo "This will cause 422 error during publish" + exit 1 + fi + echo "Repositories match" + + - name: Install dependencies + working-directory: sdk/js + run: npm ci + + - name: Build + working-directory: sdk/js + run: npm run build + + - name: Determine npm dist-tag + id: tag + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "tag=${{ github.event.inputs.npm_tag }}" >> "$GITHUB_OUTPUT" + else + # auto-detect from git tag: js-sdk-v0.5.8-beta.1 -> beta + RAW_TAG="${GITHUB_REF_NAME}" + if echo "$RAW_TAG" | grep -qiE '(beta|alpha|rc|preview)'; then + echo "tag=beta" >> "$GITHUB_OUTPUT" + else + echo "tag=latest" >> "$GITHUB_OUTPUT" + fi + fi + + - name: Publish to npm + working-directory: sdk/js + run: | + NPM_TAG="${{ steps.tag.outputs.tag }}" + echo "Publishing with dist-tag: $NPM_TAG" + npm publish --access public --provenance --tag "$NPM_TAG" diff --git a/.github/workflows/python-sdk-release.yml b/.github/workflows/python-sdk-release.yml new file mode 100644 index 000000000..eb579f9d6 --- /dev/null +++ b/.github/workflows/python-sdk-release.yml @@ -0,0 +1,52 @@ +# SPDX-FileCopyrightText: © 2025 Phala Network +# +# SPDX-License-Identifier: Apache-2.0 + +name: Publish Python SDK to PyPI +on: + push: + tags: ['python-sdk-v*'] + workflow_dispatch: + inputs: + target: + description: 'Publish target' + required: true + default: 'pypi' + type: choice + options: + - pypi + - testpypi + +permissions: + id-token: write + contents: read + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install PDM + run: pip install pdm + + - name: Build distribution + working-directory: sdk/python + run: pdm build + + - name: Publish to PyPI + if: github.event_name == 'push' || github.event.inputs.target == 'pypi' + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: sdk/python/dist + + - name: Publish to TestPyPI + if: github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'testpypi' + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + packages-dir: sdk/python/dist diff --git a/sdk/js/package.json b/sdk/js/package.json index 01946b898..0295c70c5 100644 --- a/sdk/js/package.json +++ b/sdk/js/package.json @@ -92,6 +92,11 @@ "dstack", "Phala" ], + "repository": { + "type": "git", + "url": "https://github.com/Dstack-TEE/dstack", + "directory": "sdk/js" + }, "author": "Leechael Yim", "license": "Apache-2.0", "dependencies": { From 151d384d46f30515efdf5c76a602dc70a6e90b39 Mon Sep 17 00:00:00 2001 From: Leechael Yim Date: Tue, 19 May 2026 12:44:33 +0800 Subject: [PATCH 2/3] chore(sdk/js): bump to 0.5.8-beta.0 for release workflow testing --- .github/workflows/js-sdk-release.yml | 2 +- sdk/js/.claude/settings.local.json | 20 ++++++++++++++++++++ sdk/js/package.json | 4 ++-- sdk/js/src/encrypt-env-vars.ts | 2 +- sdk/js/tsconfig.browser.json | 2 +- sdk/js/tsconfig.json | 2 +- sdk/js/tsconfig.node.json | 2 +- 7 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 sdk/js/.claude/settings.local.json diff --git a/.github/workflows/js-sdk-release.yml b/.github/workflows/js-sdk-release.yml index 4d54283d6..e1dbed611 100644 --- a/.github/workflows/js-sdk-release.yml +++ b/.github/workflows/js-sdk-release.yml @@ -65,7 +65,7 @@ jobs: - name: Install dependencies working-directory: sdk/js - run: npm ci + run: npm install - name: Build working-directory: sdk/js diff --git a/sdk/js/.claude/settings.local.json b/sdk/js/.claude/settings.local.json new file mode 100644 index 000000000..096326dc6 --- /dev/null +++ b/sdk/js/.claude/settings.local.json @@ -0,0 +1,20 @@ +{ + "permissions": { + "allow": [ + "Bash(npm run clean:*)", + "Bash(npm run build:*)", + "Bash(npm test)", + "Bash(export DSTACK_SIMULATOR_ENDPOINT=/Users/leechael/workshop/phala/dstack/sdk/simulator/dstack.sock)", + "Bash(export TAPPD_SIMULATOR_ENDPOINT=/Users/leechael/workshop/phala/dstack/sdk/simulator/tappd.sock)", + "Bash(node:*)", + "Bash(rm:*)", + "Bash(npm run test:ci:*)", + "Bash(grep:*)", + "Bash(npm test:*)", + "Bash(npm install)", + "Bash(npm run test:*)", + "Bash(npx tsc:*)" + ], + "deny": [] + } +} \ No newline at end of file diff --git a/sdk/js/package.json b/sdk/js/package.json index 0295c70c5..cfc8d8c49 100644 --- a/sdk/js/package.json +++ b/sdk/js/package.json @@ -1,6 +1,6 @@ { "name": "@phala/dstack-sdk", - "version": "0.5.7", + "version": "0.5.8-beta.0", "description": "dstack SDK", "main": "dist/node/index.js", "types": "dist/node/index.d.ts", @@ -104,7 +104,7 @@ }, "devDependencies": { "@types/node": "latest", - "typescript": "latest", + "typescript": "^5.7.0", "vitest": "^3.2.4" }, "optionalDependencies": { diff --git a/sdk/js/src/encrypt-env-vars.ts b/sdk/js/src/encrypt-env-vars.ts index 6fc453782..5f9e7d2a9 100644 --- a/sdk/js/src/encrypt-env-vars.ts +++ b/sdk/js/src/encrypt-env-vars.ts @@ -40,7 +40,7 @@ export async function encryptEnvVars(envs: EnvVar[], publicKeyHex: string) { // Import shared key for AES-GCM const importedShared = await crypto.subtle.importKey( "raw", - shared, + new Uint8Array(shared), { name: "AES-GCM", length: 256 }, true, ["encrypt"], diff --git a/sdk/js/tsconfig.browser.json b/sdk/js/tsconfig.browser.json index a790e4b31..41e7245e1 100644 --- a/sdk/js/tsconfig.browser.json +++ b/sdk/js/tsconfig.browser.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "target": "es2018", + "target": "es2020", "module": "es2015", "lib": ["es2018", "dom"], "outDir": "./dist/browser", diff --git a/sdk/js/tsconfig.json b/sdk/js/tsconfig.json index d63ebfd96..1a77147b1 100644 --- a/sdk/js/tsconfig.json +++ b/sdk/js/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2018", + "target": "es2020", "module": "commonjs", "lib": ["es2018"], "declaration": true, diff --git a/sdk/js/tsconfig.node.json b/sdk/js/tsconfig.node.json index 2814865e6..f6b90a0a0 100644 --- a/sdk/js/tsconfig.node.json +++ b/sdk/js/tsconfig.node.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "target": "es2018", + "target": "es2020", "module": "commonjs", "lib": ["es2018"], "outDir": "./dist/node", From 4520abf7756283cebb63b539d903010e0e294a97 Mon Sep 17 00:00:00 2001 From: Leechael Yim Date: Tue, 19 May 2026 16:26:03 +0800 Subject: [PATCH 3/3] chore(sdk/python): bump to 0.5.4b0 for release workflow testing --- .github/workflows/js-sdk-release.yml | 23 +++++++++++++++++++--- .github/workflows/python-sdk-release.yml | 20 ++++++++++++++++++- sdk/js/.claude/settings.local.json | 2 +- sdk/js/.claude/settings.local.json.license | 3 +++ sdk/js/src/encrypt-env-vars.ts | 2 +- sdk/js/tsconfig.browser.json | 4 ++-- sdk/js/tsconfig.node.json | 2 +- sdk/python/pyproject.toml | 6 +++--- 8 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 sdk/js/.claude/settings.local.json.license diff --git a/.github/workflows/js-sdk-release.yml b/.github/workflows/js-sdk-release.yml index e1dbed611..d0caad5de 100644 --- a/.github/workflows/js-sdk-release.yml +++ b/.github/workflows/js-sdk-release.yml @@ -20,7 +20,7 @@ on: permissions: id-token: write - contents: read + contents: write jobs: publish: @@ -74,12 +74,13 @@ jobs: - name: Determine npm dist-tag id: tag run: | + VERSION="${GITHUB_REF_NAME#js-sdk-v}" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then echo "tag=${{ github.event.inputs.npm_tag }}" >> "$GITHUB_OUTPUT" else # auto-detect from git tag: js-sdk-v0.5.8-beta.1 -> beta - RAW_TAG="${GITHUB_REF_NAME}" - if echo "$RAW_TAG" | grep -qiE '(beta|alpha|rc|preview)'; then + if echo "$VERSION" | grep -qiE '(beta|alpha|rc|preview)'; then echo "tag=beta" >> "$GITHUB_OUTPUT" else echo "tag=latest" >> "$GITHUB_OUTPUT" @@ -92,3 +93,19 @@ jobs: NPM_TAG="${{ steps.tag.outputs.tag }}" echo "Publishing with dist-tag: $NPM_TAG" npm publish --access public --provenance --tag "$NPM_TAG" + + - name: GitHub Release + if: github.event_name == 'push' + uses: softprops/action-gh-release@v1 + with: + name: "JS SDK v${{ steps.tag.outputs.version }}" + body: | + ## npm Package + + **Package**: `@phala/dstack-sdk@${{ steps.tag.outputs.version }}` + + **Install**: `npm install @phala/dstack-sdk@${{ steps.tag.outputs.version }}` + + **Dist-tag**: `${{ steps.tag.outputs.tag }}` + + **Registry**: https://www.npmjs.com/package/@phala/dstack-sdk/v/${{ steps.tag.outputs.version }} diff --git a/.github/workflows/python-sdk-release.yml b/.github/workflows/python-sdk-release.yml index eb579f9d6..692bcdd6d 100644 --- a/.github/workflows/python-sdk-release.yml +++ b/.github/workflows/python-sdk-release.yml @@ -19,7 +19,7 @@ on: permissions: id-token: write - contents: read + contents: write jobs: publish: @@ -38,6 +38,10 @@ jobs: working-directory: sdk/python run: pdm build + - name: Parse version + id: version + run: echo "version=${GITHUB_REF_NAME#python-sdk-v}" >> "$GITHUB_OUTPUT" + - name: Publish to PyPI if: github.event_name == 'push' || github.event.inputs.target == 'pypi' uses: pypa/gh-action-pypi-publish@release/v1 @@ -50,3 +54,17 @@ jobs: with: repository-url: https://test.pypi.org/legacy/ packages-dir: sdk/python/dist + + - name: GitHub Release + if: github.event_name == 'push' + uses: softprops/action-gh-release@v1 + with: + name: "Python SDK v${{ steps.version.outputs.version }}" + body: | + ## PyPI Package + + **Package**: `dstack-sdk ${{ steps.version.outputs.version }}` + + **Install**: `pip install dstack-sdk==${{ steps.version.outputs.version }}` + + **Registry**: https://pypi.org/project/dstack-sdk/${{ steps.version.outputs.version }}/ diff --git a/sdk/js/.claude/settings.local.json b/sdk/js/.claude/settings.local.json index 096326dc6..ae4e8b805 100644 --- a/sdk/js/.claude/settings.local.json +++ b/sdk/js/.claude/settings.local.json @@ -17,4 +17,4 @@ ], "deny": [] } -} \ No newline at end of file +} diff --git a/sdk/js/.claude/settings.local.json.license b/sdk/js/.claude/settings.local.json.license new file mode 100644 index 000000000..84ac4efa8 --- /dev/null +++ b/sdk/js/.claude/settings.local.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: © 2025 Phala Network + +SPDX-License-Identifier: Apache-2.0 diff --git a/sdk/js/src/encrypt-env-vars.ts b/sdk/js/src/encrypt-env-vars.ts index 5f9e7d2a9..cef27b9f1 100644 --- a/sdk/js/src/encrypt-env-vars.ts +++ b/sdk/js/src/encrypt-env-vars.ts @@ -64,4 +64,4 @@ export async function encryptEnvVars(envs: EnvVar[], publicKeyHex: string) { result.set(new Uint8Array(encrypted), publicKey.length + iv.length); return uint8ArrayToHex(result); -} \ No newline at end of file +} diff --git a/sdk/js/tsconfig.browser.json b/sdk/js/tsconfig.browser.json index 41e7245e1..cd759b2da 100644 --- a/sdk/js/tsconfig.browser.json +++ b/sdk/js/tsconfig.browser.json @@ -11,8 +11,8 @@ }, "include": [ "src/encrypt-env-vars.browser.ts", - "src/get-compose-hash.browser.ts", + "src/get-compose-hash.browser.ts", "src/verify-env-encrypt-public-key.browser.ts" ], "exclude": ["node_modules", "**/*.test.ts"] -} \ No newline at end of file +} diff --git a/sdk/js/tsconfig.node.json b/sdk/js/tsconfig.node.json index f6b90a0a0..a8004eebf 100644 --- a/sdk/js/tsconfig.node.json +++ b/sdk/js/tsconfig.node.json @@ -11,4 +11,4 @@ }, "include": ["src/**/*"], "exclude": ["node_modules", "**/*.test.ts", "src/*.browser.ts"] -} \ No newline at end of file +} diff --git a/sdk/python/pyproject.toml b/sdk/python/pyproject.toml index e0ba5d331..f0ee13107 100644 --- a/sdk/python/pyproject.toml +++ b/sdk/python/pyproject.toml @@ -4,7 +4,7 @@ [project] name = "dstack-sdk" -version = "0.5.3" +version = "0.5.4b0" description = "dstack SDK for Python" authors = [ {name = "Leechael Yim", email = "yanleech@gmail.com"}, @@ -103,7 +103,7 @@ repository = "pypi" fmt = {composite = ["ruff format src/ tests/", "ruff check --fix src/ tests/"]} format = {composite = ["ruff format src/ tests/", "ruff check --fix src/ tests/"]} -# Linting scripts +# Linting scripts lint = {composite = ["ruff check src/ tests/", "mypy src/"]} check = {composite = ["ruff check src/ tests/", "ruff format --check src/ tests/", "mypy src/"]} @@ -134,4 +134,4 @@ solana = [ ] ethereum = [ "web3", -] \ No newline at end of file +]