Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Style/RbsInline/MissingTypeAnnotation:

### RBS Signature Generation

**DO NOT** manually edit files in `sig/` directory. These files are auto-generated from inline annotations.
**DO NOT** manually edit files in the `sig/` directory of each gem (`structured_params/sig/`, `structured_params-openapi/sig/`). These files are auto-generated from inline annotations.

To generate RBS signature files:

Expand Down Expand Up @@ -107,5 +107,5 @@ This gem supports both Strong Parameters validation and Form Object pattern:

- Use RSpec for testing
- Group tests by context (e.g., "API context", "Form Object context")
- Test files are in `spec/` directory
- Support files (test helper classes) are in `spec/support/`
- Test files are in each gem's `spec/` directory (`structured_params/spec/`, `structured_params-openapi/spec/`)
- Support files (test helper classes) are in `structured_params/spec/support/`
149 changes: 149 additions & 0 deletions .github/workflows/release-openapi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
name: Release structured_params-openapi

# Publishes the structured_params-openapi gem when a GitHub Release tagged openapi-vX.Y.Z is
# published. The core gem is released by release.yml from vX.Y.Z tags.
on:
release:
types: [ published ]

# Both release workflows commit to main, so never run two at once.
concurrency:
group: release-main
cancel-in-progress: false

jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
# Pre-releases are not published to RubyGems by this workflow.
if: startsWith(github.event.release.tag_name, 'openapi-v') && !github.event.release.prerelease

steps:
- name: Checkout code
uses: actions/checkout@v5
with:
ref: main
fetch-depth: 0
Comment thread
Syati marked this conversation as resolved.

- name: Configure Git
uses: ./.github/actions/configure-git

- name: Extract and validate the version
id: extract_version
env:
TAG_NAME: ${{ github.event.release.tag_name }}
run: |
VERSION=${TAG_NAME#openapi-v}
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Release tag '$TAG_NAME' must look like openapi-vX.Y.Z"
exit 1
fi

# The tagged commit must be on main, and the version must not go backwards
# (re-running an old release must not downgrade version.rb).
if ! git merge-base --is-ancestor "$GITHUB_SHA" HEAD; then
echo "::error::The tagged commit is not on main"
exit 1
fi
CURRENT=$(sed -n "s/.*VERSION = '\([^']*\)'.*/\1/p" structured_params-openapi/lib/structured_params/openapi/version.rb)
if [ "$(printf '%s\n%s\n' "$CURRENT" "$VERSION" | sort -V | tail -n 1)" != "$VERSION" ]; then
echo "::error::Version $VERSION is older than the current $CURRENT"
exit 1
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT

- name: Update version file
env:
VERSION: ${{ steps.extract_version.outputs.version }}
run: |
sed -i "s/VERSION = '[^']*'/VERSION = '$VERSION'/" structured_params-openapi/lib/structured_params/openapi/version.rb
grep -qF "VERSION = '$VERSION'" structured_params-openapi/lib/structured_params/openapi/version.rb || { echo "::error::VERSION was not updated"; exit 1; }

- name: Update CHANGELOG
env:
VERSION: ${{ steps.extract_version.outputs.version }}
TAG_NAME: ${{ github.event.release.tag_name }}
RELEASE_NOTES: ${{ github.event.release.body }}
run: |
CHANGELOG=structured_params-openapi/CHANGELOG.md
# A re-run after a failed publish must not add the entry twice.
if grep -q "^## \[$VERSION\]" "$CHANGELOG"; then
echo "CHANGELOG already has $VERSION"
exit 0
fi
CURRENT_DATE=$(date +%Y-%m-%d)
NOTES=$(printf '%s' "$RELEASE_NOTES" | tr -d '\r')

# Create temporary changelog entry
echo "## [$VERSION] - $CURRENT_DATE" > temp_changelog.md
Comment thread
Syati marked this conversation as resolved.
echo "" >> temp_changelog.md

# Add release notes from GitHub release
if [ -n "$NOTES" ]; then
printf '%s\n' "$NOTES" >> temp_changelog.md
else
echo "- Release $TAG_NAME" >> temp_changelog.md
fi
echo "" >> temp_changelog.md

# Prepend to existing CHANGELOG (skip the header)
if [ -f "$CHANGELOG" ]; then
tail -n +3 "$CHANGELOG" >> temp_changelog.md
fi

# Add header back
echo "# Changelog" > "$CHANGELOG"
echo "" >> "$CHANGELOG"
cat temp_changelog.md >> "$CHANGELOG"

rm -f temp_changelog.md

# Committed here, pushed only after the gem has been built, so a failure
# before that point leaves main untouched.
- name: Commit version and CHANGELOG
env:
VERSION: ${{ steps.extract_version.outputs.version }}
run: |
git add structured_params-openapi/lib/structured_params/openapi/version.rb structured_params-openapi/CHANGELOG.md
if git diff --staged --quiet; then
echo "No changes to commit"
else
git commit -m "Update structured_params-openapi to $VERSION"
fi

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.4'
bundler-cache: true

- name: Build gem
working-directory: structured_params-openapi
env:
VERSION: ${{ steps.extract_version.outputs.version }}
run: |
gem build structured_params-openapi.gemspec
test -f "structured_params-openapi-$VERSION.gem"

- name: Push the version and CHANGELOG to main
run: git push origin main

# The Release created its tag before the version commit. Point the tag at the commit that is
# built and published, so the tag reproduces the gem. `rake release` then finds the tag and
# skips tagging. Done before publishing so a refused tag push fails the job with nothing released.
- name: Point the release tag at the published commit
env:
TAG_NAME: ${{ github.event.release.tag_name }}
run: |
git tag -f "$TAG_NAME" HEAD
git push --force origin "refs/tags/$TAG_NAME"

# `rake release` (see structured_params-openapi/Rakefile) tags releases openapi-vX.Y.Z. The tag exists (it was moved
# above), so only the build and the RubyGems push happen here. That relies on the tag being
# exactly openapi-v plus the VERSION written above, which the validation guarantees.
- name: Publish to RubyGems
uses: rubygems/release-gem@v1
with:
working-directory: structured_params-openapi
120 changes: 87 additions & 33 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
name: Release and Publish

# Publishes the structured_params gem when a GitHub Release tagged vX.Y.Z is published.
# The structured_params-openapi gem is released by release-openapi.yml from openapi-vX.Y.Z tags.
on:
release:
types: [ published ]
workflow_run:
workflows: ["Update Version"]
types: [completed]
branches: [main]

# Both release workflows commit to main, so never run two at once.
concurrency:
group: release-main
cancel-in-progress: false

jobs:
update-changelog-and-publish:
release:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
# Only run if the workflow_run was successful or if triggered by release event
if: |
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') ||
(github.event_name == 'release')
# Pre-releases are not published to RubyGems by this workflow.
if: startsWith(github.event.release.tag_name, 'v') && !github.event.release.prerelease

steps:
- name: Checkout code
Expand All @@ -29,53 +30,87 @@ jobs:
- name: Configure Git
uses: ./.github/actions/configure-git

- name: Extract version from release
- name: Extract and validate the version
id: extract_version
env:
TAG_NAME: ${{ github.event.release.tag_name }}
run: |
TAG_NAME=${{ github.event.release.tag_name }}
VERSION=${TAG_NAME#v}
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Release tag '$TAG_NAME' must look like vX.Y.Z"
exit 1
fi

# The tagged commit must be on main, and the version must not go backwards
# (re-running an old release must not downgrade version.rb).
if ! git merge-base --is-ancestor "$GITHUB_SHA" HEAD; then
echo "::error::The tagged commit is not on main"
exit 1
fi
CURRENT=$(sed -n "s/.*VERSION = '\([^']*\)'.*/\1/p" structured_params/lib/structured_params/version.rb)
if [ "$(printf '%s\n%s\n' "$CURRENT" "$VERSION" | sort -V | tail -n 1)" != "$VERSION" ]; then
echo "::error::Version $VERSION is older than the current $CURRENT"
exit 1
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT

- name: Update version file
env:
VERSION: ${{ steps.extract_version.outputs.version }}
run: |
sed -i "s/VERSION = '[^']*'/VERSION = '$VERSION'/" structured_params/lib/structured_params/version.rb
grep -qF "VERSION = '$VERSION'" structured_params/lib/structured_params/version.rb || { echo "::error::VERSION was not updated"; exit 1; }

- name: Update CHANGELOG
env:
VERSION: ${{ steps.extract_version.outputs.version }}
TAG_NAME: ${{ github.event.release.tag_name }}
RELEASE_NOTES: ${{ github.event.release.body }}
run: |
VERSION="${{ steps.extract_version.outputs.version }}"
CHANGELOG=structured_params/CHANGELOG.md
# A re-run after a failed publish must not add the entry twice.
if grep -q "^## \[$VERSION\]" "$CHANGELOG"; then
echo "CHANGELOG already has $VERSION"
exit 0
fi
CURRENT_DATE=$(date +%Y-%m-%d)
RELEASE_NOTES="${{ github.event.release.body }}"
NOTES=$(printf '%s' "$RELEASE_NOTES" | tr -d '\r')

# Create temporary changelog entry
echo "## [$VERSION] - $CURRENT_DATE" > temp_changelog.md
echo "" >> temp_changelog.md

# Add release notes from GitHub release
if [ -n "$RELEASE_NOTES" ]; then
echo "$RELEASE_NOTES" >> temp_changelog.md
if [ -n "$NOTES" ]; then
printf '%s\n' "$NOTES" >> temp_changelog.md
else
echo "- Release ${{ steps.extract_version.outputs.tag_name }}" >> temp_changelog.md
echo "- Release $TAG_NAME" >> temp_changelog.md
fi
echo "" >> temp_changelog.md

# Prepend to existing CHANGELOG (skip the header)
if [ -f structured_params/CHANGELOG.md ]; then
tail -n +3 structured_params/CHANGELOG.md >> temp_changelog.md
if [ -f "$CHANGELOG" ]; then
tail -n +3 "$CHANGELOG" >> temp_changelog.md
fi

# Add header back
echo "# Changelog" > structured_params/CHANGELOG.md
echo "" >> structured_params/CHANGELOG.md
cat temp_changelog.md >> structured_params/CHANGELOG.md

# Clean up temporary file
echo "# Changelog" > "$CHANGELOG"
echo "" >> "$CHANGELOG"
cat temp_changelog.md >> "$CHANGELOG"

rm -f temp_changelog.md

- name: Commit CHANGELOG update
# Committed here, pushed only after the gem has been built, so a failure
# before that point leaves main untouched.
- name: Commit version and CHANGELOG
env:
VERSION: ${{ steps.extract_version.outputs.version }}
run: |
git add structured_params/CHANGELOG.md
git add structured_params/lib/structured_params/version.rb structured_params/CHANGELOG.md
if git diff --staged --quiet; then
echo "No CHANGELOG changes to commit"
echo "No changes to commit"
else
git commit -m "Update CHANGELOG for ${{ steps.extract_version.outputs.tag_name }}"
git push origin main
git commit -m "Update structured_params to $VERSION"
fi

- name: Set up Ruby
Expand All @@ -86,9 +121,28 @@ jobs:

- name: Build gem
working-directory: structured_params
env:
VERSION: ${{ steps.extract_version.outputs.version }}
run: |
gem build structured_params.gemspec
test -f "structured_params-$VERSION.gem"

- name: Push the version and CHANGELOG to main
run: git push origin main

# The Release created its tag before the version commit. Point the tag at the commit that is
# built and published, so the tag reproduces the gem. `rake release` then finds the tag and
# skips tagging. Done before publishing so a refused tag push fails the job with nothing released.
- name: Point the release tag at the published commit
env:
TAG_NAME: ${{ github.event.release.tag_name }}
run: |
git tag -f "$TAG_NAME" HEAD
git push --force origin "refs/tags/$TAG_NAME"

# `rake release` (see structured_params/Rakefile) tags releases vX.Y.Z. The tag exists (it was moved
# above), so only the build and the RubyGems push happen here. That relies on the tag being
# exactly v plus the VERSION written above, which the validation guarantees.
- name: Publish to RubyGems
uses: rubygems/release-gem@v1
with:
Expand Down
45 changes: 0 additions & 45 deletions .github/workflows/tagging.yml

This file was deleted.

Loading
Loading