forked from NuQloud/nc-talk-ai
210 lines
7.6 KiB
YAML
210 lines
7.6 KiB
YAML
name: Build and publish app release
|
|
|
|
on:
|
|
release:
|
|
types: [published]
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: Existing GitHub release tag to build and publish
|
|
required: true
|
|
type: string
|
|
publish_appstore:
|
|
description: Publish the built release to the Nextcloud App Store
|
|
required: true
|
|
default: true
|
|
type: boolean
|
|
nightly:
|
|
description: Mark a manually dispatched App Store release as nightly
|
|
required: true
|
|
default: false
|
|
type: boolean
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
concurrency:
|
|
group: appstore-release-${{ github.event.release.tag_name || inputs.tag }}
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
APP_NAME: educai
|
|
|
|
jobs:
|
|
build-and-publish:
|
|
name: Build, sign and publish
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 25
|
|
|
|
steps:
|
|
- name: Checkout release automation
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
with:
|
|
ref: ${{ github.event.repository.default_branch }}
|
|
path: automation
|
|
persist-credentials: false
|
|
|
|
- name: Checkout released source
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
with:
|
|
ref: ${{ github.event.release.tag_name || inputs.tag }}
|
|
path: source
|
|
fetch-depth: 1
|
|
persist-credentials: false
|
|
|
|
- name: Validate release metadata
|
|
id: release
|
|
shell: bash
|
|
env:
|
|
EVENT_NAME: ${{ github.event_name }}
|
|
RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
|
|
RELEASE_PRERELEASE: ${{ github.event.release.prerelease || inputs.nightly || false }}
|
|
MANUAL_PUBLISH: ${{ inputs.publish_appstore || false }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
|
|
echo "Unsupported release tag: $RELEASE_TAG" >&2
|
|
exit 1
|
|
fi
|
|
|
|
version="$(sed -n 's:.*<version>\([^<]*\)</version>.*:\1:p' source/appinfo/info.xml)"
|
|
if [[ "$RELEASE_TAG" != "v$version" ]]; then
|
|
echo "Release tag $RELEASE_TAG does not match app version $version" >&2
|
|
exit 1
|
|
fi
|
|
|
|
nightly="false"
|
|
if [[ "$RELEASE_PRERELEASE" == "true" ]]; then
|
|
nightly="true"
|
|
fi
|
|
|
|
publish_appstore="true"
|
|
if [[ "$EVENT_NAME" == "workflow_dispatch" && "$MANUAL_PUBLISH" != "true" ]]; then
|
|
publish_appstore="false"
|
|
fi
|
|
|
|
asset="$APP_NAME-$version.tar.gz"
|
|
download_url="https://github.com/$GITHUB_REPOSITORY/releases/download/$RELEASE_TAG/$asset"
|
|
|
|
echo "tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
|
|
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
echo "asset=$asset" >> "$GITHUB_OUTPUT"
|
|
echo "artifact=$GITHUB_WORKSPACE/build/artifacts/appstore/$asset" >> "$GITHUB_OUTPUT"
|
|
echo "download_url=$download_url" >> "$GITHUB_OUTPUT"
|
|
echo "nightly=$nightly" >> "$GITHUB_OUTPUT"
|
|
echo "publish_appstore=$publish_appstore" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
|
with:
|
|
node-version-file: source/.nvmrc
|
|
cache: npm
|
|
cache-dependency-path: source/package-lock.json
|
|
|
|
- name: Set up PHP and Composer
|
|
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
|
|
with:
|
|
php-version: '8.1'
|
|
tools: composer:v2
|
|
coverage: none
|
|
|
|
- name: Build production tarball
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
bash automation/scripts/build-release.sh \
|
|
"$GITHUB_WORKSPACE/source" \
|
|
"$GITHUB_WORKSPACE/build/artifacts/appstore"
|
|
|
|
- name: Validate release archive
|
|
shell: bash
|
|
env:
|
|
ARTIFACT: ${{ steps.release.outputs.artifact }}
|
|
VERSION: ${{ steps.release.outputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
test -f "$ARTIFACT"
|
|
archive_list="$RUNNER_TEMP/$APP_NAME-archive.txt"
|
|
tar -tzf "$ARTIFACT" > "$archive_list"
|
|
|
|
test "$(cut -d/ -f1 "$archive_list" | sort -u)" = "$APP_NAME"
|
|
grep -Fx "$APP_NAME/appinfo/info.xml" "$archive_list" >/dev/null
|
|
grep -Fx "$APP_NAME/vendor/autoload.php" "$archive_list" >/dev/null
|
|
grep -Fx "$APP_NAME/js/educai-main.mjs" "$archive_list" >/dev/null
|
|
|
|
if grep -Eq "^$APP_NAME/(node_modules|src|tests|vendor-bin|\.git)/" "$archive_list"; then
|
|
echo "Development files were included in the release archive" >&2
|
|
exit 1
|
|
fi
|
|
|
|
packaged_version="$(tar -xOf "$ARTIFACT" "$APP_NAME/appinfo/info.xml" | sed -n 's:.*<version>\([^<]*\)</version>.*:\1:p')"
|
|
test "$packaged_version" = "$VERSION"
|
|
|
|
- name: Verify signing key and certificate
|
|
shell: bash
|
|
env:
|
|
ARTIFACT: ${{ steps.release.outputs.artifact }}
|
|
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
|
|
APP_PUBLIC_CRT: ${{ secrets.APP_PUBLIC_CRT }}
|
|
run: |
|
|
set -euo pipefail
|
|
umask 077
|
|
|
|
key_file="$RUNNER_TEMP/$APP_NAME.key"
|
|
cert_file="$RUNNER_TEMP/$APP_NAME.crt"
|
|
public_key_file="$RUNNER_TEMP/$APP_NAME.pub"
|
|
signature_file="$RUNNER_TEMP/$APP_NAME.signature"
|
|
|
|
printf '%s\n' "$APP_PRIVATE_KEY" > "$key_file"
|
|
printf '%s\n' "$APP_PUBLIC_CRT" > "$cert_file"
|
|
|
|
key_fingerprint="$(openssl pkey -in "$key_file" -pubout -outform DER | sha256sum | cut -d' ' -f1)"
|
|
cert_fingerprint="$(openssl x509 -in "$cert_file" -pubkey -noout | openssl pkey -pubin -outform DER | sha256sum | cut -d' ' -f1)"
|
|
test "$key_fingerprint" = "$cert_fingerprint"
|
|
|
|
openssl dgst -sha512 -sign "$key_file" -binary "$ARTIFACT" > "$signature_file"
|
|
openssl x509 -in "$cert_file" -pubkey -noout > "$public_key_file"
|
|
openssl dgst -sha512 -verify "$public_key_file" -signature "$signature_file" "$ARTIFACT"
|
|
|
|
- name: Attach tarball to GitHub release
|
|
shell: bash
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
ARTIFACT: ${{ steps.release.outputs.artifact }}
|
|
RELEASE_TAG: ${{ steps.release.outputs.tag }}
|
|
DOWNLOAD_URL: ${{ steps.release.outputs.download_url }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null
|
|
gh release upload "$RELEASE_TAG" "$ARTIFACT" \
|
|
--repo "$GITHUB_REPOSITORY" \
|
|
--clobber
|
|
|
|
local_hash="$(sha256sum "$ARTIFACT" | cut -d' ' -f1)"
|
|
public_hash="$(curl --fail --silent --show-error --location "$DOWNLOAD_URL" | sha256sum | cut -d' ' -f1)"
|
|
test "$local_hash" = "$public_hash"
|
|
|
|
- name: Publish release to Nextcloud App Store
|
|
if: ${{ steps.release.outputs.publish_appstore == 'true' }}
|
|
uses: R0Wi/nextcloud-appstore-push-action@669734a3b4fb1ff7a636ae6d5bf2c18fab10a4e0
|
|
with:
|
|
app_name: ${{ env.APP_NAME }}
|
|
appstore_token: ${{ secrets.APPSTORE_TOKEN }}
|
|
download_url: ${{ steps.release.outputs.download_url }}
|
|
app_private_key: ${{ secrets.APP_PRIVATE_KEY }}
|
|
nightly: ${{ steps.release.outputs.nightly }}
|
|
|
|
- name: Remove signing material
|
|
if: ${{ always() }}
|
|
shell: bash
|
|
run: |
|
|
rm -f \
|
|
"$GITHUB_WORKSPACE/$APP_NAME.key" \
|
|
"$RUNNER_TEMP/$APP_NAME.key" \
|
|
"$RUNNER_TEMP/$APP_NAME.crt" \
|
|
"$RUNNER_TEMP/$APP_NAME.pub" \
|
|
"$RUNNER_TEMP/$APP_NAME.signature"
|