diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3873ba..767a374 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,388 +1,388 @@ -name: CI - -on: - push: - branches: [master] - pull_request: - workflow_dispatch: - -# A new push to the same branch cancels the previous run — CI minutes are -# not free and a superseded run tells you nothing. A cancelled release run -# is safe to redo: the `gate` job re-checks what already exists, so nothing -# gets published or tagged twice. -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: write - -env: - GO_VERSION: '1.26.4' - -jobs: - go: - name: Go (vet, fmt, tests, FFI ABI) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} - cache-dependency-path: | - splitcore/go.sum - server/go.sum - - - name: gofmt - run: | - out=$(gofmt -l splitcore server) - if [ -n "$out" ]; then - echo "::error::unformatted Go files:"; echo "$out"; exit 1 - fi - - - name: go vet - run: make vet - - - name: Go tests - run: | - cd splitcore && go test ./... -count=1 - cd ../server && go test ./... -count=1 - - - name: Build libsplitcore.so and verify the C ABI - run: | - ./splitcore/build/build_linux.sh - nm -D splitcore/build/out/linux/libsplitcore.so | grep -c Splitcore - python3 splitcore/build/smoke_test.py - - - name: Upload the native library for the SDK job - uses: actions/upload-artifact@v4 - with: - name: libsplitcore-linux - path: splitcore/build/out/linux/ - retention-days: 1 - - sdk: - name: Dart SDK (analyze, tests against a real server) - runs-on: ubuntu-latest - needs: go - steps: - - uses: actions/checkout@v4 - - # The SDK's integration tests spawn `go run .` in ../server, so this - # job needs the Go toolchain even though it runs Dart tests. - - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} - cache-dependency-path: | - splitcore/go.sum - server/go.sum - - - uses: dart-lang/setup-dart@v1 - - # test/support/lib_path.dart resolves exactly this path. - - name: Download libsplitcore.so - uses: actions/download-artifact@v4 - with: - name: libsplitcore-linux - path: splitcore/build/out/linux/ - - - name: Install dependencies - working-directory: splitcore_sdk - run: dart pub get - - - name: Analyze - working-directory: splitcore_sdk - run: dart analyze --fatal-infos - - - name: Format check - working-directory: splitcore_sdk - run: dart format --line-length 100 --set-exit-if-changed -o none . - - - name: Test - working-directory: splitcore_sdk - run: dart test --reporter expanded - timeout-minutes: 15 - - flutter: - name: Flutter app (analyze, widget tests) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: subosito/flutter-action@v2 - with: - channel: stable - cache: true - - - name: Install dependencies - working-directory: app - run: flutter pub get - - - name: Analyze - working-directory: app - run: flutter analyze --fatal-infos - - - name: Format check - working-directory: app - run: dart format --line-length 100 --set-exit-if-changed -o none . - - - name: Test - working-directory: app - run: flutter test --reporter expanded - - # --------------------------------------------------------------------- - # Release. Only runs for pushes to master, only after every test job is - # green, and only when a version was actually bumped — the two version - # fields are the release button: - # - # app/pubspec.yaml version -> the GitHub release tag v - # splitcore_sdk/pubspec.yaml version -> the pub.dev publish - # - # Both are checked against what already exists, so re-running the - # workflow (or pushing a docs-only commit) publishes nothing twice. - # --------------------------------------------------------------------- - gate: - name: Decide what to release - runs-on: ubuntu-latest - needs: [go, sdk, flutter] - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - outputs: - version: ${{ steps.check.outputs.version }} - sdk_version: ${{ steps.check.outputs.sdk_version }} - do_release: ${{ steps.check.outputs.do_release }} - do_publish: ${{ steps.check.outputs.do_publish }} - steps: - - uses: actions/checkout@v4 - - - id: check - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - # `version: 1.0.0+3` -> `1.0.0`; the +build suffix is Android's - # versionCode and has no place in a git tag. - version=$(sed -n 's/^version:[[:space:]]*//p' app/pubspec.yaml | head -1 | cut -d+ -f1) - sdk_version=$(sed -n 's/^version:[[:space:]]*//p' splitcore_sdk/pubspec.yaml | head -1) - echo "version=$version" >> "$GITHUB_OUTPUT" - echo "sdk_version=$sdk_version" >> "$GITHUB_OUTPUT" - - if gh release view "v$version" >/dev/null 2>&1; then - echo "release v$version already exists — skipping the release jobs" - echo "do_release=false" >> "$GITHUB_OUTPUT" - else - echo "do_release=true" >> "$GITHUB_OUTPUT" - fi - - code=$(curl -sS -o /dev/null -w '%{http_code}' \ - "https://pub.dev/api/packages/splitcore_sdk/versions/$sdk_version") - if [ "$code" = "404" ]; then - echo "do_publish=true" >> "$GITHUB_OUTPUT" - else - echo "splitcore_sdk $sdk_version is already on pub.dev (HTTP $code) — skipping publish" - echo "do_publish=false" >> "$GITHUB_OUTPUT" - fi - - publish-sdk: - name: Publish splitcore_sdk to pub.dev - runs-on: ubuntu-latest - needs: gate - if: needs.gate.outputs.do_publish == 'true' - steps: - - uses: actions/checkout@v4 - - uses: dart-lang/setup-dart@v1 - - # pub.dev's OIDC publishing only trusts runs triggered by a tag push, - # which this master-push workflow is not. A credentials file from a - # `dart pub login` is the supported alternative — see RELEASING.md. - - name: Restore pub.dev credentials - env: - PUB_CREDENTIALS: ${{ secrets.PUB_CREDENTIALS }} - run: | - set -euo pipefail - test -n "$PUB_CREDENTIALS" || { echo "::error::PUB_CREDENTIALS secret is not set"; exit 1; } - mkdir -p "$HOME/.config/dart" - printf '%s' "$PUB_CREDENTIALS" > "$HOME/.config/dart/pub-credentials.json" - - - name: Publish - working-directory: splitcore_sdk - run: | - dart pub get - dart pub publish --force - - android: - name: Build Android APKs and the native libraries - runs-on: ubuntu-latest - needs: gate - if: needs.gate.outputs.do_release == 'true' - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} - cache-dependency-path: splitcore/go.sum - - - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: '17' - - - uses: subosito/flutter-action@v2 - with: - channel: stable - cache: true - - # The Windows DLL is cross-compiled here rather than on the windows - # runner: mingw-w64 from apt is a known quantity, the toolchain that - # happens to be on the Windows image is not. - - name: Install mingw-w64 for the Windows DLL - run: sudo apt-get update && sudo apt-get install -y gcc-mingw-w64-x86-64 - - - name: Build every native target - run: | - set -euo pipefail - ./splitcore/build/build_linux.sh - ./splitcore/build/build_windows.sh - make apk - - - name: Collect the native libraries - run: | - set -euo pipefail - mkdir -p dist/native - cp -r splitcore/build/out/android/jniLibs dist/native/android - install -D splitcore/build/out/linux/libsplitcore.so dist/native/linux/libsplitcore.so - install -D splitcore/build/out/windows/splitcore.dll dist/native/windows/splitcore.dll - cd dist && zip -r "splitcore-native-v${{ needs.gate.outputs.version }}.zip" native - - - name: Stage the APKs - run: | - set -euo pipefail - v='${{ needs.gate.outputs.version }}' - for abi in arm64-v8a armeabi-v7a x86_64; do - cp "app/build/app/outputs/flutter-apk/app-$abi-release.apk" \ - "dist/slicepay-$v-$abi.apk" - done - - - uses: actions/upload-artifact@v4 - with: - name: release-android - path: | - dist/*.apk - dist/*.zip - retention-days: 1 - - # The Windows job needs the DLL to drop next to the .exe. - - uses: actions/upload-artifact@v4 - with: - name: splitcore-dll - path: splitcore/build/out/windows/splitcore.dll - retention-days: 1 - - windows: - name: Build the Windows desktop app - runs-on: windows-latest - needs: [gate, android] - if: needs.gate.outputs.do_release == 'true' - steps: - - uses: actions/checkout@v4 - - - uses: subosito/flutter-action@v2 - with: - channel: stable - cache: true - - - uses: actions/download-artifact@v4 - with: - name: splitcore-dll - path: dll - - - name: Build - working-directory: app - run: flutter build windows --release - - # A bare "splitcore.dll" resolves against the .exe's own directory - # (see app/lib/main.dart), so the DLL ships inside the bundle. - - name: Package - shell: pwsh - run: | - $bundle = "app/build/windows/x64/runner/Release" - Copy-Item dll/splitcore.dll $bundle - New-Item -ItemType Directory -Force dist | Out-Null - Compress-Archive -Path "$bundle/*" ` - -DestinationPath "dist/slicepay-${{ needs.gate.outputs.version }}-windows-x64.zip" - - - uses: actions/upload-artifact@v4 - with: - name: release-windows - path: dist/*.zip - retention-days: 1 - - linux-desktop: - name: Build the Linux desktop app - runs-on: ubuntu-latest - needs: gate - if: needs.gate.outputs.do_release == 'true' - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} - cache-dependency-path: splitcore/go.sum - - - uses: subosito/flutter-action@v2 - with: - channel: stable - cache: true - - - name: Install the Flutter Linux toolchain - run: | - sudo apt-get update - sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev - - - name: Build - run: | - set -euo pipefail - ./splitcore/build/build_linux.sh - cd app && flutter build linux --release - - - name: Package - run: | - set -euo pipefail - bundle=app/build/linux/x64/release/bundle - mkdir -p dist - tar -C "$bundle" -czf \ - "dist/slicepay-${{ needs.gate.outputs.version }}-linux-x64.tar.gz" . - - - uses: actions/upload-artifact@v4 - with: - name: release-linux - path: dist/*.tar.gz - retention-days: 1 - - release: - name: Cut the GitHub release - runs-on: ubuntu-latest - needs: [gate, android, windows, linux-desktop] - if: needs.gate.outputs.do_release == 'true' - steps: - - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 - with: - pattern: release-* - path: dist - merge-multiple: true - - - name: Create the release - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - # --generate-notes already diffs against the previous release. - gh release create "v${{ needs.gate.outputs.version }}" \ - --target "$GITHUB_SHA" \ - --title "v${{ needs.gate.outputs.version }}" \ - --generate-notes \ - dist/* +# name: CI + +# on: +# push: +# branches: [master] +# pull_request: +# workflow_dispatch: + +# # A new push to the same branch cancels the previous run — CI minutes are +# # not free and a superseded run tells you nothing. A cancelled release run +# # is safe to redo: the `gate` job re-checks what already exists, so nothing +# # gets published or tagged twice. +# concurrency: +# group: ${{ github.workflow }}-${{ github.ref }} +# cancel-in-progress: true + +# permissions: +# contents: write + +# env: +# GO_VERSION: '1.26.4' + +# jobs: +# go: +# name: Go (vet, fmt, tests, FFI ABI) +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 + +# - uses: actions/setup-go@v5 +# with: +# go-version: ${{ env.GO_VERSION }} +# cache-dependency-path: | +# splitcore/go.sum +# server/go.sum + +# - name: gofmt +# run: | +# out=$(gofmt -l splitcore server) +# if [ -n "$out" ]; then +# echo "::error::unformatted Go files:"; echo "$out"; exit 1 +# fi + +# - name: go vet +# run: make vet + +# - name: Go tests +# run: | +# cd splitcore && go test ./... -count=1 +# cd ../server && go test ./... -count=1 + +# - name: Build libsplitcore.so and verify the C ABI +# run: | +# ./splitcore/build/build_linux.sh +# nm -D splitcore/build/out/linux/libsplitcore.so | grep -c Splitcore +# python3 splitcore/build/smoke_test.py + +# - name: Upload the native library for the SDK job +# uses: actions/upload-artifact@v4 +# with: +# name: libsplitcore-linux +# path: splitcore/build/out/linux/ +# retention-days: 1 + +# sdk: +# name: Dart SDK (analyze, tests against a real server) +# runs-on: ubuntu-latest +# needs: go +# steps: +# - uses: actions/checkout@v4 + +# # The SDK's integration tests spawn `go run .` in ../server, so this +# # job needs the Go toolchain even though it runs Dart tests. +# - uses: actions/setup-go@v5 +# with: +# go-version: ${{ env.GO_VERSION }} +# cache-dependency-path: | +# splitcore/go.sum +# server/go.sum + +# - uses: dart-lang/setup-dart@v1 + +# # test/support/lib_path.dart resolves exactly this path. +# - name: Download libsplitcore.so +# uses: actions/download-artifact@v4 +# with: +# name: libsplitcore-linux +# path: splitcore/build/out/linux/ + +# - name: Install dependencies +# working-directory: splitcore_sdk +# run: dart pub get + +# - name: Analyze +# working-directory: splitcore_sdk +# run: dart analyze --fatal-infos + +# - name: Format check +# working-directory: splitcore_sdk +# run: dart format --line-length 100 --set-exit-if-changed -o none . + +# - name: Test +# working-directory: splitcore_sdk +# run: dart test --reporter expanded +# timeout-minutes: 15 + +# flutter: +# name: Flutter app (analyze, widget tests) +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 + +# - uses: subosito/flutter-action@v2 +# with: +# channel: stable +# cache: true + +# - name: Install dependencies +# working-directory: app +# run: flutter pub get + +# - name: Analyze +# working-directory: app +# run: flutter analyze --fatal-infos + +# - name: Format check +# working-directory: app +# run: dart format --line-length 100 --set-exit-if-changed -o none . + +# - name: Test +# working-directory: app +# run: flutter test --reporter expanded + +# # --------------------------------------------------------------------- +# # Release. Only runs for pushes to master, only after every test job is +# # green, and only when a version was actually bumped — the two version +# # fields are the release button: +# # +# # app/pubspec.yaml version -> the GitHub release tag v +# # splitcore_sdk/pubspec.yaml version -> the pub.dev publish +# # +# # Both are checked against what already exists, so re-running the +# # workflow (or pushing a docs-only commit) publishes nothing twice. +# # --------------------------------------------------------------------- +# gate: +# name: Decide what to release +# runs-on: ubuntu-latest +# needs: [go, sdk, flutter] +# if: github.event_name == 'push' && github.ref == 'refs/heads/master' +# outputs: +# version: ${{ steps.check.outputs.version }} +# sdk_version: ${{ steps.check.outputs.sdk_version }} +# do_release: ${{ steps.check.outputs.do_release }} +# do_publish: ${{ steps.check.outputs.do_publish }} +# steps: +# - uses: actions/checkout@v4 + +# - id: check +# env: +# GH_TOKEN: ${{ github.token }} +# run: | +# set -euo pipefail +# # `version: 1.0.0+3` -> `1.0.0`; the +build suffix is Android's +# # versionCode and has no place in a git tag. +# version=$(sed -n 's/^version:[[:space:]]*//p' app/pubspec.yaml | head -1 | cut -d+ -f1) +# sdk_version=$(sed -n 's/^version:[[:space:]]*//p' splitcore_sdk/pubspec.yaml | head -1) +# echo "version=$version" >> "$GITHUB_OUTPUT" +# echo "sdk_version=$sdk_version" >> "$GITHUB_OUTPUT" + +# if gh release view "v$version" >/dev/null 2>&1; then +# echo "release v$version already exists — skipping the release jobs" +# echo "do_release=false" >> "$GITHUB_OUTPUT" +# else +# echo "do_release=true" >> "$GITHUB_OUTPUT" +# fi + +# code=$(curl -sS -o /dev/null -w '%{http_code}' \ +# "https://pub.dev/api/packages/splitcore_sdk/versions/$sdk_version") +# if [ "$code" = "404" ]; then +# echo "do_publish=true" >> "$GITHUB_OUTPUT" +# else +# echo "splitcore_sdk $sdk_version is already on pub.dev (HTTP $code) — skipping publish" +# echo "do_publish=false" >> "$GITHUB_OUTPUT" +# fi + +# publish-sdk: +# name: Publish splitcore_sdk to pub.dev +# runs-on: ubuntu-latest +# needs: gate +# if: needs.gate.outputs.do_publish == 'true' +# steps: +# - uses: actions/checkout@v4 +# - uses: dart-lang/setup-dart@v1 + +# # pub.dev's OIDC publishing only trusts runs triggered by a tag push, +# # which this master-push workflow is not. A credentials file from a +# # `dart pub login` is the supported alternative — see RELEASING.md. +# - name: Restore pub.dev credentials +# env: +# PUB_CREDENTIALS: ${{ secrets.PUB_CREDENTIALS }} +# run: | +# set -euo pipefail +# test -n "$PUB_CREDENTIALS" || { echo "::error::PUB_CREDENTIALS secret is not set"; exit 1; } +# mkdir -p "$HOME/.config/dart" +# printf '%s' "$PUB_CREDENTIALS" > "$HOME/.config/dart/pub-credentials.json" + +# - name: Publish +# working-directory: splitcore_sdk +# run: | +# dart pub get +# dart pub publish --force + +# android: +# name: Build Android APKs and the native libraries +# runs-on: ubuntu-latest +# needs: gate +# if: needs.gate.outputs.do_release == 'true' +# steps: +# - uses: actions/checkout@v4 + +# - uses: actions/setup-go@v5 +# with: +# go-version: ${{ env.GO_VERSION }} +# cache-dependency-path: splitcore/go.sum + +# - uses: actions/setup-java@v4 +# with: +# distribution: temurin +# java-version: '17' + +# - uses: subosito/flutter-action@v2 +# with: +# channel: stable +# cache: true + +# # The Windows DLL is cross-compiled here rather than on the windows +# # runner: mingw-w64 from apt is a known quantity, the toolchain that +# # happens to be on the Windows image is not. +# - name: Install mingw-w64 for the Windows DLL +# run: sudo apt-get update && sudo apt-get install -y gcc-mingw-w64-x86-64 + +# - name: Build every native target +# run: | +# set -euo pipefail +# ./splitcore/build/build_linux.sh +# ./splitcore/build/build_windows.sh +# make apk + +# - name: Collect the native libraries +# run: | +# set -euo pipefail +# mkdir -p dist/native +# cp -r splitcore/build/out/android/jniLibs dist/native/android +# install -D splitcore/build/out/linux/libsplitcore.so dist/native/linux/libsplitcore.so +# install -D splitcore/build/out/windows/splitcore.dll dist/native/windows/splitcore.dll +# cd dist && zip -r "splitcore-native-v${{ needs.gate.outputs.version }}.zip" native + +# - name: Stage the APKs +# run: | +# set -euo pipefail +# v='${{ needs.gate.outputs.version }}' +# for abi in arm64-v8a armeabi-v7a x86_64; do +# cp "app/build/app/outputs/flutter-apk/app-$abi-release.apk" \ +# "dist/slicepay-$v-$abi.apk" +# done + +# - uses: actions/upload-artifact@v4 +# with: +# name: release-android +# path: | +# dist/*.apk +# dist/*.zip +# retention-days: 1 + +# # The Windows job needs the DLL to drop next to the .exe. +# - uses: actions/upload-artifact@v4 +# with: +# name: splitcore-dll +# path: splitcore/build/out/windows/splitcore.dll +# retention-days: 1 + +# windows: +# name: Build the Windows desktop app +# runs-on: windows-latest +# needs: [gate, android] +# if: needs.gate.outputs.do_release == 'true' +# steps: +# - uses: actions/checkout@v4 + +# - uses: subosito/flutter-action@v2 +# with: +# channel: stable +# cache: true + +# - uses: actions/download-artifact@v4 +# with: +# name: splitcore-dll +# path: dll + +# - name: Build +# working-directory: app +# run: flutter build windows --release + +# # A bare "splitcore.dll" resolves against the .exe's own directory +# # (see app/lib/main.dart), so the DLL ships inside the bundle. +# - name: Package +# shell: pwsh +# run: | +# $bundle = "app/build/windows/x64/runner/Release" +# Copy-Item dll/splitcore.dll $bundle +# New-Item -ItemType Directory -Force dist | Out-Null +# Compress-Archive -Path "$bundle/*" ` +# -DestinationPath "dist/slicepay-${{ needs.gate.outputs.version }}-windows-x64.zip" + +# - uses: actions/upload-artifact@v4 +# with: +# name: release-windows +# path: dist/*.zip +# retention-days: 1 + +# linux-desktop: +# name: Build the Linux desktop app +# runs-on: ubuntu-latest +# needs: gate +# if: needs.gate.outputs.do_release == 'true' +# steps: +# - uses: actions/checkout@v4 + +# - uses: actions/setup-go@v5 +# with: +# go-version: ${{ env.GO_VERSION }} +# cache-dependency-path: splitcore/go.sum + +# - uses: subosito/flutter-action@v2 +# with: +# channel: stable +# cache: true + +# - name: Install the Flutter Linux toolchain +# run: | +# sudo apt-get update +# sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev + +# - name: Build +# run: | +# set -euo pipefail +# ./splitcore/build/build_linux.sh +# cd app && flutter build linux --release + +# - name: Package +# run: | +# set -euo pipefail +# bundle=app/build/linux/x64/release/bundle +# mkdir -p dist +# tar -C "$bundle" -czf \ +# "dist/slicepay-${{ needs.gate.outputs.version }}-linux-x64.tar.gz" . + +# - uses: actions/upload-artifact@v4 +# with: +# name: release-linux +# path: dist/*.tar.gz +# retention-days: 1 + +# release: +# name: Cut the GitHub release +# runs-on: ubuntu-latest +# needs: [gate, android, windows, linux-desktop] +# if: needs.gate.outputs.do_release == 'true' +# steps: +# - uses: actions/checkout@v4 + +# - uses: actions/download-artifact@v4 +# with: +# pattern: release-* +# path: dist +# merge-multiple: true + +# - name: Create the release +# env: +# GH_TOKEN: ${{ github.token }} +# run: | +# set -euo pipefail +# # --generate-notes already diffs against the previous release. +# gh release create "v${{ needs.gate.outputs.version }}" \ +# --target "$GITHUB_SHA" \ +# --title "v${{ needs.gate.outputs.version }}" \ +# --generate-notes \ +# dist/*