-
Notifications
You must be signed in to change notification settings - Fork 501
SDK specs #1117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SDK specs #1117
Changes from all commits
6eb4746
493f6e7
e5f80ce
66b066d
bcb4aa8
1c2c584
2f0d34d
154d2a6
0220219
999956b
6597d7e
f9a4a17
1034d2e
33e3291
a8869ea
a64c59f
f183e07
a91e652
7a2a324
738ded9
1b493ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,100 @@ | ||||||||||||||||||||||||||||||||||
| name: Publish Swift SDK to prerelease repo | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||||
| push: | ||||||||||||||||||||||||||||||||||
| branches: | ||||||||||||||||||||||||||||||||||
| - main | ||||||||||||||||||||||||||||||||||
| paths: | ||||||||||||||||||||||||||||||||||
| - 'sdks/implementations/swift/**' | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| concurrency: | ||||||||||||||||||||||||||||||||||
| group: ${{ github.workflow }}-${{ github.ref }} | ||||||||||||||||||||||||||||||||||
| cancel-in-progress: false # Don't cancel publishing in progress | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||
| publish: | ||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||
| - name: Checkout source repo | ||||||||||||||||||||||||||||||||||
| uses: actions/checkout@v4 | ||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||
| path: source | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Read version from package.json | ||||||||||||||||||||||||||||||||||
| id: version | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| VERSION=$(jq -r '.version' source/sdks/implementations/swift/package.json) | ||||||||||||||||||||||||||||||||||
| echo "version=$VERSION" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||||
| echo "Swift SDK version: $VERSION" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Check if tag already exists in target repo | ||||||||||||||||||||||||||||||||||
| id: check-tag | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| TAG="v${{ steps.version.outputs.version }}" | ||||||||||||||||||||||||||||||||||
| echo "Checking if tag $TAG exists in stack-auth/swift-sdk-prerelease..." | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Use the GitHub API to check if the tag exists | ||||||||||||||||||||||||||||||||||
| HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \ | ||||||||||||||||||||||||||||||||||
| -H "Authorization: Bearer ${{ secrets.SWIFT_SDK_PUBLISH_TOKEN }}" \ | ||||||||||||||||||||||||||||||||||
| -H "Accept: application/vnd.github+json" \ | ||||||||||||||||||||||||||||||||||
| "https://api.github.com/repos/stack-auth/swift-sdk-prerelease/git/refs/tags/$TAG") | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if [ "$HTTP_STATUS" = "200" ]; then | ||||||||||||||||||||||||||||||||||
| echo "Tag $TAG already exists, skipping publish" | ||||||||||||||||||||||||||||||||||
| echo "exists=true" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||
| echo "Tag $TAG does not exist, will publish" | ||||||||||||||||||||||||||||||||||
| echo "exists=false" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Clone target repo | ||||||||||||||||||||||||||||||||||
| if: steps.check-tag.outputs.exists == 'false' | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| git clone https://x-access-token:${{ secrets.SWIFT_SDK_PUBLISH_TOKEN }}@github.com/stack-auth/swift-sdk-prerelease.git target | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Copy Swift SDK to target repo | ||||||||||||||||||||||||||||||||||
| if: steps.check-tag.outputs.exists == 'false' | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| # Remove all files except .git from target | ||||||||||||||||||||||||||||||||||
| cd target | ||||||||||||||||||||||||||||||||||
| find . -maxdepth 1 -not -name '.git' -not -name '.' -exec rm -rf {} + | ||||||||||||||||||||||||||||||||||
| cd .. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Copy everything from Swift SDK | ||||||||||||||||||||||||||||||||||
| cp -r source/sdks/implementations/swift/* target/ | ||||||||||||||||||||||||||||||||||
| cp source/sdks/implementations/swift/.gitignore target/ 2>/dev/null || true | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Remove package.json (it's only for turborepo integration, not part of the Swift package) | ||||||||||||||||||||||||||||||||||
| rm -f target/package.json | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Commit and push to target repo | ||||||||||||||||||||||||||||||||||
| if: steps.check-tag.outputs.exists == 'false' | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| cd target | ||||||||||||||||||||||||||||||||||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||||||||||||||||||||||||||||||||||
| git config user.name "github-actions[bot]" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| git add -A | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Check if there are changes to commit | ||||||||||||||||||||||||||||||||||
| if git diff --staged --quiet; then | ||||||||||||||||||||||||||||||||||
| echo "No changes to commit" | ||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||
| git commit -m "Release v${{ steps.version.outputs.version }}" | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Create and push tag | ||||||||||||||||||||||||||||||||||
| TAG="v${{ steps.version.outputs.version }}" | ||||||||||||||||||||||||||||||||||
| git tag "$TAG" | ||||||||||||||||||||||||||||||||||
| git push origin main --tags | ||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Workflow creates version tag without code changesMedium Severity The workflow creates and pushes a version tag even when there are no code changes to commit. If someone bumps the version in |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| echo "Successfully published Swift SDK v${{ steps.version.outputs.version }}" | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+85
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The git push command always executes and pushes tags even when no changes were committed, which could result in orphaned tags or failed pushes to an empty repository state. View DetailsAnalysisGit tag created and pushed even when no commit was madeWhat fails: In How to reproduce:
Result: Tag is created and pushed pointing to the existing HEAD commit, even though no new commit was made. This can occur when:
Expected behavior: Tag creation and push should only occur after a successful commit. According to GitHub Actions bash script best practices, operations that depend on a prior step should be conditional on that step's success. Fix applied: Moved |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Summary | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| if [ "${{ steps.check-tag.outputs.exists }}" = "true" ]; then | ||||||||||||||||||||||||||||||||||
| echo "::notice::Skipped publishing - tag v${{ steps.version.outputs.version }} already exists" | ||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||
| echo "::notice::Published Swift SDK v${{ steps.version.outputs.version }} to stack-auth/swift-sdk-prerelease" | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| import { exec } from "child_process"; | ||
| import * as fs from "fs"; | ||
| import * as path from "path"; | ||
| import { describe } from "vitest"; | ||
| import { it } from "../helpers"; | ||
|
|
||
| // Find all SDK implementations that have a package.json | ||
| function findSdkImplementations(): string[] { | ||
| const implementationsDir = path.resolve(__dirname, "../../../../sdks/implementations"); | ||
|
|
||
| if (!fs.existsSync(implementationsDir)) { | ||
| return []; | ||
| } | ||
|
|
||
| const entries = fs.readdirSync(implementationsDir, { withFileTypes: true }); | ||
| const sdkDirs: string[] = []; | ||
|
|
||
| for (const entry of entries) { | ||
| if (entry.isDirectory()) { | ||
| const packageJsonPath = path.join(implementationsDir, entry.name, "package.json"); | ||
| if (fs.existsSync(packageJsonPath)) { | ||
| sdkDirs.push(entry.name); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return sdkDirs; | ||
| } | ||
|
|
||
| const sdkImplementations = findSdkImplementations(); | ||
|
|
||
| describe("SDK implementation tests", () => { | ||
| for (const sdk of sdkImplementations) { | ||
| describe(`${sdk} SDK`, () => { | ||
| it("runs tests successfully", async ({ expect }) => { | ||
| const sdkDir = path.resolve(__dirname, `../../../../sdks/implementations/${sdk}`); | ||
|
|
||
| const [error, stdout, stderr] = await new Promise<[Error | null, string, string]>((resolve) => { | ||
| exec("pnpm run test", { cwd: sdkDir }, (error, stdout, stderr) => { | ||
| resolve([error, stdout, stderr]); | ||
| }); | ||
| }); | ||
|
|
||
| expect( | ||
| error, | ||
| `Expected ${sdk} SDK tests to pass!\n\n\n\nstdout: ${stdout}\n\n\n\nstderr: ${stderr}` | ||
| ).toBeNull(); | ||
| }, 300_000); // 5 minute timeout for SDK tests | ||
| }); | ||
| } | ||
|
|
||
| // If no SDKs found, add a placeholder test so the describe block isn't empty | ||
| if (sdkImplementations.length === 0) { | ||
| it("has no SDK implementations to test", ({ expect }) => { | ||
| expect(true).toBe(true); | ||
| }); | ||
| } | ||
| }); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,5 +3,7 @@ packages: | |
| - apps/* | ||
| - examples/* | ||
| - docs | ||
| - sdks/* | ||
| - sdks/implementations/* | ||
|
|
||
| minimumReleaseAge: 2880 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| xcuserdata/ | ||
| *.hmap | ||
| *.ipa | ||
| *.dSYM.zip | ||
| *.dSYM | ||
| timeline.xctimeline | ||
| playground.xcworkspace | ||
| .build/ | ||
| Carthage/Build/ | ||
| fastlane/report.xml | ||
| fastlane/Preview.html | ||
| fastlane/screenshots/**/*.png | ||
| fastlane/test_output |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| // swift-tools-version: 5.9 | ||
| import PackageDescription | ||
|
|
||
| let package = Package( | ||
| name: "StackAuthMacOS", | ||
| platforms: [ | ||
| .macOS(.v14) | ||
| ], | ||
| dependencies: [ | ||
| .package(name: "StackAuth", path: "../..") | ||
| ], | ||
| targets: [ | ||
| .executableTarget( | ||
| name: "StackAuthMacOS", | ||
| dependencies: [ | ||
| .product(name: "StackAuth", package: "StackAuth") | ||
| ], | ||
| path: "StackAuthMacOS" | ||
| ) | ||
| ] | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| # Stack Auth macOS Example | ||
|
|
||
| A comprehensive macOS SwiftUI application for testing all Stack Auth SDK functions interactively. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - macOS 14.0+ | ||
| - Swift 5.9+ | ||
| - A running Stack Auth backend (default: `http://localhost:8102`) | ||
|
|
||
| ## Running the Example | ||
|
|
||
| 1. Start the Stack Auth backend: | ||
| ```bash | ||
| cd /path/to/stack-2 | ||
| pnpm run dev | ||
| ``` | ||
|
|
||
| 2. Open and run the example: | ||
| ```bash | ||
| cd Examples/StackAuthMacOS | ||
| swift run | ||
| ``` | ||
|
|
||
| Or open in Xcode: | ||
| ```bash | ||
| open Package.swift | ||
| ``` | ||
|
|
||
| ## Features | ||
|
|
||
| The example app provides a sidebar navigation with the following sections: | ||
|
|
||
| ### Configuration | ||
| - **Settings**: Configure API base URL, project ID, and API keys | ||
| - **Logs**: View real-time logs of all SDK operations | ||
|
|
||
| ### Client App Testing | ||
| - **Authentication** | ||
| - Sign up with email/password | ||
| - Sign in with credentials | ||
| - Sign in with wrong password (error testing) | ||
| - Sign out | ||
| - Get current user | ||
| - Get user (or throw) | ||
|
|
||
| - **User Management** | ||
| - Set display name | ||
| - Update client metadata | ||
| - Update password | ||
| - Get access/refresh tokens | ||
| - Get auth headers | ||
| - Get partial user from token | ||
|
|
||
| - **Teams** | ||
| - Create team | ||
| - List user's teams | ||
| - Get team by ID | ||
| - List team members | ||
|
|
||
| - **Contact Channels** | ||
| - List contact channels | ||
|
|
||
| - **OAuth** | ||
| - Generate OAuth URLs for Google, GitHub, Microsoft | ||
| - Test PKCE code generation | ||
|
|
||
| - **Tokens** | ||
| - Get access token (JWT format) | ||
| - Get refresh token | ||
| - Get auth headers | ||
| - Test different token stores | ||
|
|
||
| ### Server App Testing | ||
| - **Server Users** | ||
| - Create user (basic and with all options) | ||
| - List users with pagination | ||
| - Get user by ID | ||
| - Delete user | ||
|
|
||
| - **Server Teams** | ||
| - Create team | ||
| - List all teams | ||
| - Add/remove users from teams | ||
| - List team users | ||
| - Delete team | ||
|
|
||
| - **Sessions** | ||
| - Create session (impersonation) | ||
| - Use session tokens with client app | ||
|
|
||
| ## Default Configuration | ||
|
|
||
| The example is pre-configured for local development: | ||
| - Base URL: `http://localhost:8102` | ||
| - Project ID: `internal` | ||
| - Publishable Key: `this-publishable-client-key-is-for-local-development-only` | ||
| - Secret Key: `this-secret-server-key-is-for-local-development-only` | ||
|
|
||
| ## SDK Functions Covered | ||
|
|
||
| | Category | Functions | | ||
| |----------|-----------| | ||
| | Auth | signUpWithCredential, signInWithCredential, signOut, getUser, getOAuthUrl | | ||
| | User | setDisplayName, update (metadata), updatePassword, getAccessToken, getRefreshToken, getAuthHeaders, getPartialUser | | ||
| | Teams | createTeam, listTeams, getTeam, listUsers (team members) | | ||
| | Contact | listContactChannels | | ||
| | Server Users | createUser, listUsers, getUser, delete, update (metadata, password) | | ||
| | Server Teams | createTeam, listTeams, getTeam, addUser, removeUser, listUsers, delete | | ||
| | Sessions | createSession | | ||
| | Errors | EmailPasswordMismatchError, UserNotSignedInError, PasswordConfirmationMismatchError | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fail fast on non-404 API errors during tag check.
Right now 401/403/5xx are treated as “tag missing,” which can mask auth/rate-limit failures and attempt a publish anyway.
🛠️ Suggested fix
🤖 Prompt for AI Agents