github.com/pojntfx/hydrapp/hydrapp@v0.0.0-20240516002902-d08759d6ca9f/pkg/builders/apk/builder.go (about)

     1  package apk
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/docker/docker/client"
    14  	"github.com/pojntfx/hydrapp/hydrapp/pkg/builders"
    15  	"github.com/pojntfx/hydrapp/hydrapp/pkg/executors"
    16  	"github.com/pojntfx/hydrapp/hydrapp/pkg/renderers"
    17  	"github.com/pojntfx/hydrapp/hydrapp/pkg/renderers/apk"
    18  	"github.com/pojntfx/hydrapp/hydrapp/pkg/utils"
    19  )
    20  
    21  const (
    22  	Image = "ghcr.io/pojntfx/hydrapp-build-apk"
    23  )
    24  
    25  func NewBuilder(
    26  	ctx context.Context,
    27  	cli *client.Client,
    28  
    29  	image string, // OCI image to use
    30  	pull bool, // Whether to pull the image or not
    31  	src, // Input directory
    32  	dst string, // Output directory
    33  	onID func(id string), // Callback to handle container ID
    34  	stdout io.Writer, // Writer to handle container output
    35  	appID string, // Android app ID to use
    36  	javaKeystore []byte, // Android cert contents
    37  	javaKeystorePassword string, // Password for the Android keystore
    38  	javaCertificatePassword string, // Password for the Android certificate
    39  	pgpKey []byte, // PGP key contents
    40  	pgpKeyPassword string, // Password for the PGP key
    41  	baseURL, // Base URL where the repo is to be hosted
    42  	appName string, // App name
    43  	releases []renderers.Release, // App releases
    44  	overwrite bool, // Overwrite files even if they exist
    45  	branchID, // Branch ID
    46  	branchName string, // Branch name
    47  	branchTimestamp time.Time, // Branch timestamp
    48  	goMain, // Directory with the main package to build
    49  	goFlags, // Flags to pass to the Go command
    50  	goGenerate string, // Command to execute go generate with
    51  ) *Builder {
    52  	return &Builder{
    53  		ctx,
    54  		cli,
    55  
    56  		image,
    57  		pull,
    58  		src,
    59  		dst,
    60  		onID,
    61  		stdout,
    62  		appID,
    63  		base64.StdEncoding.EncodeToString(javaKeystore),
    64  		javaKeystorePassword,
    65  		javaCertificatePassword,
    66  		base64.StdEncoding.EncodeToString(pgpKey),
    67  		pgpKeyPassword,
    68  		baseURL,
    69  		appName,
    70  		releases,
    71  		overwrite,
    72  		branchID,
    73  		branchName,
    74  		branchTimestamp,
    75  		goMain,
    76  		goFlags,
    77  		goGenerate,
    78  	}
    79  }
    80  
    81  type Builder struct {
    82  	ctx context.Context
    83  	cli *client.Client
    84  
    85  	image string
    86  	pull  bool
    87  	src,
    88  	dst string
    89  	onID   func(id string)
    90  	stdout io.Writer
    91  	appID,
    92  	javaKeystore,
    93  	javaKeystorePassword,
    94  	javaCertificatePassword,
    95  	pgpKey,
    96  	pgpKeyPassword,
    97  	baseURL,
    98  	appName string
    99  	releases  []renderers.Release
   100  	overwrite bool
   101  	branchID,
   102  	branchName string
   103  	branchTimestamp time.Time
   104  	goMain,
   105  	goFlags,
   106  	goGenerate string
   107  }
   108  
   109  func (b *Builder) Render(workdir string, ejecting bool) error {
   110  	appID := builders.GetAppIDForBranch(b.appID, b.branchID)
   111  	appName := builders.GetAppNameForBranch(b.appName, b.branchName)
   112  
   113  	if strings.TrimSpace(b.branchID) != "" {
   114  		jniBindingsPath := filepath.Join(workdir, b.goMain, "android.go")
   115  
   116  		stableJNIBindingsContent, err := os.ReadFile(jniBindingsPath)
   117  		if err != nil {
   118  			return err
   119  		}
   120  
   121  		stableJavaID := strings.Replace(b.appID, ".", "_", -1)
   122  		mainJavaID := strings.Replace(appID, ".", "_", -1)
   123  
   124  		if !ejecting || b.overwrite {
   125  			if !strings.Contains(string(stableJNIBindingsContent), mainJavaID) {
   126  				if err := ioutil.WriteFile(jniBindingsPath, []byte(strings.Replace(string(stableJNIBindingsContent), stableJavaID, mainJavaID, -1)), 0664); err != nil {
   127  					return err
   128  				}
   129  			}
   130  		}
   131  	}
   132  
   133  	return utils.WriteRenders(
   134  		filepath.Join(workdir, b.goMain),
   135  		[]renderers.Renderer{
   136  			apk.NewManifestRenderer(
   137  				appID,
   138  				appName,
   139  				b.releases,
   140  				b.branchTimestamp,
   141  			),
   142  			apk.NewActivityRenderer(
   143  				appID,
   144  			),
   145  			apk.NewHeaderRenderer(),
   146  			apk.NewImplementationRenderer(),
   147  		},
   148  		b.overwrite,
   149  		ejecting,
   150  	)
   151  }
   152  
   153  func (b *Builder) Build() error {
   154  	dst := builders.GetFilepathForBranch(b.dst, b.branchID)
   155  	appID := builders.GetAppIDForBranch(b.appID, b.branchID)
   156  	baseURL := builders.GetPathForBranch(b.baseURL, b.branchID, "") + "/repo" // F-Droid requires the path to end with `/repo`: `CRITICAL: repo_url needs to end with /repo`
   157  
   158  	return executors.DockerRunImage(
   159  		b.ctx,
   160  		b.cli,
   161  
   162  		b.image,
   163  		b.pull,
   164  		false,
   165  		b.src,
   166  		dst,
   167  		b.onID,
   168  		b.stdout,
   169  		map[string]string{
   170  			"APP_ID":                    appID,
   171  			"JAVA_KEYSTORE":             b.javaKeystore,
   172  			"JAVA_KEYSTORE_PASSWORD":    b.javaKeystorePassword,
   173  			"JAVA_CERTIFICATE_PASSWORD": b.javaCertificatePassword,
   174  			"PGP_KEY":                   b.pgpKey,
   175  			"PGP_KEY_PASSWORD":          b.pgpKeyPassword,
   176  			"BASE_URL":                  baseURL,
   177  			"GOMAIN":                    b.goMain,
   178  			"GOFLAGS":                   b.goFlags,
   179  			"GOGENERATE":                b.goGenerate,
   180  		},
   181  		b.Render,
   182  		[]string{},
   183  	)
   184  }