Changeset - 27ae6f554220
[Not reviewed]
0 1 0
Branko Majic (branko) - 20 months ago 2022-08-18 22:53:45
branko@majic.rs
[factorio_development.sh] Added build command:

- Include default build configuration file during mod initialisation
to exclude development-specific files from the build process.
- Includes non-source files by default as well (such as README and
LICENSE).
1 file changed with 197 insertions and 1 deletions:
0 comments (0 inline, 0 general)
games/factorio_development.sh
Show inline comments
 
@@ -22,7 +22,7 @@
 
set -u
 

	
 
PROGRAM="factorio_development.sh"
 
VERSION="0.0.1"
 
VERSION="0.0.2"
 

	
 
function usage() {
 
    cat <<EOF
 
@@ -31,6 +31,7 @@ $PROGRAM $VERSION, helper tool for development of Factorio mods
 
Usage:
 

	
 
  $PROGRAM [OPTIONS] init MOD_DIRECTORY_PATH
 
  $PROGRAM [OPTIONS] build [MOD_DIRECTORY_PATH]
 

	
 
EOF
 
}
 
@@ -89,9 +90,31 @@ init MOD_DIRECTORY_PATH
 
    - README.md, provides detailed mod description and information.
 
    - LICENSE, contains license information for the mod.
 
    - .gitignore, for ignoring files and paths when working with git.
 
    - build.cfg, for configuring how the releases are built.
 
    - [src/]info.json, mod metadata.
 
    - [src/]changelog.txt, with changelog information for the mod.
 

	
 

	
 
build [MOD_DIRECTORY_PATH]
 

	
 
  Arguments:
 

	
 
    MOD_DIRECTORY_PATH (path to base directory)
 

	
 
  Builds release of a mod. Expects (optional) path to mod base
 
  directory. Default is to use working directory as mod directory
 
  path.
 

	
 
  Temporary files are stored under the "build" sub-directory, while
 
  the release zip archive is placed under the "dist" sub-directory
 
  (relative to mod base directory).
 

	
 
  By default, the build command includes all files in the release
 
  archive - even when separate directory for mod sources is
 
  used. Files can be excluded from the archive via "ignore_paths"
 
  option in build configuration file (build.cfg).
 

	
 

	
 
$PROGRAM accepts the following options:
 

	
 
    -q
 
@@ -239,6 +262,15 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
SOFTWARE.
 
EOF
 

	
 
    cat <<EOF > "$base_dir/build.cfg"
 
# -*- mode: sh-mode; sh-shell: bash -*-
 

	
 
ignore_paths=(
 
    ".gitignore"
 
    "build.cfg"
 
)
 
EOF
 

	
 
    cat <<EOF > "$base_dir/.gitignore"
 
@@ -287,6 +319,155 @@ EOF
 
}
 

	
 

	
 
#
 
# Builds mod relase archive.
 
#
 
# Arguments:
 
#
 
#   $1 (base_dir)
 
#     Base (top-level) directory with the mod files.
 
#
 
# Returns:
 
#   0 on success, 1 otherwise.
 
#
 
function command_build() {
 
    local base_dir="$1"
 

	
 
    local error_count=0
 

	
 
    declare -a ignore_paths
 
    declare -a mod_files
 

	
 
    local source_dir dist_dir build_dir info_file target_dir archive_file build_config
 
    local mod_name mod_version
 
    local path mod_file mod_file_path
 

	
 
    # Calculate absolute paths to various directories and artifacts.
 
    base_dir=$(readlink -f "$base_dir")
 
    build_dir="$base_dir/build"
 
    dist_dir="$base_dir/dist"
 
    build_config="$base_dir/build.cfg"
 

	
 
    # Read build configuration.
 
    # shellcheck disable=SC1090 # build configuration file is create per-mod directory
 
    if [[ -f $build_config ]] && ! source "$build_config"; then
 
        error "Failed to load build configuration from: $build_config"
 
        return 1
 
    fi
 

	
 
    if [[ -d $base_dir/src ]]; then
 
        source_dir="src"
 
        info_file="$base_dir/src/info.json"
 
    else
 
        source_dir="."
 
        info_file="$base_dir/info.json"
 
    fi
 

	
 
    if [[ ! -f $info_file ]]; then
 
        error "Could not locate info file under: $info_file"
 
        return 1
 
    fi
 

	
 
    # Extract modpack name and version
 
    if ! jq . "$info_file" > /dev/null; then
 
        error "Could not parse mod info file: $info_file"
 
        return 1
 
    fi
 
    mod_name=$(jq -r ".name" "$info_file")
 
    mod_version=$(jq -r ".version" "$info_file")
 

	
 
    # Validate version string format.
 
    if [[ ! $mod_version =~ ^[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$ ]]; then
 
        error "Invalid mod version read from info file: $mod_version"
 
        return 1
 
    fi
 

	
 
    target_dir="${build_dir}/${mod_name}_${mod_version}"
 
    archive_file="${dist_dir}/${mod_name}_${mod_version}.zip"
 

	
 
    # Create list of files to package. Exclude development files from the
 
    # build.
 
    readarray -t mod_files < <(git -C "$base_dir" ls-files)
 

	
 
    for path in "${ignore_paths[@]}"; do
 
        for i in "${!mod_files[@]}"; do
 
            if [[ -d $path && ${mod_files[i]} == $path/* ]] || [[ ${mod_files[i]} == "$path" ]]; then
 
                unset "mod_files[i]"
 
            fi
 
        done
 
    done
 

	
 
    # Fix the index gaps in file list array.
 
    mod_files=("${mod_files[@]}")
 

	
 
    # Validate the files.
 
    error_count=0
 

	
 
    for mod_file in "${mod_files[@]}"; do
 
        mod_file_path="${base_dir}/${mod_file}"
 

	
 
        if [[ $mod_file_path =~ .*\.lua ]] && ! luac -o /dev/null "$mod_file_path"; then
 
            (( error_count += 1 ))
 
            error "Validation failed for file: $mod_file"
 
        fi
 

	
 
        if [[ $mod_file_path =~ .*\.json ]] && ! python -m json.tool "$mod_file_path" > /dev/null; then
 
            (( error_count += 1 ))
 
            error "Validation failed for file: $mod_file"
 
        fi
 
    done
 

	
 
    if [[ $error_count != 0 ]]; then
 
        return 1
 
    fi
 

	
 
    # Do some basic validation so we don't overwrite things by mistake.
 
    if [[ -e $archive_file ]]; then
 
        error "Output archive already exists: $archive_file"
 
        return 1
 
    fi
 

	
 
    info "Building release: $mod_version"
 

	
 
    # Set-up the necessary directories.
 
    rm -rf "$target_dir"
 
    mkdir -p "$target_dir"
 
    mkdir -p "$dist_dir"
 

	
 
    # Copy the files.
 
    if ! cd "$base_dir/"; then
 
        error "Failed to switch to base directory: $base_dir"
 
        return 1
 
    fi
 
    for mod_file in "${mod_files[@]}"; do
 
        install -m 0644 -D "$mod_file" "${target_dir}/${mod_file%%${source_dir}/}"
 
    done
 

	
 
    # Move sources to base directory.
 
    if [[ $source_dir != . ]]; then
 
        mv -n "${target_dir}/${source_dir}"/* "${target_dir}"
 
        if ! rmdir "${target_dir}/${source_dir}"; then
 
            error "Failed to remove directory: ${target_dir}/${source_dir}"
 
            return 1
 
        fi
 
    fi
 

	
 
    # Zip the files.
 
    if ! cd "$build_dir/"; then
 
        error "Failed to switch to build directory: $build_dir"
 
        return 1
 
    fi
 
    if ! zip -q -r "$archive_file" "$(basename "$target_dir")"; then
 
        error "Could not prepare the release archive."
 
        return 1
 
    fi
 

	
 
    rm -rf "$target_dir"
 

	
 
    success "Release built and placed under: $archive_file"
 
}
 

	
 

	
 
# Set-up colours for message printing if we're not piping and terminal is
 
# capable of outputting the colors.
 
_COLOR_TERMINAL=$(tput colors 2>&1)
 
@@ -389,6 +570,21 @@ if [[ $COMMAND == init ]]; then
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
elif [[ $COMMAND == build ]]; then
 

	
 
    MOD_DIRECTORY_PATH="${1:-.}"
 
    shift
 

	
 
    # Ensure that passed-in base directory is the repository root.
 
    if [[ ! -d $MOD_DIRECTORY_PATH/.git ]]; then
 
        error "Passed-in path does not point to base directory of the mod (must contain the .git sub-directory)."
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    if ! command_build "$MOD_DIRECTORY_PATH"; then
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
else
 

	
 
    error "Unsupported command: $COMMAND"
0 comments (0 inline, 0 general)