chore: install dependencies directly in package (#909)

Previously, the build scripts would override the top level
`node_modules` and `bower_components`. After this commit, the
dependencies are installed directly in the Electron package.

Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This commit is contained in:
Juan Cruz Viotti 2016-11-30 11:38:31 -04:00 committed by GitHub
parent 43922f94c0
commit a8116250d2
7 changed files with 213 additions and 105 deletions

View File

@ -66,22 +66,36 @@ if [ "$COMMAND" == "develop-electron" ]; then
fi fi
if [ "$COMMAND" == "installer-dmg" ]; then if [ "$COMMAND" == "installer-dmg" ]; then
./scripts/unix/create-electron-app.sh \
-s . \
-f "lib,build,assets" \
-o "etcher-release/app"
./scripts/unix/dependencies.sh -p \ ./scripts/unix/dependencies.sh -p \
-r x64 \ -r x64 \
-v "$ELECTRON_VERSION" \ -v "$ELECTRON_VERSION" \
-x "etcher-release/app" \
-t electron -t electron
./scripts/darwin/package.sh \ ./scripts/unix/create-asar.sh \
-n $APPLICATION_NAME \ -d "etcher-release/app" \
-o "etcher-release/app.asar"
./scripts/unix/download-electron.sh \
-r x64 \ -r x64 \
-v "$ELECTRON_VERSION" \
-s darwin \
-o etcher-release/$APPLICATION_NAME-darwin-x64
./scripts/darwin/configure-electron.sh \
-d etcher-release/$APPLICATION_NAME-darwin-x64 \
-n $APPLICATION_NAME \
-v $APPLICATION_VERSION \ -v $APPLICATION_VERSION \
-b io.resin.etcher \ -b io.resin.etcher \
-c "$APPLICATION_COPYRIGHT" \ -c "$APPLICATION_COPYRIGHT" \
-t public.app-category.developer-tools \ -t public.app-category.developer-tools \
-f "package.json,lib,node_modules,bower_components,build,assets" \ -a "etcher-release/app.asar" \
-i assets/icon.icns \ -i assets/icon.icns
-e $ELECTRON_VERSION \
-o etcher-release/$APPLICATION_NAME-darwin-x64
./scripts/darwin/installer-dmg.sh \ ./scripts/darwin/installer-dmg.sh \
-n $APPLICATION_NAME \ -n $APPLICATION_NAME \
@ -96,22 +110,36 @@ if [ "$COMMAND" == "installer-dmg" ]; then
fi fi
if [ "$COMMAND" == "installer-zip" ]; then if [ "$COMMAND" == "installer-zip" ]; then
./scripts/unix/create-electron-app.sh \
-s . \
-f "lib,build,assets" \
-o "etcher-release/app"
./scripts/unix/dependencies.sh -p \ ./scripts/unix/dependencies.sh -p \
-r x64 \ -r x64 \
-v "$ELECTRON_VERSION" \ -v "$ELECTRON_VERSION" \
-x "etcher-release/app" \
-t electron -t electron
./scripts/darwin/package.sh \ ./scripts/unix/create-asar.sh \
-n $APPLICATION_NAME \ -d "etcher-release/app" \
-o "etcher-release/app.asar"
./scripts/unix/download-electron.sh \
-r x64 \ -r x64 \
-v "$ELECTRON_VERSION" \
-s darwin \
-o etcher-release/$APPLICATION_NAME-darwin-x64
./scripts/darwin/configure-electron.sh \
-d etcher-release/$APPLICATION_NAME-darwin-x64 \
-n $APPLICATION_NAME \
-v $APPLICATION_VERSION \ -v $APPLICATION_VERSION \
-b io.resin.etcher \ -b io.resin.etcher \
-c "$APPLICATION_COPYRIGHT" \ -c "$APPLICATION_COPYRIGHT" \
-t public.app-category.developer-tools \ -t public.app-category.developer-tools \
-f "package.json,lib,node_modules,bower_components,build,assets" \ -a "etcher-release/app.asar" \
-i assets/icon.icns \ -i assets/icon.icns
-e $ELECTRON_VERSION \
-o etcher-release/$APPLICATION_NAME-darwin-x64
./scripts/darwin/sign.sh \ ./scripts/darwin/sign.sh \
-a etcher-release/$APPLICATION_NAME-darwin-x64/$APPLICATION_NAME.app \ -a etcher-release/$APPLICATION_NAME-darwin-x64/$APPLICATION_NAME.app \

View File

@ -86,19 +86,33 @@ if [ "$COMMAND" == "installer-cli" ]; then
fi fi
if [ "$COMMAND" == "installer-debian" ]; then if [ "$COMMAND" == "installer-debian" ]; then
./scripts/unix/create-electron-app.sh \
-s . \
-f "lib,build,assets" \
-o "etcher-release/app"
./scripts/unix/dependencies.sh -p \ ./scripts/unix/dependencies.sh -p \
-r "$ARCH" \ -r "$ARCH" \
-v "$ELECTRON_VERSION" \ -v "$ELECTRON_VERSION" \
-x "etcher-release/app" \
-t electron -t electron
./scripts/linux/package.sh \ ./scripts/unix/create-asar.sh \
-n "$APPLICATION_NAME" \ -d "etcher-release/app" \
-o "etcher-release/app.asar"
./scripts/unix/download-electron.sh \
-r "$ARCH" \ -r "$ARCH" \
-v "$ELECTRON_VERSION" \
-s linux \
-o "etcher-release/$APPLICATION_NAME-linux-$ARCH"
./scripts/linux/configure-electron.sh \
-d etcher-release/$APPLICATION_NAME-linux-$ARCH \
-n "$APPLICATION_NAME" \
-v "$APPLICATION_VERSION" \ -v "$APPLICATION_VERSION" \
-l LICENSE \ -l LICENSE \
-f "package.json,lib,node_modules,bower_components,build,assets" \ -a "etcher-release/app.asar"
-e "$ELECTRON_VERSION" \
-o etcher-release/$APPLICATION_NAME-linux-$ARCH
./scripts/linux/installer-deb.sh \ ./scripts/linux/installer-deb.sh \
-p etcher-release/$APPLICATION_NAME-linux-$ARCH \ -p etcher-release/$APPLICATION_NAME-linux-$ARCH \
@ -112,19 +126,33 @@ fi
if [ "$COMMAND" == "installer-appimage" ]; then if [ "$COMMAND" == "installer-appimage" ]; then
check_dep zip check_dep zip
./scripts/unix/create-electron-app.sh \
-s . \
-f "lib,build,assets" \
-o "etcher-release/app"
./scripts/unix/dependencies.sh -p \ ./scripts/unix/dependencies.sh -p \
-r "$ARCH" \ -r "$ARCH" \
-v "$ELECTRON_VERSION" \ -v "$ELECTRON_VERSION" \
-x "etcher-release/app" \
-t electron -t electron
./scripts/linux/package.sh \ ./scripts/unix/create-asar.sh \
-n "$APPLICATION_NAME" \ -d "etcher-release/app" \
-o "etcher-release/app.asar"
./scripts/unix/download-electron.sh \
-r "$ARCH" \ -r "$ARCH" \
-v "$ELECTRON_VERSION" \
-s linux \
-o "etcher-release/$APPLICATION_NAME-linux-$ARCH"
./scripts/linux/configure-electron.sh \
-d etcher-release/$APPLICATION_NAME-linux-$ARCH \
-n "$APPLICATION_NAME" \
-v "$APPLICATION_VERSION" \ -v "$APPLICATION_VERSION" \
-l LICENSE \ -l LICENSE \
-f "package.json,lib,node_modules,bower_components,build,assets" \ -a "etcher-release/app.asar"
-e "$ELECTRON_VERSION" \
-o etcher-release/$APPLICATION_NAME-linux-$ARCH
./scripts/linux/installer-appimage.sh \ ./scripts/linux/installer-appimage.sh \
-n "$APPLICATION_NAME" \ -n "$APPLICATION_NAME" \

View File

@ -39,73 +39,59 @@ function usage() {
echo "" echo ""
echo "Options" echo "Options"
echo "" echo ""
echo " -d <electron directory>"
echo " -n <application name>" echo " -n <application name>"
echo " -r <application architecture>"
echo " -v <application version>" echo " -v <application version>"
echo " -b <application bundle id>" echo " -b <application bundle id>"
echo " -c <application copyright>" echo " -c <application copyright>"
echo " -t <application category>" echo " -t <application category>"
echo " -f <application files (comma separated)>" echo " -a <application asar (.asar)>"
echo " -i <application icon (.icns)>" echo " -i <application icon (.icns)>"
echo " -e <electron version>"
echo " -o <output>"
exit 0 exit 0
} }
ARGV_ELECTRON_DIRECTORY=""
ARGV_APPLICATION_NAME="" ARGV_APPLICATION_NAME=""
ARGV_ARCHITECTURE=""
ARGV_VERSION="" ARGV_VERSION=""
ARGV_BUNDLE_ID="" ARGV_BUNDLE_ID=""
ARGV_COPYRIGHT="" ARGV_COPYRIGHT=""
ARGV_CATEGORY="" ARGV_CATEGORY=""
ARGV_FILES="" ARGV_ASAR=""
ARGV_ICON="" ARGV_ICON=""
ARGV_ELECTRON_VERSION=""
ARGV_OUTPUT=""
while getopts ":n:r:v:b:c:t:f:i:e:o:" option; do while getopts ":d:n:v:b:c:t:a:i:" option; do
case $option in case $option in
d) ARGV_ELECTRON_DIRECTORY="$OPTARG" ;;
n) ARGV_APPLICATION_NAME="$OPTARG" ;; n) ARGV_APPLICATION_NAME="$OPTARG" ;;
r) ARGV_ARCHITECTURE="$OPTARG" ;;
v) ARGV_VERSION="$OPTARG" ;; v) ARGV_VERSION="$OPTARG" ;;
b) ARGV_BUNDLE_ID="$OPTARG" ;; b) ARGV_BUNDLE_ID="$OPTARG" ;;
c) ARGV_COPYRIGHT="$OPTARG" ;; c) ARGV_COPYRIGHT="$OPTARG" ;;
t) ARGV_CATEGORY="$OPTARG" ;; t) ARGV_CATEGORY="$OPTARG" ;;
f) ARGV_FILES="$OPTARG" ;; a) ARGV_ASAR="$OPTARG" ;;
i) ARGV_ICON="$OPTARG" ;; i) ARGV_ICON="$OPTARG" ;;
e) ARGV_ELECTRON_VERSION="$OPTARG" ;;
o) ARGV_OUTPUT="$OPTARG" ;;
*) usage ;; *) usage ;;
esac esac
done done
if [ -z "$ARGV_APPLICATION_NAME" ] \ if [ -z "$ARGV_ELECTRON_DIRECTORY" ] \
|| [ -z "$ARGV_ARCHITECTURE" ] \ || [ -z "$ARGV_APPLICATION_NAME" ] \
|| [ -z "$ARGV_VERSION" ] \ || [ -z "$ARGV_VERSION" ] \
|| [ -z "$ARGV_BUNDLE_ID" ] \ || [ -z "$ARGV_BUNDLE_ID" ] \
|| [ -z "$ARGV_COPYRIGHT" ] \ || [ -z "$ARGV_COPYRIGHT" ] \
|| [ -z "$ARGV_CATEGORY" ] \ || [ -z "$ARGV_CATEGORY" ] \
|| [ -z "$ARGV_FILES" ] \ || [ -z "$ARGV_ASAR" ] \
|| [ -z "$ARGV_ICON" ] \ || [ -z "$ARGV_ICON" ]
|| [ -z "$ARGV_ELECTRON_VERSION" ] \
|| [ -z "$ARGV_OUTPUT" ]
then then
usage usage
fi fi
./scripts/unix/download-electron.sh \ APPLICATION_OUTPUT="$ARGV_ELECTRON_DIRECTORY/$ARGV_APPLICATION_NAME.app"
-r "$ARGV_ARCHITECTURE" \ mv "$ARGV_ELECTRON_DIRECTORY/Electron.app" "$APPLICATION_OUTPUT"
-v "$ARGV_ELECTRON_VERSION" \
-s darwin \
-o "$ARGV_OUTPUT"
APPLICATION_OUTPUT="$ARGV_OUTPUT/$ARGV_APPLICATION_NAME.app"
mv "$ARGV_OUTPUT/Electron.app" "$APPLICATION_OUTPUT"
rm "$APPLICATION_OUTPUT/Contents/Resources/default_app.asar" rm "$APPLICATION_OUTPUT/Contents/Resources/default_app.asar"
# Don't include these for now # Don't include these for now
rm -f "$ARGV_OUTPUT"/LICENSE* rm -f "$ARGV_ELECTRON_DIRECTORY"/LICENSE*
rm -f "$ARGV_OUTPUT/version" rm -f "$ARGV_ELECTRON_DIRECTORY/version"
function plist_set() { function plist_set() {
local plist_file=$1 local plist_file=$1
@ -153,6 +139,8 @@ for id in EH NP; do
"$APPLICATION_OUTPUT/Contents/Frameworks/$ARGV_APPLICATION_NAME Helper $id.app" "$APPLICATION_OUTPUT/Contents/Frameworks/$ARGV_APPLICATION_NAME Helper $id.app"
done done
./scripts/unix/create-asar.sh \ cp "$ARGV_ASAR" "$APPLICATION_OUTPUT/Contents/Resources/app.asar"
-f "$ARGV_FILES" \
-o "$APPLICATION_OUTPUT/Contents/Resources/app.asar" if [ -d "$ARGV_ASAR.unpacked" ]; then
cp -rf "$ARGV_ASAR.unpacked" "$APPLICATION_OUTPUT/Contents/Resources/app.asar.unpacked"
fi

View File

@ -37,59 +37,47 @@ function usage() {
echo "" echo ""
echo "Options" echo "Options"
echo "" echo ""
echo " -d <electron directory>"
echo " -n <application name>" echo " -n <application name>"
echo " -r <application architecture>"
echo " -v <application version>" echo " -v <application version>"
echo " -l <application license file>" echo " -l <application license file>"
echo " -f <application files (comma separated)>" echo " -a <application asar (.asar)>"
echo " -e <electron version>"
echo " -o <output>"
exit 0 exit 0
} }
ARGV_ELECTRON_DIRECTORY=""
ARGV_APPLICATION_NAME="" ARGV_APPLICATION_NAME=""
ARGV_ARCHITECTURE=""
ARGV_VERSION="" ARGV_VERSION=""
ARGV_LICENSE="" ARGV_LICENSE=""
ARGV_FILES="" ARGV_ASAR=""
ARGV_ELECTRON_VERSION=""
ARGV_OUTPUT=""
while getopts ":n:r:v:l:f:e:o:" option; do while getopts ":d:n:v:l:a:" option; do
case $option in case $option in
d) ARGV_ELECTRON_DIRECTORY="$OPTARG" ;;
n) ARGV_APPLICATION_NAME="$OPTARG" ;; n) ARGV_APPLICATION_NAME="$OPTARG" ;;
r) ARGV_ARCHITECTURE="$OPTARG" ;;
v) ARGV_VERSION="$OPTARG" ;; v) ARGV_VERSION="$OPTARG" ;;
l) ARGV_LICENSE="$OPTARG" ;; l) ARGV_LICENSE="$OPTARG" ;;
f) ARGV_FILES="$OPTARG" ;; a) ARGV_ASAR="$OPTARG" ;;
e) ARGV_ELECTRON_VERSION="$OPTARG" ;;
o) ARGV_OUTPUT="$OPTARG" ;;
*) usage ;; *) usage ;;
esac esac
done done
if [ -z "$ARGV_APPLICATION_NAME" ] \ if [ -z "$ARGV_ELECTRON_DIRECTORY" ] \
|| [ -z "$ARGV_ARCHITECTURE" ] \ || [ -z "$ARGV_APPLICATION_NAME" ] \
|| [ -z "$ARGV_VERSION" ] \ || [ -z "$ARGV_VERSION" ] \
|| [ -z "$ARGV_LICENSE" ] \ || [ -z "$ARGV_LICENSE" ] \
|| [ -z "$ARGV_FILES" ] \ || [ -z "$ARGV_ASAR" ]
|| [ -z "$ARGV_ELECTRON_VERSION" ] \
|| [ -z "$ARGV_OUTPUT" ]
then then
usage usage
fi fi
./scripts/unix/download-electron.sh \ mv $ARGV_ELECTRON_DIRECTORY/electron $ARGV_ELECTRON_DIRECTORY/$(echo "$ARGV_APPLICATION_NAME" | tr '[:upper:]' '[:lower:]')
-r "$ARGV_ARCHITECTURE" \ cp $ARGV_LICENSE $ARGV_ELECTRON_DIRECTORY/LICENSE
-v "$ARGV_ELECTRON_VERSION" \ echo "$ARGV_VERSION" > $ARGV_ELECTRON_DIRECTORY/version
-s linux \ rm $ARGV_ELECTRON_DIRECTORY/resources/default_app.asar
-o "$ARGV_OUTPUT"
mv $ARGV_OUTPUT/electron $ARGV_OUTPUT/$(echo "$ARGV_APPLICATION_NAME" | tr '[:upper:]' '[:lower:]') cp "$ARGV_ASAR" "$ARGV_ELECTRON_DIRECTORY/resources/app.asar"
cp $ARGV_LICENSE $ARGV_OUTPUT/LICENSE
echo "$ARGV_VERSION" > $ARGV_OUTPUT/version
rm $ARGV_OUTPUT/resources/default_app.asar
./scripts/unix/create-asar.sh \ if [ -d "$ARGV_ASAR.unpacked" ]; then
-f "$ARGV_FILES" \ cp -rf "$ARGV_ASAR.unpacked" "$ARGV_ELECTRON_DIRECTORY/resources/app.asar.unpacked"
-o "$ARGV_OUTPUT/resources/app.asar" fi

View File

@ -33,34 +33,25 @@ function usage() {
echo "" echo ""
echo "Options" echo "Options"
echo "" echo ""
echo " -f <files (comma separated)>" echo " -d <directory>"
echo " -o <output>" echo " -o <output>"
exit 0 exit 0
} }
ARGV_FILES="" ARGV_DIRECTORY=""
ARGV_OUTPUT="" ARGV_OUTPUT=""
while getopts ":f:o:" option; do while getopts ":d:o:" option; do
case $option in case $option in
f) ARGV_FILES=$OPTARG ;; d) ARGV_DIRECTORY=$OPTARG ;;
o) ARGV_OUTPUT=$OPTARG ;; o) ARGV_OUTPUT=$OPTARG ;;
*) usage ;; *) usage ;;
esac esac
done done
if [ -z "$ARGV_FILES" ] || [ -z "$ARGV_OUTPUT" ]; then if [ -z "$ARGV_DIRECTORY" ] || [ -z "$ARGV_OUTPUT" ]; then
usage usage
fi fi
TEMPORAL_DIRECTORY="$ARGV_OUTPUT.tmp"
rm -rf "$TEMPORAL_DIRECTORY"
mkdir -p "$TEMPORAL_DIRECTORY"
for file in $(echo "$ARGV_FILES" | sed "s/,/ /g"); do
cp -rf "$file" "$TEMPORAL_DIRECTORY"
done
mkdir -p $(dirname "$ARGV_OUTPUT") mkdir -p $(dirname "$ARGV_OUTPUT")
asar pack "$TEMPORAL_DIRECTORY" "$ARGV_OUTPUT" --unpack *.node asar pack "$ARGV_DIRECTORY" "$ARGV_OUTPUT" --unpack *.node
rm -rf "$TEMPORAL_DIRECTORY"

View File

@ -0,0 +1,68 @@
#!/bin/bash
###
# Copyright 2016 resin.io
#
# 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.
###
set -u
set -e
function usage() {
echo "Usage: $0"
echo ""
echo "Options"
echo ""
echo " -s <source directory>"
echo " -f <extra files (comma separated)>"
echo " -o <output>"
exit 0
}
ARGV_SOURCE_DIRECTORY=""
ARGV_FILES=""
ARGV_OUTPUT=""
while getopts ":s:f:o:" option; do
case $option in
s) ARGV_SOURCE_DIRECTORY=$OPTARG ;;
f) ARGV_FILES=$OPTARG ;;
o) ARGV_OUTPUT=$OPTARG ;;
*) usage ;;
esac
done
if [ -z "$ARGV_SOURCE_DIRECTORY" ] ||
[ -z "$ARGV_FILES" ] || \
[ -z "$ARGV_OUTPUT" ]; then
usage
fi
mkdir -p "$ARGV_OUTPUT"
function copy_file_if_it_exists() {
local file=$1
if [ -f "$ARGV_SOURCE_DIRECTORY/$file" ]; then
cp "$ARGV_SOURCE_DIRECTORY/$file" "$ARGV_OUTPUT"
fi
}
copy_file_if_it_exists "package.json"
copy_file_if_it_exists "npm-shrinkwrap.json"
copy_file_if_it_exists "bower.json"
for file in $(echo "$ARGV_FILES" | sed "s/,/ /g"); do
cp -rf "$file" "$ARGV_OUTPUT"
done

View File

@ -38,6 +38,7 @@ function usage() {
echo " -r <architecture>" echo " -r <architecture>"
echo " -v <target version>" echo " -v <target version>"
echo " -t <target platform (node|electron)>" echo " -t <target platform (node|electron)>"
echo " -x <install prefix>"
echo " -f force install" echo " -f force install"
echo " -p production install" echo " -p production install"
exit 0 exit 0
@ -46,14 +47,16 @@ function usage() {
ARGV_ARCHITECTURE="" ARGV_ARCHITECTURE=""
ARGV_TARGET_VERSION="" ARGV_TARGET_VERSION=""
ARGV_TARGET_PLATFORM="" ARGV_TARGET_PLATFORM=""
ARGV_PREFIX=""
ARGV_FORCE=false ARGV_FORCE=false
ARGV_PRODUCTION=false ARGV_PRODUCTION=false
while getopts ":r:v:t:fp" option; do while getopts ":r:v:t:x:fp" option; do
case $option in case $option in
r) ARGV_ARCHITECTURE=$OPTARG ;; r) ARGV_ARCHITECTURE=$OPTARG ;;
v) ARGV_TARGET_VERSION=$OPTARG ;; v) ARGV_TARGET_VERSION=$OPTARG ;;
t) ARGV_TARGET_PLATFORM=$OPTARG ;; t) ARGV_TARGET_PLATFORM=$OPTARG ;;
x) ARGV_PREFIX=$OPTARG ;;
f) ARGV_FORCE=true ;; f) ARGV_FORCE=true ;;
p) ARGV_PRODUCTION=true ;; p) ARGV_PRODUCTION=true ;;
*) usage ;; *) usage ;;
@ -84,8 +87,6 @@ else
export npm_config_arch=$ARGV_ARCHITECTURE export npm_config_arch=$ARGV_ARCHITECTURE
fi fi
rm -rf node_modules
NPM_INSTALL_OPTS="--build-from-source" NPM_INSTALL_OPTS="--build-from-source"
if [ "$ARGV_FORCE" == "true" ]; then if [ "$ARGV_FORCE" == "true" ]; then
@ -96,9 +97,25 @@ if [ "$ARGV_PRODUCTION" == "true" ]; then
NPM_INSTALL_OPTS="$NPM_INSTALL_OPTS --production" NPM_INSTALL_OPTS="$NPM_INSTALL_OPTS --production"
fi fi
if [ -n "$ARGV_PREFIX" ]; then
NPM_INSTALL_OPTS="$NPM_INSTALL_OPTS --prefix=$ARGV_PREFIX"
fi
npm install $NPM_INSTALL_OPTS npm install $NPM_INSTALL_OPTS
if [ "$ARGV_TARGET_PLATFORM" == "electron" ]; then # Using `--prefix` might cause npm to create an empty `etc` directory
rm -rf bower_components if [ -n "$ARGV_PREFIX" ] && [ ! "$(ls -A "$ARGV_PREFIX/etc")" ]; then
bower install --production --allow-root rm -rf "$ARGV_PREFIX/etc"
fi
if [ "$ARGV_TARGET_PLATFORM" == "electron" ]; then
BOWER_INSTALL_OPTS="--production --allow-root"
if [ -n "$ARGV_PREFIX" ]; then
pushd "$ARGV_PREFIX"
bower install $BOWER_INSTALL_OPTS
popd
fi
bower install $BOWER_INSTALL_OPTS
fi fi