Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for device type for iOS apps, fix finding default iOS simulator #281

Merged
merged 3 commits into from
Sep 24, 2020
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
12 changes: 7 additions & 5 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
version: 2.1

cache_key: &cache_key tb-deps-20200924-{{ checksum "go.sum" }}

jobs:
lint-build-test:
docker:
- image: circleci/golang:1.12
working_directory: ~/tb
- image: cimg/go:1.14
steps:
- checkout
- restore_cache:
name: Restore dependency cache
keys:
- tb-deps-{{ checksum "go.sum" }}
- *cache_key
- run:
name: Install dependencies
command: make setup
- save_cache:
name: Cache dependencies
key: tb-deps-{{ checksum "go.sum" }}
key: *cache_key
paths:
- /go/pkg
- ~/go/pkg
- bin/golangci-lint
- run:
name: Run linter
command: make lint
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

# Get all dependencies
setup:
# Only install if missing
ifeq (,$(wildcard bin/golangci-lint))
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh
endif

go mod download
.PHONY: setup

Expand Down
45 changes: 40 additions & 5 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,37 @@ package app

import (
"fmt"
"strings"

"github.com/TouchBistro/tb/util"
"github.com/pkg/errors"
)

type DeviceType int

const (
DeviceTypeAll DeviceType = iota
DeviceTypeiPad
DeviceTypeiPhone
DeviceTypeUnknown
)

type Storage struct {
Provider string `yaml:"provider"`
Bucket string `yaml:"bucket"`
}

type App struct {
BundleID string `yaml:"bundleID"`
Branch string `yaml:"branch"`
GitRepo string `yaml:"repo"`
EnvVars map[string]string `yaml:"envVars"`
Storage Storage `yaml:"storage"`
// TODO(@cszatmary): Need to figure out a better way to handle iOS vs deskop
// iOS only
BundleID string `yaml:"bundleID"`
// Assume DeviceTypeAll if empty
RunsOn string `yaml:"runsOn"`

Branch string `yaml:"branch"`
GitRepo string `yaml:"repo"`
EnvVars map[string]string `yaml:"envVars"`
Storage Storage `yaml:"storage"`
// Not part of yaml, set at runtime
Name string `yaml:"-"`
RegistryName string `yaml:"-"`
Expand All @@ -27,6 +42,26 @@ func (a App) FullName() string {
return fmt.Sprintf("%s/%s", a.RegistryName, a.Name)
}

func (a App) DeviceType() DeviceType {
if a.RunsOn == "" {
return DeviceTypeAll
}

// Make it case insensitive because we don't want to worry about if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice 👍

// people wrote ipad vs iPad
runsOn := strings.ToLower(a.RunsOn)
switch runsOn {
case "all":
return DeviceTypeAll
case "ipad":
return DeviceTypeiPad
case "iphone":
return DeviceTypeiPhone
default:
return DeviceTypeUnknown
}
}

type AppCollection struct {
am map[string][]App
}
Expand Down
42 changes: 42 additions & 0 deletions app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,45 @@ func TestAppCollectionGetNonexistent(t *testing.T) {
assert.Zero(a)
assert.Error(err)
}

func TestDeviceType(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gotta get in on this table driven test game

tests := []struct {
name string
app App
expectedDeviceType DeviceType
}{
{
"No device type provided",
App{},
DeviceTypeAll,
},
{
"All devices",
App{RunsOn: "all"},
DeviceTypeAll,
},
{
"Only iPads",
App{RunsOn: "iPad"},
DeviceTypeiPad,
},
{
"Only iPhones",
App{RunsOn: "iPhone"},
DeviceTypeiPhone,
},
{
"Unknown device type",
App{RunsOn: "iPod"},
DeviceTypeUnknown,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
deviceType := tt.app.DeviceType()

assert.Equal(t, tt.expectedDeviceType, deviceType)
})
}
}
14 changes: 13 additions & 1 deletion cmd/app/ios/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/TouchBistro/goutils/command"
"github.com/TouchBistro/goutils/fatal"
"github.com/TouchBistro/tb/app"
"github.com/TouchBistro/tb/simulator"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -42,6 +43,7 @@ Examples:
fatal.ExitErr(err, "Failed to find available iOS simulators")
}

// Figure out default iOS version if it wasn't provided
if logOpts.iosVersion == "" {
logOpts.iosVersion, err = deviceList.GetLatestIOSVersion()
if err != nil {
Expand All @@ -51,6 +53,16 @@ Examples:
log.Infof("No iOS version provided, defaulting to version %s\n", logOpts.iosVersion)
}

// Figure out default iOS device if it wasn't provided
if runOpts.deviceName == "" {
runOpts.deviceName, err = deviceList.GetDefaultDevice("iOS "+runOpts.iosVersion, app.DeviceTypeAll)
if err != nil {
fatal.ExitErr(err, "failed to get default iOS simulator")
}

log.Infof("No iOS simulator provided, defaulting to %s\n", runOpts.deviceName)
}

log.Debugln("☐ Finding device UDID")

deviceUDID, err := deviceList.GetDeviceUDID("iOS "+logOpts.iosVersion, logOpts.deviceName)
Expand All @@ -76,6 +88,6 @@ Examples:
func init() {
iosCmd.AddCommand(logsCmd)
logsCmd.Flags().StringVarP(&logOpts.iosVersion, "ios-version", "i", "", "The iOS version to use")
logsCmd.Flags().StringVarP(&logOpts.deviceName, "device", "d", "iPad Air (3rd generation)", "The name of the device to use")
logsCmd.Flags().StringVarP(&logOpts.deviceName, "device", "d", "", "The name of the device to use")
logsCmd.Flags().StringVarP(&logOpts.numberOfLines, "number", "n", "10", "The number of lines to display")
}
19 changes: 18 additions & 1 deletion cmd/app/ios/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Examples:
a.Branch = runOpts.branch
}

// Figure out default iOS version if it wasn't provided
if runOpts.iosVersion == "" {
runOpts.iosVersion, err = deviceList.GetLatestIOSVersion()
if err != nil {
Expand All @@ -75,6 +76,22 @@ Examples:
log.Infof("No iOS version provided, defaulting to version %s\n", runOpts.iosVersion)
}

// Figure out default iOS device if it wasn't provided
if runOpts.deviceName == "" {
runOpts.deviceName, err = deviceList.GetDefaultDevice("iOS "+runOpts.iosVersion, a.DeviceType())
if err != nil {
fatal.ExitErr(err, "failed to get default iOS simulator")
}

log.Infof("No iOS simulator provided, defaulting to %s\n", runOpts.deviceName)
} else {
// Make sure provided device is valid for the given app
isValid := simulator.IsValidDevice(runOpts.deviceName, a.DeviceType())
if !isValid {
fatal.Exitf("Device %s is not supported by iOS app %s\n", runOpts.deviceName, appName)
}
}

downloadDest := config.IOSBuildPath()
// Check disk utilisation by ios directory
usageBytes, err := file.DirSize(downloadDest)
Expand Down Expand Up @@ -158,7 +175,7 @@ Examples:
func init() {
iosCmd.AddCommand(runCmd)
runCmd.Flags().StringVarP(&runOpts.iosVersion, "ios-version", "i", "", "The iOS version to use")
runCmd.Flags().StringVarP(&runOpts.deviceName, "device", "d", "iPad Air (3rd generation)", "The name of the device to use")
runCmd.Flags().StringVarP(&runOpts.deviceName, "device", "d", "", "The name of the device to use")
runCmd.Flags().StringVarP(&runOpts.branch, "branch", "b", "", "The name of the git branch associated build to pull down and run")
runCmd.Flags().StringVarP(&runOpts.dataPath, "data-path", "D", "", "The path to a data directory to inject into the simulator")
}
6 changes: 6 additions & 0 deletions docs/registries.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ The schema of an iOS app is:
branch: string # The base branch of the repo, ex: master
repo: string # The repo name on GitHub, format: org/repo
envVars: map<string, string> # Env vars to set for the app
runsOn: all | ipad | iphone # What type of device the app can run on
storage:
provider: s3 # The storage provider to use
bucket: string # The name of the bucket the builds are stored in
Expand All @@ -96,6 +97,11 @@ The schema of a desktop app is:
bucket: string # The name of the bucket the builds are stored in
```

#### Specifying Device Types for iOS Apps
Some iOS apps can only run on certain device types, for example only on iPad. You can specify this using `runsOn` field. Valid values are `all`, `ipad`, and `iphone`. This field is case-insensitive so `ipad` and `iPad` are equivalent.

If `runsOn` is not specified it is assumed to be `all`.

## Configuring Services

Services are configured in `services.yml`.
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/TouchBistro/tb

go 1.14

require (
github.com/Microsoft/go-winio v0.4.12 // indirect
github.com/TouchBistro/goutils v0.0.2
Expand All @@ -24,5 +26,3 @@ require (
gopkg.in/yaml.v2 v2.2.2
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
)

go 1.13
36 changes: 36 additions & 0 deletions registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ const (
staticDirName = "static"
)

// TODO(@cszatmary): Figure out a better way to differentiate between app types
type appType int

const (
appTypeiOS appType = iota
)

type Registry struct {
Name string `yaml:"name"`
LocalPath string `yaml:"localPath,omitempty"`
Expand Down Expand Up @@ -107,6 +114,20 @@ func validateService(s service.Service) error {
return nil
}

func validateApp(a app.App, t appType) error {
// No validations needed for desktop currently
if t != appTypeiOS {
return nil
}

// Make sure RunsOn is valid
if a.DeviceType() == app.DeviceTypeUnknown {
return errors.Errorf("'%s.runsOn' value is invalid, must be 'all', 'ipad', or 'iphone'", a.Name)
}

return nil
}

type readServicesOptions struct {
rootPath string
reposPath string
Expand Down Expand Up @@ -260,6 +281,11 @@ func readApps(r Registry) ([]app.App, []app.App, error) {
a.Name = n
a.RegistryName = r.Name

err := validateApp(a, appTypeiOS)
if err != nil {
return nil, nil, errors.Wrapf(err, "app %s failed validation", n)
}

iosApps = append(iosApps, a)
}

Expand Down Expand Up @@ -377,6 +403,16 @@ func Validate(path string) error {
return errors.Wrapf(err, "failed to read %s", appsPath)
}

// Make sure all iOS apps are valid
for n, a := range appConf.IOSApps {
a.Name = n
err := validateApp(a, appTypeiOS)
if err != nil {
log.Infof(color.Red("❌ app %s is invalid"), n)
return errors.Wrapf(err, "app %s failed validation", n)
}
}

log.Infof(color.Green("✅ %s is valid"), appsFileName)
} else {
log.Infof(color.Yellow("No %s file"), appsFileName)
Expand Down
Loading