master 452dc1539c67 cached
18 files
30.5 KB
7.8k tokens
33 symbols
1 requests
Download .txt
Repository: KrauseFx/fastlane-plugin-appicon
Branch: master
Commit: 452dc1539c67
Files: 18
Total size: 30.5 KB

Directory structure:
gitextract_fqhyqhsc/

├── .gitignore
├── .rspec
├── .rubocop.yml
├── .travis.yml
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── fastlane/
│   ├── Fastfile
│   └── Pluginfile
├── fastlane-plugin-appicon.gemspec
├── lib/
│   └── fastlane/
│       └── plugin/
│           ├── appicon/
│           │   ├── actions/
│           │   │   ├── android_appicon_action.rb
│           │   │   └── appicon_action.rb
│           │   ├── helper/
│           │   │   └── appicon_helper.rb
│           │   └── version.rb
│           └── appicon.rb
└── spec/
    ├── appicon_action_spec.rb
    └── spec_helper.rb

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.gem
/gems/
Gemfile.lock

## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/
fastlane/README.md
fastlane/report.xml
.DS_Store

## Ignore generated asset catalogue
Assets.xcassets/

## Ignore android generated icon
app/


================================================
FILE: .rspec
================================================
--require spec_helper
--color
--format d


================================================
FILE: .rubocop.yml
================================================
# kind_of? is a good way to check a type
Style/ClassCheck:
  EnforcedStyle: kind_of?

# specs sometimes have useless assignments, which is fine
Lint/UselessAssignment:
  Exclude:
    - '**/spec/**/*'

# We could potentially enable the 2 below:
Layout/FirstHashElementIndentation:
  Enabled: false

Layout/HashAlignment:
  Enabled: false

# HoundCI doesn't like this rule
Layout/DotPosition:
  Enabled: false

# We allow !! as it's an easy way to convert ot boolean
Style/DoubleNegation:
  Enabled: false

# Sometimes we allow a rescue block that doesn't contain code
Lint/SuppressedException:
  Enabled: false

# Cop supports --auto-correct.
Lint/UnusedBlockArgument:
  Enabled: false

# Needed for $verbose
Style/GlobalVars:
  Enabled: false

# We want to allow class Fastlane::Class
Style/ClassAndModuleChildren:
  Enabled: false

# $? Exit
Style/SpecialGlobalVars:
  Enabled: false

Metrics/AbcSize:
  Max: 60
  Exclude:
    - '**/lib/*/options.rb'

# The %w might be confusing for new users
Style/WordArray:
  MinSize: 19

# raise and fail are both okay
Style/SignalException:
  Enabled: false

# Better too much 'return' than one missing
Style/RedundantReturn:
  Enabled: false

# Having if in the same line might not always be good
Style/IfUnlessModifier:
  Enabled: false

# and and or is okay
Style/AndOr:
  Enabled: false

# Configuration parameters: CountComments.
Metrics/ClassLength:
  Max: 320

Metrics/CyclomaticComplexity:
  Max: 17

# Configuration parameters: AllowURI, URISchemes.
Layout/LineLength:
  Max: 370

# Configuration parameters: CountKeywordArgs.
Metrics/ParameterLists:
  Max: 17

Metrics/PerceivedComplexity:
  Max: 18

# Sometimes it's easier to read without guards
Style/GuardClause:
  Enabled: false

# We allow both " and '
Style/StringLiterals:
  Enabled: false

# something = if something_else
# that's confusing
Style/ConditionalAssignment:
  Enabled: false

# Better to have too much self than missing a self
Style/RedundantSelf:
  Enabled: false

# e.g.
# def self.is_supported?(platform)
# we may never use `platform`
Lint/UnusedMethodArgument:
  Enabled: false

# the let(:key) { ... }
Lint/ParenthesesAsGroupedExpression:
  Exclude:
    - '**/spec/**/*'

# This would reject is_ in front of methods
# We use `is_supported?` everywhere already
Naming/PredicateName:
  Enabled: false

# We allow the $
Style/PerlBackrefs:
  Enabled: false

# Disable '+ should be surrounded with a single space' for xcodebuild_spec.rb
Layout/SpaceAroundOperators:
  Exclude:
    - '**/spec/actions_specs/xcodebuild_spec.rb'

Metrics/MethodLength:
  Exclude:
    - '**/lib/fastlane/actions/*.rb'
    - '**/bin/fastlane'
    - '**/lib/*/options.rb'
  Max: 60

AllCops:
  Include:
    - '**/fastlane/Fastfile'
  Exclude:
    - '**/lib/assets/custom_action_template.rb'
    - './vendor/**/*'

# We're not there yet
Style/Documentation:
  Enabled: false

# Added after upgrade to 0.38.0
Style/MutableConstant:
  Enabled: false

# length > 0 is good
Style/ZeroLengthPredicate:
  Enabled: false

# Adds complexity
Style/IfInsideElse:
  Enabled: false


================================================
FILE: .travis.yml
================================================
# os: osx # enable this if you need OS X support
language: ruby
rvm:
  - 2.2.4


================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'

gemspec

plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval(File.read(plugins_path), binding) if File.exist?(plugins_path)

gem 'activesupport', '~> 4.0'


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2016 Boris Bügling <boris@icculus.org>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
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.


================================================
FILE: README.md
================================================
# appicon plugin

[![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-appicon)

![Demo image](demo.png)

## Getting Started

This project is a [fastlane](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-appicon`, add it to your project by running:

```bash
fastlane add_plugin appicon
```


Please note that this plugin uses [minimagick](https://github.com/minimagick/minimagick), which requires either GraphicsMagick or ImageMagick library. If you have neither, you can install either via Homebrew:


```
brew install graphicsmagick
```
or
```
brew install imagemagick
```

The default CLI for the `mini_magick` gem is set to auto pick. It will first try to use `GraphicsMagick` (if you have it installed) otherwise it will use `ImageMagick`. If you want to be explicit about which CLI you use, set the `minimagick_cli` option to `graphicsmagick` or `imagemagick`. Not specifying this option will set `MiniMagick` to use auto which will choose what's available.

## About appicon

Generate required icon sizes and iconset from a master application icon.

Since many apps use a single 1024x1024 icon to produce all the required sizes from, why not automate the process and save lots of time?

## Example

Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.

Just specify the source image using the `appicon_image_file`. Optionally specify the devices using `appicon_devices` and the destination path using `appicon_path`.

We recommend storing the full-size picture at `fastlane/metadata/app_icon.png` so it can be picked up by _deliver_, as well as this plugin.

If you want to use this plugin to generate a app icon for Messages(sticker) extension, set `messages_extension` to `true` and add `messages` to the `appicon_devices`.

```ruby
lane :basic do
  appicon(
    appicon_devices: [:ipad, :iphone, :ios_marketing],
    appicon_path: "MajorKey/Assets.xcassets"
  )
end

lane :test1 do
  appicon(appicon_image_file: 'spec/fixtures/Themoji.png',
             appicon_devices: [:ipad, :iphone, :ios_marketing])
end


lane :test2 do
  appicon(appicon_image_file: 'spec/fixtures/Themoji.png',
             appicon_devices: [:ipad, :iphone, :ios_marketing, :watch, :watch_marketing])
end

lane :test3 do
  # `appicon_image_file` defaults to "fastlane/metadata/app_icon.png"
  appicon(
    appicon_devices: [:iphone],
    appicon_path: 'wwdcfamily/Images.xcassets' # output path
  )
end

lane :test4 do
  appicon(appicon_image_file: 'spec/fixtures/Themoji.png',
             appicon_devices: [:macos])
end

lane :splash_screen do
  appicon(
    appicon_image_file: 'spec/fixtures/splash_screen.png',
    appicon_devices: [:universal],
    appicon_path: "ios/App/App/Assets.xcassets",
    appicon_name: 'Splash.imageset'
  )
end

lane :messages_extension do
  appicon(
    appicon_image_file: "fastlane/metadata/iMessageAppIcon.png",
    appicon_devices: [:iphone, :ipad, :ios_marketing, :messages],
    appicon_path: 'iMessageStickers/Stickers.xcassets',
    messages_extension: true
  )
end

# or

lane :android do
  android_appicon(
    appicon_image_file: 'spec/fixtures/Themoji.png',
    appicon_icon_types: [:launcher],
    appicon_path: 'app/res/mipmap'
  )
  android_appicon(
    appicon_image_file: 'spec/fixtures/ThemojiNotification.png',
    appicon_icon_types: [:notification],
    appicon_path: 'app/res/drawable',
    appicon_filename: 'ic_notification',
    generate_rounded: true
  )
  android_appicon(
    appicon_image_file: 'spec/fixtures/splash_base_image.png',
    appicon_icon_types: [:splash_port, :splash_land],
    appicon_path: 'app/res/drawable',
    appicon_filename: 'splash'
  )
end
```

## Run tests for this plugin

To run both the tests, and code style validation, run

```
rake
```

To automatically fix many of the styling issues, use
```
rubocop -a
```

## Issues and Feedback

For any other issues and feedback about this plugin, please submit it to this repository.

## Troubleshooting

If you have trouble using plugins, check out the [Plugins Troubleshooting](https://github.com/fastlane/fastlane/blob/master/fastlane/docs/PluginsTroubleshooting.md) doc in the main `fastlane` repo.

## Using `fastlane` Plugins

For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).

## About `fastlane`

`fastlane` is the easiest way to automate building and releasing your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).


================================================
FILE: Rakefile
================================================
require 'bundler/gem_tasks'

require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new

require 'rubocop/rake_task'
RuboCop::RakeTask.new(:rubocop)

task default: [:spec, :rubocop]


================================================
FILE: fastlane/Fastfile
================================================
# frozen_string_literal: true

lane :ios do
  appicon(
    appicon_image_file: 'spec/fixtures/Themoji.png',
    appicon_devices: %i[ipad iphone ios_marketing watch watch_marketing],
    appicon_path: 'app'
  )
end

lane :ios_splash do
  appicon(
    appicon_image_file: 'spec/fixtures/ThemojiSplash.png',
    appicon_devices: [:universal],
    appicon_path: 'app',
    appicon_name: 'Splash.imageset'
  )
end

lane :ios_messages_extension do
  appicon(
    appicon_image_file: 'spec/fixtures/ThemojiSplash.png',
    appicon_devices: %i[iphone ipad ios_marketing messages],
    appicon_path: 'app/iMessageStickers/Stickers.xcassets',
    messages_extension: true
  )
end

lane :macos do
  appicon(
    appicon_image_file: 'spec/fixtures/ThemojiSplash.png',
    appicon_devices: [:macos],
    appicon_path: 'app',
    appicon_name: 'Mac.appiconset'
  )
end

lane :android do
  android_appicon(
    appicon_image_file: 'spec/fixtures/Themoji.png',
    appicon_path: 'app/res/mipmap',
    generate_rounded: true
  )
end

lane :android_splash do
  android_appicon(
    appicon_image_file: 'spec/fixtures/ThemojiSplash.png',
    appicon_icon_types: %i[splash_port splash_land],
    appicon_path: 'app/res/drawable',
    appicon_filename: 'splash'
  )
end


================================================
FILE: fastlane/Pluginfile
================================================
# Autogenerated by fastlane


================================================
FILE: fastlane-plugin-appicon.gemspec
================================================
# coding: utf-8
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'fastlane/plugin/appicon/version'

Gem::Specification.new do |spec|
  spec.name          = 'fastlane-plugin-appicon'
  spec.version       = Fastlane::Appicon::VERSION
  spec.author        = ["Boris Bügling", "Felix Krause"]
  spec.email         = %q{boris@icculus.org}

  spec.summary       = %q{Generate required icon sizes and iconset from a master application icon.}
  spec.homepage      = "https://github.com/fastlane-community/fastlane-plugin-appicon"
  spec.license       = "MIT"

  spec.files         = Dir["lib/**/*"] + %w(README.md LICENSE)
  spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
  spec.require_paths = ['lib']

  spec.add_dependency 'mini_magick', '>= 4.9.4', '< 5.0.0'
  spec.add_dependency 'json'

  spec.add_development_dependency 'pry'
  spec.add_development_dependency 'bundler'
  spec.add_development_dependency 'rspec'
  spec.add_development_dependency 'rake'
  spec.add_development_dependency 'rubocop'
  spec.add_development_dependency 'fastlane', '>= 1.95.0'
end


================================================
FILE: lib/fastlane/plugin/appicon/actions/android_appicon_action.rb
================================================
require 'mini_magick'

module Fastlane
  module Actions
    class AndroidAppiconAction < Action

      def self.needed_icons
        {
          launcher: {
            :ldpi => ['36x36'],
            :mdpi => ['48x48'],
            :hdpi => ['72x72'],
            :xhdpi => ['96x96'],
            :xxhdpi => ['144x144'],
            :xxxhdpi => ['192x192']
          },
          notification: {
            :ldpi => ['18x18'],
            :mdpi => ['24x24'],
            :hdpi => ['36x36'],
            :xhdpi => ['48x48'],
            :xxhdpi => ['72x72'],
            :xxxhdpi => ['96x96'],
          },
          splash_land: {
            'land-ldpi' => ['320x200'],
            'land-mdpi' => ['480x320'],
            'land-hdpi' => ['800x480'],
            'land-xhdpi' => ['1280x720'],
            'land-xxhdpi' => ['1600x960'],
            'land-xxxhdpi' => ['1920x1280']
          },
          splash_port: {
            'port-ldpi' => ['200x320'],
            'port-mdpi' => ['320x480'],
            'port-hdpi' => ['480x800'],
            'port-xhdpi' => ['720x1280'],
            'port-xxhdpi' => ['960x1600'],
            'port-xxxhdpi' => ['1280x1920']
          }
        }
      end

      def self.run(params)

        Helper::AppiconHelper.set_cli(params[:minimagick_cli])

        fname = params[:appicon_image_file]
        custom_sizes = params[:appicon_custom_sizes]

        icons = Helper::AppiconHelper.get_needed_icons(params[:appicon_icon_types], self.needed_icons, true, custom_sizes)
        icons.each do |icon|
          image = MiniMagick::Image.open(fname)

          Helper::AppiconHelper.check_input_image_size(image, 1024, 1024)

          # Custom icons will have basepath and filename already defined
          if icon.has_key?('basepath') && icon.has_key?('filename')
            basepath = Pathname.new(icon['basepath'])
            filename = icon['filename']
          else
            basepath = Pathname.new("#{params[:appicon_path]}-#{icon['scale']}")
            filename = "#{params[:appicon_filename]}.png"
          end

          width_height = [icon['width'], icon['height']].map(&:to_i)
          width, height = width_height
          max = width_height.max

          image.format 'png'
          image.resize "#{max}x#{max}"

          unless width == height
            offset =
            if width > height
              "+0+#{(width - height) / 2}"
            elsif height > width
              "+#{(height - width) / 2}+0"
            end

            image.crop "#{icon['size']}#{offset}"
          end

          FileUtils.mkdir_p(basepath)
          image.write basepath + filename

          if basepath.to_s.match("port-")
            default_portrait_path = basepath.to_s.gsub("port-","")
            FileUtils.mkdir_p(default_portrait_path)
            image.write default_portrait_path + '/' + filename
          end

          if params[:generate_rounded]
            rounded_image = MiniMagick::Image.open(fname)
            rounded_image.format 'png'
            rounded_image.resize "#{width}x#{height}"
            rounded_image = round(rounded_image)
            rounded_image.write basepath + filename.gsub('.png', '_round.png')
          end
        end

        UI.success("Successfully stored launcher icons at '#{params[:appicon_path]}'")
      end

      def self.round(img)
        require 'mini_magick'
        img.format 'png'

        width = img[:width]-2
        radius = width/2

        mask = ::MiniMagick::Image.open img.path
        mask.format 'png'

        mask.combine_options do |m|
          m.alpha 'transparent'
          m.background 'none'
          m.fill 'white'
          m.draw 'roundrectangle 1,1,%s,%s,%s,%s' % [width, width, radius, radius]
        end

        masked = img.composite(mask, 'png') do |i|
          i.alpha "set"
          i.compose 'DstIn'
        end

        return masked
      end

      def self.get_custom_sizes(image, custom_sizes)

      end

      def self.description
        "Generate required icon sizes from a master application icon"
      end

      def self.authors
        ["@adrum"]
      end

      def self.available_options
        [
          FastlaneCore::ConfigItem.new(key: :appicon_image_file,
                                  env_name: "APPICON_IMAGE_FILE",
                               description: "Path to a square image file, at least 512x512",
                                  optional: false,
                                      type: String),
          FastlaneCore::ConfigItem.new(key: :appicon_icon_types,
                                  env_name: "APPICON_ICON_TYPES",
                             default_value: [:launcher],
                               description: "Array of device types to generate icons for",
                                  optional: true,
                                      type: Array),
          FastlaneCore::ConfigItem.new(key: :appicon_path,
                                  env_name: "APPICON_PATH",
                             default_value: 'app/res/mipmap',
                               description: "Path to res subfolder",
                                  optional: true,
                                      type: String),
          FastlaneCore::ConfigItem.new(key: :appicon_filename,
                                  env_name: "APPICON_FILENAME",
                             default_value: 'ic_launcher',
                               description: "The output filename of each image",
                                  optional: true,
                                      type: String),
          FastlaneCore::ConfigItem.new(key: :appicon_custom_sizes,
                               description: "Hash of custom sizes - {'path/icon.png' => '256x256'}",
                             default_value: {},
                                  optional: true,
                                      type: Hash),
          FastlaneCore::ConfigItem.new(key: :generate_rounded,
                               description: "Generate round icons?",
                             default_value: false,
                                      type: Boolean),
          FastlaneCore::ConfigItem.new(key: :minimagick_cli,
                                  env_name: "APPICON_MINIMAGICK_CLI",
                               description: "Set MiniMagick CLI (auto picked by default). Values are: graphicsmagick, imagemagick",
                                  optional: true,
                                      type: String,
                              verify_block: proc do |value|
                                        av = %w(graphicsmagick imagemagick)
                                        UI.user_error!("Unsupported minimagick cli '#{value}', must be: #{av}") unless av.include?(value)
                                      end)
        ]
      end

      def self.is_supported?(platform)
        [:android].include?(platform)
      end
    end
  end
end


================================================
FILE: lib/fastlane/plugin/appicon/actions/appicon_action.rb
================================================
require 'json'
require 'mini_magick'

module Fastlane
  module Actions
    class AppiconAction < Action
      def self.needed_icons
        {
          universal: {
            '1x' => ['2732x2732'],
            '2x' => ['2732x2732'],
            '3x' => ['2732x2732']
          },
          iphone: {
            '2x' => ['20x20', '29x29', '40x40', '60x60'],
            '3x' => ['20x20', '29x29', '40x40', '60x60']
          },
          ipad: {
            '1x' => ['20x20', '29x29', '40x40', '76x76'],
            '2x' => ['20x20', '29x29', '40x40', '76x76', '83.5x83.5']
          },
          :ios_marketing => {
            '1x' => ['1024x1024']
          },
          :watch => {
            '2x' => [
                      ['24x24', 'notificationCenter', '38mm'],
                      ['27.5x27.5', 'notificationCenter', '42mm'],
                      ['29x29', 'companionSettings'],
                      ['40x40', 'appLauncher', '38mm'],
                      ['44x44', 'appLauncher', '40mm'],
                      ['50x50', 'appLauncher', '44mm'],
                      ['86x86', 'quickLook', '38mm'],
                      ['98x98', 'quickLook', '42mm'],
                      ['108x108', 'quickLook', '44mm']
                    ],
            '3x' => [['29x29', 'companionSettings']]
          },
          :watch_marketing => {
            '1x' => ['1024x1024']
          },
          :macos => {
            '1x' => ['16x16', '32x32', '128x128', '256x256', '512x512'],
            '2x' => ['16x16', '32x32', '128x128', '256x256', '512x512'],
          }
        }
      end

      def self.needed_icons_messages_extension
        {
          iphone: {
            '2x' => ['60x45'],
            '3x' => ['60x45']
          },
          ipad: {
            '2x' => ['67x50', '74x55']
          },
          messages: {
            '2x' => ['27x20', '32x24'],
            '3x' => ['27x20', '32x24']
          },
          ios_marketing: {
            '1x' => ['1024x768']
          }
        }
      end

      def self.run(params)
        fname = params[:appicon_image_file]
        basename = File.basename(fname, File.extname(fname))

        Helper::AppiconHelper.set_cli(params[:minimagick_cli])

        is_messages_extension = params[:messages_extension]
        basepath = Pathname.new(File.join(params[:appicon_path], is_messages_extension ? params[:appicon_messages_name] : params[:appicon_name]))
        
        image = MiniMagick::Image.open(fname)

        if is_messages_extension
          Helper::AppiconHelper.check_input_image_size(image, 1024, 768)
        else
          Helper::AppiconHelper.check_input_image_size(image, 1024, 1024)
        end

        # Convert image to png
        image.format 'png'

        # remove alpha channel
        if params[:remove_alpha]
          image.alpha 'remove'
        end

        # Recreate the base path
        FileUtils.rm_rf(basepath)
        FileUtils.mkdir_p(basepath)

        images = []

        icons = Helper::AppiconHelper.get_needed_icons(params[:appicon_devices], is_messages_extension ? self.needed_icons_messages_extension : self.needed_icons, false)
        icons.each do |icon|
          width = icon['width']
          height = icon['height']
          filename = basename
          unless icon['device'] == 'universal'
            filename += "-#{width.to_i}x#{height.to_i}"
          end
          filename += ".png"

          # downsize icon
          # "!" resizes to exact size (needed for messages extension)
          image.resize "#{width}x#{height}!"

          # Don't write change/created times into the PNG properties
          # so unchanged files don't have different hashes.
          image.define("png:exclude-chunks=date,time")

          image.write basepath + filename

          info = {
            'idiom' => icon['device'],
            'filename' => filename,
            'scale' => icon['scale']
          }

          # if device is messages, we need to set it to universal
          if icon['device'] == 'messages'
            info['idiom'] = 'universal'
            info['platform'] = 'ios'
          end

          # if device is ios_marketing but we are generating messages extension app, set the idiom correctly
          if icon['device'] == 'ios_marketing' && is_messages_extension
            info['platform'] = 'ios'
          end
          

          unless icon['device'] == 'universal'
            info['size'] = icon['size']
          end

          info['role'] = icon['role'] unless icon['role'].nil?
          info['subtype'] = icon['subtype'] unless icon['subtype'].nil?

          images << info
        end

        contents = {
          'images' => images,
          'info' => {
            'version' => 1,
            'author' => 'fastlane'
          }
        }

        File.write(File.join(basepath, 'Contents.json'), JSON.pretty_generate(contents))
        UI.success("Successfully stored app icon at '#{basepath}'")
      end

      def self.description
        "Generate required icon sizes and iconset from a master application icon"
      end

      def self.authors
        ["@NeoNacho"]
      end

      def self.available_options
        [
          FastlaneCore::ConfigItem.new(key: :appicon_image_file,
                                  env_name: "APPICON_IMAGE_FILE",
                               description: "Path to a square image file, at least 1024x1024",
                                  optional: false,
                                      type: String,
                             default_value: Dir["fastlane/metadata/app_icon.png"].last), # that's the default when using fastlane to manage app metadata
          FastlaneCore::ConfigItem.new(key: :appicon_devices,
                                  env_name: "APPICON_DEVICES",
                             default_value: [:iphone],
                               description: "Array of device idioms to generate icons for",
                                  optional: true,
                                      type: Array),
          FastlaneCore::ConfigItem.new(key: :appicon_path,
                                  env_name: "APPICON_PATH",
                             default_value: 'Assets.xcassets',
                               description: "Path to the Asset catalogue for the generated iconset",
                                  optional: true,
                                      type: String),
          FastlaneCore::ConfigItem.new(key: :appicon_name,
                                  env_name: "APPICON_NAME",
                             default_value: 'AppIcon.appiconset',
                               description: "Name of the appiconset inside the asset catalogue",
                                  optional: true,
                                      type: String),
          FastlaneCore::ConfigItem.new(key: :appicon_messages_name,
                                  env_name: "APPICON_MESSAGES_NAME",
                             default_value: 'AppIcon.stickersiconset',
                               description: "Name of the appiconset inside the asset catalogue",
                                  optional: true,
                                      type: String),
          FastlaneCore::ConfigItem.new(key: :remove_alpha,
                                  env_name: "REMOVE_ALPHA",
                             default_value: false,
                               description: "Remove the alpha channel from generated PNG",
                                  optional: true,
                                      type: Boolean),
          FastlaneCore::ConfigItem.new(key: :messages_extension,
                                  env_name: "APPICON_MESSAGES_EXTENSION",
                             default_value: false,
                               description: "App icon is generated for Messages extension",
                                  optional: true,
                                      type: Boolean),
          FastlaneCore::ConfigItem.new(key: :minimagick_cli,
                                  env_name: "APPICON_MINIMAGICK_CLI",
                               description: "Set MiniMagick CLI (auto picked by default). Values are: graphicsmagick, imagemagick",
                                  optional: true,
                                      type: String,
                              verify_block: proc do |value|
                                        av = %w(graphicsmagick imagemagick)
                                        UI.user_error!("Unsupported minimagick cli '#{value}', must be: #{av}") unless av.include?(value)
                                      end)
        ]
      end

      def self.is_supported?(platform)
        [:ios, :mac, :macos, :caros, :rocketos].include?(platform)
      end
    end
  end
end


================================================
FILE: lib/fastlane/plugin/appicon/helper/appicon_helper.rb
================================================
require 'mini_magick'

module Fastlane
  module Helper
    class AppiconHelper
      def self.check_input_image_size(image, width, height)
        UI.user_error!("Minimum width of input image should be #{width}") if image.width < width
        UI.user_error!("Minimum height of input image should be #{height}") if image.height < height
        UI.user_error!("Input image should be square") if image.width / image.height != width / height
      end

      def self.set_cli(minimagick_cli)
        MiniMagick.configure do |config|
          case minimagick_cli
          when "graphicsmagick"
          config.cli = :graphicsmagick
          when "imagemagick"
            config.cli = :imagemagick
          else
            config.cli = MiniMagick.cli()
          end
          config.timeout = 5
        end
      end

      def self.get_needed_icons(devices, needed_icons, is_android = false, custom_sizes = {})
        icons = []
        devices.each do |device|
          needed_icons[device].each do |scale, sizes|
            sizes.each do |size|
              if size.kind_of?(Array)
                size, role, subtype = size
              end

              if is_android
                width, height = size.split('x').map { |v| v.to_f }
              else
                multiple = device.match(/universal/) ? 1 : scale.to_i
                width, height = size.split('x').map { |v| v.to_f * multiple }
              end

              icons << {
                'width' => width,
                'height' => height,
                'size' => size,
                'device' => device.to_s.gsub('_', '-'),
                'scale' => scale,
                'role' => role,
                'subtype' => subtype
              }

            end
          end
        end

        # Add custom icon sizes (probably for notifications)
        custom_sizes.each do |path, size|
          path = path.to_s
          width, height = size.split('x').map { |v| v.to_f }

          icons << {
            'width' => width,
            'height' => height,
            'size' => size,
            'basepath' => File.dirname(path),
            'filename' => File.basename(path)
          }
        end

        # Sort from the largest to the smallest needed icon
        icons = icons.sort_by {|value| value['width']} .reverse
      end
    end
  end
end


================================================
FILE: lib/fastlane/plugin/appicon/version.rb
================================================
module Fastlane
  module Appicon
    VERSION = "0.16.0"
  end
end


================================================
FILE: lib/fastlane/plugin/appicon.rb
================================================
require 'fastlane/plugin/appicon/version'

module Fastlane
  module Appicon
    # Return all .rb files inside the "actions" directory
    def self.all_classes
      Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
    end
  end
end

# By default we want to import all available actions
# A plugin can contain any number of actions and plugins
Fastlane::Appicon.all_classes.each do |current|
  require current
end


================================================
FILE: spec/appicon_action_spec.rb
================================================
describe Fastlane::Actions::AppiconAction do
end


================================================
FILE: spec/spec_helper.rb
================================================
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)

# This module is only used to check the environment is currently a testing env
module SpecHelper
end

require 'fastlane' # to import the Action super class
require 'fastlane/plugin/appicon' # import the actual plugin

Fastlane.load_actions # load other actions (in case your plugin calls other actions or shared values)
Download .txt
gitextract_fqhyqhsc/

├── .gitignore
├── .rspec
├── .rubocop.yml
├── .travis.yml
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── fastlane/
│   ├── Fastfile
│   └── Pluginfile
├── fastlane-plugin-appicon.gemspec
├── lib/
│   └── fastlane/
│       └── plugin/
│           ├── appicon/
│           │   ├── actions/
│           │   │   ├── android_appicon_action.rb
│           │   │   └── appicon_action.rb
│           │   ├── helper/
│           │   │   └── appicon_helper.rb
│           │   └── version.rb
│           └── appicon.rb
└── spec/
    ├── appicon_action_spec.rb
    └── spec_helper.rb
Download .txt
SYMBOL INDEX (33 symbols across 6 files)

FILE: lib/fastlane/plugin/appicon.rb
  type Fastlane (line 3) | module Fastlane
    type Appicon (line 4) | module Appicon
      function all_classes (line 6) | def self.all_classes

FILE: lib/fastlane/plugin/appicon/actions/android_appicon_action.rb
  type Fastlane (line 3) | module Fastlane
    type Actions (line 4) | module Actions
      class AndroidAppiconAction (line 5) | class AndroidAppiconAction < Action
        method needed_icons (line 7) | def self.needed_icons
        method run (line 44) | def self.run(params)
        method round (line 105) | def self.round(img)
        method get_custom_sizes (line 130) | def self.get_custom_sizes(image, custom_sizes)
        method description (line 134) | def self.description
        method authors (line 138) | def self.authors
        method available_options (line 142) | def self.available_options
        method is_supported? (line 188) | def self.is_supported?(platform)

FILE: lib/fastlane/plugin/appicon/actions/appicon_action.rb
  type Fastlane (line 4) | module Fastlane
    type Actions (line 5) | module Actions
      class AppiconAction (line 6) | class AppiconAction < Action
        method needed_icons (line 7) | def self.needed_icons
        method needed_icons_messages_extension (line 49) | def self.needed_icons_messages_extension
        method run (line 68) | def self.run(params)
        method description (line 159) | def self.description
        method authors (line 163) | def self.authors
        method available_options (line 167) | def self.available_options
        method is_supported? (line 223) | def self.is_supported?(platform)

FILE: lib/fastlane/plugin/appicon/helper/appicon_helper.rb
  type Fastlane (line 3) | module Fastlane
    type Helper (line 4) | module Helper
      class AppiconHelper (line 5) | class AppiconHelper
        method check_input_image_size (line 6) | def self.check_input_image_size(image, width, height)
        method set_cli (line 12) | def self.set_cli(minimagick_cli)
        method get_needed_icons (line 26) | def self.get_needed_icons(devices, needed_icons, is_android = fals...

FILE: lib/fastlane/plugin/appicon/version.rb
  type Fastlane (line 1) | module Fastlane
    type Appicon (line 2) | module Appicon

FILE: spec/spec_helper.rb
  type SpecHelper (line 4) | module SpecHelper
Condensed preview — 18 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (33K chars).
[
  {
    "path": ".gitignore",
    "chars": 246,
    "preview": "*.gem\n/gems/\nGemfile.lock\n\n## Documentation cache and generated files:\n/.yardoc/\n/_yardoc/\n/doc/\n/rdoc/\nfastlane/README."
  },
  {
    "path": ".rspec",
    "chars": 41,
    "preview": "--require spec_helper\n--color\n--format d\n"
  },
  {
    "path": ".rubocop.yml",
    "chars": 3068,
    "preview": "# kind_of? is a good way to check a type\nStyle/ClassCheck:\n  EnforcedStyle: kind_of?\n\n# specs sometimes have useless ass"
  },
  {
    "path": ".travis.yml",
    "chars": 79,
    "preview": "# os: osx # enable this if you need OS X support\nlanguage: ruby\nrvm:\n  - 2.2.4\n"
  },
  {
    "path": "Gemfile",
    "chars": 214,
    "preview": "source 'https://rubygems.org'\n\ngemspec\n\nplugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')\neval("
  },
  {
    "path": "LICENSE",
    "chars": 1101,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Boris Bügling <boris@icculus.org>\n\nPermission is hereby granted, free of char"
  },
  {
    "path": "README.md",
    "chars": 4755,
    "preview": "# appicon plugin\n\n[![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-b"
  },
  {
    "path": "Rakefile",
    "chars": 180,
    "preview": "require 'bundler/gem_tasks'\n\nrequire 'rspec/core/rake_task'\nRSpec::Core::RakeTask.new\n\nrequire 'rubocop/rake_task'\nRuboC"
  },
  {
    "path": "fastlane/Fastfile",
    "chars": 1249,
    "preview": "# frozen_string_literal: true\n\nlane :ios do\n  appicon(\n    appicon_image_file: 'spec/fixtures/Themoji.png',\n    appicon_"
  },
  {
    "path": "fastlane/Pluginfile",
    "chars": 28,
    "preview": "# Autogenerated by fastlane\n"
  },
  {
    "path": "fastlane-plugin-appicon.gemspec",
    "chars": 1135,
    "preview": "# coding: utf-8\nlib = File.expand_path(\"../lib\", __FILE__)\n$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)\nrequi"
  },
  {
    "path": "lib/fastlane/plugin/appicon/actions/android_appicon_action.rb",
    "chars": 6977,
    "preview": "require 'mini_magick'\n\nmodule Fastlane\n  module Actions\n    class AndroidAppiconAction < Action\n\n      def self.needed_i"
  },
  {
    "path": "lib/fastlane/plugin/appicon/actions/appicon_action.rb",
    "chars": 8824,
    "preview": "require 'json'\nrequire 'mini_magick'\n\nmodule Fastlane\n  module Actions\n    class AppiconAction < Action\n      def self.n"
  },
  {
    "path": "lib/fastlane/plugin/appicon/helper/appicon_helper.rb",
    "chars": 2354,
    "preview": "require 'mini_magick'\n\nmodule Fastlane\n  module Helper\n    class AppiconHelper\n      def self.check_input_image_size(ima"
  },
  {
    "path": "lib/fastlane/plugin/appicon/version.rb",
    "chars": 66,
    "preview": "module Fastlane\n  module Appicon\n    VERSION = \"0.16.0\"\n  end\nend\n"
  },
  {
    "path": "lib/fastlane/plugin/appicon.rb",
    "chars": 438,
    "preview": "require 'fastlane/plugin/appicon/version'\n\nmodule Fastlane\n  module Appicon\n    # Return all .rb files inside the \"actio"
  },
  {
    "path": "spec/appicon_action_spec.rb",
    "chars": 49,
    "preview": "describe Fastlane::Actions::AppiconAction do\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "chars": 380,
    "preview": "$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)\n\n# This module is only used to check the environment is curre"
  }
]

About this extraction

This page contains the full source code of the KrauseFx/fastlane-plugin-appicon GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 18 files (30.5 KB), approximately 7.8k tokens, and a symbol index with 33 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!