Skip to content

fix: remove redundant 'needs' declaration in build-and-release job #14

fix: remove redundant 'needs' declaration in build-and-release job

fix: remove redundant 'needs' declaration in build-and-release job #14

Workflow file for this run

name: 🚀 Build and Release Android

Check failure on line 1 in .github/workflows/publish.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/publish.yml

Invalid workflow file

(Line: 159, Col: 7): There's not enough info to determine what you meant. Add one of these properties: run, shell, uses, with, working-directory, (Line: 189, Col: 7): There's not enough info to determine what you meant. Add one of these properties: run, shell, uses, with, working-directory, (Line: 283, Col: 7): There's not enough info to determine what you meant. Add one of these properties: cancel, run, shell, uses, wait, wait-all, with, working-directory, (Line: 341, Col: 9): 'name' is already defined, (Line: 342, Col: 9): 'path' is already defined, (Line: 351, Col: 9): 'path' is already defined
on:
push:
branches: [ main ]
permissions:
contents: write
issues: write
pull-requests: write
jobs:
# ─────────────────────────────────────────────
# Job 1 — Resolve the next semantic version
# Runs semantic-release in dry-run mode so we
# know the version before the build starts.
# ─────────────────────────────────────────────
resolve-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.semver.outputs.version }}
version_code: ${{ steps.semver.outputs.version_code }}
will_release: ${{ steps.semver.outputs.will_release }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "lts/*"
- name: Install semantic-release
run: |
npm install --save-dev semantic-release \
@semantic-release/changelog \
@semantic-release/git \
@semantic-release/github \
conventional-changelog-conventionalcommits
- name: Write .releaserc.json (dry-run config)
run: |
cat > .releaserc.json << 'EOF'
{
"branches": ["main"],
"plugins": [
["@semantic-release/commit-analyzer", { "preset": "conventionalcommits" }],
["@semantic-release/release-notes-generator", { "preset": "conventionalcommits" }]
]
}
EOF
- name: Determine next version (dry-run)
id: semver
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# semantic-release exits 1 when no release is triggered — that's not an
# error for us, so we capture the exit code manually instead of letting
# bash -e kill the step.
set +e
OUTPUT=$(npx semantic-release --dry-run --no-ci 2>&1)
SR_EXIT=$?
set -e
echo "$OUTPUT"
echo "--- semantic-release exit code: $SR_EXIT ---"
VERSION=$(echo "$OUTPUT" | grep -oP 'The next release version is \K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
# Exit code 2+ means a real error (bad config, auth failure, etc.)
if [ $SR_EXIT -ge 2 ]; then
echo "❌ semantic-release failed with exit code $SR_EXIT"
exit $SR_EXIT
fi
if [ -z "$VERSION" ]; then
echo "⚠️ No release triggered — using last tag or 1.0.0"
VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "1.0.0")
echo "will_release=false" >> $GITHUB_OUTPUT
else
echo "will_release=true" >> $GITHUB_OUTPUT
fi
# versionCode: major*10000 + minor*100 + patch (e.g. 1.2.3 → 10203)
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)
PATCH=$(echo "$VERSION" | cut -d. -f3)
VERSION_CODE=$(( MAJOR * 10000 + MINOR * 100 + PATCH ))
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "version_code=$VERSION_CODE" >> $GITHUB_OUTPUT
echo "✅ Version: $VERSION (code: $VERSION_CODE)"
# ─────────────────────────────────────────────
# Job 2 — Build & release
# Uses the version resolved above.
# ─────────────────────────────────────────────
build-and-release:
runs-on: ubuntu-latest
needs: resolve-version
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: gradle
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Cache Gradle dependencies
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Cache Android build
uses: actions/cache@v3
with:
path: |
~/.android/build-cache
app/build
key: ${{ runner.os }}-android-${{ hashFiles('**/build.gradle*', '**/gradle.properties', '**/settings.gradle*') }}
restore-keys: |
${{ runner.os }}-android-
- name: Cache Kotlin
uses: actions/cache@v3
with:
path: |
~/.kotlin/cache
~/.konan
key: ${{ runner.os }}-kotlin-${{ hashFiles('**/*.kt', '**/*.kts') }}
restore-keys: |
${{ runner.os }}-kotlin-
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Decode Keystore
run: |
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > $RUNNER_TEMP/release.keystore
- name: Build Release APK + AAB (signed, versioned)
env:
VERSION: ${{ needs.resolve-version.outputs.version }}
VERSION_CODE: ${{ needs.resolve-version.outputs.version_code }}
KEYSTORE_PATH: ${{ runner.temp }}/release.keystore
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
- name: Build Release APK + AAB (signed, versioned)
env:
VERSION: ${{ needs.resolve-version.outputs.version }}
VERSION_CODE: ${{ needs.resolve-version.outputs.version_code }}
KEYSTORE_PATH: ${{ runner.temp }}/release.keystore
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
run: |
echo "🏗️ Building v$VERSION (code $VERSION_CODE)"
echo "🏗️ Building v$VERSION (code $VERSION_CODE)"
./gradlew assembleRelease bundleRelease \
-PversionName="$VERSION" \
-PversionCode="$VERSION_CODE" \
-PversionName="$VERSION" \
-PversionCode="$VERSION_CODE" \
-Pandroid.injected.signing.store.file=$RUNNER_TEMP/release.keystore \
-Pandroid.injected.signing.store.password=${{ secrets.KEYSTORE_PASSWORD }} \
-Pandroid.injected.signing.key.alias=${{ secrets.KEY_ALIAS }} \
-Pandroid.injected.signing.key.password=${{ secrets.KEYSTORE_PASSWORD }} \
--build-cache
- name: Rename and verify outputs
id: outputs
env:
VERSION: ${{ needs.resolve-version.outputs.version }}
- name: Rename and verify outputs
id: outputs
env:
VERSION: ${{ needs.resolve-version.outputs.version }}
run: |
APK_SRC="app/build/outputs/apk/release/app-release.apk"
AAB_SRC="app/build/outputs/bundle/release/app-release.aab"
[ ! -f "$APK_SRC" ] && APK_SRC=$(find app/build/outputs -name "*release*.apk" -type f | head -1)
[ ! -f "$AAB_SRC" ] && AAB_SRC=$(find app/build/outputs -name "*release*.aab" -type f | head -1)
if [ -z "$APK_SRC" ] || [ ! -f "$APK_SRC" ]; then echo "❌ APK release non trouvé"; exit 1; fi
if [ -z "$AAB_SRC" ] || [ ! -f "$AAB_SRC" ]; then echo "❌ AAB release non trouvé"; exit 1; fi
APK_OUT="app/build/outputs/apk/release/app-release-v${VERSION}.apk"
AAB_OUT="app/build/outputs/bundle/release/app-release-v${VERSION}.aab"
mv "$APK_SRC" "$APK_OUT"
mv "$AAB_SRC" "$AAB_OUT"
echo "APK_PATH=$APK_OUT" >> $GITHUB_OUTPUT
echo "AAB_PATH=$AAB_OUT" >> $GITHUB_OUTPUT
echo "✅ APK: $APK_OUT"
echo "✅ AAB: $AAB_OUT"
APK_SRC="app/build/outputs/apk/release/app-release.apk"
AAB_SRC="app/build/outputs/bundle/release/app-release.aab"
[ ! -f "$APK_SRC" ] && APK_SRC=$(find app/build/outputs -name "*release*.apk" -type f | head -1)
[ ! -f "$AAB_SRC" ] && AAB_SRC=$(find app/build/outputs -name "*release*.aab" -type f | head -1)
if [ -z "$APK_SRC" ] || [ ! -f "$APK_SRC" ]; then echo "❌ APK release non trouvé"; exit 1; fi
if [ -z "$AAB_SRC" ] || [ ! -f "$AAB_SRC" ]; then echo "❌ AAB release non trouvé"; exit 1; fi
APK_OUT="app/build/outputs/apk/release/app-release-v${VERSION}.apk"
AAB_OUT="app/build/outputs/bundle/release/app-release-v${VERSION}.aab"
mv "$APK_SRC" "$APK_OUT"
mv "$AAB_SRC" "$AAB_OUT"
echo "APK_PATH=$APK_OUT" >> $GITHUB_OUTPUT
echo "AAB_PATH=$AAB_OUT" >> $GITHUB_OUTPUT
echo "✅ APK: $APK_OUT"
echo "✅ AAB: $AAB_OUT"
- name: Setup Node.js for semantic-release
uses: actions/setup-node@v4
with:
node-version: "lts/*"
- name: Install semantic-release and plugins
run: |
npm install --save-dev semantic-release \
@semantic-release/changelog \
@semantic-release/git \
@semantic-release/github \
conventional-changelog-conventionalcommits
- name: Write .releaserc.json (real release with assets)
env:
APK_PATH: ${{ steps.outputs.outputs.APK_PATH }}
AAB_PATH: ${{ steps.outputs.outputs.AAB_PATH }}
VERSION: ${{ needs.resolve-version.outputs.version }}
run: |
cat > .releaserc.json << EOF
{
"branches": ["main"],
"plugins": [
["@semantic-release/commit-analyzer", { "preset": "conventionalcommits" }],
["@semantic-release/release-notes-generator", { "preset": "conventionalcommits" }],
"@semantic-release/changelog",
["@semantic-release/github", {
"assets": [
{
"path": "${APK_PATH}",
"name": "app-release-v${VERSION}.apk",
"label": "Android APK v${VERSION} (universal)"
},
{
"path": "${AAB_PATH}",
"name": "app-release-v${VERSION}.aab",
"label": "Android App Bundle v${VERSION} (Play Store)"
}
]
}],
["@semantic-release/git", {
"assets": ["CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}]
]
}
EOF
- name: Run semantic-release
- name: Write .releaserc.json (real release with assets)
env:
APK_PATH: ${{ steps.outputs.outputs.APK_PATH }}
AAB_PATH: ${{ steps.outputs.outputs.AAB_PATH }}
VERSION: ${{ needs.resolve-version.outputs.version }}
run: |
cat > .releaserc.json << EOF
{
"branches": ["main"],
"plugins": [
["@semantic-release/commit-analyzer", { "preset": "conventionalcommits" }],
["@semantic-release/release-notes-generator", { "preset": "conventionalcommits" }],
"@semantic-release/changelog",
["@semantic-release/github", {
"assets": [
{
"path": "${APK_PATH}",
"name": "app-release-v${VERSION}.apk",
"label": "Android APK v${VERSION} (universal)"
},
{
"path": "${AAB_PATH}",
"name": "app-release-v${VERSION}.aab",
"label": "Android App Bundle v${VERSION} (Play Store)"
}
]
}],
["@semantic-release/git", {
"assets": ["CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}]
]
}
EOF
- name: Run semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release
# - name: Publish AAB to Play Store
# uses: r0adkll/upload-google-play@v1
# with:
# serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
# packageName: com.bob1.app
# releaseFiles: ${{ steps.outputs.outputs.AAB_PATH }}
# packageName: com.bob1.app
# releaseFiles: ${{ steps.outputs.outputs.AAB_PATH }}
# track: internal
# status: completed
- name: Upload APK artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: bob1-apk
path: ${{ steps.outputs.outputs.APK_PATH }}
name: bob1-apk
path: ${{ steps.outputs.outputs.APK_PATH }}
retention-days: 30
- name: Upload AAB artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: bob1-aab
path: ${{ steps.outputs.outputs.AAB_PATH }}
path: ${{ steps.outputs.outputs.AAB_PATH }}
retention-days: 30