Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
- [ ] Built native assets: `bash src/Kapusch.TikTokApisForAndroidComponents/Native/Android/build.sh`
- [ ] Packed NuGet: `dotnet pack src/Kapusch.TikTokApisForAndroidComponents/Kapusch.TikTokApisForAndroidComponents.csproj -c Release -o artifacts/nuget`
- [ ] Validated package: `scripts/validate-package.sh`
- [ ] Business package (if touched): built, packed and validated with `scripts/validate-business-package.sh`, sample built in Release
- [ ] Public tree scan: `python3 -I scripts/verify-public.py`
- [ ] Updated dependency locks, Gradle verification and notices if upstream inputs changed
- [ ] Updated docs if behavior or integration changed
48 changes: 48 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ permissions:
contents: read

jobs:
public_scan:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Scan tracked files for private terms and credentials
env:
# Private terms kept outside the public tree; the scan skips that check when unset.
KAPUSCH_PRIVATE_TERMS: ${{ secrets.KAPUSCH_PRIVATE_TERMS }}
run: python3 -I scripts/verify-public.py

build_pack_android:
# Pinned: on ubuntu-26.04 (ubuntu-latest since Oct 2026) the android workload install fails with "Disk quota exceeded".
runs-on: ubuntu-24.04
Expand Down Expand Up @@ -37,3 +47,41 @@ jobs:
with:
name: nuget
path: artifacts/nuget/*.nupkg

build_pack_business_android:
# Same pinned runner and toolchain as the Share package job.
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.203"
- uses: actions/setup-java@v4
with:
distribution: microsoft
java-version: "21"
- uses: android-actions/setup-android@v4
with:
cmdline-tools-version: '12266719'
packages: 'platform-tools'
- name: Install Android workload
run: dotnet workload install android --version 10.0.203
- name: Build locked TikTok Business SDK wrapper
run: bash src/Kapusch.TikTok.Business.Android/Native/Android/build.sh
- name: Pack
run: dotnet pack src/Kapusch.TikTok.Business.Android/Kapusch.TikTok.Business.Android.csproj -c Release -o artifacts/nuget
- name: Validate package
run: scripts/validate-business-package.sh
- name: Clean-consumer sample (Release, R8, with Billing and Install Referrer)
run: >-
dotnet build samples/Kapusch.TikTok.Business.Android.Sample/Kapusch.TikTok.Business.Android.Sample.csproj
-c Release -p:RuntimeIdentifier=android-arm64
- name: Clean-consumer sample (Release, R8, without optional dependencies)
run: |
rm -rf samples/Kapusch.TikTok.Business.Android.Sample/bin samples/Kapusch.TikTok.Business.Android.Sample/obj
dotnet build samples/Kapusch.TikTok.Business.Android.Sample/Kapusch.TikTok.Business.Android.Sample.csproj \
-c Release -p:RuntimeIdentifier=android-arm64 -p:SampleWithoutOptionalAndroidDeps=true
- uses: actions/upload-artifact@v4
with:
name: nuget-business
path: artifacts/nuget/Kapusch.TikTok.Business.Android.*.nupkg
123 changes: 109 additions & 14 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
name: publish

# One workflow releases either package so the NuGet Trusted Publishing policy
# bound to this file covers both:
# - tags v* -> Kapusch.TikTok.Android (OpenSDK Share), unchanged contract;
# - tags business-v* -> Kapusch.TikTok.Business.Android.
# GitHub tag filters match the whole ref name, so "v*" never matches "business-v*".
on:
workflow_dispatch:
inputs:
package:
description: "Package to publish"
required: true
default: share
type: choice
options:
- share
- business
manual_version:
description: "Publish this version to NuGet.org; leave empty for a GitHub Packages preview"
required: false
type: string
push:
tags:
- "v*"
- "business-v*"

permissions:
contents: read
Expand All @@ -28,26 +42,84 @@ jobs:
with:
fetch-depth: 0

- name: Select package
id: package
shell: bash
env:
REF: ${{ github.ref }}
EVENT: ${{ github.event_name }}
INPUT_PACKAGE: ${{ github.event.inputs.package }}
run: |
if [[ "$REF" =~ ^refs/tags/business-v ]]; then
TAG_PACKAGE="business"
elif [[ "$REF" =~ ^refs/tags/v ]]; then
TAG_PACKAGE="share"
else
TAG_PACKAGE=""
fi

if [[ "$EVENT" == "workflow_dispatch" ]]; then
PACKAGE="${INPUT_PACKAGE:-share}"
if [[ -n "$TAG_PACKAGE" && "$TAG_PACKAGE" != "$PACKAGE" ]]; then
echo "ERROR: tag $REF belongs to the $TAG_PACKAGE package, not $PACKAGE."
exit 1
fi
else
PACKAGE="$TAG_PACKAGE"
fi

case "$PACKAGE" in
share)
echo "package_id=Kapusch.TikTok.Android" >> "$GITHUB_OUTPUT"
echo "csproj=src/Kapusch.TikTokApisForAndroidComponents/Kapusch.TikTokApisForAndroidComponents.csproj" >> "$GITHUB_OUTPUT"
echo "native_build=src/Kapusch.TikTokApisForAndroidComponents/Native/Android/build.sh" >> "$GITHUB_OUTPUT"
echo "validator=scripts/validate-package.sh" >> "$GITHUB_OUTPUT"
echo "tag_prefix=v" >> "$GITHUB_OUTPUT"
;;
business)
echo "package_id=Kapusch.TikTok.Business.Android" >> "$GITHUB_OUTPUT"
echo "csproj=src/Kapusch.TikTok.Business.Android/Kapusch.TikTok.Business.Android.csproj" >> "$GITHUB_OUTPUT"
echo "native_build=src/Kapusch.TikTok.Business.Android/Native/Android/build.sh" >> "$GITHUB_OUTPUT"
echo "validator=scripts/validate-business-package.sh" >> "$GITHUB_OUTPUT"
echo "tag_prefix=business-v" >> "$GITHUB_OUTPUT"
;;
*)
echo "ERROR: cannot determine the package to publish for $EVENT on $REF."
exit 1
;;
esac
echo "package=$PACKAGE" >> "$GITHUB_OUTPUT"
echo "Publishing package: $PACKAGE"

- name: Determine package version
id: version
shell: bash
env:
REF: ${{ github.ref }}
MANUAL_VERSION: ${{ github.event.inputs.manual_version }}
PACKAGE: ${{ steps.package.outputs.package }}
CSPROJ: ${{ steps.package.outputs.csproj }}
TAG_PREFIX: ${{ steps.package.outputs.tag_prefix }}
run: |
REF="${{ github.ref }}"
MANUAL_VERSION="${{ github.event.inputs.manual_version }}"
CSPROJ="src/Kapusch.TikTokApisForAndroidComponents/Kapusch.TikTokApisForAndroidComponents.csproj"
BASE_VERSION=$(grep -oE '<Version>[^<]+' "$CSPROJ" | head -n 1 | sed 's/<Version>//')

if [[ -n "$MANUAL_VERSION" ]]; then
VERSION="$MANUAL_VERSION"
PUBLISH_TARGET="nuget"
elif [[ "$REF" =~ ^refs/tags/v ]]; then
VERSION="${REF#refs/tags/v}"
elif [[ "$REF" == refs/tags/${TAG_PREFIX}* ]]; then
VERSION="${REF#refs/tags/${TAG_PREFIX}}"
PUBLISH_TARGET="nuget"
else
VERSION="${BASE_VERSION}-preview.${{ github.run_number }}.$(git rev-parse --short HEAD)"
PUBLISH_TARGET="github"
fi

if [[ "$PACKAGE" == "business" && "$PUBLISH_TARGET" == "nuget" ]] \
&& ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$ ]]; then
echo "ERROR: Business releases must be X.Y.Z or X.Y.Z-rc.N (got '$VERSION')."
exit 1
fi

if [[ "$PUBLISH_TARGET" == "nuget" ]]; then
git fetch origin master
if ! git merge-base --is-ancestor "${{ github.sha }}" origin/master; then
Expand All @@ -59,6 +131,21 @@ jobs:
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "publish_target=$PUBLISH_TARGET" >> "$GITHUB_OUTPUT"

- name: Refuse to republish an existing NuGet.org version
if: steps.version.outputs.publish_target == 'nuget'
shell: bash
env:
PACKAGE_ID: ${{ steps.package.outputs.package_id }}
VERSION: ${{ steps.version.outputs.version }}
run: |
id_lower="$(tr '[:upper:]' '[:lower:]' <<<"$PACKAGE_ID")"
version_lower="$(tr '[:upper:]' '[:lower:]' <<<"$VERSION")"
index="$(curl -fsS "https://api.nuget.org/v3-flatcontainer/$id_lower/index.json" || true)"
if grep -Fq "\"$version_lower\"" <<<"$index"; then
echo "ERROR: $PACKAGE_ID $VERSION is already on NuGet.org. Versions are immutable: publish a new version."
exit 1
fi

- uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.203"
Expand All @@ -77,18 +164,22 @@ jobs:
- name: Install Android workload
run: dotnet workload install android --version 10.0.203

- name: Build pinned Core + Share wrapper
run: bash src/Kapusch.TikTokApisForAndroidComponents/Native/Android/build.sh
- name: Build pinned native assets
run: bash "${{ steps.package.outputs.native_build }}"

- name: Pack
run: |
dotnet pack src/Kapusch.TikTokApisForAndroidComponents/Kapusch.TikTokApisForAndroidComponents.csproj \
dotnet pack "${{ steps.package.outputs.csproj }}" \
-c Release \
-o artifacts/nuget \
/p:PackageVersion="${{ steps.version.outputs.version }}"
/p:PackageVersion="${{ steps.version.outputs.version }}" \
/p:ContinuousIntegrationBuild=true \
/p:RepositoryCommit="${{ github.sha }}"

- name: Validate package
run: scripts/validate-package.sh "artifacts/nuget/Kapusch.TikTok.Android.${{ steps.version.outputs.version }}.nupkg"
run: |
"${{ steps.package.outputs.validator }}" \
"artifacts/nuget/${{ steps.package.outputs.package_id }}.${{ steps.version.outputs.version }}.nupkg"

- name: Push to NuGet.org
if: steps.version.outputs.publish_target == 'nuget'
Expand All @@ -100,17 +191,21 @@ jobs:
- name: Push to NuGet.org
if: steps.version.outputs.publish_target == 'nuget'
run: |
dotnet nuget push artifacts/nuget/*.nupkg \
dotnet nuget push "artifacts/nuget/${{ steps.package.outputs.package_id }}.${{ steps.version.outputs.version }}.nupkg" \
--api-key "${{ steps.nuget_login.outputs.NUGET_API_KEY }}" \
--source "https://api.nuget.org/v3/index.json" \
--skip-duplicate
--source "https://api.nuget.org/v3/index.json"

- name: Push to GitHub Packages
if: steps.version.outputs.publish_target == 'github'
env:
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
dotnet nuget push artifacts/nuget/*.nupkg \
dotnet nuget push "artifacts/nuget/${{ steps.package.outputs.package_id }}.${{ steps.version.outputs.version }}.nupkg" \
--api-key "$NUGET_AUTH_TOKEN" \
--source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \
--skip-duplicate

- uses: actions/upload-artifact@v4
with:
name: ${{ steps.package.outputs.package_id }}-${{ steps.version.outputs.version }}
path: artifacts/nuget/${{ steps.package.outputs.package_id }}.${{ steps.version.outputs.version }}.nupkg
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ src/**/Native/Android/local.properties
src/**/Native/Android/upstream/
src/**/Native/Android/tiktok-open-sdk-core
src/**/Native/Android/tiktok-open-sdk-share
samples/**/Assets/*.local.json
15 changes: 11 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,31 @@
## Goals

- Produce a reproducible `Kapusch.TikTok.Android` NuGet for TikTok OpenSDK Core + Share 2.3.1.
- Do not commit client keys, signing material, or app-specific secrets.
- Produce a reproducible `Kapusch.TikTok.Business.Android` NuGet for the TikTok Business SDK 1.7.1.
- Keep the two packages independent: no shared native artifact, separate locks, validators and release tags (`v*` and `business-v*`).
- Do not commit client keys, app secrets, signing material, or app-specific secrets.

## Packaging constraints

- Public OSS repo: keep docs and samples generic and not app-specific.
- Consumers must never download native dependencies. Build and package all required AAR/JAR artifacts here.
- Keep the OpenSDK version, source archive and checksums pinned in `DependencyLocks/Android/lockstate.txt` and Gradle verification metadata.
- Keep the Business SDK AAR, its license and the Gradle distribution pinned in `DependencyLocks/Android/business-lockstate.txt`, plus the Business Gradle verification metadata.
- The Business package ships only the upstream AAR and the interop AAR. Shared AndroidX/Kotlin classes come from managed NuGets; Google Play Billing and Install Referrer stay consumer-owned. Never bundle them.
- Nothing may initialize the Business SDK or reach the network before `TikTokBusinessSdk.InitializeAsync` is called.
- Do not patch upstream TikTok sources silently. Document and checksum-audit every necessary patch.

## Repo layout

- `src/Kapusch.TikTokApisForAndroidComponents/` — managed API, native wrapper and buildTransitive assets.
- `DependencyLocks/Android/` — locked upstream source and native dependency inventory.
- `src/Kapusch.TikTokApisForAndroidComponents/` — Share package: managed API, native wrapper and buildTransitive assets.
- `src/Kapusch.TikTok.Business.Android/` — Business package: managed API, Kotlin JNI facade and buildTransitive assets.
- `DependencyLocks/Android/` — locked upstream inputs for both packages.
- `scripts/` — deterministic package validation.
- `Docs/` — integration and source-mode documentation.
- `samples/` — secret-free integration example.

## Safety

- Do not add dependency ingestion paths without documenting and locking them.
- When package contents change, update `scripts/validate-package.sh` in the same commit.
- When package contents change, update `scripts/validate-package.sh` (Share) or `scripts/validate-business-package.sh` (Business) in the same commit.
- Keep every tracked file generic: `scripts/verify-public.py` must pass.
29 changes: 27 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,38 @@ dotnet pack src/Kapusch.TikTokApisForAndroidComponents/Kapusch.TikTokApisForAndr
scripts/validate-package.sh
```

For direct project consumption, see [`Docs/SourceMode.md`](Docs/SourceMode.md).
Business package:

```sh
bash src/Kapusch.TikTok.Business.Android/Native/Android/build.sh
dotnet pack src/Kapusch.TikTok.Business.Android/Kapusch.TikTok.Business.Android.csproj \
-c Release \
-o artifacts/nuget
scripts/validate-business-package.sh
dotnet build samples/Kapusch.TikTok.Business.Android.Sample/Kapusch.TikTok.Business.Android.Sample.csproj \
-c Release -p:RuntimeIdentifier=android-arm64
python3 -I scripts/verify-public.py
```

`verify-public.py` checks tracked files for pasted App Secrets and checks that the sample
`TikTokBusiness.template.json` keeps placeholder values. It also rejects private terms, such as
the names of private consuming apps, that must never be written in this public tree. Those terms
are never tracked: the scanner reads them at run time, one per line or comma-separated, from the
`KAPUSCH_PRIVATE_TERMS` environment variable and from the untracked file `.git/info/private-terms`
(`#` starts a comment). Failures name the file and `private term #N`, never the term. The check
is skipped with a notice when no term is configured. CI reads the variable from the
`KAPUSCH_PRIVATE_TERMS` repository secret.

For direct project consumption, see [`Docs/SourceMode.md`](Docs/SourceMode.md) and
[`Docs/Business/SourceMode.md`](Docs/Business/SourceMode.md). Releases follow
[`Docs/Release.md`](Docs/Release.md).

## Pull requests

- Keep changes focused.
- Never commit TikTok credentials or signing material.
- Never commit TikTok credentials, app secrets or signing material.
- Update `DependencyLocks/Android/lockstate.txt`, Gradle verification metadata and third-party notices together when changing OpenSDK or runtime dependencies.
- Update `DependencyLocks/Android/business-lockstate.txt`, the Business Gradle verification metadata, `THIRD_PARTY_NOTICES.Business.md`, `BundledSdkVersion` and the validator together when changing the Business SDK. Regenerate verification metadata with `gradle --write-verification-metadata sha256` and review the diff.
- Validate both bundled-runtime and consumer-provided-runtime modes when changing package injection.

Contributions are licensed under the repository MIT license.
8 changes: 8 additions & 0 deletions DependencyLocks/Android/business-lockstate.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Kapusch.TikTok.Business.Android upstream inputs: <url> <sha256> <filename>
# TikTok Business SDK Android 1.7.1 release AAR (byte-identical to the JitPack
# artifact com.github.tiktok:tiktok-business-android-sdk:1.7.1).
https://github.com/tiktok/tiktok-business-android-sdk/releases/download/1.7.1/tiktok-business-android-sdk-1.7.1.aar 629561d285b1cfb7b6c513f59a68c4f38f0d3cc5d2c85c7b8a57a8a957bcbf52 tiktok-business-android-sdk-1.7.1.aar
# Upstream license at the same tag.
https://raw.githubusercontent.com/tiktok/tiktok-business-android-sdk/1.7.1/LICENSE d9245608d897216466775f788e54c0986900ce222867477b5d96acd3c935bb14 TIKTOK-BUSINESS-SDK-LICENSE
# Gradle distribution used to build the interop AAR.
https://services.gradle.org/distributions/gradle-8.7-bin.zip 544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d gradle-8.7-bin.zip
Loading
Loading