name: CI
permissions: read-all

on:
  # For manual tests.
  workflow_dispatch:
  push:
    tags:
      - "*" # new tag version, like `0.8.4` or else
    branches:
      - master
  pull_request:
    branches:
      - master

jobs:
  build-linux:
    permissions:
      contents: write
    outputs:
      digests-gcc: ${{ steps.hash-gcc.outputs.hashes }}
      digests-clang: ${{ steps.hash-clang.outputs.hashes }}
    name: Build Linux
    runs-on: ubuntu-latest
    strategy:
      matrix:
        cxx: [g++-10, clang++-12]
      fail-fast: false
    steps:
    - uses: actions/checkout@v3
    - name: cmake
      run: CXX=${{ matrix.cxx }} cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_STRICT_MODE=ON .
    - name: build
      run: make -j
    - name: test
      run: ./flattests
    - name: make flatc executable
      run: |
        chmod +x flatc
        ./flatc --version
    # - name: flatc tests
    #   run: |
    #     yarn global add esbuild
    #     python3 tests/flatc/main.py
    - name: upload build artifacts
      uses: actions/upload-artifact@v1
      with:
        name: Linux flatc binary ${{ matrix.cxx }}
        path: flatc
    # Below if only for release.
    - name: Zip file
      if: startsWith(github.ref, 'refs/tags/')
      run: zip Linux.flatc.binary.${{ matrix.cxx }}.zip flatc
    - name: Release zip file
      uses: softprops/action-gh-release@v1
      if: startsWith(github.ref, 'refs/tags/')
      with:
        files: Linux.flatc.binary.${{ matrix.cxx }}.zip
    - name: Generate SLSA subjects - clang
      if: matrix.cxx == 'clang++-12' && startsWith(github.ref, 'refs/tags/')
      id: hash-clang
      run: echo "::set-output name=hashes::$(sha256sum Linux.flatc.binary.${{ matrix.cxx }}.zip | base64 -w0)"
    - name: Generate SLSA subjects - gcc
      if: matrix.cxx == 'g++-10' && startsWith(github.ref, 'refs/tags/')
      id: hash-gcc
      run: echo "::set-output name=hashes::$(sha256sum Linux.flatc.binary.${{ matrix.cxx }}.zip | base64 -w0)"
      
  build-linux-no-file-tests:    
    name: Build Linux with -DFLATBUFFERS_NO_FILE_TESTS
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: cmake
      run: CXX=clang++-12 cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_STRICT_MODE=ON -DFLATBUFFERS_CXX_FLAGS="-DFLATBUFFERS_NO_FILE_TESTS" .
    - name: build
      run: make -j

  build-linux-cpp-std:
    name: Build Linux C++
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        std: [11, 14, 17, 20, 23]
        cxx: [g++-10, clang++-12]
        exclude:
          # GCC 10.3.0 doesn't support std 23
          - cxx: g++-10
            std: 23
    steps:
    - uses: actions/checkout@v3
    - name: cmake
      run: >
        CXX=${{ matrix.cxx }} cmake -G "Unix Makefiles" 
        -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_STRICT_MODE=ON 
        -DFLATBUFFERS_CPP_STD=${{ matrix.std }}
        -DFLATBUFFERS_BUILD_CPP17=${{ matrix.std >= 17 && 'On' || 'Off'}}
    - name: build
      run: make -j
    - name: test
      run: ./flattests
    - name: test C++17
      if: matrix.std >= 17
      run: ./flattests_cpp17

  build-windows-cpp-std:
    name: Build Windows C++
    runs-on: windows-2019
    strategy:
      matrix:
        std: [11, 14, 17, 20, 23]
      fail-fast: false
    steps:
    - uses: actions/checkout@v3
    - name: Add msbuild to PATH
      uses: microsoft/setup-msbuild@v1.1
    - name: cmake
      run: >
        cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Release 
        -DFLATBUFFERS_STRICT_MODE=ON 
        -DFLATBUFFERS_CPP_STD=${{ matrix.std }}
        -DFLATBUFFERS_BUILD_CPP17=${{ matrix.std >= 17 && 'On' || 'Off'}}
    - name: build
      run: msbuild.exe FlatBuffers.sln /p:Configuration=Release /p:Platform=x64
    - name: test
      run: Release\flattests.exe
    - name: test C++17
      if: matrix.std >= 17
      run: Release\flattests_cpp17.exe

  build-windows:
    permissions:
      contents: write
    outputs:
      digests: ${{ steps.hash.outputs.hashes }}
    name: Build Windows 2019 
    runs-on: windows-2019
    steps:
    - uses: actions/checkout@v3
    - name: Add msbuild to PATH
      uses: microsoft/setup-msbuild@v1.1
    - name: cmake
      run: cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_BUILD_CPP17=ON -DFLATBUFFERS_STRICT_MODE=ON .
    - name: build
      run: msbuild.exe FlatBuffers.sln /p:Configuration=Release /p:Platform=x64
    - name: test
      run: Release\flattests.exe
    # - name: flatc tests
    #   run: python3 tests/flatc/main.py --flatc Release\flatc.exe
    - name: upload build artifacts
      uses: actions/upload-artifact@v1
      with:
        name: Windows flatc binary
        path: Release\flatc.exe
    # Below if only for release.
    - name: Zip file
      if: startsWith(github.ref, 'refs/tags/')
      run: move Release/flatc.exe . && Compress-Archive flatc.exe Windows.flatc.binary.zip
    - name: Release binary
      uses: softprops/action-gh-release@v1
      if: startsWith(github.ref, 'refs/tags/')
      with:
        files: Windows.flatc.binary.zip
    - name: Generate SLSA subjects
      if: startsWith(github.ref, 'refs/tags/')
      id: hash
      shell: bash
      run: echo "::set-output name=hashes::$(sha256sum Windows.flatc.binary.zip | base64 -w0)"

  build-windows-2017:
    name: Build Windows 2017
    runs-on: windows-2019
    steps:
    - uses: actions/checkout@v3
    - name: Add msbuild to PATH
      uses: microsoft/setup-msbuild@v1.1
    - name: cmake
      run: cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_STRICT_MODE=ON .
    - name: build tool version 15 (VS 2017)
      run: msbuild.exe FlatBuffers.sln /p:Configuration=Release /p:Platform=x64 /p:VisualStudioVersion=15.0
    - name: test
      run: Release\flattests.exe

  build-windows-2015:
    name: Build Windows 2015
    runs-on: windows-2019
    steps:
    - uses: actions/checkout@v3
    - name: Add msbuild to PATH
      uses: microsoft/setup-msbuild@v1.1
    - name: cmake
      run: cmake -G "Visual Studio 14 2015" -A x64 -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_STRICT_MODE=ON .
    - name: build tool version 14 (VS 2015)
      run: msbuild.exe FlatBuffers.sln /p:Configuration=Release /p:Platform=x64 /p:VisualStudioVersion=14.0
    - name: test
      run: Release\flattests.exe

  build-dotnet-windows:
    name: Build .NET Windows
    runs-on: windows-2019
    strategy:
      matrix:
        configuration: [
          '',
          '-p:UnsafeByteBuffer=true', 
          # Fails two tests currently.
          #'-p:EnableSpanT=true,UnsafeByteBuffer=true'
          ]
    steps:
    - uses: actions/checkout@v3
    - name: Setup .NET Core SDK
      uses: actions/setup-dotnet@v3
      with: 
        dotnet-version: '3.1.x'
    - name: Build
      run: |
        cd tests\FlatBuffers.Test
        dotnet new sln --force --name FlatBuffers.Core.Test
        dotnet sln FlatBuffers.Core.Test.sln add FlatBuffers.Core.Test.csproj
        dotnet build -c Release ${{matrix.configuration}} -o out FlatBuffers.Core.Test.sln
    - name: Run
      run: |
        cd tests\FlatBuffers.Test
        out\FlatBuffers.Core.Test.exe

  build-mac-intel:
    permissions:
      contents: write
    outputs:
      digests: ${{ steps.hash.outputs.hashes }}
    name: Build Mac (for Intel)
    runs-on: macos-latest
    steps:
    - uses: actions/checkout@v3
    - name: cmake
      run: cmake -G "Xcode" -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_STRICT_MODE=ON .
    - name: build
      run: xcodebuild -toolchain clang -configuration Release -target flattests
    - name: check that the binary is x86_64
      run: |
        info=$(file Release/flatc)
        echo $info
        echo $info | grep "Mach-O 64-bit executable x86_64"
    - name: test
      run: Release/flattests
    - name: make flatc executable
      run: |
        chmod +x Release/flatc
        Release/flatc --version
    # - name: flatc tests
    #  run: python3 tests/flatc/main.py --flatc Release/flatc
    - name: upload build artifacts
      uses: actions/upload-artifact@v1
      with:
        name: Mac flatc binary
        path: Release/flatc
    # Below if only for release.
    - name: Zip file
      if: startsWith(github.ref, 'refs/tags/')
      run: mv Release/flatc . && zip MacIntel.flatc.binary.zip flatc
    - name: Release binary
      uses: softprops/action-gh-release@v1
      if: startsWith(github.ref, 'refs/tags/')
      with:
        files: MacIntel.flatc.binary.zip
    - name: Generate SLSA subjects
      if: startsWith(github.ref, 'refs/tags/')
      id: hash
      run: echo "::set-output name=hashes::$(shasum -a 256 MacIntel.flatc.binary.zip | base64)"

  build-mac-universal:
    permissions:
      contents: write
    outputs:
      digests: ${{ steps.hash.outputs.hashes }}
    name: Build Mac (universal build)
    runs-on: macos-latest
    steps:
    - uses: actions/checkout@v3
    - name: cmake
      run: cmake -G "Xcode" -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_STRICT_MODE=ON .
    - name: build
      run: xcodebuild -toolchain clang -configuration Release -target flattests
    - name: check that the binary is "universal"
      run: |
        info=$(file Release/flatc)
        echo $info
        echo $info | grep "Mach-O universal binary with 2 architectures"
    - name: test
      run: Release/flattests
    - name: make flatc executable
      run: |
        chmod +x Release/flatc
        Release/flatc --version
    - name: upload build artifacts
      uses: actions/upload-artifact@v1
      with:
        name: Mac flatc binary
        path: Release/flatc
    # Below if only for release.
    - name: Zip file
      if: startsWith(github.ref, 'refs/tags/')
      run: mv Release/flatc . && zip Mac.flatc.binary.zip flatc
    - name: Release binary
      uses: softprops/action-gh-release@v1
      if: startsWith(github.ref, 'refs/tags/')
      with:
        files: Mac.flatc.binary.zip
    - name: Generate SLSA subjects
      if: startsWith(github.ref, 'refs/tags/')
      id: hash
      run: echo "::set-output name=hashes::$(shasum -a 256 Mac.flatc.binary.zip | base64)"

  build-android:
   name: Build Android (on Linux)
   runs-on: ubuntu-latest
   steps:
   - uses: actions/checkout@v3
   - name: set up Java
     uses: actions/setup-java@v3
     with:
       distribution: 'temurin'
       java-version: '11'
   - name: set up flatc
     run: |
       cmake -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_BUILD_FLATLIB=OFF -DFLATBUFFERS_BUILD_FLATHASH=OFF -DFLATBUFFERS_STRICT_MODE=ON .
       make -j
       echo "${PWD}" >> $GITHUB_PATH
   - name: build
     working-directory: android
     run: gradle clean build

  build-generator:
    name: Check Generated Code
    runs-on: ubuntu-latest
    strategy:
      matrix:
        cxx: [g++-10, clang++-12]
    steps:
    - uses: actions/checkout@v3
    - name: cmake
      run: CXX=${{ matrix.cxx }} cmake -G "Unix Makefiles" -DFLATBUFFERS_BUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_STRICT_MODE=ON . && make -j
    - name: Generate
      run: scripts/check_generate_code.py
    - name: Generate gRPC
      run: scripts/check-grpc-generated-code.py

  build-generator-windows:
    name: Check Generated Code on Windows
    runs-on: windows-2019
    steps:
    - uses: actions/checkout@v3
    - name: Add msbuild to PATH
      uses: microsoft/setup-msbuild@v1.1
    - name: cmake
      run: cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_BUILD_CPP17=ON -DFLATBUFFERS_STRICT_MODE=ON .
    - name: build
      run: msbuild.exe FlatBuffers.sln /p:Configuration=Release /p:Platform=x64
    - name: Generate
      run: python3 scripts/check_generate_code.py --flatc Release\flatc.exe
    - name: Generate gRPC
      run: python3 scripts/check-grpc-generated-code.py --flatc Release\flatc.exe

  build-benchmarks:
    name: Build Benchmarks (on Linux)
    runs-on: ubuntu-latest
    strategy:
      matrix:
        cxx: [g++-10]
    steps:
    - uses: actions/checkout@v3
    - name: cmake
      run: CXX=${{ matrix.cxx }} cmake -G "Unix Makefiles" -DFLATBUFFERS_CXX_FLAGS="-Wno-unused-parameter -fno-aligned-new" -DFLATBUFFERS_BUILD_BENCHMARKS=ON -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_STRICT_MODE=ON . && make -j
    - name: Run benchmarks
      run: ./flatbenchmark --benchmark_repetitions=5 --benchmark_display_aggregates_only=true --benchmark_out_format=console --benchmark_out=benchmarks/results_${{matrix.cxx}}
    - name: Upload benchmarks results
      uses: actions/upload-artifact@v1
      with:
        name: Linux flatbenchmark results ${{matrix.cxx}}
        path: benchmarks/results_${{matrix.cxx}}

  build-java:
    name: Build Java
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: test
      working-directory: java
      run: mvn test

  build-kotlin-macos:
    name: Build Kotlin MacOS
    runs-on: macos-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v3
    - uses: gradle/wrapper-validation-action@v1.0.5
    - uses: actions/setup-java@v3
      with:
        distribution: 'temurin'
        java-version: '11'
    - name: Build
      working-directory: kotlin
      run: ./gradlew clean iosX64Test macosX64Test

  build-kotlin-linux:
    name: Build Kotlin Linux
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v3
    - uses: actions/setup-java@v3
      with:
        distribution: 'temurin'
        java-version: '11'
    - uses: gradle/wrapper-validation-action@v1.0.5
    - name: Build
      working-directory: kotlin
      # we are using docker's version of gradle
      # so no need for wrapper validation or user
      # gradlew
      run: gradle jvmMainClasses jvmTest jsTest jsBrowserTest

  build-rust:
    name: Build Rust
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: test
      working-directory: tests
      run: bash RustTest.sh

  build-python:
    name: Build Python
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: test
      working-directory: tests
      run: bash PythonTest.sh

  build-go:
    name: Build Go
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: flatc
      # FIXME: make test script not rely on flatc
      run: cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_INSTALL=OFF -DFLATBUFFERS_BUILD_FLATLIB=OFF -DFLATBUFFERS_BUILD_FLATHASH=OFF -DFLATBUFFERS_STRICT_MODE=ON . && make -j
    - name: test
      working-directory: tests
      run: bash GoTest.sh

  build-php:
   name: Build PHP
   runs-on: ubuntu-latest
   steps:
   - uses: actions/checkout@v3
   - name: flatc
     # FIXME: make test script not rely on flatc
     run: cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_INSTALL=OFF -DFLATBUFFERS_BUILD_FLATLIB=OFF -DFLATBUFFERS_BUILD_FLATHASH=OFF -DFLATBUFFERS_STRICT_MODE=ON . && make -j
   - name: test
     working-directory: tests
     run: |
       php phpTest.php
       sh phpUnionVectorTest.sh

  build-swift:
    name: Build Swift
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: test
      working-directory: tests/swift/tests
      run: |
        swift build --build-tests
        swift test

  build-swift-wasm:
    name: Build Swift Wasm
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/swiftwasm/carton:0.15.3
    steps:
      - uses: actions/checkout@v3
      - name: Setup Wasmer
        uses: wasmerio/setup-wasmer@v2
      - name: Test
        working-directory: tests/swift/Wasm.tests
        run: carton test

  build-ts:
    name: Build TS
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: flatc
      # FIXME: make test script not rely on flatc
      run: cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_INSTALL=OFF -DFLATBUFFERS_BUILD_FLATLIB=OFF -DFLATBUFFERS_BUILD_FLATHASH=OFF . && make -j
    - name: deps
      run: yarn
    - name: compile
      run: yarn compile
    - name: test
      working-directory: tests/ts
      run: |
        yarn global add esbuild
        python3 TypeScriptTest.py

  build-dart:
    name: Build Dart
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: dart-lang/setup-dart@v1
        with:
          sdk: stable
      - name: flatc
        # FIXME: make test script not rely on flatc
        run: cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_INSTALL=OFF -DFLATBUFFERS_BUILD_FLATLIB=OFF -DFLATBUFFERS_BUILD_FLATHASH=OFF -DFLATBUFFERS_STRICT_MODE=ON . && make -j
      - name: test
        working-directory: tests
        run: bash DartTest.sh

  build-nim:
    name: Build Nim
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: flatc
      # FIXME: make test script not rely on flatc
      run: cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_INSTALL=OFF -DFLATBUFFERS_BUILD_FLATLIB=OFF -DFLATBUFFERS_BUILD_FLATHASH=OFF . && make -j
    - uses: jiro4989/setup-nim-action@v1
    - name: install library
      working-directory: nim
      run: nimble -y develop
    - name: test
      working-directory: tests/nim
      run: python3 testnim.py

  release-digests:
    if: startsWith(github.ref, 'refs/tags/')
    needs: [build-linux, build-windows, build-mac-intel, build-mac-universal]
    outputs:
      digests: ${{ steps.hash.outputs.digests }}
    runs-on: ubuntu-latest
    steps:
      - name: Merge results
        id: hash
        env:
          LINUXGCC_DIGESTS: "${{ needs.build-linux.outputs.digests-gcc }}"
          LINUXCLANG_DIGESTS: "${{ needs.build-linux.outputs.digests-clang }}"
          MAC_DIGESTS: "${{ needs.build-mac-universal.outputs.digests }}"
          MACINTEL_DIGESTS: "${{ needs.build-mac-intel.outputs.digests }}"
          WINDOWS_DIGESTS: "${{ needs.build-windows.outputs.digests }}"
        run: |
          set -euo pipefail
          echo "$LINUXGCC_DIGESTS" | base64 -d > checksums.txt
          echo "$LINUXCLANG_DIGESTS" | base64 -d >> checksums.txt
          echo "$MAC_DIGESTS" | base64 -d >> checksums.txt
          echo "$MACINTEL_DIGESTS" | base64 -d >> checksums.txt
          echo "$WINDOWS_DIGESTS" | base64 -d >> checksums.txt
          echo "::set-output name=digests::$(cat checksums.txt | base64 -w0)"

  provenance:
    if: startsWith(github.ref, 'refs/tags/')
    needs: [release-digests]
    permissions:
      actions: read   # To read the workflow path.
      id-token: write # To sign the provenance.
      contents: write # To add assets to a release.
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.2.1
    with:
      base64-subjects: "${{ needs.release-digests.outputs.digests }}"
      upload-assets: true # Optional: Upload to a new release
      compile-generator: true # Workaround for https://github.com/slsa-framework/slsa-github-generator/issues/1163
