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

Adds support for "fyne release" #3

Merged
merged 12 commits into from Dec 2, 2020
5 changes: 5 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog - Fyne.io fyne-cross

## Unreleased
- Add support for "fyne release" #3
- Add support for creating packaged .tar.gz bundles on freebsd #6
- Update fyne cli to v1.4.2-0.20201125075943-97ad77d2abe0 (fyne-io#1538 fyne-io#1527)

## [0.9.0]
- Releaseing under project namespace with previous 2.2.1 becoming 0.9.0 in fyne-io namespace

Expand Down
5 changes: 4 additions & 1 deletion README.md
Expand Up @@ -19,7 +19,10 @@ Supported targets are:
- android
- ios

> Note: iOS compilation is supported only on darwin hosts. See [fyne README mobile](https://github.com/fyne-io/fyne/blob/v1.2.4/README-mobile.md#ios) for pre-requisites.
> Note:
> - iOS compilation is supported only on darwin hosts. See [fyne pre-requisites](https://developer.fyne.io/started/#prerequisites) for details.
> - macOS packaging for public distrubution (release mode) is supported only on darwin hosts.
> - windows packaging for public distrubution (release mode) is supported only on windows hosts.

## Requirements

Expand Down
12 changes: 12 additions & 0 deletions docker/CHANGELOG.md
Expand Up @@ -5,6 +5,18 @@ All notable changes to the fyne-cross docker images will be documented in this f
Release cycle won't follow the fyne-cross one, so the images will be tagged using the label
year.month.day along with the latest one.

# Release 20.11.28
- Update fyne cli to v1.4.2-0.20201127180716-f9f91c194737 fyne-io#1609

# Release 20.11.25
- Update fyne cli to v1.4.2-0.20201125075943-97ad77d2abe0 fyne-io#1538

# Release 20.11.23
- Update fyne cli to v1.4.2-0.20201122132119-67b762f56dc0 fyne-io#1527

# Release 20.11.04
- fyne cli updated to v1.4.0

# Archive

These releases occurred in the original namspace, lucor/fyne-cross
Expand Down
2 changes: 1 addition & 1 deletion docker/base/Dockerfile
@@ -1,7 +1,7 @@
# docker cross 1.13.15
ARG DOCKER_CROSS_VERSION=sha256:11a04661d910f74c419623ef7880024694f9151c17578af15e86c45cdf6c8588
# fyne stable branch
ARG FYNE_VERSION=v1.3.3
ARG FYNE_VERSION=f9f91c1947370189c73384fb12ee799b1cf68638

# Build the fyne command utility
FROM dockercore/golang-cross@${DOCKER_CROSS_VERSION} AS fyne
Expand Down
36 changes: 34 additions & 2 deletions internal/command/android.go
Expand Up @@ -42,6 +42,10 @@ func (cmd *Android) Parse(args []string) error {
CommonFlags: commonFlags,
}

flagSet.StringVar(&flags.Keystore, "keystore", "", "The location of .keystore file containing signing information")
flagSet.StringVar(&flags.KeystorePass, "keystore-pass", "", "Password for the .keystore file")
flagSet.StringVar(&flags.KeyPass, "key-pass", "", "Password for the signer's private key, which is needed if the private key is password-protected")

flagSet.Usage = cmd.Usage
flagSet.Parse(args)

Expand Down Expand Up @@ -95,13 +99,33 @@ func (cmd *Android) Run() error {
return err
}

err = fynePackage(ctx)
if ctx.Release {
err = fyneRelease(ctx)
} else {
err = fynePackage(ctx)
}
if err != nil {
return fmt.Errorf("could not package the Fyne app: %v", err)
}

// move the dist package into the "dist" folder
srcFile := volume.JoinPathHost(ctx.WorkDirHost(), ctx.Package, packageName)
// The fyne tool sanitizes the package name to be acceptable as a
// android package name. For details, see:
// https://github.com/fyne-io/fyne/blob/v1.4.0/cmd/fyne/internal/mobile/build_androidapp.go#L297
// To avoid to duplicate the fyne tool sanitize logic here, the location of
// the dist package to move will be detected using a matching pattern
apkFilePattern := volume.JoinPathHost(ctx.WorkDirHost(), ctx.Package, "*.apk")
apks, err := filepath.Glob(apkFilePattern)
if err != nil {
return fmt.Errorf("could not find any apk file matching %q: %v", apkFilePattern, err)
}
if apks == nil {
return fmt.Errorf("could not find any apk file matching %q", apkFilePattern)
}
if len(apks) > 1 {
return fmt.Errorf("multiple apk files matching %q: %v. Please remove and build again", apkFilePattern, apks)
}
srcFile := apks[0]
distFile := volume.JoinPathHost(ctx.DistDirHost(), ctx.ID, packageName)
err = os.MkdirAll(filepath.Dir(distFile), 0755)
if err != nil {
Expand Down Expand Up @@ -142,6 +166,10 @@ Options:
// androidFlags defines the command-line flags for the android command
type androidFlags struct {
*CommonFlags

Keystore string //Keystore represents the location of .keystore file containing signing information
KeystorePass string //Password for the .keystore file
KeyPass string //Password for the signer's private key, which is needed if the private key is password-protected
}

// makeAndroidContext returns the command context for an android target
Expand All @@ -159,6 +187,10 @@ func makeAndroidContext(flags *androidFlags, args []string) (Context, error) {
ctx.OS = androidOS
ctx.ID = androidOS

ctx.Keystore = flags.Keystore
ctx.KeystorePass = flags.KeystorePass
ctx.KeyPass = flags.KeyPass

// set context based on command-line flags
if flags.DockerImage == "" {
ctx.DockerImage = androidImage
Expand Down
116 changes: 116 additions & 0 deletions internal/command/command.go
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"

"github.com/fyne-io/fyne-cross/internal/icon"
"github.com/fyne-io/fyne-cross/internal/log"
Expand Down Expand Up @@ -102,3 +104,117 @@ func prepareIcon(ctx Context) error {
func printUsage(template string, data interface{}) {
log.PrintTemplate(os.Stderr, template, data)
}

// checkFyneBinHost checks if the fyne cli tool is installed on the host
func checkFyneBinHost(ctx Context) (string, error) {
fyne, err := exec.LookPath("fyne")
if err != nil {
return "", fmt.Errorf("missed requirement: fyne. To install: `go get fyne.io/fyne/cmd/fyne` and add $GOPATH/bin to $PATH")
}

if ctx.Debug {
out, err := exec.Command(fyne, "version").Output()
if err != nil {
return fyne, fmt.Errorf("could not get fyne cli %s version: %v", fyne, err)
}
log.Debugf("%s", out)
}

return fyne, nil
}

// fynePackageHost package the application using the fyne cli tool from the host
// Note: at the moment this is used only for the ios builds
func fynePackageHost(ctx Context) error {

fyne, err := checkFyneBinHost(ctx)
if err != nil {
return err
}

args := []string{
"package",
"-os", ctx.OS,
"-name", ctx.Output,
"-icon", volume.JoinPathContainer(ctx.TmpDirHost(), ctx.ID, icon.Default),
"-appID", ctx.AppID,
"-appBuild", ctx.AppBuild,
"-appVersion", ctx.AppVersion,
}

// add tags to command, if any
tags := ctx.Tags
if len(tags) > 0 {
args = append(args, "-tags", fmt.Sprintf("'%s'", strings.Join(tags, ",")))
}

// run the command from the host
fyneCmd := exec.Command(fyne, args...)
fyneCmd.Dir = ctx.WorkDirHost()
fyneCmd.Stdout = os.Stdout
fyneCmd.Stderr = os.Stderr

if ctx.Debug {
log.Debug(fyneCmd)
}

err = fyneCmd.Run()
if err != nil {
return fmt.Errorf("could not package the Fyne app: %v", err)
}
return nil
}

// fyneReleaseHost package and release the application using the fyne cli tool from the host
// Note: at the moment this is used only for the ios and windows builds
func fyneReleaseHost(ctx Context) error {

fyne, err := checkFyneBinHost(ctx)
if err != nil {
return err
}

args := []string{
"release",
"-os", ctx.OS,
"-name", ctx.Output,
"-icon", volume.JoinPathContainer(ctx.TmpDirHost(), ctx.ID, icon.Default),
"-appID", ctx.AppID,
"-appBuild", ctx.AppBuild,
"-appVersion", ctx.AppVersion,
}

// add tags to command, if any
tags := ctx.Tags
if len(tags) > 0 {
args = append(args, "-tags", fmt.Sprintf("'%s'", strings.Join(tags, ",")))
}

switch ctx.OS {
case darwinOS:
args = append(args, "-category", ctx.Category)
case iosOS:
args = append(args, "-certificate", ctx.Certificate)
args = append(args, "-profile", ctx.Profile)
case windowsOS:
args = append(args, "-certificate", ctx.Certificate)
args = append(args, "-developer", ctx.Developer)
args = append(args, "-password", ctx.Password)
}

// run the command from the host
fyneCmd := exec.Command(fyne, args...)
fyneCmd.Dir = ctx.WorkDirHost()
fyneCmd.Stdout = os.Stdout
fyneCmd.Stderr = os.Stderr

if ctx.Debug {
log.Debug(fyneCmd)
}

err = fyneCmd.Run()
if err != nil {
return fmt.Errorf("could not package the Fyne app: %v", err)
}
return nil
}
23 changes: 23 additions & 0 deletions internal/command/context.go
Expand Up @@ -2,9 +2,11 @@ package command

import (
"bytes"
"errors"
"fmt"
"path/filepath"
"runtime"
"strconv"
"strings"

"github.com/fyne-io/fyne-cross/internal/log"
Expand Down Expand Up @@ -41,15 +43,28 @@ type Context struct {
OS string // OS defines the target OS
Tags []string // Tags defines the tags to use

AppBuild string // Build number
AppID string // AppID is the appID to use for distribution
AppVersion string // AppVersion is the version number in the form x, x.y or x.y.z semantic version
CacheEnabled bool // CacheEnabled if true enable go build cache
DockerImage string // DockerImage defines the docker image used to build
Icon string // Icon is the optional icon in png format to use for distribution
Output string // Output is the name output
Package string // Package is the package to build named by the import path as per 'go build'
Release bool // Enable release mode. If true, prepares an application for public distribution
StripDebug bool // StripDebug if true, strips binary output
Debug bool // Debug if true enable debug log
Pull bool // Pull if true attempts to pull a newer version of the docker image

// Release context
Category string //Category represents the category of the app for store listing [macOS]
Certificate string //Certificate represents the name of the certificate to sign the build [iOS, Windows]
Developer string //Developer represents the developer identity for your Microsoft store account [Windows]
Keystore string //Keystore represents the location of .keystore file containing signing information [Android]
KeystorePass string //KeystorePass represents the password for the .keystore file [Android]
KeyPass string //KeyPass represents the assword for the signer's private key, which is needed if the private key is password-protected [Android]
Password string //Password represents the password for the certificate used to sign the build [Windows]
Profile string //Profile represents the name of the provisioning profile for this release build [iOS]
}

// String implements the Stringer interface
Expand All @@ -76,6 +91,7 @@ func makeDefaultContext(flags *CommonFlags, args []string) (Context, error) {
// set context based on command-line flags
ctx := Context{
AppID: flags.AppID,
AppVersion: flags.AppVersion,
CacheEnabled: !flags.NoCache,
DockerImage: flags.DockerImage,
Env: flags.Env,
Expand All @@ -86,8 +102,15 @@ func makeDefaultContext(flags *CommonFlags, args []string) (Context, error) {
Debug: flags.Debug,
Volume: vol,
Pull: flags.Pull,
Release: flags.Release,
}

if flags.AppBuild <= 0 {
return ctx, errors.New("build number should be greater than 0")
}

ctx.AppBuild = strconv.Itoa(flags.AppBuild)

ctx.Package, err = packageFromArgs(args, vol)
if err != nil {
return ctx, err
Expand Down