diff --git a/.github/workflows/contract-deploy.yml b/.github/workflows/contract-deploy.yml new file mode 100644 index 0000000..a7ebfd7 --- /dev/null +++ b/.github/workflows/contract-deploy.yml @@ -0,0 +1,86 @@ +name: Contract Deploy & Auto-Regenerate Bindings + +on: + push: + branches: + - main + paths: + - 'contracts/**' + - 'scripts/deploy.sh' + - 'scripts/generate-contract-bindings.sh' + workflow_dispatch: + +jobs: + deploy-and-regenerate: + name: Deploy Contract, Auto-Regenerate Bindings & Verify TS + runs-on: ubuntu-latest + + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown + + - name: Install Soroban CLI + run: | + cargo install --locked soroban-cli --version 21.0.0 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + + - name: Create Test Account / Prepare Secret + run: | + if [ -z "${{ secrets.SECRET_KEY }}" ]; then + echo "SECRET_KEY secret not set, generating temporary deployer account..." + DEPLOYER_SECRET=$(soroban keys generate deployer --network testnet --format secret) + DEPLOYER_PUBLIC=$(soroban keys address deployer) + echo "SECRET_KEY=$DEPLOYER_SECRET" >> $GITHUB_ENV + echo "DEPLOYER_PUBLIC=$DEPLOYER_PUBLIC" >> $GITHUB_ENV + curl -s -X GET "https://friendbot.stellar.org?addr=$DEPLOYER_PUBLIC" > /dev/null + else + echo "Using SECRET_KEY from secrets" + echo "SECRET_KEY=${{ secrets.SECRET_KEY }}" >> $GITHUB_ENV + fi + + - name: Build and Deploy Contract to Testnet + run: | + chmod +x scripts/deploy.sh + ./scripts/deploy.sh + CONTRACT_ID=$(cat contracts/contract_id.txt) + echo "CONTRACT_ID=$CONTRACT_ID" >> $GITHUB_ENV + echo "Contract deployed successfully with ID: $CONTRACT_ID" + + - name: Auto-Regenerate Contract Bindings + run: | + chmod +x scripts/generate-contract-bindings.sh + npm run gen:bindings + + - name: Validate Frontend TypeScript Compilation + working-directory: frontend + run: | + npx tsc --noEmit + + - name: Create Pull Request with Updated Bindings + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore(contracts): auto-regenerate contract bindings after testnet deploy" + title: "chore(contracts): update contract bindings after deploy" + body: | + Automated PR created after testnet contract deployment. + + - Deployed Contract ID: `${{ env.CONTRACT_ID }}` + - TypeScript bindings generated via `npm run gen:bindings` + - Frontend TypeScript check (`tsc --noEmit`) verified clean compilation + branch: auto-update-contract-bindings + delete-branch: true diff --git a/.github/workflows/contract-smoke.yml b/.github/workflows/contract-smoke.yml index 04c65ea..81b63c5 100644 --- a/.github/workflows/contract-smoke.yml +++ b/.github/workflows/contract-smoke.yml @@ -10,6 +10,9 @@ jobs: smoke-test: name: Deploy and Smoke Test runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write steps: - name: Checkout code @@ -50,6 +53,37 @@ jobs: echo "CONTRACT_ID=$CONTRACT_ID" >> $GITHUB_ENV echo "Contract deployed with ID: $CONTRACT_ID" + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + + - name: Auto-Regenerate Contract Bindings + run: | + chmod +x scripts/generate-contract-bindings.sh + npm run gen:bindings + + - name: Validate Frontend TypeScript Compilation + working-directory: frontend + run: | + npx tsc --noEmit + + - name: Create Pull Request with Updated Bindings + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore(contracts): auto-regenerate contract bindings after testnet deploy" + title: "chore(contracts): update contract bindings after smoke test deploy" + body: | + Automated PR created after testnet contract deployment in smoke test. + + - Deployed Contract ID: `${{ env.CONTRACT_ID }}` + - TypeScript bindings auto-generated via `npm run gen:bindings` + - Frontend TypeScript check (`tsc --noEmit`) verified clean compilation + branch: auto-update-contract-bindings + delete-branch: true + - name: Initialize Contract run: | # Call initialize(admin: Address) diff --git a/docs/CONTRACT_BINDINGS.md b/docs/CONTRACT_BINDINGS.md index 40d0b34..783c269 100644 --- a/docs/CONTRACT_BINDINGS.md +++ b/docs/CONTRACT_BINDINGS.md @@ -316,17 +316,35 @@ public method: ## CI / automated regeneration -To regenerate bindings in a CI pipeline, add a step after deployment: +Automated contract binding regeneration runs in CI after testnet contract deployment (`.github/workflows/contract-deploy.yml` and `.github/workflows/contract-smoke.yml`). + +Workflow steps after contract deployment: + +1. **Auto-generate contract bindings:** + Runs `npm run gen:bindings` using the deployed `CONTRACT_ID` (either from environment variable or automatically read from `contracts/contract_id.txt`). + +2. **Frontend TypeScript compilation check:** + Validates that the generated TypeScript bindings compile cleanly without errors using `npx tsc --noEmit` in `frontend/`. + +3. **Automated Pull Request creation:** + If generated bindings differ from repository state, CI automatically creates a pull request (branch: `auto-update-contract-bindings`) containing the updated bindings. ```yaml -- name: Generate contract bindings - env: - CONTRACT_ID: ${{ steps.deploy.outputs.contract_id }} +- name: Auto-Regenerate Contract Bindings run: npm run gen:bindings -``` -The generated files do not need to be committed they can be regenerated from -the deployed contract ID on every CI run. +- name: Validate Frontend TypeScript Compilation + working-directory: frontend + run: npx tsc --noEmit + +- name: Create PR with Updated Bindings + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore(contracts): auto-regenerate contract bindings after testnet deploy" + title: "chore(contracts): update contract bindings after deploy" + branch: auto-update-contract-bindings +``` --- diff --git a/scripts/generate-contract-bindings.sh b/scripts/generate-contract-bindings.sh index f6d677c..1b17e22 100755 --- a/scripts/generate-contract-bindings.sh +++ b/scripts/generate-contract-bindings.sh @@ -30,6 +30,11 @@ NETWORK_PASSPHRASE="${NETWORK_PASSPHRASE:-Test SDF Network ; September 2015}" RPC_URL="${RPC_URL:-https://soroban-testnet.stellar.org:443}" # ── Checks ──────────────────────────────────────────────────────────────── +if [ -z "$CONTRACT_ID" ] && [ -f "contracts/contract_id.txt" ]; then + CONTRACT_ID=$(cat contracts/contract_id.txt) + echo -e "${YELLOW}Notice: CONTRACT_ID not set directly, read from contracts/contract_id.txt ($CONTRACT_ID)${NC}" +fi + if [ -z "$CONTRACT_ID" ]; then echo -e "${RED}Error: CONTRACT_ID environment variable is required${NC}" echo "" @@ -75,6 +80,20 @@ echo -e "${GREEN}========================================${NC}" echo -e "${GREEN}Bindings generated successfully!${NC}" echo -e "${GREEN}========================================${NC}" echo "" + +if [ "$SKIP_TS_CHECK" != "true" ] && [ -d "frontend" ] && [ -f "frontend/tsconfig.json" ]; then + echo -e "${YELLOW}Validating frontend TypeScript compilation...${NC}" + if command -v npx &> /dev/null; then + (cd frontend && npx tsc --noEmit) + if [ $? -eq 0 ]; then + echo -e "${GREEN}Frontend TypeScript verification passed!${NC}" + else + echo -e "${RED}Frontend TypeScript verification failed!${NC}" + exit 1 + fi + fi +fi + echo -e "Output location: ${YELLOW}$OUTPUT_DIR/${NC}" echo "" echo -e "${GREEN}Next steps:${NC}"