Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ workflows:
- test_ios
- test_android_apk
- test_android_aab
- test_android_split_apk
- test_both

ci-android-only:
Expand All @@ -46,6 +47,7 @@ workflows:
- content: git clone $SAMPLE_APP_URL -b manual .
- path::./:
inputs:
- is_debug_mode: "true"
- platform: ios

test_android_apk:
Expand All @@ -58,6 +60,21 @@ workflows:
- content: git clone $SAMPLE_APP_URL .
- path::./:
inputs:
- is_debug_mode: "true"
- platform: android

test_android_split_apk:
before_run:
- _clear_workdir
steps:
- script:
title: Clone sample app
inputs:
- content: git clone $SAMPLE_APP_URL .
- path::./:
inputs:
- android_additional_params: --release --split-per-abi
- is_debug_mode: "true"
- platform: android

test_android_aab:
Expand All @@ -70,6 +87,7 @@ workflows:
- content: git clone $SAMPLE_APP_URL .
- path::./:
inputs:
- is_debug_mode: "true"
- platform: android
- android_output_type: appbundle

Expand All @@ -82,7 +100,8 @@ workflows:
inputs:
- content: git clone $SAMPLE_APP_URL -b manual .
- path::./:
inputs:
inputs:
- is_debug_mode: "true"
- platform: both

_clear_workdir:
Expand Down
90 changes: 60 additions & 30 deletions buildspec.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"

Expand All @@ -22,52 +23,60 @@ type buildSpecification struct {
displayName string
platformCmdFlag string
platformSelectors []string
outputPathPattern string
outputPathPatterns []string
additionalParameters string
projectLocation string
}

func (spec buildSpecification) exportArtifacts(outputPathPattern string) error {
func (spec buildSpecification) exportArtifacts(artifacts []string) error {
deployDir := os.Getenv("BITRISE_DEPLOY_DIR")
switch spec.platformCmdFlag {
case "apk":
fallthrough
return spec.exportAndroidArtifacts(APK, artifacts, deployDir)
case "appbundle":
return spec.exportAndroidArtifacts(outputPathPattern, deployDir)
return spec.exportAndroidArtifacts(AppBundle, artifacts, deployDir)
case "ios":
paths, err := findPaths(spec.projectLocation, outputPathPattern, true)
if err != nil {
return err
}
return spec.exportIOSApp(artifacts, deployDir)
default:
return fmt.Errorf("unsupported platform for exporting artifacts: %s. Supported platforms: apk, appbundle, ios", spec.platformCmdFlag)
}
}

path := paths[len(paths)-1]
if len(paths) > 1 {
log.Warnf("- Multiple artifacts found for pattern \"%s\": %v, exporting %s", outputPathPattern, paths, path)
func (spec buildSpecification) artifactPaths(outputPathPatterns []string, isDir bool) ([]string, error) {
var paths []string
for _, outputPathPattern := range outputPathPatterns {
pths, err := findPaths(spec.projectLocation, outputPathPattern, isDir)
if err != nil {
return nil, err
}
paths = append(paths, pths...)
}
return paths, nil
}

fileName := filepath.Base(path)
func (spec buildSpecification) exportIOSApp(artifacts []string, deployDir string) error {
artifact := artifacts[len(artifacts)-1]
fileName := filepath.Base(artifact)

if err := ziputil.ZipDir(path, filepath.Join(deployDir, fileName+".zip"), false); err != nil {
return err
}
log.Donef("- $BITRISE_DEPLOY_DIR/" + fileName + ".zip")

if err := tools.ExportEnvironmentWithEnvman("BITRISE_APP_DIR_PATH", path); err != nil {
return err
}
log.Donef("- $BITRISE_APP_DIR_PATH: " + path)
if len(artifacts) > 1 {
log.Warnf("- Multiple artifacts found: %v, exporting %s", artifacts, artifact)
}

return nil
default:
return fmt.Errorf("unsupported platform for exporting artifacts")
if err := ziputil.ZipDir(artifact, filepath.Join(deployDir, fileName+".zip"), false); err != nil {
return err
}
}
log.Donef("- $BITRISE_DEPLOY_DIR/" + fileName + ".zip")

func (spec buildSpecification) exportAndroidArtifacts(outputPathPattern string, deployDir string) error {
paths, err := findPaths(spec.projectLocation, outputPathPattern, false)
if err != nil {
if err := tools.ExportEnvironmentWithEnvman("BITRISE_APP_DIR_PATH", artifact); err != nil {
return err
}
log.Donef("- $BITRISE_APP_DIR_PATH: " + artifact)

return nil
}

func (spec buildSpecification) exportAndroidArtifacts(androidOutputType AndroidArtifactType, artifacts []string, deployDir string) error {
artifacts = filterAndroidArtifactsBy(androidOutputType, artifacts)

var singleFileOutputEnvName string
var multipleFileOutputEnvName string
Expand All @@ -81,7 +90,7 @@ func (spec buildSpecification) exportAndroidArtifacts(outputPathPattern string,
}

var deployedFiles []string
for _, path := range paths {
for _, path := range artifacts {
deployedFilePath := filepath.Join(deployDir, filepath.Base(path))

if err := output.ExportOutputFile(path, deployedFilePath, singleFileOutputEnvName); err != nil {
Expand All @@ -98,6 +107,27 @@ func (spec buildSpecification) exportAndroidArtifacts(outputPathPattern string,
return nil
}

func filterAndroidArtifactsBy(androidOutputType AndroidArtifactType, artifacts []string) []string {
var index int
for _, artifact := range artifacts {
switch androidOutputType {
case APK:
if path.Ext(artifact) != ".apk" {
log.Debugf("Arfifact (%s) found by output patterns, but it's not the selected output type (%s) - Skip", artifact, androidOutputType)
continue // drop artifact
}
case AppBundle:
if path.Ext(artifact) != ".aab" {
log.Debugf("Arfifact (%s) found by output patterns, but it's not the selected output type (%s) - Skip", artifact, androidOutputType)
continue // drop artifact
}
}
artifacts[index] = artifact
index++
}
return artifacts[:index]
}

func (spec buildSpecification) buildable(platform string) bool {
return sliceutil.IsStringInSlice(platform, spec.platformSelectors)
}
Expand All @@ -116,7 +146,7 @@ func findPaths(location string, outputPathPattern string, dir bool) (out []strin
return nil
})
if len(out) == 0 && err == nil {
err = fmt.Errorf("couldn't find output artifact on path: " + filepath.Join(location, outputPathPattern))
log.Debugf("couldn't find output artifact on path: " + filepath.Join(location, outputPathPattern))
}
return
}
Expand Down
50 changes: 50 additions & 0 deletions buildspec_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package main

import (
"reflect"
"testing"
)

func Test_filterAndroidArtifactsBy(t *testing.T) {
tests := []struct {
name string
androidOutputType AndroidArtifactType
artifacts []string
want []string
}{
{
name: "Filter APK",
androidOutputType: APK,
artifacts: []string{
"test.apk",
"test_2.apk",
"test.aab",
},
want: []string{
"test.apk",
"test_2.apk",
},
},
{
name: "Filter AAB",
androidOutputType: AppBundle,
artifacts: []string{
"test.apk",
"test_2.apk",
"test.aab",
"test_2.aab",
},
want: []string{
"test.aab",
"test_2.aab",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := filterAndroidArtifactsBy(tt.androidOutputType, tt.artifacts); !reflect.DeepEqual(got, tt.want) {
t.Errorf("filterAndroidArtifactsBy() = %v, want %v", got, tt.want)
}
})
}
}
72 changes: 48 additions & 24 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/bitrise-io/go-steputils/stepconf"
"github.com/bitrise-io/go-utils/fileutil"
Expand All @@ -16,39 +17,60 @@ import (
shellquote "github.com/kballard/go-shellquote"
)

// AndroidArtifactType is an enum
// **APK** or **AppBundle**
type AndroidArtifactType string

// const ...
const (
codesignField = "ios-signing-cert"
noCodesignFlag = "--no-codesign"

APK AndroidArtifactType = "apk"
AppBundle AndroidArtifactType = "appbundle"
)

var flutterConfigPath = filepath.Join(os.Getenv("HOME"), ".flutter_settings")
var errCodeSign = errors.New("CODESIGN")

type config struct {
IOSAdditionalParams string `env:"ios_additional_params"`
AndroidAdditionalParams string `env:"android_additional_params"`
Platform string `env:"platform,opt[both,ios,android]"`
IOSExportPattern string `env:"ios_output_pattern"`
AndroidOutputType string `env:"android_output_type,opt[apk,appbundle]"`
AndroidExportPattern string `env:"android_output_pattern"`
IOSAdditionalParams string `env:"ios_additional_params"`
AndroidAdditionalParams string `env:"android_additional_params"`
Platform string `env:"platform,opt[both,ios,android]"`
IOSExportPattern string `env:"ios_output_pattern,required"`
AndroidOutputType AndroidArtifactType `env:"android_output_type,opt[apk,appbundle]"`
AndroidExportPattern string `env:"android_output_pattern,required"`
IOSCodesignIdentity string `env:"ios_codesign_identity"`
ProjectLocation string `env:"project_location,dir"`
DebugMode bool `env:"is_debug_mode,opt[true,false]"`

// Deprecated
AndroidBundleExportPattern string `env:"android_bundle_output_pattern"`
IOSCodesignIdentity string `env:"ios_codesign_identity"`
ProjectLocation string `env:"project_location,dir"`
IsVerbose bool `env:"verbose,opt[true,false]"`
}

func failf(msg string, args ...interface{}) {
log.Errorf(msg, args...)
os.Exit(1)
}

func handleDeprecatedInputs(cfg *config) {
if cfg.AndroidBundleExportPattern != "" && cfg.AndroidBundleExportPattern != "*build/app/outputs/bundle/*/*.aab" {
log.Warnf("step input 'App bundle output pattern' (android_bundle_output_pattern) is deprecated and will be removed on 20 November 2019, use 'Output (.apk, .aab) pattern' (android_output_pattern) instead!")
log.Printf("Using 'App bundle output pattern' (android_bundle_output_pattern) instead of 'Output (.apk, .aab) pattern' (android_output_pattern).")
log.Printf("If you don't want to use 'App bundle output pattern' (android_bundle_output_pattern), empty it's value.")

cfg.AndroidExportPattern = cfg.AndroidBundleExportPattern
}
}

func main() {
var cfg config
if err := stepconf.Parse(&cfg); err != nil {
failf("Issue with input: %s", err)
}
stepconf.Print(cfg)
log.SetEnableDebugLog(cfg.IsVerbose)
handleDeprecatedInputs(&cfg)
log.SetEnableDebugLog(cfg.DebugMode)

projectLocationAbs, err := filepath.Abs(cfg.ProjectLocation)
if err != nil {
Expand Down Expand Up @@ -142,14 +164,14 @@ build:
displayName: "iOS",
platformCmdFlag: "ios",
platformSelectors: []string{"both", "ios"},
outputPathPattern: cfg.IOSExportPattern,
outputPathPatterns: append(strings.Split(cfg.IOSExportPattern, "\n")),
additionalParameters: cfg.IOSAdditionalParams,
},
{
displayName: "Android",
platformCmdFlag: cfg.AndroidOutputType,
platformCmdFlag: string(cfg.AndroidOutputType),
platformSelectors: []string{"both", "android"},
outputPathPattern: cfg.getAndroidOutputPathPattern(),
outputPathPatterns: append(strings.Split(cfg.AndroidExportPattern, "\n")),
additionalParameters: cfg.AndroidAdditionalParams,
},
} {
Expand All @@ -176,7 +198,19 @@ build:
fmt.Println()
log.Infof("Export " + spec.displayName + " artifact")

if err := spec.exportArtifacts(spec.outputPathPattern); err != nil {
var artifacts []string
var err error

if spec.platformCmdFlag == "apk" || spec.platformCmdFlag == "appbundle" {
artifacts, err = spec.artifactPaths(spec.outputPathPatterns, false)
} else {
artifacts, err = spec.artifactPaths(spec.outputPathPatterns, true)
}
if err != nil {
failf("failed to find artifacts, error: %s", err)
}

if err := spec.exportArtifacts(artifacts); err != nil {
failf("Failed to export %s artifacts, error: %s", spec.displayName, err)
}
}
Expand All @@ -200,13 +234,3 @@ build:
log.Warnf("Failed to collect flutter cache, error: %s", err)
}
}

func (cfg config) getAndroidOutputPathPattern() string {
switch cfg.AndroidOutputType {
case "appbundle":
return cfg.AndroidBundleExportPattern
default:
return cfg.AndroidExportPattern

}
}
Loading