diff --git a/.github/actions/mmm-proto-schema-create-version-tag/action.yml b/.github/actions/mmm-proto-schema-create-version-tag/action.yml new file mode 100644 index 000000000..d9f9c3227 --- /dev/null +++ b/.github/actions/mmm-proto-schema-create-version-tag/action.yml @@ -0,0 +1,38 @@ +inputs: + new_version: + description: The new Meridian semantic version string to create a tag for. + required: true + tag_prefix: + description: The prefix to use for the git tag. + required: true +permissions: + contents: write +runs: + using: composite + steps: + - uses: actions/checkout@v4 + - name: Create and Push Tag + uses: actions/github-script@v6 + with: + script: | + const newTag = '${{ inputs.tag_prefix }}${{ inputs.new_version }}'; + try { + await github.rest.git.createTag({ + owner: context.repo.owner, + repo: context.repo.repo, + tag: newTag, + message: `Release ${newTag}`, + object: context.sha, + type: 'commit' + }); + await github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `refs/tags/${newTag}`, + sha: context.sha + }); + console.log(`Created and pushed new tag: ${newTag}`); + } catch (error) { + console.error('Error creating tag:', error); + core.setFailed(`Failed to create tag: ${error.message}`); + } diff --git a/.github/actions/mmm-proto-schema-version-check/action.yml b/.github/actions/mmm-proto-schema-version-check/action.yml new file mode 100644 index 000000000..9028bc904 --- /dev/null +++ b/.github/actions/mmm-proto-schema-version-check/action.yml @@ -0,0 +1,59 @@ +# Check if version in version_file_path has been incremented. If so, set a `new_version` output. +inputs: + python_version: + description: The Python version of the build toolchain to use. + required: true + default: 3.11 + version_file_path: + description: The path to the file containing the __version__ string. + required: true + tag_match: + description: The git tag pattern to match for version comparison. + required: true + tag_prefix: + description: The prefix to strip from the tag to get the version. + required: true +outputs: + new_version: + description: The new version if semver was incremented, otherwise an empty string. + value: ${{ steps.version_check.outputs.new_version }} +runs: + using: composite + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python_version }} + - shell: bash + run: pip install semver + - id: version_check + name: Check Version + shell: bash + run: | + CURRENT_VERSION=$(python3 -c "import re; print(re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', open('${{ inputs.version_file_path }}').read(), re.M).group(1))") + echo "File version: $CURRENT_VERSION" + if ! python3 -m semver check "$CURRENT_VERSION"; then + echo "Error: Invalid semantic version in file: $CURRENT_VERSION" + exit 1 + fi + PREVIOUS_TAG=$(git describe --tags --abbrev=0 --match '${{ inputs.tag_match }}' 2>/dev/null) + if [[ -z "$PREVIOUS_TAG" ]]; then + echo "No previous tag found matching ${{ inputs.tag_match }}. Using 0.0.0 as previous version." + PREVIOUS_VERSION="0.0.0" + else + echo "Found previous tag: $PREVIOUS_TAG" + PREVIOUS_VERSION=$(echo "$PREVIOUS_TAG" | sed 's|^${{ inputs.tag_prefix }}||') + fi + echo "Comparing $CURRENT_VERSION with $PREVIOUS_VERSION" + if [[ $(python3 -m semver compare "$CURRENT_VERSION" "$PREVIOUS_VERSION") -eq 1 ]]; then + echo "New version detected: $CURRENT_VERSION" + NEW_VERSION=$CURRENT_VERSION + else + echo "No version increment detected." + NEW_VERSION="" + fi + echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT diff --git a/.github/workflows/publish-mmm-proto-schema.yml b/.github/workflows/publish-mmm-proto-schema.yml new file mode 100644 index 000000000..ff036c131 --- /dev/null +++ b/.github/workflows/publish-mmm-proto-schema.yml @@ -0,0 +1,129 @@ +name: Publish mmm-proto-schema to PyPI # For tagging and publishing releases. + +on: + push: + branches: + - main # This entire workflow only runs on a push to the `main` branch (i.e. POST SUBMIT). + paths: + - 'proto/**' + tags: + - 'mmm-proto-schema/v*' # Trigger only on pushed tags whose names start with 'v'. + workflow_dispatch: # For triggering publication workflows manually, on demend, from GitHub UI. + inputs: + publish_proto_schema_to_pypi: # Creates a boolean `github.event.inputs.publish_proto_schema_to_pypi` variable. + description: 'Publish proto schema to PyPI' + required: false + type: boolean + default: false + publish_proto_schema_to_testpypi: + description: 'Publish proto schema to TestPyPI' + required: false + type: boolean + default: false + +env: + USE_PYTHON_VERSION: "3.11" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: ./.github/actions/build + with: + python_version: ${{ env.USE_PYTHON_VERSION }} + src_dir: proto + artifact_name: mmm-proto-schema-distributions + + # Check if the current `meridian.__version__` semver has been incremented. If so, set a + # `new_version` output. + check-version: + runs-on: ubuntu-latest + timeout-minutes: 10 + outputs: + new_version: ${{ steps.version_check.outputs.new_version }} + steps: + - uses: actions/checkout@v4 + - id: version_check + uses: ./.github/actions/mmm-proto-schema-version-check + with: + python_version: ${{ env.USE_PYTHON_VERSION }} + version_file_path: proto/__init__.py + tag_match: mmm-proto-schema/v* + tag_prefix: mmm-proto-schema/v + + create-tag: + needs: check-version + # Only run if new_version was discovered, and do this only on the main branch. + if: > + github.ref == 'refs/heads/main' + && needs.check-version.outputs.new_version != '' + runs-on: ubuntu-latest + permissions: + contents: write # Allow to create tags + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/mmm-proto-schema-create-version-tag + with: + new_version: ${{ needs.check-version.outputs.new_version }} + tag_prefix: mmm-proto-schema/v + + publish-proto-schema-to-pypi: + name: Publish MMM proto schema to PyPI + needs: + - build + - check-version + # Check if it's a tag push, or the `publish_proto_schema_to_pypi` workflow is selected in a manual trigger. + # or if a new version was discovered in a push to the `main` branch. + if: > + github.repository == 'google/meridian' && ( + (github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/')) + || (github.event_name == 'workflow_dispatch' + && github.event.inputs.publish_proto_schema_to_pypi == 'true') + || needs.check-version.outputs.new_version != '' + ) + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/mmm-proto-schema + permissions: + id-token: write # IMPORTANT: mandatory for trusted publishing + steps: + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: mmm-proto-schema-distributions + path: dist/ + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + verbose: true + + publish-proto-schema-to-testpypi: + name: Publish MMM proto schema to TestPyPI + needs: + - build + - check-version + # Check if the `publish_proto_schema_to_testpypi` workflow is selected in a manual trigger. + if: > + github.repository == 'google/meridian' && ( + (github.event_name == 'workflow_dispatch' + && github.event.inputs.publish_proto_schema_to_testpypi == 'true') + ) + runs-on: ubuntu-latest + environment: + name: testpypi + url: https://test.pypi.org/p/mmm-proto-schema + permissions: + id-token: write # IMPORTANT: mandatory for trusted publishing + steps: + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: mmm-proto-schema-distributions + path: dist/ + - name: Publish distribution to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + verbose: true