Skip to content

Commit 8ec00da

Browse files
committed
feat(release): update gemini model to 2.0-flash, enable abi splits, and enforce professional artifact naming schema
1 parent 1ad662b commit 8ec00da

4 files changed

Lines changed: 89 additions & 12 deletions

File tree

.github/release-prompt-template.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,8 @@ Instructions:
1313
3. Use bullet points for changes.
1414
4. Do not include commit hashes or author names.
1515
5. Output using clean Markdown syntax without markdown code block wrappers around the entire response.
16-
6. Always append the following Markdown table at the end of the changelog to provide build artifact details, preserving the exact placeholders:
16+
6. Always append the following section at the end of the changelog to provide build artifact details, preserving the exact placeholder `{{ARTIFACT_TABLE}}`:
1717

1818
### Build Artifacts
1919

20-
| File Name | Format | Download Link |
21-
| --- | --- | --- |
22-
| app-release.apk | APK (Universal) | [Download](https://github.com/{{GITHUB_REPOSITORY}}/releases/download/{{RELEASE_VERSION}}/app-release.apk) |
23-
| app-release.aab | AAB (Google Play Bundle) | [Download](https://github.com/{{GITHUB_REPOSITORY}}/releases/download/{{RELEASE_VERSION}}/app-release.aab) |
20+
{{ARTIFACT_TABLE}}

.github/scripts/generate_release_notes.js

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ async function run() {
4545
console.log('------------------------------------');
4646

4747
// 3. Request Gemini API
48-
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`;
48+
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;
4949
const requestBody = {
5050
contents: [
5151
{
@@ -58,6 +58,10 @@ async function run() {
5858
]
5959
};
6060

61+
const githubRepository = process.env.GITHUB_REPOSITORY || 'owner/repo';
62+
const releaseVersion = process.env.GITHUB_REF_NAME || 'v1.0.0';
63+
const artifactTable = generateArtifactTable(githubRepository, releaseVersion);
64+
6165
try {
6266
const response = await fetch(url, {
6367
method: 'POST',
@@ -80,24 +84,69 @@ async function run() {
8084
}
8185

8286
let finalNotes = generatedText;
83-
const githubRepository = process.env.GITHUB_REPOSITORY || 'owner/repo';
84-
const releaseVersion = process.env.GITHUB_REF_NAME || 'v1.0.0';
8587

8688
finalNotes = finalNotes
8789
.replace(/\{\{GITHUB_REPOSITORY\}\}/g, githubRepository)
88-
.replace(/\{\{RELEASE_VERSION\}\}/g, releaseVersion);
90+
.replace(/\{\{RELEASE_VERSION\}\}/g, releaseVersion)
91+
.replace(/\{\{ARTIFACT_TABLE\}\}/g, artifactTable);
8992

9093
// 4. Output the release notes to a file for use in next steps
9194
const outputPath = path.join(process.cwd(), 'release_notes.md');
9295
fs.writeFileSync(outputPath, finalNotes, 'utf8');
9396
console.log(`Release notes successfully generated and written to ${outputPath}`);
9497
} catch (error) {
95-
console.error('Failed to generate release notes:', error.message);
98+
console.error('Failed to generate release notes via Gemini API:', error.message);
9699
// Fallback release notes file to prevent workflow failure
97100
const outputPath = path.join(process.cwd(), 'release_notes.md');
98-
fs.writeFileSync(outputPath, `### Commits in this Release\n\n\`\`\`\n${commitLog}\n\`\`\``, 'utf8');
101+
const fallbackNotes = `### Release Summary (${releaseVersion})\n\n### Commits in this Release\n\n\`\`\`\n${commitLog}\n\`\`\`\n\n### Build Artifacts\n\n${artifactTable}`;
102+
fs.writeFileSync(outputPath, fallbackNotes, 'utf8');
99103
console.log(`Fallback release notes written to ${outputPath}`);
100104
}
101105
}
102106

107+
function generateArtifactTable(githubRepository, releaseVersion) {
108+
const apkDir = path.join(process.cwd(), 'app', 'build', 'outputs', 'apk', 'release');
109+
const aabDir = path.join(process.cwd(), 'app', 'build', 'outputs', 'bundle', 'release');
110+
111+
const files = [];
112+
113+
if (fs.existsSync(apkDir)) {
114+
const apkFiles = fs.readdirSync(apkDir).filter(f => f.endsWith('.apk'));
115+
for (const f of apkFiles) {
116+
files.push({ name: f, type: 'APK' });
117+
}
118+
}
119+
120+
if (fs.existsSync(aabDir)) {
121+
const aabFiles = fs.readdirSync(aabDir).filter(f => f.endsWith('.aab'));
122+
for (const f of aabFiles) {
123+
files.push({ name: f, type: 'AAB' });
124+
}
125+
}
126+
127+
if (files.length === 0) {
128+
return 'No artifacts found.';
129+
}
130+
131+
files.sort((a, b) => a.name.localeCompare(b.name));
132+
133+
let table = '| File Name | Architecture / Description | Download Link |\n| --- | --- | --- |\n';
134+
for (const file of files) {
135+
let arch = 'Universal';
136+
if (file.name.includes('arm64-v8a')) arch = 'ARM64 (v8a)';
137+
else if (file.name.includes('armeabi-v7a')) arch = 'ARMv7 (32-bit)';
138+
else if (file.name.includes('x86_64')) arch = 'Intel x86_64';
139+
else if (file.name.includes('x86')) arch = 'Intel x86';
140+
141+
if (file.type === 'AAB') {
142+
arch += ' (App Bundle)';
143+
}
144+
145+
const downloadUrl = `https://github.com/${githubRepository}/releases/download/${releaseVersion}/${file.name}`;
146+
table += `| \`${file.name}\` | ${arch} | [Download](${downloadUrl}) |\n`;
147+
}
148+
149+
return table;
150+
}
151+
103152
run();

.github/workflows/android-release.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,19 @@ jobs:
4343
fi
4444
4545
- name: Build Android Release Artifacts (APK & AAB)
46-
run: gradle assembleRelease bundleRelease
46+
run: |
47+
gradle assembleRelease bundleRelease
48+
TAG_NAME="${{ github.ref_name }}"
49+
AAB_DIR="app/build/outputs/bundle/release"
50+
if [ -d "$AAB_DIR" ]; then
51+
for f in $AAB_DIR/*.aab; do
52+
if [ -f "$f" ]; then
53+
mv "$f" "$AAB_DIR/camera-background-twinpath-labs-${TAG_NAME}-universal-nodpi.aab"
54+
fi
55+
done
56+
fi
4757
env:
58+
RELEASE_TAG: ${{ github.ref_name }}
4859
KEYSTORE_PATH: ${{ steps.decode_keystore.outputs.keystore_decoded == 'true' && './my-upload-key.jks' || '' }}
4960
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
5061
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}

app/build.gradle.kts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,26 @@ android {
7979
includeInApk = false
8080
includeInBundle = true
8181
}
82+
83+
splits {
84+
abi {
85+
isEnable = true
86+
reset()
87+
include("arm64-v8a", "armeabi-v7a", "x86_64", "x86")
88+
isUniversalApk = true
89+
}
90+
}
91+
92+
applicationVariants.all {
93+
val variant = this
94+
val tag = System.getenv("RELEASE_TAG") ?: "v${variant.versionName}"
95+
variant.outputs.all {
96+
val output = this as com.android.build.gradle.internal.api.ApkVariantOutputImpl
97+
val abiFilter = output.getFilter(com.android.build.OutputFile.ABI)
98+
val arch = if (abiFilter.isNullOrEmpty()) "universal" else abiFilter
99+
output.outputFileName = "camera-background-twinpath-labs-$tag-$arch-nodpi.apk"
100+
}
101+
}
82102
}
83103

84104
// Configure the Secrets Gradle Plugin to use .env and .env.example files

0 commit comments

Comments
 (0)