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
26 changes: 26 additions & 0 deletions .github/workflows/generate_assets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,32 @@ jobs:
-Dorg.gradle.workers.max=1 \
-Dorg.gradle.parallel=false

- name: Create Release Assets
run: |
flox activate -d flox/base -- ./gradlew :app:bundleV7LlamaAssets :app:bundleV8LlamaAssets :app:bundlePluginArtifactsForRelease --no-daemon \
-Dorg.gradle.jvmargs="-Xmx10g -XX:MaxMetaspaceSize=2g -XX:+HeapDumpOnOutOfMemoryError --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED" \
-Dandroid.aapt2.daemonHeapSize=4096M \
-Dorg.gradle.workers.max=1 \
-Dorg.gradle.parallel=false

- name: Upload Release Assets to Server
env:
SCP_HOST: ${{ vars.GREENGEEKS_SSH_HOST }}
run: |
if [ -z "$SCP_HOST" ]; then
echo "Warning: SCP_HOST not set, skipping release assets upload"
exit 0
fi

if [ ! -d "assets/release" ]; then
echo "Warning: assets/release directory does not exist, skipping upload"
exit 0
fi

echo "Uploading release assets to server..."
scp -r assets/release "$SCP_HOST:public_html/dev-assets/"
echo "Release assets uploaded successfully"

- name: V8 Assets Path
id: assets_v8
run: |
Expand Down
67 changes: 0 additions & 67 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,61 +165,6 @@ jobs:
if: env.nix_installed == 'false'
uses: flox/install-flox-action@v2

- name: Set up SSH key
env:
GREENGEEKS_HOST: ${{ vars.GREENGEEKS_SSH_HOST }}
GREENGEEKS_KEY: ${{ secrets.GREENGEEKS_SSH_PRIVATE_KEY }}
GREENGEEKS_USER: ${{ vars.GREENGEEKS_SSH_USER }}
run: |
mkdir -p ~/.ssh
if [ -z "$GREENGEEKS_HOST" ]; then
echo "Error: SSH_HOST variable is not set"
exit 1
fi
# Write the SSH key, ensuring proper formatting
echo "$GREENGEEKS_KEY" > ~/.ssh/id_rsa
# Remove any trailing newlines and ensure proper key format
sed -i '' -e '$ { /^$/ d; }' ~/.ssh/id_rsa 2>/dev/null || sed -i '$ { /^$/ d; }' ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
# Verify key format
if ! grep -q "BEGIN.*PRIVATE KEY" ~/.ssh/id_rsa; then
echo "Error: SSH key does not appear to be in correct format"
exit 1
fi
# Configure SSH to use only the key file and disable other auth methods
cat > ~/.ssh/config <<EOF
Host *
IdentitiesOnly yes
PreferredAuthentications publickey
StrictHostKeyChecking no
UserKnownHostsFile ~/.ssh/known_hosts
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
GSSAPIAuthentication no
GSSAPIKeyExchange no
GSSAPIDelegateCredentials no
Host $GREENGEEKS_HOST
User $GREENGEEKS_USER
IdentityFile ~/.ssh/id_rsa
IdentitiesOnly yes
PreferredAuthentications publickey
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
GSSAPIAuthentication no
GSSAPIKeyExchange no
GSSAPIDelegateCredentials no
NumberOfPasswordPrompts 0
EOF
chmod 600 ~/.ssh/config
# Disable SSH agent completely
unset SSH_AUTH_SOCK
unset SSH_AGENT_PID
# Remove any default SSH keys that might interfere
rm -f ~/.ssh/id_ed25519 ~/.ssh/id_ecdsa ~/.ssh/id_dsa ~/.ssh/id_rsa.pub 2>/dev/null
ssh-keyscan -H "$GREENGEEKS_HOST" >> ~/.ssh/known_hosts 2>/dev/null

- name: Download release assets
env:
SCP_HOST: ${{ vars.GREENGEEKS_SSH_HOST }}
Expand Down Expand Up @@ -740,15 +685,3 @@ jobs:
run: |
rm -f app/google-services.json
echo "google-services.json cleaned up successfully"
- name: Cleanup ssh
if: always()
run: |
# Remove SSH key
rm -f ~/.ssh/id_rsa
# Clean up SSH known_hosts (remove the entry for this host)
SSH_HOST="${{ vars.GREENGEEKS_SSH_HOST }}"
if [ -n "$SSH_HOST" ]; then
ssh-keygen -R "$SSH_HOST" 2>/dev/null || true
fi
# Remove entire .ssh directory if empty
rmdir ~/.ssh 2>/dev/null || true
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,3 @@ testing/**/bin/
# checksum files
.checksum/

# assets files
assets/
64 changes: 62 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,64 @@ tasks.register("assembleAssets") {
val bundleLlamaV7Assets = registerBundleLlamaAssetsTask(flavor = "v7", arch = "armeabi-v7a")
val bundleLlamaV8Assets = registerBundleLlamaAssetsTask(flavor = "v8", arch = "arm64-v8a")

tasks.register("bundlePluginArtifactsForRelease") {
dependsOn("createPluginArtifactsZip")

val sourceZip = rootProject.file("assets/plugin-artifacts.zip")
val targetDir = rootProject.file("assets/release/common/data/common")
val destBr = File(targetDir, "plugin-artifacts.zip.br")
val destZip = File(targetDir, "plugin-artifacts.zip")

inputs.file(sourceZip)
outputs.file(destBr)

doLast {
if (!sourceZip.exists()) {
throw GradleException("plugin-artifacts.zip not found: ${sourceZip.absolutePath}")
}

targetDir.mkdirs()
destBr.delete()
destZip.delete()

val brotliAvailable =
try {
val result =
project.exec {
commandLine("brotli", "--version")
isIgnoreExitValue = true
}
result.exitValue == 0
} catch (_: Exception) {
false
}

if (brotliAvailable) {
project.exec {
commandLine("brotli", "-f", "-o", destBr.absolutePath, sourceZip.absolutePath)
}
project.logger.lifecycle(
"Bundled plugin-artifacts.zip compressed to ${
destBr.relativeTo(
project.rootProject.projectDir
)
}"
)
destZip.delete()
} else {
project.logger.warn(
"brotli CLI not found; bundling plugin-artifacts.zip uncompressed at ${
destZip.relativeTo(
project.rootProject.projectDir
)
}"
)
sourceZip.copyTo(destZip, overwrite = true)
destBr.delete()
}
}
}

tasks.register("recompressApk") {
doLast {
val abi: String = extensions.extraProperties["abi"].toString()
Expand Down Expand Up @@ -719,7 +777,7 @@ afterEvaluate {
}
}

dependsOn(bundleLlamaV8Assets)
dependsOn(bundleLlamaV8Assets, "bundlePluginArtifactsForRelease")
if (!isCiCd) {
dependsOn("assetsDownloadRelease")
}
Expand All @@ -736,7 +794,7 @@ afterEvaluate {
}
}

dependsOn(bundleLlamaV7Assets)
dependsOn(bundleLlamaV7Assets, "bundlePluginArtifactsForRelease")
if (!isCiCd) {
dependsOn("assetsDownloadRelease")
}
Expand Down Expand Up @@ -933,6 +991,8 @@ val releaseAssets = listOf(
"gradle-api-8.14.3.jar.br", "release"),
Asset("assets/release/common/data/common/localMvnRepository.zip.br", "https://appdevforall.org/dev-assets/release/localMvnRepository.zip.br",
"localMvnRepository.zip.br", "release"),
Asset("assets/release/common/data/common/plugin-artifacts.zip.br", "https://appdevforall.org/dev-assets/release/plugin-artifacts.zip.br",
"plugin-artifacts.zip.br", "release"),
Asset("assets/release/common/database/documentation.db.br", "https://appdevforall.org/dev-assets/release/documentation.db.br",
"documentation.db.br", "release"),
Asset("assets/release/v7/data/common/android-sdk.zip.br", "https://appdevforall.org/dev-assets/release/v7/android-sdk.zip.br",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import java.io.File
import java.io.FileNotFoundException
import java.nio.file.Files
import java.nio.file.Path
import java.util.zip.ZipInputStream

data object BundledAssetsInstaller : BaseAssetsInstaller() {
private val logger = LoggerFactory.getLogger(BundledAssetsInstaller::class.java)
Expand Down Expand Up @@ -139,6 +140,41 @@ data object BundledAssetsInstaller : BaseAssetsInstaller() {
input.copyTo(output)
}
}
}
AssetsInstallationHelper.PLUGIN_ARTIFACTS_ZIP -> {
logger.debug("Extracting plugin artifacts from '{}'", entryName)
val pluginDir = Environment.PLUGIN_API_JAR.parentFile
?: throw IllegalStateException("Plugin API parent directory is null")
pluginDir.mkdirs()
val pluginDirPath = pluginDir.toPath().toAbsolutePath().normalize()

val assetPath = ToolsManager.getCommonAsset("$entryName.br")
assets.open(assetPath).use { assetStream ->
BrotliInputStream(assetStream).use { brotliStream ->
ZipInputStream(brotliStream).use { pluginZip ->
var pluginEntry = pluginZip.nextEntry
while (pluginEntry != null) {
if (!pluginEntry.isDirectory) {
val targetPath = pluginDirPath.resolve(pluginEntry.name).normalize()
// Security check: prevent path traversal attacks
if (!targetPath.startsWith(pluginDirPath)) {
throw IllegalStateException(
"Zip entry '${pluginEntry.name}' would escape target directory"
)
}
val targetFile = targetPath.toFile()
targetFile.parentFile?.mkdirs()
logger.debug("Extracting '{}' to {}", pluginEntry.name, targetFile)
targetFile.outputStream().use { output ->
pluginZip.copyTo(output)
}
}
pluginEntry = pluginZip.nextEntry
}
}
}
}
logger.debug("Completed extracting plugin artifacts")
}
else -> throw IllegalStateException("Unknown entry: $entryName")
}
Expand All @@ -151,6 +187,7 @@ data object BundledAssetsInstaller : BaseAssetsInstaller() {
LOCAL_MAVEN_REPO_ARCHIVE_ZIP_NAME -> 97485855L
AssetsInstallationHelper.BOOTSTRAP_ENTRY_NAME -> 124120151L
GRADLE_API_NAME_JAR_ZIP -> 29447748L
AssetsInstallationHelper.PLUGIN_ARTIFACTS_ZIP -> 86442L
else -> 0L
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,21 @@ data object SplitAssetsInstaller : BaseAssetsInstaller() {
val pluginDir = Environment.PLUGIN_API_JAR.parentFile
?: throw IllegalStateException("Plugin API parent directory is null")
pluginDir.mkdirs()
val pluginDirPath = pluginDir.toPath().toAbsolutePath().normalize()

ZipInputStream(zipInput).use { pluginZip ->
var pluginEntry = pluginZip.nextEntry
while (pluginEntry != null) {
if (!pluginEntry.isDirectory) {
val targetFile = pluginDir.resolve(pluginEntry.name)
val targetPath = pluginDirPath.resolve(pluginEntry.name).normalize()
// Security check: prevent path traversal attacks
if (!targetPath.startsWith(pluginDirPath)) {
throw IllegalStateException(
"Zip entry '${pluginEntry.name}' would escape target directory"
)
}
val targetFile = targetPath.toFile()
targetFile.parentFile?.mkdirs()
logger.debug("Extracting '{}' to {}", pluginEntry.name, targetFile)
targetFile.outputStream().use { output ->
pluginZip.copyTo(output)
Expand Down