name: Release on: push: tags: - 'v*' jobs: release-python-packages: runs-on: ubuntu-latest environment: pypi permissions: id-token: write steps: - uses: actions/checkout@v6 - name: Install uv uses: astral-sh/setup-uv@v7 with: enable-cache: true - name: Set up Python uses: actions/setup-python@v6 with: python-version-file: ".python-version" # Build all packages - name: Build hindsight-client working-directory: ./hindsight-clients/python run: uv build --out-dir dist - name: Build hindsight-api-slim working-directory: ./hindsight-api-slim run: uv build --out-dir dist - name: Build hindsight-api working-directory: ./hindsight-api run: uv build --out-dir dist - name: Build hindsight-all working-directory: ./hindsight-all run: uv build --out-dir dist - name: Build hindsight-all-slim working-directory: ./hindsight-all-slim run: uv build --out-dir dist - name: Build hindsight-embed working-directory: ./hindsight-embed run: uv build --out-dir dist # Publish in order (client and api-slim first, then api/all wrappers which depend on them) - name: Publish hindsight-client to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: packages-dir: ./hindsight-clients/python/dist skip-existing: true - name: Publish hindsight-api-slim to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: packages-dir: ./hindsight-api-slim/dist skip-existing: true - name: Publish hindsight-api to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: packages-dir: ./hindsight-api/dist skip-existing: true - name: Publish hindsight-all to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: packages-dir: ./hindsight-all/dist skip-existing: true - name: Publish hindsight-all-slim to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: packages-dir: ./hindsight-all-slim/dist skip-existing: true - name: Publish hindsight-embed to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: packages-dir: ./hindsight-embed/dist skip-existing: true # Upload artifacts for GitHub release - name: Upload artifacts uses: actions/upload-artifact@v7 with: name: python-packages path: | hindsight-clients/python/dist/* hindsight-api-slim/dist/* hindsight-api/dist/* hindsight-all/dist/* hindsight-all-slim/dist/* hindsight-embed/dist/* retention-days: 1 release-typescript-client: runs-on: ubuntu-latest environment: npm steps: - uses: actions/checkout@v6 - name: Set up Node.js uses: actions/setup-node@v6 with: node-version: '20' registry-url: 'https://registry.npmjs.org' cache: 'npm' cache-dependency-path: package-lock.json - name: Install dependencies run: npm ci --workspace=hindsight-clients/typescript - name: Build run: npm run build --workspace=hindsight-clients/typescript - name: Publish to npm working-directory: ./hindsight-clients/typescript run: | set +e OUTPUT=$(npm publish --access public 2>&1) EXIT_CODE=$? echo "$OUTPUT" if [ $EXIT_CODE -ne 0 ]; then if echo "$OUTPUT" | grep -q "cannot publish over"; then echo "Package version already published, skipping..." exit 0 fi exit $EXIT_CODE fi env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Pack for GitHub release working-directory: ./hindsight-clients/typescript run: npm pack - name: Upload artifacts uses: actions/upload-artifact@v7 with: name: typescript-client path: hindsight-clients/typescript/*.tgz retention-days: 1 release-control-plane: runs-on: ubuntu-latest environment: npm steps: - uses: actions/checkout@v6 - name: Set up Node.js uses: actions/setup-node@v6 with: node-version: '20' registry-url: 'https://registry.npmjs.org' cache: 'npm' cache-dependency-path: package-lock.json - name: Install dependencies run: npm ci - name: Build TypeScript client (dependency) run: npm run build --workspace=hindsight-clients/typescript - name: Fix platform-specific native modules run: | # npm ci installs from lockfile which may have wrong platform binaries # Delete hoisted native modules and reinstall for current platform rm -rf node_modules/lightningcss node_modules/@tailwindcss npm install lightningcss @tailwindcss/postcss @tailwindcss/node - name: Build run: npm run build --workspace=hindsight-control-plane - name: Verify standalone build run: test -f hindsight-control-plane/standalone/server.js || (echo 'standalone/server.js missing - build failed' && exit 1) - name: Publish to npm working-directory: ./hindsight-control-plane run: | set +e OUTPUT=$(npm publish --access public --ignore-scripts 2>&1) EXIT_CODE=$? echo "$OUTPUT" if [ $EXIT_CODE -ne 0 ]; then if echo "$OUTPUT" | grep -q "cannot publish over"; then echo "Package version already published, skipping..." exit 0 fi exit $EXIT_CODE fi env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Pack for GitHub release working-directory: ./hindsight-control-plane run: npm pack - name: Upload artifacts uses: actions/upload-artifact@v7 with: name: control-plane path: hindsight-control-plane/*.tgz retention-days: 1 release-rust-cli: runs-on: ${{ matrix.os }} strategy: matrix: include: - os: ubuntu-latest target: x86_64-unknown-linux-gnu artifact_name: hindsight asset_name: hindsight-linux-amd64 - os: macos-latest target: x86_64-apple-darwin artifact_name: hindsight asset_name: hindsight-darwin-amd64 - os: macos-latest target: aarch64-apple-darwin artifact_name: hindsight asset_name: hindsight-darwin-arm64 - os: ubuntu-24.04-arm target: aarch64-unknown-linux-gnu artifact_name: hindsight asset_name: hindsight-linux-arm64 steps: - uses: actions/checkout@v6 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.target }} - name: Build working-directory: hindsight-cli run: cargo build --release --target ${{ matrix.target }} - name: Prepare artifact run: | mkdir -p artifacts cp hindsight-cli/target/${{ matrix.target }}/release/${{ matrix.artifact_name }} artifacts/${{ matrix.asset_name }} chmod +x artifacts/${{ matrix.asset_name }} - name: Upload artifacts uses: actions/upload-artifact@v7 with: name: rust-cli-${{ matrix.asset_name }} path: artifacts/${{ matrix.asset_name }} retention-days: 1 release-docker-images: name: Release Docker (${{ matrix.image_name }}${{ matrix.tag_suffix }}) runs-on: ubuntu-latest permissions: contents: read packages: write strategy: matrix: include: - target: api-only image_name: hindsight-api tag_suffix: "" build_args: "" - target: api-only image_name: hindsight-api tag_suffix: "-slim" build_args: | INCLUDE_LOCAL_MODELS=false PRELOAD_ML_MODELS=false - target: cp-only image_name: hindsight-control-plane tag_suffix: "" build_args: "" - target: standalone image_name: hindsight tag_suffix: "" build_args: "" - target: standalone image_name: hindsight tag_suffix: "-slim" build_args: | INCLUDE_LOCAL_MODELS=false PRELOAD_ML_MODELS=false steps: - uses: actions/checkout@v6 - name: Free Disk Space uses: jlumbroso/free-disk-space@main with: tool-cache: true android: true dotnet: true haskell: true large-packages: true docker-images: true swap-storage: true - name: Set up QEMU uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Log in to GitHub Container Registry uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract version from tag id: get_version run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT - name: Extract metadata for release tags id: meta uses: docker/metadata-action@v6 with: images: ghcr.io/${{ github.repository_owner }}/${{ matrix.image_name }} flavor: | latest=auto suffix=${{ matrix.tag_suffix }} tags: | type=semver,pattern={{version}},value=${{ steps.get_version.outputs.VERSION }} type=semver,pattern={{major}}.{{minor}},value=${{ steps.get_version.outputs.VERSION }} type=semver,pattern={{major}},value=${{ steps.get_version.outputs.VERSION }} type=raw,value=latest # TODO: Re-enable smoke test when disk space issue is resolved # # Step 1: Build for local testing (single platform, no push) # # This creates an identical image to what will be released, just for one platform # - name: Build image for testing # uses: docker/build-push-action@v7 # with: # context: . # file: docker/standalone/Dockerfile # target: ${{ matrix.target }} # push: false # load: true # tags: ${{ matrix.image_name }}:test # cache-from: type=gha # cache-to: type=gha,mode=max # # Step 2: Test the image before pushing anything # - name: Smoke test - verify container starts # env: # GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} # run: ./docker/test-image.sh "${{ matrix.image_name }}:test" "${{ matrix.target }}" # Build multi-platform and push to release tags - name: Build and push release images uses: docker/build-push-action@v7 with: context: . file: docker/standalone/Dockerfile target: ${{ matrix.target }} build-args: ${{ matrix.build_args }} push: true platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} release-helm-chart: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v6 - name: Install Helm uses: azure/setup-helm@v5 with: version: 'latest' - name: Log in to GHCR run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin - name: Lint Helm chart run: helm lint helm/hindsight - name: Package Helm chart run: helm package helm/hindsight --destination ./helm-packages - name: Push to GHCR OCI run: helm push helm-packages/*.tgz oci://ghcr.io/${{ github.repository_owner }}/charts - name: Upload artifacts uses: actions/upload-artifact@v7 with: name: helm-chart path: helm-packages/*.tgz retention-days: 1 create-github-release: runs-on: ubuntu-latest needs: [release-python-packages, release-typescript-client, release-control-plane, release-rust-cli, release-docker-images, release-helm-chart] permissions: contents: write steps: - uses: actions/checkout@v6 - name: Extract version from tag id: get_version run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT - name: Download Python packages uses: actions/download-artifact@v8 with: name: python-packages path: ./artifacts/python-packages - name: Download TypeScript client uses: actions/download-artifact@v8 with: name: typescript-client path: ./artifacts/typescript-client - name: Download Control Plane uses: actions/download-artifact@v8 with: name: control-plane path: ./artifacts/control-plane - name: Download Rust CLI (Linux) uses: actions/download-artifact@v8 with: name: rust-cli-hindsight-linux-amd64 path: ./artifacts/rust-cli-linux - name: Download Rust CLI (macOS Intel) uses: actions/download-artifact@v8 with: name: rust-cli-hindsight-darwin-amd64 path: ./artifacts/rust-cli-darwin-amd64 - name: Download Rust CLI (macOS ARM) uses: actions/download-artifact@v8 with: name: rust-cli-hindsight-darwin-arm64 path: ./artifacts/rust-cli-darwin-arm64 - name: Download Helm chart uses: actions/download-artifact@v8 with: name: helm-chart path: ./artifacts/helm-chart - name: Prepare release assets run: | mkdir -p release-assets # Python packages cp artifacts/python-packages/hindsight-clients/python/dist/* release-assets/ || true cp artifacts/python-packages/hindsight-api-slim/dist/* release-assets/ || true cp artifacts/python-packages/hindsight-api/dist/* release-assets/ || true cp artifacts/python-packages/hindsight-all/dist/* release-assets/ || true cp artifacts/python-packages/hindsight-all-slim/dist/* release-assets/ || true cp artifacts/python-packages/hindsight-embed/dist/* release-assets/ || true # TypeScript client cp artifacts/typescript-client/*.tgz release-assets/ || true # Control Plane cp artifacts/control-plane/*.tgz release-assets/ || true # Rust CLI binaries cp artifacts/rust-cli-linux/hindsight-linux-amd64 release-assets/ || true cp artifacts/rust-cli-darwin-amd64/hindsight-darwin-amd64 release-assets/ || true cp artifacts/rust-cli-darwin-arm64/hindsight-darwin-arm64 release-assets/ || true # Helm chart cp artifacts/helm-chart/*.tgz release-assets/ || true ls -la release-assets/ - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: files: release-assets/* generate_release_notes: true draft: false prerelease: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}