#!/usr/bin/env node
// Copyright 2019 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================

const {exec} = require('./test-util');
const shell = require('shelljs');
const {readdirSync, statSync} = require('fs');
const {join} = require('path');


const filesAllowlistToTriggerBuild = [
  'cloudbuild.yml', 'package.json', 'tsconfig.json', 'tslint.json',
  'scripts/find_packages_with_diff.js', 'scripts/run-build.sh',
  'scripts/generate_cloudbuild.js'
];

const CLONE_PATH = 'clone';
let commitSha = process.env['COMMIT_SHA'];
let branchName = process.env['BRANCH_NAME'];
let baseBranch = process.env['BASE_BRANCH'];


const allPackages = readdirSync('.').filter(f => {
  if (f === 'node_modules' || f === '.git' || f === 'clone' ||
      !statSync(f).isDirectory()) {
    return false;
  }
  const directoryContents = readdirSync(join('.', f));
  return directoryContents.includes('cloudbuild.yml');
});


function findPackagesWithDiff() {
  // For Nightly build, baseBranch is one of the falsey values. We use master
  // for Nightly build.
  if (!baseBranch) {
    baseBranch = 'master';
  }
  console.log('commitSha: ', commitSha);
  console.log('branchName: ', branchName);
  console.log('baseBranch: ', baseBranch);

  // We cannot do --depth=1 here because we need to check out an old merge base.
  // We cannot do --single-branch here because we need multiple branches.
  console.log(`Clone branch ${baseBranch}`);
  shell.rm('-rf', CLONE_PATH);
  exec(`git clone -b ${baseBranch} https://github.com/tensorflow/tfjs ${
      CLONE_PATH}`);

  console.log();  // Break up the console for readability.

  shell.cd(CLONE_PATH);

  // If we cannot check out the commit then this PR is coming from a fork.
  const res = shell.exec(`git checkout ${commitSha}`, {silent: true});
  const isPullRequestFromFork = res.code !== 0;

  // Only checkout the merge base if the pull requests comes from a
  // tensorflow/tfjs branch. Otherwise clone master and diff against master.
  if (!isPullRequestFromFork) {
    console.log('PR is coming from tensorflow/tfjs. Finding the merge base...');
    exec(`git checkout ${branchName}`);
    const mergeBase =
        exec(`git merge-base ${baseBranch} ${branchName}`).stdout.trim();
    exec(`git fetch origin ${mergeBase}`);
    exec(`git checkout ${mergeBase}`);
    console.log('mergeBase: ', mergeBase);
  } else {
    console.log(`PR is going to diff against branch ${baseBranch}.`);
  }
  shell.cd('..');
  console.log();  // Break up the console for readability.

  let triggerAllBuilds = false;
  let allowlistDiffOutput = [];
  filesAllowlistToTriggerBuild.forEach(fileToTriggerBuild => {
    const diffOutput = diff(fileToTriggerBuild);
    if (diffOutput !== '') {
      console.log(fileToTriggerBuild, 'has changed. Triggering all builds.');
      triggerAllBuilds = true;
      allowlistDiffOutput.push(diffOutput);
    }
  });

  console.log();  // Break up the console for readability.

  let packagesWithDiff = [];
  allPackages.forEach(dir => {
    const diffOutput = diff(`${dir}/`);
    if (diffOutput !== '') {
      console.log(`${dir} has modified files.`);
    } else {
      console.log(`No modified files found in ${dir}`);
    }

    const shouldDiff = diffOutput !== '' || triggerAllBuilds;
    if (shouldDiff) {
      packagesWithDiff.push(dir);
    }
  });

  console.log();  // Break up the console for readability.

  console.log(`Packages directly affected: ${packagesWithDiff.join(', ')}`);
  return packagesWithDiff;
}

function diff(fileOrDirName) {
  const diffCmd = `diff -rq --exclude='settings.json' ` +
      `${CLONE_PATH}/${fileOrDirName} ` +
      `${fileOrDirName}`;
  return exec(diffCmd, {silent: true}, true).stdout.trim();
}

exports.findPackagesWithDiff = findPackagesWithDiff;
exports.allPackages = allPackages;

@@ -792,21 +792,12 @@
 read
-dirSync, stat
+File
 Sync
@@ -1079,16 +1079,31 @@
 PATH = '
+/tmp/tfjs-diff-
 clone';%0A
@@ -1247,285 +1247,166 @@
 nst 
-allP
+p
 ackage
-s = readdirSync('.').filter(f =%3E %7B%0A  if (f === 'node_modules' %7C%7C f === '.git' %7C%7C f === 'clone' %7C%7C%0A      !statSync(f).isDirectory()) %7B%0A    return false;%0A  %7D%0A  const directoryContents = readdirSync(join('.', f));%0A  return directoryContents.includes('cloudbuild.yml');%0A%7D
+Dependencies = JSON.parse(readFileSync(%0A    join(__dirname, 'package_dependencies.json'), 'utf8'));%0Aconst allPackages = Object.keys(packageDependencies
 );%0A%0A
@@ -2108,16 +2108,54 @@
 ility.%0A%0A
+  const originalPath = process.cwd();%0A
   shell.
@@ -2985,12 +2985,20 @@
 .cd(
-'..'
+originalPath
 );%0A 
@@ -4179,16 +4179,39 @@
      %60$%7B
+join(__dirname, '../', 
 fileOrDi
@@ -4215,16 +4215,17 @@
 rDirName
+)
 %7D%60;%0A  re

Fix find_packages_with_diff.js not checking all packages (#6427)