structured 0.16.0

Data structures to handle large, structured data.
Documentation
name: Release

on:
  push:
    tags:
      - '[0-9]+.[0-9]+.[0-9]+'  # Matches x.y.z version tags

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v5

      - name: Extract version from tag
        id: version
        run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT

      - name: Extract release notes from CHANGELOG.md
        id: release_notes
        run: |
          # Find the section for this version in CHANGELOG.md
          VERSION="${{ steps.version.outputs.version }}"
          # Look for the version heading (## [x.y.z] or ## x.y.z)
          SECTION_START=$(grep -n "^## \[$VERSION\]\|^## $VERSION" CHANGELOG.md | head -1 | cut -d: -f1)

          if [ -z "$SECTION_START" ]; then
            echo "Could not find version $VERSION in CHANGELOG.md"
            echo "release_notes=Release for version $VERSION" >> $GITHUB_OUTPUT
          else
            # Find the next version section or end of file
            NEXT_SECTION=$(tail -n +$((SECTION_START + 1)) CHANGELOG.md | grep -n "^## " | head -1 | cut -d: -f1)

            if [ -z "$NEXT_SECTION" ]; then
              # No next section, read to end of file, but skip the first line (version header)
              # Write directly to temp file to avoid shell interpretation of backticks
              tail -n +$((SECTION_START + 1)) CHANGELOG.md > /tmp/release_notes_temp.txt
            else
              # Read until next section, but skip the first line (version header)
              # Write directly to temp file to avoid shell interpretation of backticks
              tail -n +$((SECTION_START + 1)) CHANGELOG.md | head -n $((NEXT_SECTION - 2)) > /tmp/release_notes_temp.txt
            fi

            # Clean up the notes (remove empty lines at start/end) directly in the file
            sed -i '/^$/d' /tmp/release_notes_temp.txt
            sed -i -e :a -e '/^\n*$/{$d;N;ba' -e '}' /tmp/release_notes_temp.txt

            # Use base64 encoding to safely pass the content through GitHub Actions
            RELEASE_NOTES_B64=$(base64 -w 0 < /tmp/release_notes_temp.txt)
            echo "release_notes_b64=$RELEASE_NOTES_B64" >> $GITHUB_OUTPUT
          fi

      - name: Create Release
        run: |
          # Decode the base64-encoded release notes
          echo "${{ steps.release_notes.outputs.release_notes_b64 }}" | base64 -d > release_notes.txt
          gh release create ${{ steps.version.outputs.version }} \
            --title "${{ steps.version.outputs.version }}" \
            --notes-file release_notes.txt \
            --repo ${{ github.repository }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}