kitcode 0.1.14

KitCode Agent is an AI agent built in Rust.
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json

name: Release

on:
  push:
    branches:
      - main

env:
  NPM_SCOPE: "@taitide"
  HOMEBREW_TAP: "taitide-labs/homebrew-tap"

permissions: {}

jobs:
  release-please:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    outputs:
      release_created: ${{ steps.release.outputs.release_created }}
      tag_name: ${{ steps.release.outputs.tag_name }}
    steps:
      - uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
        id: app-token
        with:
          client-id: ${{ secrets.RELEASE_APP_CLIENT_ID }}
          private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
          permission-contents: write
          permission-pull-requests: write
      - uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
        id: release
        with:
          token: ${{ steps.app-token.outputs.token }}

  build:
    needs: release-please
    if: ${{ needs.release-please.outputs.release_created == 'true' }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: aarch64-apple-darwin
            runner: macos-latest
            ext: tar.gz
          - target: x86_64-apple-darwin
            runner: macos-latest
            ext: tar.gz
          - target: x86_64-unknown-linux-gnu
            runner: ubuntu-latest
            ext: tar.gz
          - target: aarch64-unknown-linux-gnu
            runner: ubuntu-latest
            ext: tar.gz
            cross_linux_arm64: true
          - target: x86_64-pc-windows-msvc
            runner: windows-latest
            ext: zip
    runs-on: ${{ matrix.runner }}
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1
        with:
          toolchain: stable
          targets: ${{ matrix.target }}
      - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3
        with:
          key: ${{ matrix.target }}
      - name: Install aarch64 Linux cross-linker
        if: ${{ matrix.cross_linux_arm64 }}
        run: |
          sudo apt-get update
          sudo apt-get install -y gcc-aarch64-linux-gnu
      - name: Run cross-platform tests
        if: ${{ !matrix.cross_linux_arm64 }}
        shell: bash
        env:
          TARGET: ${{ matrix.target }}
        run: cargo test --locked --target "$TARGET"
      - name: Build binary
        shell: bash
        env:
          TARGET: ${{ matrix.target }}
          CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
        run: cargo build --release --locked --target "$TARGET"
      - name: Package artifact
        shell: bash
        env:
          TARGET: ${{ matrix.target }}
          EXT: ${{ matrix.ext }}
        run: |
          meta=$(cargo metadata --no-deps --format-version 1)
          pkg_name=$(echo "$meta" | jq -r '.packages[0].name')
          bin_name=$(echo "$meta" | jq -r '(.packages[0].targets[] | select(.kind | index("bin")) | .name)' | head -n 1)
          bin_name="${bin_name:-$pkg_name}"
          mkdir -p dist
          if [ "$EXT" = "zip" ]; then
            7z a "dist/${pkg_name}-${TARGET}.zip" "./target/${TARGET}/release/${bin_name}.exe"
          else
            tar -C "target/${TARGET}/release" -czf "dist/${pkg_name}-${TARGET}.tar.gz" "$bin_name"
          fi
      - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: dist-${{ matrix.target }}
          path: dist/*

  upload-assets:
    needs: [release-please, build]
    runs-on: ubuntu-latest
    permissions:
      contents: write
      id-token: write
      attestations: write
    steps:
      - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
        with:
          pattern: dist-*
          path: artifacts
          merge-multiple: true
      - name: Generate checksums
        run: |
          cd artifacts
          sha256sum *.tar.gz *.zip > SHA256SUMS.txt
      - name: Attest build provenance
        uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
        with:
          subject-path: |
            artifacts/*.tar.gz
            artifacts/*.zip
      - name: Upload release assets and publish
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GH_REPO: ${{ github.repository }}
          TAG: ${{ needs.release-please.outputs.tag_name }}
        run: |
          gh release upload "$TAG" artifacts/* --clobber
          gh release edit "$TAG" --draft=false

  publish-crates:
    needs: [release-please, upload-assets]
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - uses: rust-lang/crates-io-auth-action@3268eb413133e2960fa4ae0e963ca76380c54cd9 # v1.0.6
        id: auth
      - name: Publish to crates.io
        env:
          CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
        run: cargo publish --locked

  publish-npm:
    needs: [release-please, upload-assets]
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
        with:
          node-version: "lts/*"
          registry-url: "https://registry.npmjs.org"
          package-manager-cache: false
      - name: Build and publish npm wrapper
        env:
          TAG: ${{ needs.release-please.outputs.tag_name }}
          REPO: ${{ github.repository }}
        run: |
          version="${TAG#v}"
          meta=$(cargo metadata --no-deps --format-version 1)
          pkg_name=$(echo "$meta" | jq -r '.packages[0].name')
          pkg_desc=$(echo "$meta" | jq -r '.packages[0].description')
          pkg_license=$(echo "$meta" | jq -r '.packages[0].license')
          pkg_repo=$(echo "$meta" | jq -r '.packages[0].repository')

          mkdir -p npm-pkg/bin
          jq -n \
            --arg name "${NPM_SCOPE}/${pkg_name}" \
            --arg version "$version" \
            --arg desc "$pkg_desc" \
            --arg license "$pkg_license" \
            --arg repo "${pkg_repo}.git" \
            --arg bin "$pkg_name" \
            '{
              name: $name,
              version: $version,
              description: $desc,
              license: $license,
              repository: { type: "git", url: $repo },
              bin: { ($bin): ("bin/" + $bin + ".js") },
              scripts: { postinstall: "node install.js" }
            }' > npm-pkg/package.json

          cat > "npm-pkg/bin/${pkg_name}.js" <<EOF
          #!/usr/bin/env node
          const { spawnSync } = require("child_process");
          const path = require("path");
          const bin = path.join(__dirname, process.platform === "win32" ? "${pkg_name}.exe" : "${pkg_name}");
          const res = spawnSync(bin, process.argv.slice(2), { stdio: "inherit" });
          process.exit(res.status ?? 1);
          EOF

          cat > npm-pkg/install.js <<EOF
          const https = require("https");
          const fs = require("fs");
          const path = require("path");
          const { execSync } = require("child_process");
          const pkg = require("./package.json");
          const map = {
            "darwin-arm64": "aarch64-apple-darwin.tar.gz",
            "darwin-x64": "x86_64-apple-darwin.tar.gz",
            "linux-x64": "x86_64-unknown-linux-gnu.tar.gz",
            "linux-arm64": "aarch64-unknown-linux-gnu.tar.gz",
            "win32-x64": "x86_64-pc-windows-msvc.zip"
          };
          const suffix = map[\`\${process.platform}-\${process.arch}\`];
          if (!suffix) throw new Error(\`Unsupported platform: \${process.platform}-\${process.arch}\`);
          const url = \`https://github.com/${REPO}/releases/download/v\${pkg.version}/${pkg_name}-\${suffix}\`;
          const out = path.join(__dirname, "bin", suffix);
          function download(u, cb) {
            https.get(u, (res) => {
              if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
                return download(res.headers.location, cb);
              }
              if (res.statusCode !== 200) return cb(new Error(\`HTTP \${res.statusCode}\`));
              const file = fs.createWriteStream(out);
              res.pipe(file).on("finish", () => file.close(cb));
            }).on("error", cb);
          }
          download(url, (err) => {
            if (err) throw err;
            if (suffix.endsWith(".zip")) {
              execSync(\`powershell -Command "Expand-Archive -Force '\${out}' '\${path.join(__dirname, "bin")}'"\`);
            } else {
              execSync(\`tar -xzf "\${out}" -C "\${path.join(__dirname, "bin")}"\`);
            }
            fs.unlinkSync(out);
          });
          EOF

          npm publish --provenance --access public ./npm-pkg

  publish-homebrew:
    needs: [release-please, upload-assets]
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
        with:
          pattern: dist-*
          path: artifacts
          merge-multiple: true
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          repository: ${{ env.HOMEBREW_TAP }}
          token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
          path: tap
          persist-credentials: false
      - name: Update Homebrew formula
        env:
          TAG: ${{ needs.release-please.outputs.tag_name }}
          REPO: ${{ github.repository }}
          TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
        run: |
          version="${TAG#v}"
          meta=$(cargo metadata --no-deps --format-version 1)
          pkg_name=$(echo "$meta" | jq -r '.packages[0].name')
          pkg_desc=$(echo "$meta" | jq -r '.packages[0].description')
          pkg_license=$(echo "$meta" | jq -r '.packages[0].license')
          pkg_home=$(echo "$meta" | jq -r '.packages[0].homepage')
          formula_class=$(echo "$pkg_name" | awk -F'[-_]' '{for(i=1;i<=NF;i++) printf toupper(substr($i,1,1)) substr($i,2)}')

          sha_mac_arm=$(sha256sum "artifacts/${pkg_name}-aarch64-apple-darwin.tar.gz" | awk '{print $1}')
          sha_mac_x64=$(sha256sum "artifacts/${pkg_name}-x86_64-apple-darwin.tar.gz" | awk '{print $1}')
          sha_lin_x64=$(sha256sum "artifacts/${pkg_name}-x86_64-unknown-linux-gnu.tar.gz" | awk '{print $1}')
          sha_lin_arm=$(sha256sum "artifacts/${pkg_name}-aarch64-unknown-linux-gnu.tar.gz" | awk '{print $1}')

          mkdir -p tap/Formula
          cat > "tap/Formula/${pkg_name}.rb" <<EOF
          class ${formula_class} < Formula
            desc "${pkg_desc}"
            homepage "${pkg_home}"
            version "${version}"
            license "${pkg_license}"

            on_macos do
              on_arm do
                url "https://github.com/${REPO}/releases/download/${TAG}/${pkg_name}-aarch64-apple-darwin.tar.gz"
                sha256 "${sha_mac_arm}"
              end
              on_intel do
                url "https://github.com/${REPO}/releases/download/${TAG}/${pkg_name}-x86_64-apple-darwin.tar.gz"
                sha256 "${sha_mac_x64}"
              end
            end

            on_linux do
              on_arm do
                url "https://github.com/${REPO}/releases/download/${TAG}/${pkg_name}-aarch64-unknown-linux-gnu.tar.gz"
                sha256 "${sha_lin_arm}"
              end
              on_intel do
                url "https://github.com/${REPO}/releases/download/${TAG}/${pkg_name}-x86_64-unknown-linux-gnu.tar.gz"
                sha256 "${sha_lin_x64}"
              end
            end

            def install
              bin.install "${pkg_name}"
            end

            test do
              assert_match version.to_s, shell_output("#{bin}/${pkg_name} --version")
            end
          end
          EOF

          cd tap
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add "Formula/${pkg_name}.rb"
          git commit -m "${pkg_name} ${version}"
          git push "https://x-access-token:${TAP_TOKEN}@github.com/${HOMEBREW_TAP}.git" HEAD