diff --git a/.github/workflows/check-paper-integrity.yml b/.github/workflows/check-paper-integrity.yml new file mode 100644 index 000000000..bfae85bca --- /dev/null +++ b/.github/workflows/check-paper-integrity.yml @@ -0,0 +1,47 @@ +name: Test Paper Architecture integrity +on: [pull_request] +jobs: + check: + runs-on: ubuntu-latest + concurrency: + group: kotlin-lint-${{ github.ref }} + cancel-in-progress: true + steps: + - name: checkout + uses: actions/checkout@v2 + + - name: Use Java 17 + uses: actions/setup-java@v3 + with: + distribution: 'oracle' + java-version: '17' + + - name: Use Node.js 18 + uses: actions/setup-node@v2 + with: + node-version: 18 + cache: 'yarn' + + - uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} + + - name: Install node dependencies + run: yarn install --frozen-lockfile + + - name: Install node dependencies of FabricExample + run: (cd FabricExample && yarn install --frozen-lockfile) + + - name: Restore build from cache + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + android/build + android/.gradle + key: ${{ runner.os }}-kotlin-lint-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'android/build.gradle') }} + + - name: Check old arch integrity + run: yarn checkIntegrity diff --git a/Example/android/gradle.properties b/Example/android/gradle.properties index a46a5b90f..1a8de6684 100644 --- a/Example/android/gradle.properties +++ b/Example/android/gradle.properties @@ -39,3 +39,5 @@ newArchEnabled=false # Use this property to enable or disable the Hermes JS engine. # If set to false, you will be using JSC instead. hermesEnabled=true + +isRNSVGExampleApp=true diff --git a/FabricExample/android/gradle.properties b/FabricExample/android/gradle.properties index 99fc223ed..ff79b10e5 100644 --- a/FabricExample/android/gradle.properties +++ b/FabricExample/android/gradle.properties @@ -39,3 +39,5 @@ newArchEnabled=true # Use this property to enable or disable the Hermes JS engine. # If set to false, you will be using JSC instead. hermesEnabled=true + +isRNSVGExampleApp=true diff --git a/TestsExample/android/gradle.properties b/TestsExample/android/gradle.properties index a46a5b90f..1a8de6684 100644 --- a/TestsExample/android/gradle.properties +++ b/TestsExample/android/gradle.properties @@ -39,3 +39,5 @@ newArchEnabled=false # Use this property to enable or disable the Hermes JS engine. # If set to false, you will be using JSC instead. hermesEnabled=true + +isRNSVGExampleApp=true diff --git a/android/build.gradle b/android/build.gradle index 212df94ce..21020a4ea 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -93,3 +93,107 @@ repositories { dependencies { implementation 'com.facebook.react:react-native:+' } + +def isRNSVGExampleApp() { + return project.hasProperty('isRNSVGExampleApp') && project.property('isRNSVGExampleApp') == "true" +} + +def getAbsoluteCodegenArtifactsPaperDestination() { + if (!project.hasProperty('codegenArtifactsPaperDestination')) { + throw new Exception('[RNSVG] Please fill codegenArtifactsPaperDestination variable in android/gradle.properties to point to the correct path to generated specs for paper') + } + + return "${project.rootDir}/../../${project.property('codegenArtifactsPaperDestination')}" +} + +def getAbsoluteCodegenArtifactsSource() { + if (!project.hasProperty('codegenArtifactsSource')) { + throw new Exception('[RNSVG] Please fill codegenArtifactsSource variable in android/gradle.properties to point to the correct path to codegenerated artifacts') + } + + return "${project.rootDir}/../../${project.property('codegenArtifactsSource')}" +} + + +tasks.register('copyCodegenArtifacts') { + group 'After build tasks' + description 'Task which copies codegen artifacts to paper architecture' + + if (!isRNSVGExampleApp() || !isNewArchitectureEnabled()) { + return + } + + dependsOn tasks.generateCodegenArtifactsFromSchema + + doLast { + + def absoluteCodegenArtifactsPaperDestination = getAbsoluteCodegenArtifactsPaperDestination() + def absoluteCodegenArtifactsSource = getAbsoluteCodegenArtifactsSource() + + def existingFiles = fileTree(absoluteCodegenArtifactsPaperDestination).matching { + include '**/*.java' + } + + def generatedFiles = fileTree(absoluteCodegenArtifactsSource).matching { + include '**/*.java' + } + + def existingFilesMap = [:] + + existingFiles.forEach { existingFile -> + existingFilesMap[existingFile.name] = 1 + } + + generatedFiles.forEach { generatedFile -> + if (!existingFilesMap.containsKey(generatedFile.name)) { + logger.warn("[RNSVG] ${generatedFile.name} not found in paper dir, if it's used on Android you need to copy it manually and implement yourself before using auto-copy feature.") + } + } + + if (existingFiles.size() == 0) { + logger.warn("[RNSVG] Paper destination with codegen interfaces is empty. This might be okay if you don't have any interfaces/delegates used on Android, but if that's not the case please check if codegenArtifactsPaperDestination property in android/gradle.properties is correct.") + } + + existingFiles.forEach { existingFile -> + def generatedFile = new File("${absoluteCodegenArtifactsSource}/${existingFile.name}") + + if (!generatedFile.exists()) { + logger.warn("[RNSVG] ${existingFile.name} file does not exist in codegen artifacts source destination. Please check if you still need this interface/delagete.") + } + } + + copy { + from absoluteCodegenArtifactsSource + include existingFiles.collect { it.name } + into absoluteCodegenArtifactsPaperDestination + } + } +} + +if (isRNSVGExampleApp() && isNewArchitectureEnabled() && !project.hasProperty('skipCodegenCopyTask')) { + tasks.generateCodegenArtifactsFromSchema.finalizedBy('copyCodegenArtifacts') +} + +tasks.register('checkIntegrityBetweenArchitectures') { + group 'Verification tasks' + description 'Task to check integrity between fabric and paper architecture in terms of codegen generated interfaces/delegates' + + if (isRNSVGExampleApp()) { + return + } + + def absoluteCodegenArtifactsPaperDestination = "../${project.property('codegenArtifactsPaperDestination')}" + def absoluteCodegenArtifactsSource = "../${project.property('codegenArtifactsSource')}" + + def existingFiles = fileTree(absoluteCodegenArtifactsPaperDestination).matching { + include '**/*.java' + } + + existingFiles.forEach { existingFile -> + def generatedFile = new File("${absoluteCodegenArtifactsSource}/${existingFile.name}") + + if (existingFile.text != generatedFile.text) { + throw new RuntimeException("RNSVG] The source of ${existingFile.name} does not match with the one generated by codegen. Please check if you commited changes produced by copyCodegenArtifacts task.") + } + } +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 000000000..4b37d560a --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,6 @@ +# Path to codegen output directory with this library view managers' interfaces & delegates. Used by `copyCodegenArtifacts` task that helps to synchronize newly generated files with their Paper counterparts. +codegenArtifactsSource=android/build/generated/source/codegen/java/com/facebook/react/viewmanagers + +# Path to directory with view managers' interfaces & delegates used while running on Paper architecture. This property is used as the output path for `copyCodegenArtifacts` task. +# Used by copyCodegenArtifacts task that automates copying those interfaces/delegates after codegen is run. +codegenArtifactsPaperDestination=android/src/paper/java/com/facebook/react/viewmanagers diff --git a/package.json b/package.json index 1d438996a..510151ed3 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,8 @@ "prepare": "npm run bob && husky install", "release": "npm login && release-it", "test": "npm run lint && npm run tsc", - "tsc": "tsc --noEmit" + "tsc": "tsc --noEmit", + "checkIntegrity": "(cd ./FabricExample/android && ./gradlew generateCodegenArtifactsFromSchema -PskipCodegenCopyTask) && (cd ./android && ./gradlew checkIntegrityBetweenArchitectures)" }, "peerDependencies": { "react": "*",