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
13 changes: 13 additions & 0 deletions assets/markdown/confirm-change-android-build-method.md.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Confirm change to export_presets.cfg

**In order to publish your game on Google Play, an edit must be made to your `export_presets.cfg` file to enable the Gradle build method.**

This change is necessary because Google Play requires Android App Bundles (AAB files) for new apps, and the Gradle build method is needed to create AAB files.

You can read more about this in the **ShipThis Documentation** [<%= docsURL %>](<%= docsURL %>)

You are using Godot version **<%= godotVersion %>**, ShipThis will update the **<%= optionKey %>** option in your `export_presets.cfg` file to enable the Gradle build method.

## Do you want to proceed with this change?

Please press **Y** to confirm and proceed with the change or **N** to cancel the operation and exit the Android wizard.
26 changes: 26 additions & 0 deletions docs/util/android-build-method.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Command: `util android-build-method`

## Description

Gets and sets the Android build method in export_presets.cfg

## Help Output

```help
USAGE
$ shipthis util android-build-method [-l] [-g]

FLAGS
-g, --gradle use gradle build method
-l, --legacy use legacy build method

DESCRIPTION
Gets and sets the Android build method in export_presets.cfg

EXAMPLES
$ shipthis util android-build-method

$ shipthis util android-build-method --legacy

$ shipthis util android-build-method --gradle
```
29 changes: 10 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@
"axios": "^1.7.7",
"chalk": "^5.4.1",
"crypto-js": "^4.2.0",
"deepmerge": "^4.3.1",
"fast-glob": "^3.3.2",
"fs-extra": "^11.2.0",
"fullscreen-ink": "^0.1.0",
"ini": "^5.0.0",
"godot-export-presets": "^0.1.6",
"ink": "^5.0.1",
"ink-spinner": "^5.0.0",
"isomorphic-git": "^1.27.1",
Expand Down Expand Up @@ -54,7 +53,6 @@
"@types/crypto-js": "^4.2.2",
"@types/ejs": "^3.1.5",
"@types/fs-extra": "^11.0.4",
"@types/ini": "^4.1.1",
"@types/jsonwebtoken": "^9.0.6",
"@types/luxon": "^3.4.2",
"@types/mocha": "^10",
Expand Down Expand Up @@ -108,6 +106,7 @@
"exports": [
"./dist/utils/help.js",
"./dist/commands/util/glass.js",
"./dist/commands/util/android-build-method.js",
"./dist/commands/apple/apiKey/export.js",
"./dist/commands/apple/apiKey/create.js",
"./dist/commands/apple/apiKey/status.js",
Expand Down
3 changes: 2 additions & 1 deletion src/baseCommands/baseGameAndroidCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export abstract class BaseGameAndroidCommand<T extends typeof Command> extends B
protected async getAndroidPackageName(gameId: string): Promise<string> {
const game = await this.getGame()
const generated = generatePackageName(game.name)
const suggested = game.details?.iosBundleId || getGodotAndroidPackageName() || generated || 'com.example.game'
const godotPackageName = await getGodotAndroidPackageName()
const suggested = game.details?.iosBundleId || godotPackageName || generated || 'com.example.game'
const question = `Please enter the Android Package Name, or press enter to use ${suggested}: `
const entered = await getInput(question)
return entered || suggested
Expand Down
3 changes: 2 additions & 1 deletion src/commands/game/ios/app/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ export default class GameIosAppCreate extends BaseGameCommand<typeof GameIosAppC
const getBundleIdentifier = async (): Promise<string> => {
if (bundleId) return bundleId
const generatedBundleId = generatePackageName(game.name)
const godotBundleId = await getGodotAppleBundleIdentifier()
const suggestedBundleId =
game.details?.iosBundleId || getGodotAppleBundleIdentifier() || generatedBundleId || 'com.example.game'
game.details?.iosBundleId || godotBundleId || generatedBundleId || 'com.example.game'
const question = `Please enter the BundleId in the Apple Developer Portal, or press enter to use ${suggestedBundleId}: `
const enteredBundleId = await getInput(question)
return enteredBundleId || suggestedBundleId
Expand Down
40 changes: 40 additions & 0 deletions src/commands/util/android-build-method.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {Flags} from '@oclif/core'

import {BaseCommand} from '@cli/baseCommands/index.js'
import {isGradleBuildEnabled, setGradleBuildEnabled} from '@cli/utils/godot.js'

export default class UtilAndroidBuildMethod extends BaseCommand<typeof UtilAndroidBuildMethod> {
static override args = {}
static override description = 'Gets and sets the Android build method in export_presets.cfg'
static override examples = [
'<%= config.bin %> <%= command.id %>',
'<%= config.bin %> <%= command.id %> --legacy',
'<%= config.bin %> <%= command.id %> --gradle',
]

static override flags = {
legacy: Flags.boolean({char: 'l', description: 'use legacy build method'}),
gradle: Flags.boolean({char: 'g', description: 'use gradle build method'}),
}

public async run(): Promise<void> {
const {flags} = await this.parse(UtilAndroidBuildMethod)

if (flags.legacy && flags.gradle) {
this.error('Cannot use both --legacy and --gradle flags together')
}

if (!flags.legacy && !flags.gradle) {
// Show current build method
const buildMethod = (await isGradleBuildEnabled()) ? 'gradle' : 'legacy'
this.log(`Current Android build method: ${buildMethod}`)
return
}

const isGradle = flags.gradle ? true : false
const buildMethod = flags.legacy ? 'legacy' : 'gradle'
this.log(`Setting Android build method to: ${buildMethod}`)
// Set the build method in the export presets file
await setGradleBuildEnabled(isGradle)
}
}
35 changes: 29 additions & 6 deletions src/components/JobFollow.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Job, JobLogEntry, LogLevel} from '@cli/types/api.js'
import {useJobWatching} from '@cli/utils/index.js'
import { useStderr, useStdout } from 'ink'

interface JobFollowProps {
jobId: string
Expand All @@ -9,18 +10,40 @@ interface JobFollowProps {
}

// Outputs job logs to stdout/stderr as they come in
export const JobFollow = ({jobId, onComplete, onFailure, projectId}: JobFollowProps) => {
export const JobFollow = ({
jobId,
onComplete,
onFailure,
projectId,
}: JobFollowProps) => {
const {stdout, write: writeOut} = useStdout();
const {stderr, write: writeErr} = useStderr();

// Only emit ANSI when we're actually writing to a TTY.
const useAnsi = Boolean((stderr as any).isTTY ?? (stdout as any).isTTY);

const yellow = useAnsi ? '\x1b[33m' : '';
const red = useAnsi ? '\x1b[31m' : '';
const reset = useAnsi ? '\x1b[0m' : '';

useJobWatching({
isWatching: true,
jobId,
onComplete,
onFailure,
onNewLogEntry(logEntry: JobLogEntry) {
if (logEntry.level === LogLevel.ERROR) console.error(logEntry.message)
else console.log(logEntry.message)
const msg = logEntry.message + '\n';

if (logEntry.level === LogLevel.ERROR) {
writeErr(`${red}${msg}${reset}`);
} else if (logEntry.level === LogLevel.WARN) {
writeErr(`${yellow}${msg}${reset}`);
} else {
writeOut(msg);
}
},
projectId,
})
});

return null
}
return null;
};
27 changes: 6 additions & 21 deletions src/components/JobLogTail.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,24 @@
import {Box, Text} from 'ink'
import {Box} from 'ink'
import Spinner from 'ink-spinner'

import {JobLogEntry} from '@cli/types'
import {JobLogTailProps, useJobLogTail} from '@cli/utils/hooks/index.js'
import {getMessageColor, getShortTime, getStageColor} from '@cli/utils/index.js'

import {Title} from './common/Title.js'
import {TruncatedText} from './common/TruncatedText.js'

import {JobLogLine} from './common/JobLogLine.js'

export const JobLogTail = (props: JobLogTailProps) => {
const {data, isLoading} = useJobLogTail(props)

// TODO: the <TruncatedText> is causing issues
return (
<Box flexDirection="column">
<Title>Job Logs</Title>
{isLoading && <Spinner type="dots" />}
<Box flexDirection="column">
{data.map((log: JobLogEntry) => {
const stageColor = getStageColor(log.stage)
const messageColor = getMessageColor(log.level)
return (
<Box flexDirection="row" height={1} key={log.id} overflow="hidden">
<Box>
<Text>{getShortTime(log.sentAt)}</Text>
</Box>
<Box justifyContent="flex-start" marginLeft={1} width={9}>
<Text color={stageColor}>{log.stage}</Text>
</Box>
<Box height={1} marginLeft={1} marginRight={2} overflow="hidden">
<TruncatedText color={messageColor}>{log.message}</TruncatedText>
</Box>
</Box>
)
})}
{data.map((log: JobLogEntry) => (
<JobLogLine key={log.id} log={log} />
))}
</Box>
</Box>
)
Expand Down
6 changes: 4 additions & 2 deletions src/components/android/AndroidWizard/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {getGoogleStatus, getProject, getProjectCredentials} from '@cli/api/index.js'
import {BaseCommand} from '@cli/baseCommands/baseCommand.js'
import {CredentialsType, Platform} from '@cli/types/index.js'
import {BuildType, CredentialsType, Platform} from '@cli/types/index.js'
import {KeyTestError, KeyTestStatus, fetchKeyTestResult, queryBuilds} from '@cli/utils/index.js'

export enum StepStatus {
Expand Down Expand Up @@ -91,7 +91,9 @@ export const getStatusFlags = async (cmd: BaseCommand<any>): Promise<StatusFlags
)

const buildsResponse = projectId && hasShipThisProject ? await queryBuilds({pageNumber: 0, projectId: `${projectId}`}) : null
const hasInitialBuild = Boolean(buildsResponse?.data?.some((build) => build.platform === Platform.ANDROID))
const hasInitialBuild = Boolean(
buildsResponse?.data?.some((build) => build.platform === Platform.ANDROID && build.buildType === BuildType.AAB),
)

const testResult = projectId ? await fetchKeyTestResult({projectId}) : null

Expand Down
38 changes: 38 additions & 0 deletions src/components/android/CreateInitialBuild/EnableGradle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {Box} from 'ink'
import {getMajorVersion} from 'godot-export-presets'

import {Markdown} from '@cli/components/index.js'
import {getGodotVersion, getGradleBuildOptionKey} from '@cli/utils/godot.js'

import {WEB_URL} from '@cli/constants/index.js'
import {useSafeInput} from '@cli/utils/index.js'

interface Props extends React.ComponentPropsWithoutRef<typeof Box> {
onConfirm?: () => void
onCancel?: () => void
}

export const EnableGradle = ({onConfirm, onCancel, ...boxProps}: Props) => {
const godotVersion = getGodotVersion()
const majorVersion = getMajorVersion(godotVersion)

useSafeInput(async (input) => {
if (input == 'y') return onConfirm && onConfirm()
if (input == 'n') return onCancel && onCancel()
})

return (
<Box flexDirection="column" gap={1} {...boxProps}>
<Box flexDirection="row" gap={1}>
<Markdown
filename="confirm-change-android-build-method.md.ejs"
templateVars={{
docsURL: `${WEB_URL}docs/guides/android-build-methods`,
godotVersion,
optionKey: getGradleBuildOptionKey(majorVersion),
}}
/>
</Box>
</Box>
)
}
Loading