Skip to content

Commit 5c37b25

Browse files
authored
Merge pull request #686 from Dstack-TEE/feat/sdk-release-workflows
feat(ci): add trusted publisher release workflows for JS and Python SDKs
2 parents 2af9ac2 + b5602c3 commit 5c37b25

9 files changed

Lines changed: 229 additions & 16 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# SPDX-FileCopyrightText: © 2025 Phala Network <dstack@phala.network>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
name: Publish JS SDK to npm
6+
on:
7+
push:
8+
tags: ['js-sdk-v*']
9+
workflow_dispatch:
10+
inputs:
11+
npm_tag:
12+
description: 'npm dist-tag (latest, beta, canary)'
13+
required: true
14+
default: 'latest'
15+
type: choice
16+
options:
17+
- latest
18+
- beta
19+
- canary
20+
21+
permissions:
22+
id-token: write
23+
contents: write
24+
25+
jobs:
26+
publish:
27+
runs-on: ubuntu-latest
28+
steps:
29+
- uses: actions/checkout@v4
30+
31+
- uses: actions/setup-node@v4
32+
with:
33+
node-version: '20'
34+
registry-url: 'https://registry.npmjs.org'
35+
36+
- name: Verify OIDC token availability
37+
run: |
38+
if [ -n "${ACTIONS_ID_TOKEN_REQUEST_URL}" ] && [ -n "${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" ]; then
39+
echo "OIDC token available"
40+
else
41+
echo "OIDC token NOT available"
42+
echo "Check workflow permissions include 'id-token: write'"
43+
exit 1
44+
fi
45+
46+
- name: Verify repository configuration
47+
working-directory: sdk/js
48+
run: |
49+
echo "Checking repository consistency..."
50+
GIT_REPO=$(git remote get-url origin | sed 's/.*github.com[/:]//; s/.git$//')
51+
PKG_REPO=$(node -e "console.log(require('./package.json').repository?.url || '')" | sed 's|https://github.com/||; s|git+||; s|.git$||')
52+
echo "Git remote: $GIT_REPO"
53+
echo "package.json: $PKG_REPO"
54+
if [ "$GIT_REPO" != "$PKG_REPO" ]; then
55+
echo "Repository mismatch!"
56+
echo "This will cause 422 error during publish"
57+
exit 1
58+
fi
59+
echo "Repositories match"
60+
61+
- name: Install dependencies
62+
working-directory: sdk/js
63+
run: npm install
64+
65+
- name: Build
66+
working-directory: sdk/js
67+
run: npm run build
68+
69+
- name: Determine version and npm dist-tag
70+
id: tag
71+
working-directory: sdk/js
72+
run: |
73+
PKG_VERSION=$(node -e "console.log(require('./package.json').version)")
74+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
75+
VERSION="$PKG_VERSION"
76+
echo "tag=${{ github.event.inputs.npm_tag }}" >> "$GITHUB_OUTPUT"
77+
else
78+
TAG_VERSION="${GITHUB_REF_NAME#js-sdk-v}"
79+
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
80+
echo "::error::tag version ($TAG_VERSION) does not match package.json version ($PKG_VERSION)"
81+
exit 1
82+
fi
83+
VERSION="$TAG_VERSION"
84+
# auto-detect from git tag: js-sdk-v0.5.8-beta.1 -> beta
85+
if echo "$VERSION" | grep -qiE '(beta|alpha|rc|preview)'; then
86+
echo "tag=beta" >> "$GITHUB_OUTPUT"
87+
else
88+
echo "tag=latest" >> "$GITHUB_OUTPUT"
89+
fi
90+
fi
91+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
92+
93+
- name: Publish to npm
94+
working-directory: sdk/js
95+
run: |
96+
NPM_TAG="${{ steps.tag.outputs.tag }}"
97+
echo "Publishing with dist-tag: $NPM_TAG"
98+
npm publish --access public --provenance --tag "$NPM_TAG"
99+
100+
- name: GitHub Release
101+
if: github.event_name == 'push'
102+
uses: softprops/action-gh-release@v1
103+
with:
104+
name: "JS SDK v${{ steps.tag.outputs.version }}"
105+
body: |
106+
## npm Package
107+
108+
**Package**: `@phala/dstack-sdk@${{ steps.tag.outputs.version }}`
109+
110+
**Install**: `npm install @phala/dstack-sdk@${{ steps.tag.outputs.version }}`
111+
112+
**Dist-tag**: `${{ steps.tag.outputs.tag }}`
113+
114+
**Registry**: https://www.npmjs.com/package/@phala/dstack-sdk/v/${{ steps.tag.outputs.version }}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# SPDX-FileCopyrightText: © 2025 Phala Network <dstack@phala.network>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
name: Publish Python SDK to PyPI
6+
on:
7+
push:
8+
tags: ['python-sdk-v*']
9+
workflow_dispatch:
10+
inputs:
11+
target:
12+
description: 'Publish target'
13+
required: true
14+
default: 'pypi'
15+
type: choice
16+
options:
17+
- pypi
18+
- testpypi
19+
20+
permissions:
21+
id-token: write
22+
contents: write
23+
24+
jobs:
25+
publish:
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v4
29+
30+
- uses: actions/setup-python@v5
31+
with:
32+
python-version: '3.11'
33+
34+
- name: Install PDM
35+
run: pip install pdm
36+
37+
- name: Build distribution
38+
working-directory: sdk/python
39+
run: pdm build
40+
41+
- name: Parse and verify version
42+
id: version
43+
working-directory: sdk/python
44+
run: |
45+
PKG_VERSION=$(python -c "
46+
import re
47+
with open('pyproject.toml') as f:
48+
m = re.search(r'^version\s*=\s*\"([^\"]+)\"', f.read(), re.M)
49+
print(m.group(1) if m else '')
50+
")
51+
if [ -z "$PKG_VERSION" ]; then
52+
echo "::error::failed to parse version from pyproject.toml"
53+
exit 1
54+
fi
55+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
56+
VERSION="$PKG_VERSION"
57+
else
58+
TAG_VERSION="${GITHUB_REF_NAME#python-sdk-v}"
59+
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
60+
echo "::error::tag version ($TAG_VERSION) does not match pyproject.toml version ($PKG_VERSION)"
61+
exit 1
62+
fi
63+
VERSION="$TAG_VERSION"
64+
fi
65+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
66+
67+
- name: Publish to PyPI
68+
if: github.event_name == 'push' || github.event.inputs.target == 'pypi'
69+
uses: pypa/gh-action-pypi-publish@release/v1
70+
with:
71+
packages-dir: sdk/python/dist
72+
73+
- name: Publish to TestPyPI
74+
if: github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'testpypi'
75+
uses: pypa/gh-action-pypi-publish@release/v1
76+
with:
77+
repository-url: https://test.pypi.org/legacy/
78+
packages-dir: sdk/python/dist
79+
80+
- name: GitHub Release
81+
if: github.event_name == 'push'
82+
uses: softprops/action-gh-release@v1
83+
with:
84+
name: "Python SDK v${{ steps.version.outputs.version }}"
85+
body: |
86+
## PyPI Package
87+
88+
**Package**: `dstack-sdk ${{ steps.version.outputs.version }}`
89+
90+
**Install**: `pip install dstack-sdk==${{ steps.version.outputs.version }}`
91+
92+
**Registry**: https://pypi.org/project/dstack-sdk/${{ steps.version.outputs.version }}/

sdk/js/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ node_modules/
22
dist/
33
*.log
44
package-lock.json
5+
.claude/settings.local.json
6+
.claude/settings.local.json.license

sdk/js/package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@phala/dstack-sdk",
3-
"version": "0.5.7",
3+
"version": "0.5.8-beta.0",
44
"description": "dstack SDK",
55
"main": "dist/node/index.js",
66
"types": "dist/node/index.d.ts",
@@ -92,14 +92,19 @@
9292
"dstack",
9393
"Phala"
9494
],
95+
"repository": {
96+
"type": "git",
97+
"url": "https://github.com/Dstack-TEE/dstack",
98+
"directory": "sdk/js"
99+
},
95100
"author": "Leechael Yim",
96101
"license": "Apache-2.0",
97102
"dependencies": {
98103
"crypto-browserify": "^3.12.0"
99104
},
100105
"devDependencies": {
101106
"@types/node": "latest",
102-
"typescript": "latest",
107+
"typescript": "^5.7.0",
103108
"vitest": "^3.2.4"
104109
},
105110
"optionalDependencies": {

sdk/js/src/encrypt-env-vars.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export async function encryptEnvVars(envs: EnvVar[], publicKeyHex: string) {
4040
// Import shared key for AES-GCM
4141
const importedShared = await crypto.subtle.importKey(
4242
"raw",
43-
shared,
43+
new Uint8Array(shared),
4444
{ name: "AES-GCM", length: 256 },
4545
true,
4646
["encrypt"],
@@ -64,4 +64,4 @@ export async function encryptEnvVars(envs: EnvVar[], publicKeyHex: string) {
6464
result.set(new Uint8Array(encrypted), publicKey.length + iv.length);
6565

6666
return uint8ArrayToHex(result);
67-
}
67+
}

sdk/js/tsconfig.browser.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
{
22
"extends": "./tsconfig.json",
33
"compilerOptions": {
4-
"target": "es2018",
4+
"target": "es2020",
55
"module": "es2015",
6-
"lib": ["es2018", "dom"],
6+
"lib": ["es2020", "dom"],
77
"outDir": "./dist/browser",
88
"declaration": true,
99
"declarationMap": true,
1010
"sourceMap": true
1111
},
1212
"include": [
1313
"src/encrypt-env-vars.browser.ts",
14-
"src/get-compose-hash.browser.ts",
14+
"src/get-compose-hash.browser.ts",
1515
"src/verify-env-encrypt-public-key.browser.ts"
1616
],
1717
"exclude": ["node_modules", "**/*.test.ts"]
18-
}
18+
}

sdk/js/tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"compilerOptions": {
3-
"target": "es2018",
3+
"target": "es2020",
44
"module": "commonjs",
5-
"lib": ["es2018"],
5+
"lib": ["es2020"],
66
"declaration": true,
77
"declarationMap": true,
88
"sourceMap": true,

sdk/js/tsconfig.node.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"extends": "./tsconfig.json",
33
"compilerOptions": {
4-
"target": "es2018",
4+
"target": "es2020",
55
"module": "commonjs",
6-
"lib": ["es2018"],
6+
"lib": ["es2020"],
77
"outDir": "./dist/node",
88
"declaration": true,
99
"declarationMap": true,
1010
"sourceMap": true
1111
},
1212
"include": ["src/**/*"],
1313
"exclude": ["node_modules", "**/*.test.ts", "src/*.browser.ts"]
14-
}
14+
}

sdk/python/pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
[project]
66
name = "dstack-sdk"
7-
version = "0.5.3"
7+
version = "0.5.4b0"
88
description = "dstack SDK for Python"
99
authors = [
1010
{name = "Leechael Yim", email = "yanleech@gmail.com"},
@@ -103,7 +103,7 @@ repository = "pypi"
103103
fmt = {composite = ["ruff format src/ tests/", "ruff check --fix src/ tests/"]}
104104
format = {composite = ["ruff format src/ tests/", "ruff check --fix src/ tests/"]}
105105

106-
# Linting scripts
106+
# Linting scripts
107107
lint = {composite = ["ruff check src/ tests/", "mypy src/"]}
108108
check = {composite = ["ruff check src/ tests/", "ruff format --check src/ tests/", "mypy src/"]}
109109

@@ -134,4 +134,4 @@ solana = [
134134
]
135135
ethereum = [
136136
"web3",
137-
]
137+
]

0 commit comments

Comments
 (0)