mixpanel-ruby 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d3a5c579488b2cbe1c3b2c6e489070a0f438dca91b8e5f91d863709c517c978
4
- data.tar.gz: '089600f43e3c29ef4f83d129dd1f8f58682ae041b1ffe776fad3ff3036140a5e'
3
+ metadata.gz: 6e8271da642afa8ee7d980b2508db17f84f342f33405bde9dd6d0a15b8a10df0
4
+ data.tar.gz: 2d2da06b34ee942c35286b155bc5942a3ee56d8f17727063ebec16e5eb8d2149
5
5
  SHA512:
6
- metadata.gz: 81511ab17dfe9e72ffeb81a97752d82a581297dc002aa722ee50e2d12a5faa835f7dc198ea1be5b2b62cec7cb117fd8906177b427d8e8e9eddfab21f24c5d930
7
- data.tar.gz: 97bac85d2cb9edd8dc4655f3657a42ff4a1c56fcb0f6fd983da8bb5b44baec1c684a63afd16f5612a02c545e53dd84d07853416f8aab79a0ddd00ac88dfbe2dd
6
+ metadata.gz: 0f8236551ad44db0c9b3afdcefa17083bbca02ff00b6b9a236123857eaf38595070b258c923671b0f0672c76aa6baadefc7d169e02f27ec33cb475b4b9eabb70
7
+ data.tar.gz: 469d8373977e082acc887a8e11d8eeb973190a3488f40d5bb6fa66f4be7cd5c6e83772923e3ee291641f7aa25d8b0dd6d6123541f2e2ed90677c3b34f0e48bba
@@ -0,0 +1,14 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "bundler"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ cooldown:
8
+ default-days: 30
9
+ - package-ecosystem: "github-actions"
10
+ directory: "/"
11
+ schedule:
12
+ interval: "weekly"
13
+ cooldown:
14
+ default-days: 30
@@ -0,0 +1,18 @@
1
+ {
2
+ "analytics": {
3
+ "tag_prefix": "v",
4
+ "gemspec": "mixpanel-ruby.gemspec",
5
+ "version_file": "lib/mixpanel-ruby/version.rb",
6
+ "changelog": "CHANGELOG.md",
7
+ "readme": "Readme.rdoc",
8
+ "package_name": "mixpanel-ruby"
9
+ },
10
+ "openfeature": {
11
+ "tag_prefix": "openfeature/v",
12
+ "gemspec": "openfeature-provider/mixpanel-ruby-openfeature.gemspec",
13
+ "version_file": "openfeature-provider/lib/mixpanel/openfeature/version.rb",
14
+ "changelog": "openfeature-provider/CHANGELOG.md",
15
+ "readme": "openfeature-provider/README.md",
16
+ "package_name": "mixpanel-ruby-openfeature"
17
+ }
18
+ }
@@ -0,0 +1,87 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ MODULE="$1"
5
+ VERSION_LABEL="$2"
6
+ REPO_URL="$3"
7
+ END_REF="${4:-HEAD}"
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
10
+ MODULES_JSON="$SCRIPT_DIR/../modules.json"
11
+
12
+ TAG_PREFIX=$(jq -e -r --arg m "$MODULE" '.[$m].tag_prefix' "$MODULES_JSON") || {
13
+ echo "Unknown module: $MODULE. Valid modules: $(jq -r 'keys | join(", ")' "$MODULES_JSON")" >&2
14
+ exit 1
15
+ }
16
+ TAG_GLOB="${TAG_PREFIX}*"
17
+
18
+ PREVIOUS_TAG=$(git tag --sort=-creatordate --list "$TAG_GLOB" | head -1 || true)
19
+
20
+ if [ -z "$PREVIOUS_TAG" ]; then
21
+ RANGE="$END_REF"
22
+ else
23
+ RANGE="${PREVIOUS_TAG}..${END_REF}"
24
+ fi
25
+
26
+ DATE=$(date +%Y-%m-%d)
27
+ SAFE_URL=$(printf '%s' "$REPO_URL" | sed 's|[&/\]|\\&|g')
28
+
29
+ declare -a FEATURES=()
30
+ declare -a FIXES=()
31
+ declare -a CHORES=()
32
+
33
+ while IFS= read -r line; do
34
+ [ -z "$line" ] && continue
35
+ MSG=$(echo "$line" | cut -d' ' -f2-)
36
+
37
+ # feat / fix: include whether bare, scoped to our module, or scoped to
38
+ # `all` (cross-cutting changes that appear in every module's changelog).
39
+ # chore: include only when explicitly scoped to our module or `all` —
40
+ # bare `chore:` is the convention for changes intentionally hidden from
41
+ # the changelog (release prep PRs, CI tweaks, lockfile bumps, internal
42
+ # docs).
43
+ if [[ "$MSG" =~ ^(feat|fix)(\((${MODULE}|all)\))?:\ (.+) ]]; then
44
+ TYPE="${BASH_REMATCH[1]}"
45
+ DESC="${BASH_REMATCH[4]}"
46
+ DESC=$(echo "$DESC" | sed -E "s|\(#([0-9]+)\)|([#\1](${SAFE_URL}/pull/\1))|g")
47
+ case "$TYPE" in
48
+ feat) FEATURES+=("$DESC") ;;
49
+ fix) FIXES+=("$DESC") ;;
50
+ esac
51
+ elif [[ "$MSG" =~ ^chore\((${MODULE}|all)\):\ (.+) ]]; then
52
+ DESC="${BASH_REMATCH[2]}"
53
+ DESC=$(echo "$DESC" | sed -E "s|\(#([0-9]+)\)|([#\1](${SAFE_URL}/pull/\1))|g")
54
+ CHORES+=("$DESC")
55
+ fi
56
+ done < <(git log --oneline "$RANGE")
57
+
58
+ echo "## [${VERSION_LABEL}](${REPO_URL}/tree/${VERSION_LABEL}) (${DATE})"
59
+ echo ""
60
+
61
+ if [ ${#FEATURES[@]} -gt 0 ]; then
62
+ echo "### Features"
63
+ for entry in "${FEATURES[@]}"; do
64
+ echo "- ${entry}"
65
+ done
66
+ echo ""
67
+ fi
68
+
69
+ if [ ${#FIXES[@]} -gt 0 ]; then
70
+ echo "### Fixes"
71
+ for entry in "${FIXES[@]}"; do
72
+ echo "- ${entry}"
73
+ done
74
+ echo ""
75
+ fi
76
+
77
+ if [ ${#CHORES[@]} -gt 0 ]; then
78
+ echo "### Chores"
79
+ for entry in "${CHORES[@]}"; do
80
+ echo "- ${entry}"
81
+ done
82
+ echo ""
83
+ fi
84
+
85
+ if [ -n "$PREVIOUS_TAG" ]; then
86
+ echo "[Full Changelog](${REPO_URL}/compare/${PREVIOUS_TAG}...${VERSION_LABEL})"
87
+ fi
@@ -0,0 +1,51 @@
1
+ name: PR Title Check
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, edited, synchronize, reopened]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ check-title:
12
+ name: Validate PR title
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Checkout
16
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
17
+ with:
18
+ sparse-checkout: .github/modules.json
19
+ sparse-checkout-cone-mode: false
20
+
21
+ - name: Check PR title format
22
+ env:
23
+ PR_TITLE: ${{ github.event.pull_request.title }}
24
+ run: |
25
+ MODULE_LIST=$(jq -r 'keys | join("|")' .github/modules.json)
26
+ # Scope is optional. Bare, scoped to a known module, or scoped to
27
+ # `all` (cross-cutting changes that appear in every module's
28
+ # changelog) all pass.
29
+ MAIN_PATTERN="^(feat|fix|chore)(\((${MODULE_LIST}|all)\))?: .+"
30
+ RELEASE_PATTERN="^release: .+"
31
+
32
+ if [[ "$PR_TITLE" =~ $MAIN_PATTERN ]] || [[ "$PR_TITLE" =~ $RELEASE_PATTERN ]]; then
33
+ echo "PR title is valid: $PR_TITLE"
34
+ exit 0
35
+ fi
36
+
37
+ echo "PR title does not match the required format."
38
+ echo ""
39
+ echo " Got: $PR_TITLE"
40
+ echo ""
41
+ echo "Expected one of:"
42
+ echo " feat: description"
43
+ echo " fix: description"
44
+ echo " chore: description"
45
+ echo " feat(<module>|all): description"
46
+ echo " fix(<module>|all): description"
47
+ echo " chore(<module>|all): description"
48
+ echo " release: description"
49
+ echo ""
50
+ echo "Valid scopes: ${MODULE_LIST//|/, }, all"
51
+ exit 1
@@ -0,0 +1,189 @@
1
+ name: Prepare Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ module:
7
+ description: 'Module to release (must match a key in .github/modules.json)'
8
+ required: true
9
+ type: string
10
+ version:
11
+ description: 'Release version (e.g., 3.2.0 or 0.2.0)'
12
+ required: true
13
+ type: string
14
+
15
+ permissions:
16
+ contents: write
17
+ pull-requests: write
18
+
19
+ concurrency:
20
+ group: prepare-release-${{ inputs.module }}
21
+ cancel-in-progress: false
22
+
23
+ jobs:
24
+ prepare:
25
+ name: "Prepare ${{ inputs.module }} ${{ inputs.version }}"
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - name: Checkout
29
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
30
+ with:
31
+ fetch-depth: 0
32
+ token: ${{ secrets.GITHUB_TOKEN }}
33
+
34
+ - name: Validate inputs
35
+ env:
36
+ MODULE: ${{ inputs.module }}
37
+ VERSION: ${{ inputs.version }}
38
+ run: |
39
+ if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
40
+ echo "::error::Invalid version format: $VERSION"
41
+ exit 1
42
+ fi
43
+ jq -e --arg m "$MODULE" '.[$m]' .github/modules.json > /dev/null || {
44
+ echo "::error::Unknown module '$MODULE'. Valid modules: $(jq -r 'keys | join(", ")' .github/modules.json)"
45
+ exit 1
46
+ }
47
+
48
+ - name: Resolve module config
49
+ id: config
50
+ env:
51
+ MODULE: ${{ inputs.module }}
52
+ VERSION: ${{ inputs.version }}
53
+ run: |
54
+ MODULE_CONFIG=$(jq -e --arg m "$MODULE" '.[$m]' .github/modules.json)
55
+
56
+ TAG_PREFIX=$(echo "$MODULE_CONFIG" | jq -r '.tag_prefix')
57
+ {
58
+ echo "tag=${TAG_PREFIX}${VERSION}"
59
+ echo "tag_prefix=${TAG_PREFIX}"
60
+ echo "gemspec=$(echo "$MODULE_CONFIG" | jq -r '.gemspec')"
61
+ echo "version_file=$(echo "$MODULE_CONFIG" | jq -r '.version_file')"
62
+ echo "changelog=$(echo "$MODULE_CONFIG" | jq -r '.changelog')"
63
+ echo "readme=$(echo "$MODULE_CONFIG" | jq -r '.readme')"
64
+ echo "package_name=$(echo "$MODULE_CONFIG" | jq -r '.package_name')"
65
+ echo "branch=release/${MODULE}/${VERSION}"
66
+ } >> "$GITHUB_OUTPUT"
67
+
68
+ - name: Validate version not already released
69
+ env:
70
+ TAG: ${{ steps.config.outputs.tag }}
71
+ run: |
72
+ if git ls-remote --exit-code --tags origin "refs/tags/${TAG}" >/dev/null 2>&1; then
73
+ echo "::error::Tag $TAG already exists on origin"
74
+ exit 1
75
+ fi
76
+
77
+ - name: Clean up existing release branch and PR
78
+ env:
79
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
80
+ BRANCH: ${{ steps.config.outputs.branch }}
81
+ run: |
82
+ EXISTING_PR=$(gh pr list --head "$BRANCH" --json number --jq '.[0].number' 2>/dev/null || true)
83
+ if [[ -n "$EXISTING_PR" ]]; then
84
+ echo "Closing existing PR #$EXISTING_PR and deleting branch"
85
+ gh pr close "$EXISTING_PR" --delete-branch
86
+ elif git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
87
+ echo "Deleting orphaned branch $BRANCH"
88
+ git push origin --delete "$BRANCH"
89
+ fi
90
+
91
+ - name: Create release branch
92
+ env:
93
+ BRANCH: ${{ steps.config.outputs.branch }}
94
+ run: git checkout -b "$BRANCH"
95
+
96
+ - name: Bump version in version.rb
97
+ env:
98
+ VERSION: ${{ inputs.version }}
99
+ VERSION_FILE: ${{ steps.config.outputs.version_file }}
100
+ run: |
101
+ sed -i -E "s/VERSION = .*/VERSION = '${VERSION}'/" "$VERSION_FILE"
102
+ echo "Updated $VERSION_FILE:"
103
+ grep -n "VERSION" "$VERSION_FILE" || true
104
+
105
+ - name: Update README version header
106
+ env:
107
+ REPO_URL: ${{ github.server_url }}/${{ github.repository }}
108
+ TAG: ${{ steps.config.outputs.tag }}
109
+ README: ${{ steps.config.outputs.readme }}
110
+ run: |
111
+ DATE=$(date +"%B %d, %Y")
112
+ # Replace the first matching version-header line. The 1,/pat/ address
113
+ # range bounds the substitution so multi-version READMEs aren't
114
+ # trampled. Works for both Markdown and rdoc files (rdoc passes
115
+ # unrecognized lines through as text).
116
+ sed -i -E \
117
+ "1,/^##### _.*_ - \[.*\]\(.*\)\$/ s|^##### _.*_ - \[.*\]\(.*\)\$|##### _${DATE}_ - [${TAG}](${REPO_URL}/releases/tag/${TAG})|" \
118
+ "$README"
119
+
120
+ - name: Generate changelog
121
+ env:
122
+ REPO_URL: ${{ github.server_url }}/${{ github.repository }}
123
+ MODULE: ${{ inputs.module }}
124
+ TAG: ${{ steps.config.outputs.tag }}
125
+ CHANGELOG_FILE: ${{ steps.config.outputs.changelog }}
126
+ run: |
127
+ CHANGELOG=$(.github/scripts/generate-changelog.sh \
128
+ "$MODULE" "$TAG" "$REPO_URL" HEAD)
129
+
130
+ if [ -f "$CHANGELOG_FILE" ]; then
131
+ {
132
+ printf '# Changelog\n\n%s\n' "$CHANGELOG"
133
+ sed '1{/^# Changelog$/d;}' "$CHANGELOG_FILE"
134
+ } > CHANGELOG.new.md
135
+ mv CHANGELOG.new.md "$CHANGELOG_FILE"
136
+ else
137
+ printf '# Changelog\n\n%s\n' "$CHANGELOG" > "$CHANGELOG_FILE"
138
+ fi
139
+
140
+ - name: Commit and push
141
+ env:
142
+ MODULE: ${{ inputs.module }}
143
+ VERSION: ${{ inputs.version }}
144
+ BRANCH: ${{ steps.config.outputs.branch }}
145
+ VERSION_FILE: ${{ steps.config.outputs.version_file }}
146
+ CHANGELOG_FILE: ${{ steps.config.outputs.changelog }}
147
+ README: ${{ steps.config.outputs.readme }}
148
+ run: |
149
+ git config user.name "github-actions[bot]"
150
+ git config user.email "github-actions[bot]@users.noreply.github.com"
151
+ git add "$VERSION_FILE" "$CHANGELOG_FILE" "$README"
152
+ git commit -m "release: prepare ${MODULE} ${VERSION}"
153
+ git push origin "$BRANCH"
154
+
155
+ - name: Create pull request
156
+ env:
157
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
158
+ MODULE: ${{ inputs.module }}
159
+ VERSION: ${{ inputs.version }}
160
+ TAG: ${{ steps.config.outputs.tag }}
161
+ VERSION_FILE: ${{ steps.config.outputs.version_file }}
162
+ CHANGELOG_FILE: ${{ steps.config.outputs.changelog }}
163
+ README: ${{ steps.config.outputs.readme }}
164
+ BRANCH: ${{ steps.config.outputs.branch }}
165
+ run: |
166
+ gh pr create \
167
+ --title "release: prepare ${MODULE} ${VERSION}" \
168
+ --body "$(cat <<EOF
169
+ ## Release ${MODULE} ${VERSION}
170
+
171
+ This PR prepares the ${MODULE} module for release.
172
+
173
+ ### Changes
174
+ - Bumps \`VERSION\` to \`${VERSION}\` in \`${VERSION_FILE}\`
175
+ - Updates \`${CHANGELOG_FILE}\` with a new section since the last \`${TAG%${VERSION}}*\` tag
176
+ - Updates \`${README}\` version header
177
+
178
+ ### After merging
179
+ 1. Push tag \`${TAG}\` from the merge commit on \`master\` to trigger the publish workflow:
180
+ \`\`\`
181
+ git checkout master && git pull
182
+ git tag ${TAG}
183
+ git push origin ${TAG}
184
+ \`\`\`
185
+ 2. The publish workflow creates a draft GitHub release and publishes to RubyGems via OIDC trusted publishing. Review and publish the GitHub release after the workflow finishes.
186
+ EOF
187
+ )" \
188
+ --base master \
189
+ --head "$BRANCH"
@@ -0,0 +1,217 @@
1
+ name: Publish Release to RubyGems
2
+
3
+ # Tag-triggered: RubyGems trusted publishing requires the publish to come from
4
+ # a workflow run triggered by a tag-push matching the pattern configured in
5
+ # the gem's RubyGems "Trusted Publishers" entry. Add new tag prefixes here as
6
+ # additional modules are added to .github/modules.json.
7
+ on:
8
+ push:
9
+ tags:
10
+ - 'v*'
11
+ - 'openfeature/v*'
12
+
13
+ concurrency:
14
+ group: rubygems-publish
15
+ cancel-in-progress: false
16
+
17
+ permissions:
18
+ contents: write # create the draft GitHub release
19
+ id-token: write # OIDC for RubyGems trusted publishing
20
+
21
+ jobs:
22
+ publish:
23
+ name: "Publish ${{ github.ref_name }} to RubyGems"
24
+ runs-on: ubuntu-latest
25
+ environment: release
26
+ timeout-minutes: 30
27
+
28
+ # One-time RubyGems / GitHub Environment / tag ruleset setup is required.
29
+ # The `environment:` name above must match what RubyGems' Trusted Publishers
30
+ # entry is configured to require.
31
+
32
+ steps:
33
+ - name: Checkout tagged commit
34
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
35
+ with:
36
+ fetch-depth: 0
37
+
38
+ - name: Resolve module from tag
39
+ id: module
40
+ env:
41
+ TAG: ${{ github.ref_name }}
42
+ run: |
43
+ MODULE=$(jq -r --arg t "$TAG" '
44
+ to_entries
45
+ | map(select(.value.tag_prefix as $p | $t | startswith($p)))
46
+ | max_by(.value.tag_prefix | length)
47
+ | .key // empty
48
+ ' .github/modules.json)
49
+
50
+ if [ -z "$MODULE" ]; then
51
+ echo "::error::Tag '$TAG' does not match any module tag_prefix in .github/modules.json"
52
+ exit 1
53
+ fi
54
+
55
+ MODULE_CONFIG=$(jq -e --arg m "$MODULE" '.[$m]' .github/modules.json)
56
+ TAG_PREFIX=$(echo "$MODULE_CONFIG" | jq -r '.tag_prefix')
57
+ VERSION="${TAG#${TAG_PREFIX}}"
58
+
59
+ if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
60
+ echo "::error::Version '$VERSION' (from tag '$TAG') is not a valid semver"
61
+ exit 1
62
+ fi
63
+
64
+ {
65
+ echo "module=$MODULE"
66
+ echo "version=$VERSION"
67
+ echo "tag_prefix=$TAG_PREFIX"
68
+ echo "gemspec=$(echo "$MODULE_CONFIG" | jq -r '.gemspec')"
69
+ echo "version_file=$(echo "$MODULE_CONFIG" | jq -r '.version_file')"
70
+ echo "changelog=$(echo "$MODULE_CONFIG" | jq -r '.changelog')"
71
+ echo "package_name=$(echo "$MODULE_CONFIG" | jq -r '.package_name')"
72
+ } >> "$GITHUB_OUTPUT"
73
+
74
+ echo "Resolved tag '$TAG' -> module '$MODULE', version '$VERSION'"
75
+
76
+ - name: Verify tag commit is on master
77
+ env:
78
+ TAG: ${{ github.ref_name }}
79
+ run: |
80
+ git fetch origin master
81
+ TAG_SHA=$(git rev-parse HEAD)
82
+ if ! git merge-base --is-ancestor "$TAG_SHA" origin/master; then
83
+ echo "::error::Tag '$TAG' ($TAG_SHA) is not an ancestor of origin/master."
84
+ echo "Tags must be pushed from a commit on the master branch."
85
+ exit 1
86
+ fi
87
+
88
+ - name: Validate version_file matches tag
89
+ env:
90
+ VERSION: ${{ steps.module.outputs.version }}
91
+ VERSION_FILE: ${{ steps.module.outputs.version_file }}
92
+ run: |
93
+ FILE_VERSION=$(grep -E "^[[:space:]]*VERSION[[:space:]]*=" "$VERSION_FILE" \
94
+ | head -1 \
95
+ | sed -E "s/.*VERSION[[:space:]]*=[[:space:]]*['\"]([^'\"]+)['\"].*/\1/")
96
+ if [ -z "$FILE_VERSION" ]; then
97
+ echo "::error::Could not parse VERSION from $VERSION_FILE"
98
+ exit 1
99
+ fi
100
+ if [ "$VERSION" != "$FILE_VERSION" ]; then
101
+ echo "::error::Tag version '$VERSION' does not match $VERSION_FILE version '$FILE_VERSION'"
102
+ exit 1
103
+ fi
104
+ echo "Version confirmed: $FILE_VERSION"
105
+
106
+ - name: Set up Ruby
107
+ uses: ruby/setup-ruby@d697be2f83c6234b20877c3b5eac7a7f342f0d0c # v1.269.0
108
+ with:
109
+ ruby-version: '3.3'
110
+ bundler-cache: false
111
+
112
+ - name: Install dependencies
113
+ env:
114
+ GEMSPEC: ${{ steps.module.outputs.gemspec }}
115
+ run: |
116
+ GEM_DIR=$(dirname "$GEMSPEC")
117
+ if [ -f "$GEM_DIR/Gemfile" ]; then
118
+ (cd "$GEM_DIR" && bundle install)
119
+ else
120
+ bundle install
121
+ fi
122
+
123
+ - name: Run tests
124
+ env:
125
+ MODULE: ${{ steps.module.outputs.module }}
126
+ GEMSPEC: ${{ steps.module.outputs.gemspec }}
127
+ run: |
128
+ GEM_DIR=$(dirname "$GEMSPEC")
129
+ if [ -f "$GEM_DIR/Gemfile" ] && [ "$GEM_DIR" != "." ]; then
130
+ (cd "$GEM_DIR" && bundle exec rspec)
131
+ else
132
+ bundle exec rake
133
+ fi
134
+
135
+ - name: Build gem
136
+ id: build
137
+ env:
138
+ GEMSPEC: ${{ steps.module.outputs.gemspec }}
139
+ run: |
140
+ GEM_DIR=$(dirname "$GEMSPEC")
141
+ GEMSPEC_BASENAME=$(basename "$GEMSPEC")
142
+ if [ "$GEM_DIR" = "." ]; then
143
+ gem build "$GEMSPEC_BASENAME"
144
+ GEM_FILE=$(ls -t *.gem | head -1)
145
+ echo "gem_path=${GEM_FILE}" >> "$GITHUB_OUTPUT"
146
+ else
147
+ (cd "$GEM_DIR" && gem build "$GEMSPEC_BASENAME")
148
+ GEM_FILE=$(ls -t "$GEM_DIR"/*.gem | head -1)
149
+ echo "gem_path=${GEM_FILE}" >> "$GITHUB_OUTPUT"
150
+ fi
151
+ echo "Built: $(ls -la "${GEM_FILE}")"
152
+
153
+ - name: Extract changelog section for tag
154
+ env:
155
+ TAG: ${{ github.ref_name }}
156
+ CHANGELOG: ${{ steps.module.outputs.changelog }}
157
+ run: |
158
+ # Extract this tag's section from CHANGELOG.md via a sed range.
159
+ # The address `\@^## \[TAG\]@,\@^## \[@` selects from the version
160
+ # header through the next `## [` line; the inner block deletes
161
+ # both markers and prints the body. sed range patterns don't test
162
+ # the end pattern against the start line, so the start doesn't
163
+ # self-terminate. `\@...@` switches the address delimiter from
164
+ # `/` to `@` so tags containing `/` (e.g. `openfeature/v0.1.0`)
165
+ # don't conflict with the default `/`. Mirrors the deployed
166
+ # mixpanel-android extraction logic. Falls back to a placeholder
167
+ # if the section is missing or empty.
168
+ sed -n '\@^## \['"${TAG}"'\]@,\@^## \[@{\@^## \['"${TAG}"'\]@d;\@^## \[@d;p;}' "$CHANGELOG" 2>/dev/null > release_notes.md || true
169
+ if [ ! -s release_notes.md ]; then
170
+ echo "Release $TAG" > release_notes.md
171
+ fi
172
+ echo "--- release_notes.md ---"
173
+ cat release_notes.md
174
+
175
+ - name: Create draft GitHub release
176
+ env:
177
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
178
+ TAG: ${{ github.ref_name }}
179
+ run: |
180
+ # Idempotent: if a draft release for this tag already exists, leave it alone.
181
+ if gh release view "$TAG" >/dev/null 2>&1; then
182
+ echo "GitHub release for $TAG already exists; skipping creation"
183
+ else
184
+ gh release create "$TAG" \
185
+ --draft \
186
+ --title "$TAG" \
187
+ --notes-file release_notes.md
188
+ fi
189
+
190
+ - name: Configure RubyGems trusted publishing credentials
191
+ uses: rubygems/configure-rubygems-credentials@762a4b77c3300434bb57c7ce80b20e36231927aa # v2.0.0
192
+
193
+ # Publish last - RubyGems uploads are effectively irreversible (yank is
194
+ # restricted). The draft GitHub release + `release` environment reviewer
195
+ # acts as the human gate.
196
+ - name: Publish gem to RubyGems
197
+ env:
198
+ GEM_PATH: ${{ steps.build.outputs.gem_path }}
199
+ run: |
200
+ gem push "$GEM_PATH"
201
+
202
+ - name: Summary
203
+ env:
204
+ MODULE: ${{ steps.module.outputs.module }}
205
+ VERSION: ${{ steps.module.outputs.version }}
206
+ PACKAGE_NAME: ${{ steps.module.outputs.package_name }}
207
+ TAG: ${{ github.ref_name }}
208
+ run: |
209
+ {
210
+ echo "## ${MODULE} ${VERSION} published"
211
+ echo ""
212
+ echo "- [RubyGems](https://rubygems.org/gems/${PACKAGE_NAME}/versions/${VERSION})"
213
+ echo "- [Draft GitHub Release](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/tag/${TAG})"
214
+ echo ""
215
+ echo "### Next step"
216
+ echo "Review the draft GitHub release and click **Publish release** to make it live."
217
+ } >> "$GITHUB_STEP_SUMMARY"
data/CHANGELOG.md ADDED
@@ -0,0 +1,30 @@
1
+ # Changelog
2
+
3
+ ## [v3.2.0](https://github.com/mixpanel/mixpanel-ruby/tree/v3.2.0) (2026-07-10)
4
+
5
+ ### Features
6
+ - add service account support ([#152](https://github.com/mixpanel/mixpanel-ruby/pull/152))
7
+
8
+ ### Fixes
9
+ - Make local-flags polling loop shutdown promptly ([#149](https://github.com/mixpanel/mixpanel-ruby/pull/149))
10
+
11
+ [Full Changelog](https://github.com/mixpanel/mixpanel-ruby/compare/v3.1.0...v3.2.0)
12
+
13
+ ## [Unreleased]
14
+
15
+ ### Added
16
+
17
+ - Service account authentication support via `ServiceAccountCredentials` class
18
+ - `Tracker.new()`, `Events.new()`, `Consumer.new()`, and `BufferedConsumer.new()` now accept `credentials:` parameter
19
+ - Credentials are passed to feature flags providers (local and remote) for authenticated API access
20
+ - Authentication validation: `import()` now raises `ArgumentError` when called without either `api_key` or `credentials`
21
+ - Comprehensive test coverage for credential handling and security in `spec/mixpanel-ruby/credentials_spec.rb` and `spec/mixpanel-ruby/credentials_security_spec.rb`
22
+
23
+ ### Changed
24
+
25
+ - When using service account credentials, pass `nil` as the first parameter to `import()`: `tracker.import(nil, distinct_id, event, properties)` instead of passing an API key
26
+ - Credentials are stored as instance variables and used only for HTTP Basic Auth headers, never serialized to JSON payloads
27
+
28
+ ## [v3.1.0](https://github.com/mixpanel/mixpanel-ruby/tree/v3.1.0) (2026-05-13)
29
+
30
+ Initial entry for the standardized release process. See `Readme.rdoc` for prior version history.