|
1 | 1 | on: |
2 | 2 | push: |
3 | 3 | tags: |
4 | | - - '*' |
| 4 | + - "[0-9]*.[0-9]*.[0-9]*" |
| 5 | + |
| 6 | +permissions: |
| 7 | + contents: write |
| 8 | + id-token: write |
| 9 | + attestations: write |
| 10 | + |
| 11 | +env: |
| 12 | + BUILDOZER_BUILD_LOG: buildozer-build.log |
| 13 | + BUILDOZER_IMAGE: kivy/buildozer@sha256:fcfb08f4f7beecfdea10e23968c16933b935a2c820e012d8415f318cd8586aab |
| 14 | + RUST_TOOLCHAIN: 1.83.0 |
5 | 15 |
|
6 | 16 | jobs: |
7 | 17 | release: |
8 | | - runs-on: ubuntu-latest |
| 18 | + runs-on: ubuntu-22.04 |
9 | 19 | steps: |
10 | | - - uses: "actions/checkout@v2" |
11 | | - |
12 | | - - name: Get the version |
13 | | - id: version |
14 | | - run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/} |
| 20 | + - uses: "actions/checkout@v6" |
15 | 21 |
|
16 | 22 | - name: Update version from tag |
17 | | - env: |
18 | | - VERSION: ${{ steps.version.outputs.VERSION }} |
19 | 23 | run: | |
| 24 | + VERSION="${GITHUB_REF_NAME}" |
20 | 25 | echo "Version is: ${VERSION}" |
21 | | - test -n "${VERSION}" |
| 26 | + [[ "${VERSION}" =~ ^[0-9]+[.][0-9]+[.][0-9]+$ ]] |
22 | 27 | echo "__version__ = \"${VERSION}\"" > pythonhere/version_here.py |
23 | 28 |
|
24 | 29 | - name: Set up Python |
25 | | - uses: "actions/setup-python@v2" |
| 30 | + uses: "actions/setup-python@v6" |
26 | 31 | with: |
27 | | - python-version: "3.8" |
| 32 | + python-version: "3.10" |
28 | 33 | - name: Install dependencies |
29 | 34 | run: | |
30 | 35 | python -m pip install --upgrade pip |
31 | | - pip install wheel twine |
| 36 | + pip install build twine |
32 | 37 | - name: Build package |
33 | | - run: python setup.py sdist bdist_wheel |
| 38 | + run: python -m build --sdist --wheel |
34 | 39 | - name: List result |
35 | 40 | run: ls -l dist |
36 | 41 | - name: Check distribution |
37 | 42 | run: python -m twine check dist/* |
38 | 43 |
|
39 | | - - name: Build Android APK with Buildozer |
40 | | - id: buildozer |
41 | | - uses: ArtemSBulgakov/buildozer-action@v1 |
42 | | - with: |
43 | | - command: buildozer android debug |
44 | | - repository_root: . |
45 | | - workdir: . |
46 | | - buildozer_version: stable |
| 44 | + - name: Extract release notes from changelog |
| 45 | + run: | |
| 46 | + python scripts/release/extract-changelog-section.py "${GITHUB_REF_NAME}" |
| 47 | + cat release-notes.rst |
47 | 48 |
|
48 | | - - name: List APK building result |
49 | | - run: sudo ls -l "${{ steps.buildozer.outputs.filename }}" |
| 49 | + - name: Prepare Android release signing key |
| 50 | + env: |
| 51 | + ANDROID_RELEASE_KEYSTORE_BASE64: ${{ secrets.ANDROID_RELEASE_KEYSTORE_BASE64 }} |
| 52 | + P4A_RELEASE_KEYSTORE_PASSWD: ${{ secrets.P4A_RELEASE_KEYSTORE_PASSWD }} |
| 53 | + P4A_RELEASE_KEYALIAS: ${{ secrets.P4A_RELEASE_KEYALIAS }} |
| 54 | + P4A_RELEASE_KEYALIAS_PASSWD: ${{ secrets.P4A_RELEASE_KEYALIAS_PASSWD }} |
| 55 | + run: | |
| 56 | + test -n "${ANDROID_RELEASE_KEYSTORE_BASE64}" |
| 57 | + test -n "${P4A_RELEASE_KEYSTORE_PASSWD}" |
| 58 | + test -n "${P4A_RELEASE_KEYALIAS}" |
| 59 | + test -n "${P4A_RELEASE_KEYALIAS_PASSWD}" |
| 60 | + mkdir -p .release-signing/android |
| 61 | + printf '%s' "${ANDROID_RELEASE_KEYSTORE_BASE64}" | base64 --decode > .release-signing/android/pythonhere-release.keystore |
| 62 | + chmod 600 .release-signing/android/pythonhere-release.keystore |
50 | 63 |
|
51 | | - - name: Show APK checksum |
52 | | - run: sha256sum "${{ steps.buildozer.outputs.filename }}" |
| 64 | + - name: Prepare Buildozer release spec |
| 65 | + run: | |
| 66 | + grep -qx 'title = PythonHereDev' buildozer.spec |
| 67 | + sed -i 's/^title = PythonHereDev$/title = PythonHere/' buildozer.spec |
| 68 | + sed -n '1,8p' buildozer.spec |
53 | 69 |
|
54 | | - - name: Create release |
55 | | - id: create_release |
56 | | - uses: actions/create-release@v1 |
| 70 | + - name: Install Rust |
| 71 | + run: | |
| 72 | + rustup toolchain install "$RUST_TOOLCHAIN" --profile minimal |
| 73 | + rustup default "$RUST_TOOLCHAIN" |
| 74 | + rustc --version |
| 75 | + cargo --version |
| 76 | +
|
| 77 | + - name: Build Android release APK with Buildozer Docker image |
| 78 | + shell: bash |
57 | 79 | env: |
58 | | - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 80 | + P4A_RELEASE_KEYSTORE: /home/user/hostcwd/.release-signing/android/pythonhere-release.keystore |
| 81 | + P4A_RELEASE_KEYSTORE_PASSWD: ${{ secrets.P4A_RELEASE_KEYSTORE_PASSWD }} |
| 82 | + P4A_RELEASE_KEYALIAS: ${{ secrets.P4A_RELEASE_KEYALIAS }} |
| 83 | + P4A_RELEASE_KEYALIAS_PASSWD: ${{ secrets.P4A_RELEASE_KEYALIAS_PASSWD }} |
| 84 | + run: | |
| 85 | + set -o pipefail |
| 86 | + mkdir -p .buildozer bin |
| 87 | + sudo chown -R 1000:1000 .buildozer bin |
| 88 | +
|
| 89 | + docker run --rm \ |
| 90 | + -e RUSTUP_HOME=/home/user/.rustup \ |
| 91 | + -e CARGO_HOME=/home/user/.cargo \ |
| 92 | + -e PATH=/home/user/.cargo/bin:/home/user/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ |
| 93 | + -e P4A_RELEASE_KEYSTORE="${P4A_RELEASE_KEYSTORE}" \ |
| 94 | + -e P4A_RELEASE_KEYSTORE_PASSWD="${P4A_RELEASE_KEYSTORE_PASSWD}" \ |
| 95 | + -e P4A_RELEASE_KEYALIAS="${P4A_RELEASE_KEYALIAS}" \ |
| 96 | + -e P4A_RELEASE_KEYALIAS_PASSWD="${P4A_RELEASE_KEYALIAS_PASSWD}" \ |
| 97 | + -v "$PWD":/home/user/hostcwd \ |
| 98 | + -v "$HOME/.rustup":/home/user/.rustup \ |
| 99 | + -v "$HOME/.cargo":/home/user/.cargo \ |
| 100 | + -w /home/user/hostcwd \ |
| 101 | + "$BUILDOZER_IMAGE" \ |
| 102 | + android release 2>&1 | tee "$BUILDOZER_BUILD_LOG" |
| 103 | +
|
| 104 | + - name: Prepare APK release assets |
| 105 | + id: apk |
| 106 | + run: | |
| 107 | + apk_name="pythonhere-${GITHUB_REF_NAME}-arm64-v8a-release.apk" |
| 108 | + apk="$(find bin -name '*.apk' -type f | head -n 1)" |
| 109 | +
|
| 110 | + if [[ -z "$apk" ]]; then |
| 111 | + echo "::error::No APK found" |
| 112 | + exit 1 |
| 113 | + fi |
| 114 | +
|
| 115 | + sudo ls -l "$apk" |
| 116 | + sudo cp "$apk" "${apk_name}" |
| 117 | + sudo chown "$(id -u):$(id -g)" "${apk_name}" |
| 118 | + sha256sum "${apk_name}" > "${apk_name}.sha256" |
| 119 | + echo "filename=${apk_name}" >> "$GITHUB_OUTPUT" |
| 120 | + echo "sha256=${apk_name}.sha256" >> "$GITHUB_OUTPUT" |
| 121 | + ls -lh "${apk_name}" "${apk_name}.sha256" |
| 122 | + cat "${apk_name}.sha256" |
| 123 | +
|
| 124 | + - name: Generate APK provenance attestation |
| 125 | + uses: actions/attest@v4 |
59 | 126 | with: |
60 | | - tag_name: "${{ steps.version.outputs.VERSION }}" |
61 | | - release_name: "Release ${{ steps.version.outputs.VERSION }}" |
| 127 | + subject-path: | |
| 128 | + ${{ steps.apk.outputs.filename }} |
| 129 | + ${{ steps.apk.outputs.sha256 }} |
62 | 130 |
|
63 | | - - name: Upload APK |
64 | | - uses: actions/upload-release-asset@v1 |
| 131 | + - name: Create draft GitHub Release |
| 132 | + id: create_release |
65 | 133 | env: |
66 | | - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
67 | | - with: |
68 | | - upload_url: ${{ steps.create_release.outputs.upload_url }} |
69 | | - asset_path: "${{ steps.buildozer.outputs.filename }}" |
70 | | - asset_name: "pythonhere-debug-${{ steps.version.outputs.VERSION }}.apk" |
71 | | - asset_content_type: application/vnd.android.package-archive |
| 134 | + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 135 | + run: | |
| 136 | + if ! release_json="$(gh api "/repos/${GITHUB_REPOSITORY}/releases/tags/${GITHUB_REF_NAME}" 2>/dev/null)"; then |
| 137 | + release_json="$( |
| 138 | + gh api \ |
| 139 | + --method POST \ |
| 140 | + "/repos/${GITHUB_REPOSITORY}/releases" \ |
| 141 | + -f tag_name="${GITHUB_REF_NAME}" \ |
| 142 | + -f name="Release ${GITHUB_REF_NAME}" \ |
| 143 | + -F body=@release-notes.rst \ |
| 144 | + -F draft=true |
| 145 | + )" |
| 146 | + else |
| 147 | + gh api \ |
| 148 | + --method PATCH \ |
| 149 | + "/repos/${GITHUB_REPOSITORY}/releases/$(printf '%s' "${release_json}" | python -c 'import json, sys; print(json.load(sys.stdin)["id"])')" \ |
| 150 | + -F body=@release-notes.rst \ |
| 151 | + >/dev/null |
| 152 | + fi |
| 153 | + release_id="$(printf '%s' "${release_json}" | python -c 'import json, sys; print(json.load(sys.stdin)["id"])')" |
| 154 | + echo "release_id=${release_id}" >> "$GITHUB_OUTPUT" |
72 | 155 |
|
73 | | - - name: Upload package to PyPI |
| 156 | + - name: Upload APK to draft GitHub Release |
74 | 157 | env: |
75 | | - TWINE_USERNAME: __token__ |
76 | | - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} |
77 | | - run: twine upload dist/* |
| 158 | + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 159 | + APK_FILE: ${{ steps.apk.outputs.filename }} |
| 160 | + APK_SHA256_FILE: ${{ steps.apk.outputs.sha256 }} |
| 161 | + run: | |
| 162 | + gh release upload "${GITHUB_REF_NAME}" "${APK_FILE}" "${APK_SHA256_FILE}" --clobber |
| 163 | +
|
| 164 | + # - name: Upload package to PyPI |
| 165 | + # env: |
| 166 | + # TWINE_USERNAME: __token__ |
| 167 | + # TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} |
| 168 | + # run: twine upload --skip-existing dist/* |
| 169 | + |
| 170 | + # - name: Publish GitHub Release |
| 171 | + # env: |
| 172 | + # GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 173 | + # RELEASE_ID: ${{ steps.create_release.outputs.release_id }} |
| 174 | + # run: | |
| 175 | + # gh api \ |
| 176 | + # --method PATCH \ |
| 177 | + # "/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}" \ |
| 178 | + # -F draft=false |
| 179 | + |
| 180 | + - name: Upload Buildozer build log |
| 181 | + if: always() |
| 182 | + uses: actions/upload-artifact@v4 |
| 183 | + with: |
| 184 | + name: buildozer-build-log |
| 185 | + path: ${{ env.BUILDOZER_BUILD_LOG }} |
| 186 | + if-no-files-found: ignore |
| 187 | + retention-days: 8 |
0 commit comments