diff --git a/.editorconfig b/.editorconfig index 21e257ee9..1c436d1ea 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,5 +15,5 @@ indent_size = 4 [*.md] trim_trailing_whitespace = false -[*.{js,yaml}] +[*.{js,yaml,yml}] indent_size = 2 diff --git a/.github/workflows/bundle_webapp_and_release_v3.yml b/.github/workflows/bundle_webapp_and_release_v3.yml new file mode 100644 index 000000000..548f2b47a --- /dev/null +++ b/.github/workflows/bundle_webapp_and_release_v3.yml @@ -0,0 +1,183 @@ +name: Bundle Webapp and Release + +on: + push: + branches: + - 'future3/main' + - 'future3/develop' + +jobs: + + check: + if: ${{ github.repository_owner == 'MiczFlor' }} + runs-on: ubuntu-latest + + outputs: + tag_name: ${{ steps.vars.outputs.tag_name }} + release_type: ${{ steps.vars.outputs.release_type }} + check_abort: ${{ steps.vars.outputs.check_abort }} + + steps: + - uses: actions/checkout@v3 + + - name: Set Output vars + id: vars + env: + BRANCH_NAME: ${{ github.ref_name }} + run: | + # Official SemVer Regex definition + # https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + # Needed changes to the regex: + # - '?:' capture command needed to be removed as it wasn't working in shell + # - '\d' had to be replaced with [0-9] + # + # Release versions like 1.0.0, 3.5.0, 100.4.50000+metadata + REGEX_VERSION_RELEASE="^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$" + # + # Prerelease versions like 1.0.0-alpha, 3.5.0-whatsoever.12, 100.4.50000-identifier.12+metadata + REGEX_VERSION_PRERELEASE="^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$" + + + # Get the version and calculate release type + VERSION=$(python ./src/jukebox/jukebox/version.py) + if echo "$VERSION" | grep -qoE "$REGEX_VERSION_RELEASE" ; then + RELEASE_TYPE=release + elif echo "$VERSION" | grep -qoE "$REGEX_VERSION_PRERELEASE" ; then + RELEASE_TYPE=prerelease + else + RELEASE_TYPE=none + fi + + if [ "$BRANCH_NAME" == 'future3/main' -a "$RELEASE_TYPE" == 'release' ] || [ "$BRANCH_NAME" == 'future3/develop' -a "$RELEASE_TYPE" == 'prerelease' ] ; then + CHECK_ABORT=false + else + echo "::notice title=Abort due to not matching ${RELEASE_TYPE} version for branch!::'${VERSION}' on '${BRANCH_NAME}'" + CHECK_ABORT=true + fi + + echo "::group::Output values" + echo "Version: ${VERSION}" + echo "RELEASE_TYPE: ${RELEASE_TYPE}" + echo "BRANCH_NAME: ${BRANCH_NAME}" + echo "CHECK_ABORT: ${CHECK_ABORT}" + + echo "tag_name=v${VERSION}" >> $GITHUB_OUTPUT + echo "release_type=${RELEASE_TYPE}" >> $GITHUB_OUTPUT + echo "branch_name=${BRANCH_NAME}" >> $GITHUB_OUTPUT + echo "check_abort=${CHECK_ABORT}" >> $GITHUB_OUTPUT + echo "::endgroup::" + + build: + needs: [check] + if: ${{ needs.check.outputs.check_abort == 'false' }} + runs-on: ubuntu-latest + + env: + WEBAPP_ROOT_PATH: ./src/webapp + + outputs: + tag_name: ${{ needs.check.outputs.tag_name }} + release_type: ${{ needs.check.outputs.release_type }} + commit_sha: ${{ steps.vars.outputs.commit_sha }} + webapp_bundle_name: ${{ steps.vars.outputs.webapp_bundle_name }} + webapp_bundle_name_latest: ${{ steps.vars.outputs.webapp_bundle_name_latest }} + + steps: + - uses: actions/checkout@v3 + + - name: Set Output vars + id: vars + env: + COMMIT_SHA: ${{ github.sha }} + run: | + echo "commit_sha=${COMMIT_SHA}" >> $GITHUB_OUTPUT + echo "webapp_bundle_name=webapp-build-${COMMIT_SHA:0:10}.tar.gz" >> $GITHUB_OUTPUT + echo "webapp_bundle_name_latest=webapp-build-latest.tar.gz" >> $GITHUB_OUTPUT + + - name: Setup Node.js 20.x + uses: actions/setup-node@v3 + with: + node-version: 20.x + - name: npm install + working-directory: ${{ env.WEBAPP_ROOT_PATH }} + run: npm install + - name: npm build + working-directory: ${{ env.WEBAPP_ROOT_PATH }} + env: + CI: false + run: npm run build + + - name: Create Bundle + working-directory: ${{ env.WEBAPP_ROOT_PATH }} + run: | + tar -czvf ${{ steps.vars.outputs.webapp_bundle_name }} build + + - name: Artifact Upload + uses: actions/upload-artifact@v3 + with: + name: ${{ steps.vars.outputs.webapp_bundle_name }} + path: ${{ env.WEBAPP_ROOT_PATH }}/${{ steps.vars.outputs.webapp_bundle_name }} + retention-days: 5 + + release: + needs: [build] + runs-on: ubuntu-latest + + concurrency: + group: ${{ needs.build.outputs.tag_name }} + + permissions: + contents: write + + steps: + - name: Artifact Download + uses: actions/download-artifact@v3 + with: + name: ${{ needs.build.outputs.webapp_bundle_name }} + + - name: Create Release + uses: ncipollo/release-action@v1 + with: + commit: ${{ needs.build.outputs.commit_sha }} + tag: ${{ needs.build.outputs.tag_name }} + body: "Automated Release for ${{ needs.build.outputs.tag_name }}" + makeLatest: 'false' + prerelease: ${{ needs.build.outputs.release_type == 'prerelease' }} + generateReleaseNotes: ${{ needs.build.outputs.release_type == 'release' }} + skipIfReleaseExists: false + allowUpdates: true + removeArtifacts: false + replacesArtifacts: false + omitBodyDuringUpdate: true + omitNameDuringUpdate: true + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Get Release by tag + id: get_release + uses: joutvhu/get-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ needs.build.outputs.tag_name }} + + - name: Upload Release Asset + uses: shogo82148/actions-upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.get_release.outputs.upload_url }} + asset_name: ${{ needs.build.outputs.webapp_bundle_name }} + asset_path: ${{ needs.build.outputs.webapp_bundle_name }} + asset_content_type: application/gzip + overwrite: true + + - name: Upload Release Asset as Latest + uses: shogo82148/actions-upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.get_release.outputs.upload_url }} + asset_name: ${{ needs.build.outputs.webapp_bundle_name_latest }} + asset_path: ${{ needs.build.outputs.webapp_bundle_name }} + asset_content_type: application/gzip + overwrite: true diff --git a/.github/workflows/test_docker_debian_v3.yml b/.github/workflows/test_docker_debian_v3.yml index 6f90048ec..f333d15eb 100644 --- a/.github/workflows/test_docker_debian_v3.yml +++ b/.github/workflows/test_docker_debian_v3.yml @@ -5,9 +5,27 @@ on: # run at 17:00 every sunday - cron: '0 17 * * 0' push: + branches: + - 'future3/**' + paths: + - 'installation/**' + - 'ci/**' + - 'resources/**' + - 'src/jukebox/jukebox/version.py' + - 'packages*.txt' + - 'requirements*.txt' pull_request: # The branches below must be a subset of the branches above - branches: [ future3/develop ] + branches: + - future3/develop + - future3/main + paths: + - 'installation/**' + - 'ci/**' + - 'resources/**' + - 'src/jukebox/jukebox/version.py' + - 'packages*.txt' + - 'requirements*.txt' # let only one instance run the test so cache is not corrupted. # cancel already running instances as only the last run will be relevant diff --git a/ci/installation/run_install_common.sh b/ci/installation/run_install_common.sh index 102c71aa4..3d4c59778 100644 --- a/ci/installation/run_install_common.sh +++ b/ci/installation/run_install_common.sh @@ -24,8 +24,8 @@ export ENABLE_WEBAPP_PROD_DOWNLOAD=true # n - setup rfid reader # y - setup samba # y - setup webapp -# n - setup kiosk mode # - - install node (forced WebApp Download) +# n - setup kiosk mode # n - reboot "${LOCAL_INSTALL_SCRIPT_PATH}/install-jukebox.sh" <<< 'y diff --git a/ci/installation/run_install_faststartup.sh b/ci/installation/run_install_faststartup.sh index 46cda25ec..249d78ffc 100644 --- a/ci/installation/run_install_faststartup.sh +++ b/ci/installation/run_install_faststartup.sh @@ -22,8 +22,8 @@ LOCAL_INSTALL_SCRIPT_PATH="${LOCAL_INSTALL_SCRIPT_PATH%/}" # n - setup rfid reader # n - setup samba # n - setup webapp -# - - setup kiosk mode (only with webapp = y) # - - install node (only with webapp = y) +# - - setup kiosk mode (only with webapp = y) # n - reboot "${LOCAL_INSTALL_SCRIPT_PATH}/install-jukebox.sh" <<< 'y diff --git a/ci/installation/run_install_libzmq_local.sh b/ci/installation/run_install_libzmq_local.sh index 20f246ff8..aa3726b2f 100644 --- a/ci/installation/run_install_libzmq_local.sh +++ b/ci/installation/run_install_libzmq_local.sh @@ -23,8 +23,8 @@ export BUILD_LIBZMQ_WITH_DRAFTS_ON_DEVICE=true # n - setup rfid reader # n - setup samba # n - setup webapp -# - - setup kiosk mode (only with webapp = y) # - - install node (only with webapp = y) +# - - setup kiosk mode (only with webapp = y) # n - reboot "${LOCAL_INSTALL_SCRIPT_PATH}/install-jukebox.sh" <<< 'y diff --git a/ci/installation/run_install_webapp_download.sh b/ci/installation/run_install_webapp_download.sh index 69496e8e4..698e057b9 100644 --- a/ci/installation/run_install_webapp_download.sh +++ b/ci/installation/run_install_webapp_download.sh @@ -11,7 +11,6 @@ SCRIPT_DIR="$(dirname "$SOURCE")" LOCAL_INSTALL_SCRIPT_PATH="${INSTALL_SCRIPT_PATH:-${SCRIPT_DIR}/../../installation}" LOCAL_INSTALL_SCRIPT_PATH="${LOCAL_INSTALL_SCRIPT_PATH%/}" -export ENABLE_WEBAPP_PROD_DOWNLOAD=true # Run installation (in interactive mode) # y - start setup # n - use static ip @@ -23,8 +22,8 @@ export ENABLE_WEBAPP_PROD_DOWNLOAD=true # n - setup rfid reader # n - setup samba # y - setup webapp +# n - install node # y - setup kiosk mode -# - - install node (forced webapp download) # n - reboot "${LOCAL_INSTALL_SCRIPT_PATH}/install-jukebox.sh" <<< 'y @@ -36,6 +35,7 @@ n n n y +n y n ' diff --git a/ci/installation/run_install_webapp_local.sh b/ci/installation/run_install_webapp_local.sh index d4f122fd5..917f985af 100644 --- a/ci/installation/run_install_webapp_local.sh +++ b/ci/installation/run_install_webapp_local.sh @@ -23,8 +23,8 @@ export ENABLE_WEBAPP_PROD_DOWNLOAD=false # n - setup rfid reader # n - setup samba # y - setup webapp -# y - setup kiosk mode # y - install node +# y - setup kiosk mode # n - reboot "${LOCAL_INSTALL_SCRIPT_PATH}/install-jukebox.sh" <<< 'y diff --git a/installation/includes/02_helpers.sh b/installation/includes/02_helpers.sh index 7520241c6..239a18ccf 100644 --- a/installation/includes/02_helpers.sh +++ b/installation/includes/02_helpers.sh @@ -49,22 +49,17 @@ get_architecture() { echo $arch } -get_version_string() { - local python_file="$1" - local version_major - local version_minor - local version_patch - - version_major=$(grep 'VERSION_MAJOR\s*=\s*[0-9]*' "${python_file}" | awk -F= '{print $2}' | tr -d ' ') - version_minor=$(grep 'VERSION_MINOR\s*=\s*[0-9]*' "${python_file}" | awk -F= '{print $2}' | tr -d ' ') - version_patch=$(grep 'VERSION_PATCH\s*=\s*[0-9]*' "${python_file}" | awk -F= '{print $2}' | tr -d ' ') - - if [ -n "$version_major" ] && [ -n "$version_minor" ] && [ -n "$version_patch" ]; then - local version_string="${version_major}.${version_minor}.${version_patch}" - echo ${version_string} - else - exit_on_error "ERROR: Unable to extract version information from ${python_file}" - fi +validate_url() { + local url=$1 + wget --spider ${url} >/dev/null 2>&1 + return $? +} + +download_from_url() { + local url=$1 + local output_filename=$2 + wget --quiet ${url} -O ${output_filename} || exit_on_error "Download failed" + return $? } ### Verify helpers diff --git a/installation/install-jukebox.sh b/installation/install-jukebox.sh index 84827f99d..2df7ab528 100755 --- a/installation/install-jukebox.sh +++ b/installation/install-jukebox.sh @@ -130,11 +130,11 @@ _download_jukebox_source() { _load_sources() { # Load / Source dependencies for i in "${INSTALLATION_PATH}"/installation/includes/*; do - source "$i" + source "$i" || exit_on_error done for j in "${INSTALLATION_PATH}"/installation/routines/*; do - source "$j" + source "$j" || exit_on_error done } diff --git a/installation/routines/customize_options.sh b/installation/routines/customize_options.sh index 4007b88f5..7409a6e07 100644 --- a/installation/routines/customize_options.sh +++ b/installation/routines/customize_options.sh @@ -292,9 +292,14 @@ _option_webapp_devel_build() { clear_c print_c "--------------------- WEBAPP NODE --------------------- -You are installing from a non-release branch. -This means, you will need to build the web app locally. -For that you'll need Node. +You are installing from an unofficial branch. +Therefore a prebuilt web app is not available and +you will have to build it locally. +This requires Node to be installed. + +You can choose to decline the Node installation and +the lastest prebuilt version from the main repository +will be installed. This can lead to incompatibilities. Do you want to install Node? [Y/n]" read -r response @@ -304,14 +309,14 @@ Do you want to install Node? [Y/n]" ENABLE_WEBAPP_PROD_DOWNLOAD=true ;; *) - ;; - esac - # This message will be displayed at the end of the installation process - local tmp_fin_message="ATTENTION: You need to build the web app locally with + # This message will be displayed at the end of the installation process + local tmp_fin_message="ATTENTION: You need to build the web app locally with $ cd ~/RPi-Jukebox-RFID/src/webapp && ./run_rebuild.sh -u This must be done after reboot, due to memory restrictions. Read the documentation regarding local Web App builds!" - FIN_MESSAGE="${FIN_MESSAGE:+$FIN_MESSAGE\n}${tmp_fin_message}" + FIN_MESSAGE="${FIN_MESSAGE:+$FIN_MESSAGE\n}${tmp_fin_message}" + ;; + esac fi fi @@ -332,8 +337,8 @@ _run_customize_options() { _option_samba _option_webapp if [[ $ENABLE_WEBAPP == true ]] ; then - _option_kiosk_mode _option_webapp_devel_build + _option_kiosk_mode fi # Bullseye is currently under active development and should be updated in any case. # Hence, removing the step below as it becomse mandatory diff --git a/installation/routines/setup_jukebox_core.sh b/installation/routines/setup_jukebox_core.sh index a7d0f29b6..1c524abb0 100644 --- a/installation/routines/setup_jukebox_core.sh +++ b/installation/routines/setup_jukebox_core.sh @@ -54,7 +54,7 @@ _jukebox_core_build_libzmq_with_drafts() { local cpu_count=${CPU_COUNT:-$(python3 -c "import os; print(os.cpu_count())")} cd "${JUKEBOX_ZMQ_TMP_DIR}" || exit_on_error - wget --quiet https://github.com/zeromq/libzmq/releases/download/v${JUKEBOX_ZMQ_VERSION}/${zmq_tar_filename} + wget --quiet https://github.com/zeromq/libzmq/releases/download/v${JUKEBOX_ZMQ_VERSION}/${zmq_tar_filename} || exit_on_error "Download failed" tar -xzf ${zmq_tar_filename} rm -f ${zmq_tar_filename} cd ${zmq_filename} || exit_on_error @@ -68,7 +68,7 @@ _jukebox_core_download_prebuilt_libzmq_with_drafts() { ARCH=$(get_architecture) cd "${JUKEBOX_ZMQ_TMP_DIR}" || exit_on_error - wget --quiet https://github.com/pabera/libzmq/releases/download/v${JUKEBOX_ZMQ_VERSION}/libzmq5-${ARCH}-${JUKEBOX_ZMQ_VERSION}.tar.gz -O ${zmq_tar_filename} + wget --quiet https://github.com/pabera/libzmq/releases/download/v${JUKEBOX_ZMQ_VERSION}/libzmq5-${ARCH}-${JUKEBOX_ZMQ_VERSION}.tar.gz -O ${zmq_tar_filename} || exit_on_error "Download failed" tar -xzf ${zmq_tar_filename} rm -f ${zmq_tar_filename} sudo rsync -a ./* ${JUKEBOX_ZMQ_PREFIX}/ diff --git a/installation/routines/setup_jukebox_webapp.sh b/installation/routines/setup_jukebox_webapp.sh index f7407f96c..2884f18cb 100644 --- a/installation/routines/setup_jukebox_webapp.sh +++ b/installation/routines/setup_jukebox_webapp.sh @@ -55,16 +55,26 @@ _jukebox_webapp_build() { _jukebox_webapp_download() { print_lc " Downloading web application" - local JUKEBOX_VERSION=$(get_version_string "${INSTALLATION_PATH}/src/jukebox/jukebox/version.py") - local TAR_FILENAME="webapp-build.tar.gz" - local DOWNLOAD_URL="https://github.com/MiczFlor/RPi-Jukebox-RFID/releases/download/v${JUKEBOX_VERSION}/webapp-v${JUKEBOX_VERSION}.tar.gz" - log " DOWNLOAD_URL: ${DOWNLOAD_URL}" + local jukebox_version=$(python "${INSTALLATION_PATH}/src/jukebox/jukebox/version.py") + local git_head_hash=$(git -C "${INSTALLATION_PATH}" rev-parse --verify --quiet HEAD) + local git_head_hash_short=${git_head_hash:0:10} + local tar_filename="webapp-build.tar.gz" + # URL must be set to default repo as installation can be run from different repos as well where releases may not exist + local download_url_commit="https://github.com/${GIT_UPSTREAM_USER}/RPi-Jukebox-RFID/releases/download/v${jukebox_version}/webapp-build-${git_head_hash_short}.tar.gz" + local download_url_latest="https://github.com/${GIT_UPSTREAM_USER}/RPi-Jukebox-RFID/releases/download/v${jukebox_version}/webapp-build-latest.tar.gz" cd "${INSTALLATION_PATH}/src/webapp" || exit_on_error - # URL must be set to default repo as installation can be run from different repos as well where releases may not exist - wget --quiet ${DOWNLOAD_URL} -O ${TAR_FILENAME} - tar -xzf ${TAR_FILENAME} - rm -f ${TAR_FILENAME} + if validate_url ${download_url_commit} ; then + log " DOWNLOAD_URL ${download_url_commit}" + download_from_url ${download_url_commit} ${tar_filename} + elif [[ $ENABLE_WEBAPP_PROD_DOWNLOAD == true ]] && validate_url ${download_url_latest} ; then + log " DOWNLOAD_URL ${download_url_latest}" + download_from_url ${download_url_latest} ${tar_filename} + else + exit_on_error "No prebuild webapp bundle found!" + fi + tar -xzf ${tar_filename} + rm -f ${tar_filename} cd "${INSTALLATION_PATH}" || exit_on_error } diff --git a/src/jukebox/jukebox/version.py b/src/jukebox/jukebox/version.py index 03fc34a66..37382cee7 100644 --- a/src/jukebox/jukebox/version.py +++ b/src/jukebox/jukebox/version.py @@ -1,14 +1,16 @@ VERSION_MAJOR = 3 -VERSION_MINOR = 4 +VERSION_MINOR = 5 VERSION_PATCH = 0 -VERSION_EXTRA = "" +VERSION_EXTRA = "alpha" +# build a version string in compliance with the SemVer specification +# https://semver.org/#semantic-versioning-specification-semver __version__ = '%i.%i.%i' % (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) __version_info__ = (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) if VERSION_EXTRA: - __version__ = "%s.%s" % (__version__, VERSION_EXTRA) + __version__ = "%s-%s" % (__version__, VERSION_EXTRA) __version_info__ = (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_EXTRA) @@ -23,3 +25,7 @@ def version_info(): If this is a development version, an identifier string will be appended after the third integer. """ return __version_info__ + + +if __name__ == '__main__': + print(version())