Jelajahi Sumber

Feature/single package actions (#1102)

* feat: add workflows for publishing single package (#1070)

* feat: add workflows for publishing single package

* feat: remove unneeded taipy package job

* fix: whitespace in variable name (#1101)

* feat: add version interval to taipy dependencies
João André 1 tahun lalu
induk
melakukan
70d7625119

+ 166 - 0
.github/workflows/build-and-release-single-package.yml

@@ -0,0 +1,166 @@
+name: Build and release one taipy sub-package
+
+on:
+  workflow_dispatch:
+    inputs:
+      internal_dep_on_pypi:
+        description: "Point taipy internal dependencies to Pypi? If false it will point to the github .tar.gz release file"
+        default: "false"
+        required: true
+      release_type:
+        description: "The type of release to be made (dev or production)"
+        default: "dev"
+        required: true
+      target_version:
+        description: "The version of the package to be released"
+        required: true
+      target_package:
+        description: "The package to be released (gui, config, core, rest, templates, taipy)"
+        required: true
+
+jobs:
+  fetch-versions:
+    runs-on: ubuntu-latest
+    outputs:
+        config_VERSION: ${{ steps.version-setup.outputs.config_VERSION }}
+        core_VERSION: ${{ steps.version-setup.outputs.core_VERSION }}
+        gui_VERSION: ${{ steps.version-setup.outputs.gui_VERSION }}
+        rest_VERSION: ${{ steps.version-setup.outputs.rest_VERSION }}
+        templates_VERSION: ${{ steps.version-setup.outputs.templates_VERSION }}
+        VERSION: ${{ steps.version-setup.outputs.VERSION }}
+        NEW_VERSION: ${{ steps.version-setup.outputs.NEW_VERSION }}
+    steps:
+      - uses: actions/checkout@v4
+      - name: Extract branch name
+        shell: bash
+        run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
+        id: extract_branch
+
+      - name: Setup Version
+        id: version-setup
+        run: |
+          python tools/release/fetch_latest_versions.py \
+          ${{ github.event.inputs.release_type }} \
+          ${{ github.event.inputs.internal_dep_on_pypi }} \
+          ${{ github.event.inputs.target_version }} \
+          ${{ github.event.inputs.target_package }} >> $GITHUB_OUTPUT
+
+  build-and-release-package:
+    needs: [fetch-versions]
+    timeout-minutes: 20
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          ssh-key: ${{secrets.DEPLOY_KEY}}
+      - uses: actions/setup-python@v4
+        with:
+          python-version: 3.9
+      - uses: actions/setup-node@v4
+        with:
+          node-version: '20'
+
+      - name: Extract commit hash
+        shell: bash
+        run: echo "HASH=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
+        id: extract_hash
+
+      - name: Set Build Variables
+        id: set-variables
+        run: |
+          if [ "${{ github.event.inputs.target_package }}" == "config" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.config_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/config" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.config_VERSION}}-config" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-config-${{needs.fetch-versions.outputs.config_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          elif [ "${{ github.event.inputs.target_package }}" == "core" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.core_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/core" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.core_VERSION}}-core" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-core-${{needs.fetch-versions.outputs.core_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          elif [ "${{ github.event.inputs.target_package }}" == "gui" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.gui_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/gui" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.gui_VERSION}}-gui" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-gui-${{needs.fetch-versions.outputs.gui_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          elif [ "${{ github.event.inputs.target_package }}" == "rest" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.rest_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/rest" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.rest_VERSION}}-rest" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-rest-${{needs.fetch-versions.outputs.rest_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          elif [ "${{ github.event.inputs.target_package }}" == "templates" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.templates_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/templates" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.templates_VERSION}}-templates" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-templates-${{needs.fetch-versions.outputs.templates_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          fi
+        shell: bash
+
+      - name: Update setup.requirements.txt
+        run: |
+          python tools/release/update_setup_requirements.py taipy-${{ github.event.inputs.target_package }} \
+            ${{needs.fetch-versions.outputs.config_VERSION}} \
+            ${{needs.fetch-versions.outputs.core_VERSION}} \
+            ${{needs.fetch-versions.outputs.gui_VERSION}} \
+            ${{needs.fetch-versions.outputs.rest_VERSION}} \
+            ${{needs.fetch-versions.outputs.templates_VERSION}} \
+            ${{ github.event.inputs.internal_dep_on_pypi }}
+
+      - name: Copy tools
+        run: |
+          cp -r tools ${{ steps.set-variables.outputs.package_dir }}
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install build wheel pipenv mypy black isort
+
+      - name: Install GUI dependencies
+        if: github.event.inputs.target_package == 'gui'
+        run: |
+          pipenv install --dev
+
+      - name: Generate GUI pyi file
+        if: github.event.inputs.target_package == 'gui'
+        run: |
+          cp tools/gui/generate_pyi.py pyi_temp.py && pipenv run python pyi_temp.py && rm pyi_temp.py
+
+      - name: Build frontends
+        if: github.event.inputs.target_package == 'gui'
+        run: |
+          python tools/frontend/bundle_build.py
+
+      - name: Copy files from tools
+        run: |
+          cp -r tools/packages/taipy-${{ github.event.inputs.target_package }}/. ${{ steps.set-variables.outputs.package_dir }}
+
+      - name: Build Package Structure
+        working-directory: ${{ steps.set-variables.outputs.package_dir }}
+        run: |
+          python tools/release/build_package_structure.py  ${{ github.event.inputs.target_package }}
+
+      - name: Copy Taipy Logger
+        if: github.event.inputs.target_package == 'config'
+        run: |
+          cp -r taipy/logger/. ${{ steps.set-variables.outputs.package_dir }}/taipy/logger
+
+      - name: Copy _cli folder
+        run: |
+          cp -r taipy/_cli/. ${{ steps.set-variables.outputs.package_dir }}/taipy/_cli
+
+      - name: Build package
+        working-directory: ${{ steps.set-variables.outputs.package_dir }}
+        run: |
+          python setup.py build_py && python -m build
+
+      - name: Create tag and release
+        working-directory: ${{ steps.set-variables.outputs.package_dir }}
+        run: |
+           if [ "${{ github.event.inputs.release_type }}" == "dev" ]; then
+            gh release create ${{ steps.set-variables.outputs.release_name }} ${{ steps.set-variables.outputs.tar_path }} --target ${{ steps.extract_hash.outputs.HASH }} --prerelease --title ${{ steps.set-variables.outputs.release_name }} --notes "Release Draft ${{ steps.set-variables.outputs.release_name }}"
+           else
+            gh release create ${{ steps.set-variables.outputs.release_name }} ${{ steps.set-variables.outputs.tar_path }} --target ${{ steps.extract_hash.outputs.HASH }} --title ${{ steps.set-variables.outputs.release_name }} --notes "Release ${{ steps.set-variables.outputs.release_name }}"
+           fi
+        shell: bash
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

+ 6 - 5
.github/workflows/build-and-release.yml

@@ -38,6 +38,12 @@ jobs:
         run: |
         run: |
           python tools/release/setup_version.py ALL ${{ github.event.inputs.release_type }} ${{ github.event.inputs.target_version }} ${{ steps.extract_branch.outputs.branch }} >> $GITHUB_OUTPUT
           python tools/release/setup_version.py ALL ${{ github.event.inputs.release_type }} ${{ github.event.inputs.target_version }} ${{ steps.extract_branch.outputs.branch }} >> $GITHUB_OUTPUT
 
 
+      - uses: stefanzweifel/git-auto-commit-action@v4
+        with:
+          commit_message: Update version to ${{ steps.version-setup.outputs.NEW_VERSION }}
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
   build-and-release-taipy-packages:
   build-and-release-taipy-packages:
     needs: [fetch-versions]
     needs: [fetch-versions]
     timeout-minutes: 20
     timeout-minutes: 20
@@ -237,11 +243,6 @@ jobs:
         env:
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
 
-      - uses: stefanzweifel/git-auto-commit-action@v4
-        with:
-          file_pattern: '*/version.json'
-          commit_message: Update version to ${{ needs.fetch-versions.outputs.NEW_VERSION }}
-
       - name: Reset changes
       - name: Reset changes
         run: |
         run: |
           git reset --hard HEAD
           git reset --hard HEAD

+ 45 - 0
.github/workflows/publish-single-package.yml

@@ -0,0 +1,45 @@
+name: Publish a taipy package on Pypi
+
+on:
+  workflow_dispatch:
+    inputs:
+      version:
+        description: "The tag of the package to publish on Pypi (ex: 1.0.0, 1.0.0.dev0)"
+        required: true
+      target_package:
+        description: "The package to be released (gui, config, core, rest, templates, taipy)"
+        required: true
+
+jobs:
+
+  publish-package-to-pypi:
+    permissions:
+      id-token: write  # IMPORTANT: this permission is mandatory for trusted publishing
+    timeout-minutes: 20
+    environment: publish
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          sparse-checkout: taipy/${{ matrix.package }}
+          sparse-checkout-cone-mode: false
+
+      - name: Checks if package is already on Pypi
+        id: check-version
+        run: |
+          if curl https://pypi.org/simple/taipy-${{ github.event.inputs.target_package }}} | grep -o ">taipy-${{ github.event.inputs.target_package }}}-${{ github.event.inputs.version }}\.tar\.gz<"; then
+            echo "exists=true" >> $GITHUB_OUTPUT
+          else
+            echo "exists=false" >> $GITHUB_OUTPUT
+          fi
+
+      - name: Download assets from tag
+        if: steps.check-version.outputs.exists == 'false'
+        run: |
+          gh release download ${{ github.event.inputs.version }}-${{ github.event.inputs.target_package }} --dir dist
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Publish to PyPI
+        if: steps.check-version.outputs.exists == 'false'
+        uses: pypa/gh-action-pypi-publish@release/v1

+ 78 - 0
tools/release/fetch_latest_versions.py

@@ -0,0 +1,78 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+import sys
+
+import requests  # type: ignore
+
+
+def fetch_latest_releases_from_github(dev=False, target_version="", target_package=""):
+    releases = {}
+    url = "https://api.github.com/repos/Avaiga/taipy/releases"
+    response = requests.get(url)
+    resp_json = response.json()
+
+    for rel in resp_json:
+        tag = rel["tag_name"]
+
+        if not dev and ".dev" in tag:
+            continue
+        if "config" in tag:
+            releases["config"] = releases.get("config") or tag.split("-")[0]
+        elif "core" in tag:
+            releases["core"] = releases.get("core") or tag.split("-")[0]
+        elif "gui" in tag:
+            releases["gui"] = releases.get("gui") or tag.split("-")[0]
+        elif "rest" in tag:
+            releases["rest"] = releases.get("rest") or tag.split("-")[0]
+        elif "templates" in tag:
+            releases["templates"] = releases.get("templates") or tag.split("-")[0]
+    releases[target_package] = target_version
+    return releases
+
+
+def fetch_latest_releases_from_pypi(dev=False, target_version="", target_package=""):
+    releases = {}
+
+    for pkg in ["config", "core", "gui", "rest", "templates"]:
+        url = f"https://pypi.org/pypi/taipy-{pkg}/json"
+        response = requests.get(url)
+        resp_json = response.json()
+        versions = list(resp_json["releases"].keys())
+        versions.reverse()
+
+        for ver in versions:
+            if not dev and ".dev" in ver:
+                continue
+            releases[pkg] = ver
+            break
+    releases[target_package] = target_version
+    return releases
+
+
+if __name__ == "__main__":
+    is_dev_version = sys.argv[1] == "dev"
+    is_pypi = sys.argv[2] == "true"
+    target_version = sys.argv[3]
+    target_package = sys.argv[4]
+
+    if is_dev_version and ".dev" not in target_version:
+        raise Exception("Version does not contain suffix .dev")
+
+    versions = {}
+
+    if not is_pypi:
+        versions = fetch_latest_releases_from_github(is_dev_version, target_version, target_package)
+    else:
+        versions = fetch_latest_releases_from_pypi(is_dev_version, target_version, target_package)
+
+    for name, version in versions.items():
+        print(f"{name}_VERSION={version}")  # noqa: T201

+ 3 - 1
tools/release/update_setup_requirements.py

@@ -18,8 +18,10 @@ BASE_PATH = "./tools/packages"
 
 
 def __build_taipy_package_line(line: str, version: str, publish_on_py_pi: bool) -> str:
 def __build_taipy_package_line(line: str, version: str, publish_on_py_pi: bool) -> str:
     _line = line.strip()
     _line = line.strip()
+    max_version = f'{version.split(".")[0]}.{int(version.split(".")[1]) + 1}.0'
     if publish_on_py_pi:
     if publish_on_py_pi:
-        return f"{_line}=={version}\n"
+        return f"{_line}>={version}, <{max_version}\n"
+
     tag = f"{version}-{_line.split('-')[1]}"
     tag = f"{version}-{_line.split('-')[1]}"
     tar_name = f"{_line}-{version}"
     tar_name = f"{_line}-{version}"
     return f"{_line} @ https://github.com/Avaiga/taipy/releases/download/{tag}/{tar_name}.tar.gz\n"
     return f"{_line} @ https://github.com/Avaiga/taipy/releases/download/{tag}/{tar_name}.tar.gz\n"