Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
316 changes: 316 additions & 0 deletions .github/workflows/tidy3d-python-client-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
name: "public/tidy3d/python-client-deploy"

on:
workflow_dispatch:
inputs:
release_tag:
description: 'Release tag to deploy (e.g., v2.10.0, v2.10.0rc1)'
required: true
type: string

deploy_testpypi:
description: 'Deploy to TestPyPI (recommended first step)'
type: boolean
default: true

deploy_pypi:
description: 'Deploy to production PyPI'
type: boolean
default: false

deploy_aws:
description: 'Deploy to AWS CodeArtifact'
type: boolean
default: false

workflow_call:
inputs:
release_tag:
description: 'Release tag to deploy'
required: true
type: string
deploy_testpypi:
type: boolean
default: false
deploy_pypi:
type: boolean
default: false
deploy_aws:
type: boolean
default: false

permissions:
contents: read
id-token: write

env:
AWS_REGION: "us-east-1"

jobs:
validate-inputs:
name: validate-deployment-inputs
runs-on: ubuntu-latest
outputs:
release_tag: ${{ env.RELEASE_TAG }}
deploy_testpypi: ${{ env.DEPLOY_TESTPYPI }}
deploy_pypi: ${{ env.DEPLOY_PYPI }}
deploy_aws: ${{ env.DEPLOY_AWS }}
env:
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || inputs.release_tag }}
DEPLOY_TESTPYPI: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_testpypi || inputs.deploy_testpypi }}
DEPLOY_PYPI: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_pypi || inputs.deploy_pypi }}
DEPLOY_AWS: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_aws || inputs.deploy_aws }}
steps:
- name: validate-inputs
run: |
set -e
echo "=== Deployment Configuration ==="
echo "Release tag: $RELEASE_TAG"
echo "Deploy to TestPyPI: $DEPLOY_TESTPYPI"
echo "Deploy to PyPI: $DEPLOY_PYPI"
echo "Deploy to AWS CodeArtifact: $DEPLOY_AWS"
echo ""

# Validate at least one target is selected
if [[ "$DEPLOY_TESTPYPI" != "true" && "$DEPLOY_PYPI" != "true" && "$DEPLOY_AWS" != "true" ]]; then
echo "Error: At least one deployment target must be selected"
exit 1
fi

# Validate tag format
TAG_REGEX='^v[0-9]+\.[0-9]+\.[0-9]+(rc[0-9]+)?$'
if [[ ! "$RELEASE_TAG" =~ $TAG_REGEX ]]; then
echo " Warning: Tag format doesn't match standard pattern v{major}.{minor}.{patch}[rc{num}]"
echo " Tag: $RELEASE_TAG"
echo " Continuing anyway..."
fi

echo "Validation passed"

build-package:
name: build-distribution-package
needs: validate-inputs
runs-on: ubuntu-latest
steps:
- name: checkout-tag
uses: actions/checkout@v4
with:
ref: ${{ needs.validate-inputs.outputs.release_tag }}
persist-credentials: false

- name: setup-python
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: install-poetry
run: |
python -m pip install --upgrade pip
python -m pip install poetry

- name: build-package
run: |
echo "Building package from tag ${{ needs.validate-inputs.outputs.release_tag }}..."
poetry build
echo ""
echo "Build artifacts:"
ls -lh dist/
echo ""
echo "Package built successfully"

- name: upload-artifacts
uses: actions/upload-artifact@v4
with:
name: dist-${{ needs.validate-inputs.outputs.release_tag }}
path: dist/
retention-days: 7

deploy-testpypi:
name: deploy-to-testpypi
needs: [validate-inputs, build-package]
if: needs.validate-inputs.outputs.deploy_testpypi == 'true'
runs-on: ubuntu-latest
steps:
- name: download-artifacts
uses: actions/download-artifact@v4
with:
name: dist-${{ needs.validate-inputs.outputs.release_tag }}
path: dist/

- name: setup-python
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: install-twine
run: |
python -m pip install --upgrade pip
python -m pip install twine

- name: publish-to-testpypi
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
run: |
echo "Publishing to TestPyPI..."
python -m twine upload \
--repository-url https://test.pypi.org/legacy/ \
--verbose \
dist/*
echo ""
echo "Successfully published to TestPyPI"
echo "View at: https://test.pypi.org/project/tidy3d/"

deploy-pypi:
name: deploy-to-pypi
needs: [validate-inputs, build-package, deploy-testpypi]
# Run after TestPyPI succeeds (or is skipped if not selected)
if: |
always() &&
needs.validate-inputs.outputs.deploy_pypi == 'true' &&
needs.build-package.result == 'success' &&
(needs.deploy-testpypi.result == 'success' || needs.deploy-testpypi.result == 'skipped')
runs-on: ubuntu-latest
steps:
- name: download-artifacts
uses: actions/download-artifact@v4
with:
name: dist-${{ needs.validate-inputs.outputs.release_tag }}
path: dist/

- name: setup-python
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: install-twine
run: |
python -m pip install --upgrade pip
python -m pip install twine

- name: publish-to-pypi
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
echo "Publishing to production PyPI..."
python -m twine upload \
--repository pypi \
--verbose \
dist/*
echo ""
echo "Successfully published to PyPI"
echo " View at: https://pypi.org/project/tidy3d/"

deploy-aws-codeartifact:
name: deploy-to-aws-codeartifact
needs: [validate-inputs, build-package, deploy-testpypi]
# Run in parallel with PyPI after TestPyPI succeeds
if: |
always() &&
needs.validate-inputs.outputs.deploy_aws == 'true' &&
needs.build-package.result == 'success' &&
(needs.deploy-testpypi.result == 'success' || needs.deploy-testpypi.result == 'skipped')
runs-on: ubuntu-latest
steps:
- name: download-artifacts
uses: actions/download-artifact@v4
with:
name: dist-${{ needs.validate-inputs.outputs.release_tag }}
path: dist/

- name: setup-python
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: install-twine
run: |
python -m pip install --upgrade pip
python -m pip install twine

- name: configure-aws-credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ env.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_CODEARTIFACT_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.AWS_CODEARTIFACT_ACCESS_SECRET }}

- name: configure-codeartifact-package
run: |
echo "Configuring CodeArtifact package settings..."
aws codeartifact put-package-origin-configuration \
--domain flexcompute \
--repository pypi-releases \
--format pypi \
--package tidy3d \
--restrictions '{"publish":"ALLOW", "upstream":"BLOCK"}'

- name: get-codeartifact-token
run: |
echo "Getting CodeArtifact authorization token..."
CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \
--domain flexcompute \
--domain-owner 625554095313 \
--region us-east-1 \
--query authorizationToken \
--output text)
echo "CODEARTIFACT_AUTH_TOKEN=${CODEARTIFACT_AUTH_TOKEN}" >> $GITHUB_ENV

- name: publish-to-codeartifact
env:
TWINE_PASSWORD: ${{ env.CODEARTIFACT_AUTH_TOKEN }}
run: |
echo "Publishing to AWS CodeArtifact..."
python -m twine upload \
--repository-url https://flexcompute-625554095313.d.codeartifact.us-east-1.amazonaws.com/pypi/pypi-releases/ \
--verbose \
-u aws \
-p "$TWINE_PASSWORD" \
dist/*
echo ""
echo "Successfully published to AWS CodeArtifact"

deployment-summary:
name: deployment-summary
needs: [validate-inputs, build-package, deploy-testpypi, deploy-pypi, deploy-aws-codeartifact]
if: always()
runs-on: ubuntu-latest
steps:
- name: generate-summary
run: |
echo "=== Deployment Summary ==="
echo "Release Tag: ${{ needs.validate-inputs.outputs.release_tag }}"
echo ""
echo "Build Package: ${{ needs.build-package.result }}"
echo "TestPyPI: ${{ needs.deploy-testpypi.result }}"
echo "PyPI: ${{ needs.deploy-pypi.result }}"
echo "AWS CodeArtifact: ${{ needs.deploy-aws-codeartifact.result }}"
echo ""

# Check for failures
if [[ "${{ needs.build-package.result }}" == "failure" ]]; then
echo "Build failed"
exit 1
fi

# Check if any selected deployment failed
failed=false
if [[ "${{ needs.validate-inputs.outputs.deploy_testpypi }}" == "true" && "${{ needs.deploy-testpypi.result }}" == "failure" ]]; then
echo "TestPyPI deployment failed"
failed=true
fi
if [[ "${{ needs.validate-inputs.outputs.deploy_pypi }}" == "true" && "${{ needs.deploy-pypi.result }}" == "failure" ]]; then
echo "PyPI deployment failed"
failed=true
fi
if [[ "${{ needs.validate-inputs.outputs.deploy_aws }}" == "true" && "${{ needs.deploy-aws-codeartifact.result }}" == "failure" ]]; then
echo "AWS CodeArtifact deployment failed"
failed=true
fi

if [[ "$failed" == "true" ]]; then
exit 1
fi

echo "All selected deployments completed successfully"
Loading