feat: add cancellation support for file and source uploads #38
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: publish-pre-and-qa | |
| on: | |
| pull_request: | |
| branches: [main] | |
| types: [closed] | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: "Git ref to use (defaults to main)" | |
| required: false | |
| type: string | |
| pre_version: | |
| description: "Override release version (e.g., 3.5.0.postYYYYMMDDHHMMSS). If empty, semantic-release dry-run is used." | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| packages: write | |
| issues: write | |
| jobs: | |
| publish-pre: | |
| name: Publish pre-release to TestPyPI (core-first) | |
| if: ${{ github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'prepare-release') || github.event_name == 'workflow_dispatch' }} | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.ver.outputs.version }} | |
| clean_version: ${{ steps.ver.outputs.clean_version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Derive version from PR title | |
| id: ver | |
| run: | | |
| set -euo pipefail | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| VERSION="${{ github.event.inputs.pre_version || '' }}" | |
| if [ -n "$VERSION" ]; then | |
| CLEAN_VERSION="${VERSION%%.post*}" | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "clean_version=$CLEAN_VERSION" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| fi | |
| TITLE="${{ github.event.pull_request.title }}" | |
| VERSION=$(echo "$TITLE" | sed -nE 's/.*prepare ([0-9]+\.[0-9]+\.[0-9]+(\.post[0-9]+)?).*/\1/p' || true) | |
| if [ -z "$VERSION" ]; then | |
| echo "Could not derive version from PR title: $TITLE" >&2 | |
| exit 1 | |
| fi | |
| CLEAN_VERSION="${VERSION%%.post*}" | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "clean_version=$CLEAN_VERSION" >> $GITHUB_OUTPUT | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.13' | |
| - name: Install Poetry | |
| run: | | |
| pip install poetry==2.1.3 | |
| - name: Configure TestPyPI repository | |
| run: | | |
| poetry config repositories.testpypi https://test.pypi.org/legacy/ | |
| - name: Install jq | |
| run: sudo apt-get update && sudo apt-get install -y jq | |
| - name: Publish rag-core-lib first (TestPyPI) and wait for index | |
| env: | |
| VERSION: ${{ steps.ver.outputs.version }} | |
| POETRY_HTTP_BASIC_TESTPYPI_USERNAME: __token__ | |
| POETRY_HTTP_BASIC_TESTPYPI_PASSWORD: ${{ secrets.TEST_PYPI_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${POETRY_HTTP_BASIC_TESTPYPI_PASSWORD:-}" ]; then | |
| echo "Missing TEST_PYPI_TOKEN secret" >&2 | |
| exit 1 | |
| fi | |
| source tools/publish_libs.sh | |
| publish_lib "rag-core-lib" "-r testpypi" "$VERSION" | |
| wait_for_index "rag-core-lib" "$VERSION" "https://test.pypi.org" "TestPyPI" | |
| - name: Publish remaining libs to TestPyPI | |
| env: | |
| VERSION: ${{ steps.ver.outputs.version }} | |
| POETRY_HTTP_BASIC_TESTPYPI_USERNAME: __token__ | |
| POETRY_HTTP_BASIC_TESTPYPI_PASSWORD: ${{ secrets.TEST_PYPI_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| source tools/publish_libs.sh | |
| for lib in rag-core-api admin-api-lib extractor-api-lib; do | |
| path="libs/$lib" | |
| [ -d "$path" ] || { echo "Missing $path" >&2; exit 1; } | |
| publish_lib "$lib" "-r testpypi" "$VERSION" "$path" | |
| done | |
| - name: Persist version metadata for downstream workflows | |
| env: | |
| VERSION: ${{ steps.ver.outputs.version }} | |
| CLEAN_VERSION: ${{ steps.ver.outputs.clean_version }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p meta | |
| { | |
| echo "PRE_VERSION=${VERSION}" | |
| echo "CLEAN_VERSION=${CLEAN_VERSION}" | |
| } > meta/version.env | |
| # Upload artifact for workflow_run trigger consumption | |
| - name: Upload version artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: pre-release-meta | |
| path: meta/version.env | |
| retention-days: 5 | |
| qa-verify: | |
| name: Build QA images (${{ matrix.name }}) | |
| runs-on: ubuntu-latest | |
| needs: publish-pre | |
| if: ${{ needs.publish-pre.result == 'success' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: rag-backend | |
| dockerfile: services/rag-backend/Dockerfile | |
| context: . | |
| - name: admin-backend | |
| dockerfile: services/admin-backend/Dockerfile | |
| context: . | |
| - name: document-extractor | |
| dockerfile: services/document-extractor/Dockerfile | |
| context: . | |
| - name: mcp-server | |
| dockerfile: services/mcp-server/Dockerfile | |
| context: . | |
| - name: frontend | |
| dockerfile: services/frontend/apps/chat-app/Dockerfile | |
| context: . | |
| - name: admin-frontend | |
| dockerfile: services/frontend/apps/admin-app/Dockerfile | |
| context: . | |
| env: | |
| PRE_RELEASE_VERSION: ${{ needs.publish-pre.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Validate QA secrets are present | |
| run: | | |
| missing=() | |
| [ -z "${{ secrets.STACKIT_REGISTRY_USERNAME }}" ] && missing+=("STACKIT_REGISTRY_USERNAME") | |
| [ -z "${{ secrets.STACKIT_REGISTRY_PASSWORD }}" ] && missing+=("STACKIT_REGISTRY_PASSWORD") | |
| [ -z "${{ secrets.QA_IMAGE_REGISTRY }}" ] && echo "QA_IMAGE_REGISTRY not set, defaulting to registry.onstackit.cloud/qa-rag-template" | |
| if [ ${#missing[@]} -gt 0 ]; then | |
| echo "Missing required secrets: ${missing[*]}" >&2 | |
| exit 1 | |
| fi | |
| - name: Set image registry and version | |
| env: | |
| QA_IMAGE_REGISTRY: ${{ secrets.QA_IMAGE_REGISTRY }} | |
| run: | | |
| IMAGE_REGISTRY="${QA_IMAGE_REGISTRY:-registry.onstackit.cloud/qa-rag-template}" | |
| echo "IMAGE_REGISTRY=${IMAGE_REGISTRY}" >> "$GITHUB_ENV" | |
| echo "IMAGE_TAG=${PRE_RELEASE_VERSION}" >> "$GITHUB_ENV" | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to STACKIT registry | |
| env: | |
| STACKIT_REGISTRY_USERNAME: ${{ secrets.STACKIT_REGISTRY_USERNAME }} | |
| STACKIT_REGISTRY_PASSWORD: ${{ secrets.STACKIT_REGISTRY_PASSWORD }} | |
| run: | | |
| echo "$STACKIT_REGISTRY_PASSWORD" | docker login registry.onstackit.cloud -u "$STACKIT_REGISTRY_USERNAME" --password-stdin | |
| - name: Build and push QA image | |
| env: | |
| IMAGE_REGISTRY: ${{ env.IMAGE_REGISTRY }} | |
| IMAGE_TAG: ${{ env.IMAGE_TAG }} | |
| run: | | |
| set -euo pipefail | |
| ref="$IMAGE_REGISTRY/${{ matrix.name }}:$IMAGE_TAG" | |
| echo "Building and pushing $ref (Dockerfile=${{ matrix.dockerfile }} context=${{ matrix.context }})" | |
| docker buildx build --platform linux/amd64 -t "$ref" -f "${{ matrix.dockerfile }}" "${{ matrix.context }}" --push | |
| update-qa-deploy: | |
| name: Update QA deployment repo | |
| runs-on: ubuntu-latest | |
| needs: [publish-pre, qa-verify] | |
| if: ${{ needs.publish-pre.result == 'success' && needs.qa-verify.result == 'success' }} | |
| env: | |
| PRE_RELEASE_VERSION: ${{ needs.publish-pre.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Validate QA secrets are present | |
| run: | | |
| missing=() | |
| [ -z "${{ secrets.DEPLOY_REPO_URL }}" ] && missing+=("DEPLOY_REPO_URL") | |
| [ -z "${{ secrets.DEPLOY_REPO_TOKEN }}" ] && missing+=("DEPLOY_REPO_TOKEN") | |
| if [ ${#missing[@]} -gt 0 ]; then | |
| echo "Missing required secrets: ${missing[*]}" >&2 | |
| exit 1 | |
| fi | |
| - name: Set image registry and version | |
| env: | |
| QA_IMAGE_REGISTRY: ${{ secrets.QA_IMAGE_REGISTRY }} | |
| run: | | |
| IMAGE_REGISTRY="${QA_IMAGE_REGISTRY:-registry.onstackit.cloud/qa-rag-template}" | |
| echo "IMAGE_REGISTRY=${IMAGE_REGISTRY}" >> "$GITHUB_ENV" | |
| echo "IMAGE_TAG=${PRE_RELEASE_VERSION}" >> "$GITHUB_ENV" | |
| - name: Setup Git credentials securely | |
| env: | |
| DEPLOY_REPO_TOKEN: ${{ secrets.DEPLOY_REPO_TOKEN }} | |
| DEPLOY_REPO_URL: ${{ secrets.DEPLOY_REPO_URL }} | |
| run: | | |
| set -euo pipefail | |
| # Extract domain from repo URL | |
| DOMAIN=$(echo "$DEPLOY_REPO_URL" | sed -E 's|https?://([^/]+)/.*|\1|') | |
| # Configure git credential helper to use cache | |
| git config --global credential.helper 'cache --timeout=300' | |
| # Create credential entry for the specific domain | |
| printf "protocol=https\nhost=%s\nusername=git\npassword=%s\n" "$DOMAIN" "$DEPLOY_REPO_TOKEN" | git credential approve | |
| - name: Checkout deployment repo | |
| env: | |
| DEPLOY_REPO_URL: ${{ secrets.DEPLOY_REPO_URL }} | |
| DEPLOY_REPO_BRANCH: ${{ secrets.DEPLOY_REPO_BRANCH || 'main' }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p /tmp/deploy-repo | |
| git clone --depth 1 --branch "${DEPLOY_REPO_BRANCH}" "$DEPLOY_REPO_URL" /tmp/deploy-repo | |
| - name: Update values file in deployment repo | |
| env: | |
| IMAGE_REGISTRY: ${{ env.IMAGE_REGISTRY }} | |
| IMAGE_TAG: ${{ env.IMAGE_TAG }} | |
| DEPLOY_VALUES_FILE: ${{ secrets.DEPLOY_VALUES_FILE || 'values-qa.yaml' }} | |
| run: | | |
| set -euo pipefail | |
| cd /tmp/deploy-repo | |
| python -m pip install --quiet pyyaml | |
| python "$GITHUB_WORKSPACE/tools/update_deploy_values.py" \ | |
| --values-file "$DEPLOY_VALUES_FILE" \ | |
| --image-registry "$IMAGE_REGISTRY" \ | |
| --image-tag "$IMAGE_TAG" | |
| - name: Commit and push deployment repo changes | |
| env: | |
| DEPLOY_REPO_BRANCH: ${{ secrets.DEPLOY_REPO_BRANCH || 'main' }} | |
| DEPLOY_GIT_USER_NAME: ${{ secrets.DEPLOY_GIT_USER_NAME || 'github-actions' }} | |
| DEPLOY_GIT_USER_EMAIL: ${{ secrets.DEPLOY_GIT_USER_EMAIL || 'github-actions@users.noreply.github.com' }} | |
| run: | | |
| set -euo pipefail | |
| cd /tmp/deploy-repo | |
| git config user.name "${DEPLOY_GIT_USER_NAME}" | |
| git config user.email "${DEPLOY_GIT_USER_EMAIL}" | |
| git add . | |
| if git diff --cached --quiet; then | |
| echo "No changes to commit" | |
| exit 0 | |
| fi | |
| git commit -m "chore: update QA images to ${IMAGE_TAG}" | |
| git push origin "HEAD:${DEPLOY_REPO_BRANCH}" | |
| - name: Cleanup credentials | |
| if: always() | |
| run: | | |
| # Clear git credential cache | |
| git credential-cache exit 2>/dev/null || true | |
| git config --global --unset credential.helper || true |