familia 2.10.1 → 2.11.1
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 +4 -4
- data/.github/dependabot.yml +15 -0
- data/.github/workflows/ci.yml +2 -5
- data/.github/workflows/claude-code-review.yml +33 -9
- data/.github/workflows/claude.yml +9 -2
- data/.github/workflows/code-smells.yml +2 -2
- data/.github/workflows/codeql.yml +101 -0
- data/.github/workflows/release-gem.yml +161 -0
- data/.github/workflows/ruby-lint.yml +66 -0
- data/.github/workflows/yardoc.yml +110 -0
- data/.rubocop.yml +3 -1
- data/.yardopts +1 -1
- data/CHANGELOG.rst +207 -0
- data/Gemfile +9 -3
- data/Gemfile.lock +15 -22
- data/README.md +26 -0
- data/Rakefile +29 -0
- data/docs/guides/feature-encrypted-fields.md +45 -12
- data/docs/investigation/memory-audit.md +465 -0
- data/docs/overview.md +1 -1
- data/docs/reference/api-technical.md +2 -1
- data/examples/encryption_upgrade_proof/.gitignore +3 -0
- data/examples/encryption_upgrade_proof/README.md +84 -0
- data/examples/encryption_upgrade_proof/common.rb +83 -0
- data/examples/encryption_upgrade_proof/gemfiles/Gemfile.dev-no-libsodium +4 -0
- data/examples/encryption_upgrade_proof/gemfiles/Gemfile.dev-with-libsodium +5 -0
- data/examples/encryption_upgrade_proof/gemfiles/Gemfile.production-today +4 -0
- data/examples/encryption_upgrade_proof/model.rb +32 -0
- data/examples/encryption_upgrade_proof/phase0_production_today.rb +100 -0
- data/examples/encryption_upgrade_proof/phase1_gem_upgrade_no_libsodium.rb +99 -0
- data/examples/encryption_upgrade_proof/phase2_libsodium_enabled.rb +297 -0
- data/examples/encryption_upgrade_proof/phase3_rollback_hazard.rb +56 -0
- data/examples/encryption_upgrade_proof/run.sh +60 -0
- data/lib/familia/connection.rb +1 -1
- data/lib/familia/core_ext/securerandom.rb +57 -0
- data/lib/familia/data_type/database_commands.rb +8 -1
- data/lib/familia/data_type/types/sorted_set.rb +4 -2
- data/lib/familia/data_type/types/stringkey.rb +10 -3
- data/lib/familia/encryption/manager.rb +47 -8
- data/lib/familia/encryption/provider.rb +9 -0
- data/lib/familia/encryption/providers/aes_gcm_provider.rb +62 -2
- data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +9 -0
- data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +10 -0
- data/lib/familia/encryption/registry.rb +47 -5
- data/lib/familia/encryption/request_cache.rb +18 -1
- data/lib/familia/encryption.rb +19 -37
- data/lib/familia/features/encrypted_fields/concealed_string.rb +27 -0
- data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +21 -3
- data/lib/familia/features/encrypted_fields.rb +31 -5
- data/lib/familia/features/external_identifier.rb +50 -19
- data/lib/familia/features/relationships/participation/target_methods.rb +10 -7
- data/lib/familia/features/relationships/participation_membership.rb +12 -5
- data/lib/familia/horreum/management.rb +13 -0
- data/lib/familia/horreum/persistence.rb +58 -16
- data/lib/familia/migration/script.rb +12 -1
- data/lib/familia/secure_identifier.rb +32 -21
- data/lib/familia/settings.rb +66 -8
- data/lib/familia/verifiable_identifier.rb +44 -7
- data/lib/familia/version.rb +1 -1
- data/try/bug_fixes/class_destroy_index_cleanup_try.rb +50 -0
- data/try/bug_fixes/permission_query_try.rb +64 -0
- data/try/bug_fixes/sorted_set_members_count_try.rb +40 -0
- data/try/bug_fixes/stale_unique_index_try.rb +54 -0
- data/try/bug_fixes/stringkey_exists_try.rb +153 -0
- data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +15 -24
- data/try/features/encrypted_fields/per_field_algorithm_try.rb +164 -0
- data/try/features/encryption/aes_gcm_salt_rotation_try.rb +219 -0
- data/try/features/encryption/algorithm_upgrade_try.rb +116 -0
- data/try/features/encryption/config_persistence_try.rb +35 -0
- data/try/features/encryption/registry_algorithm_resolution_try.rb +125 -0
- data/try/features/encryption/request_cache_try.rb +15 -0
- data/try/features/external_identifier/external_identifier_try.rb +73 -0
- data/try/features/relationships/participation_membership_security_try.rb +64 -0
- data/try/integration/verifiable_identifier_try.rb +90 -5
- data/try/investigation/memory_leak_proof.rb +415 -0
- data/try/investigation/process_memory_leak_proof.rb +205 -0
- data/try/support/stress/atomic_write_ownership_stress.rb +152 -0
- data/try/thread_safety/atomic_write_ownership_race_try.rb +107 -104
- data/try/unit/core/securerandom_polyfill_try.rb +61 -0
- data/try/unit/data_types/enumerable_consistency/large_scale_consistency_try.rb +34 -8
- data/try/unit/horreum/auto_indexing_on_save_try.rb +9 -8
- metadata +35 -3
- data/.github/workflows/docs.yml +0 -87
- data/changelog.d/20260605_220911_anthropic_sleepy-allen.rst +0 -35
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 515d7050ae261290c4e85c6757a68edaa2aae60e21e05b9a6d1fd0457d90fad5
|
|
4
|
+
data.tar.gz: 653efc3da9a21910e7e071ae3065ab06e730334226d8f4820c0ed692fbec8453
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9e82443e034e2a2457bdc5366edcc18586d5df1f1ef4e60dc8c50d3d7431850c8899c42e15200d8ad0638455f13f2024a357b5e904f424fc9315312194329cce
|
|
7
|
+
data.tar.gz: 5eed6b2a2017510bac669b43602a1960b0719fa839eb8e61114be27df7010ea4d90eb73abdefc202b86e6c05d0e9dc674087e90e8c828dd649aaad4c446d38a4
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for all configuration options:
|
|
4
|
+
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
5
|
+
|
|
6
|
+
version: 2
|
|
7
|
+
updates:
|
|
8
|
+
- package-ecosystem: "github-actions"
|
|
9
|
+
directory: "/" # Location of package manifests
|
|
10
|
+
schedule:
|
|
11
|
+
interval: "weekly"
|
|
12
|
+
- package-ecosystem: "bundler"
|
|
13
|
+
directory: "/" # Location of package manifests
|
|
14
|
+
schedule:
|
|
15
|
+
interval: "weekly"
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -23,11 +23,8 @@ jobs:
|
|
|
23
23
|
strategy:
|
|
24
24
|
fail-fast: false
|
|
25
25
|
matrix:
|
|
26
|
-
ruby: ["3.4", "3.5"]
|
|
26
|
+
ruby: ["3.2", "3.3", "3.4", "3.5", "4.0"]
|
|
27
27
|
continue-on-error: [false]
|
|
28
|
-
include:
|
|
29
|
-
- ruby: "4.0"
|
|
30
|
-
continue-on-error: true
|
|
31
28
|
|
|
32
29
|
services:
|
|
33
30
|
redis:
|
|
@@ -44,7 +41,7 @@ jobs:
|
|
|
44
41
|
- 2525:6379
|
|
45
42
|
|
|
46
43
|
steps:
|
|
47
|
-
- uses: actions/checkout@
|
|
44
|
+
- uses: actions/checkout@v7
|
|
48
45
|
- name: Set up Ruby
|
|
49
46
|
uses: ruby/setup-ruby@v1
|
|
50
47
|
with:
|
|
@@ -4,14 +4,34 @@ on:
|
|
|
4
4
|
pull_request:
|
|
5
5
|
types: [opened, synchronize, labeled]
|
|
6
6
|
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
model:
|
|
9
|
+
description: "Claude model for this run (overrides the CLAUDE_MODEL repo variable)"
|
|
10
|
+
type: choice
|
|
11
|
+
required: false
|
|
12
|
+
default: claude-opus-4-6
|
|
13
|
+
options:
|
|
14
|
+
- claude-opus-4-6
|
|
15
|
+
- claude-sonnet-4-6
|
|
16
|
+
- claude-haiku-4-5-20251001
|
|
7
17
|
|
|
8
18
|
jobs:
|
|
9
19
|
claude-review:
|
|
10
|
-
# Run
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
# Run only when ALL of the following hold:
|
|
21
|
+
# 1. Author is not a bot (renovate, dependabot, etc. surface as 'name[bot]').
|
|
22
|
+
# 2. PR head is not from a fork. Fork PRs run without repository secrets, so
|
|
23
|
+
# CLAUDE_CODE_OAUTH_TOKEN is empty and the action would fail.
|
|
24
|
+
# 3. The event is a freshly opened PR, a 'claude-review' label add, or a push
|
|
25
|
+
# to a PR that already carries the 'claude-review' label.
|
|
26
|
+
if: ${{
|
|
27
|
+
!endsWith(github.actor, '[bot]') &&
|
|
28
|
+
!github.event.pull_request.head.repo.fork &&
|
|
29
|
+
(
|
|
30
|
+
(github.event.action == 'opened') ||
|
|
31
|
+
(github.event.action == 'labeled' && github.event.label.name == 'claude-review') ||
|
|
32
|
+
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'claude-review'))
|
|
33
|
+
)
|
|
34
|
+
}}
|
|
15
35
|
|
|
16
36
|
runs-on: ubuntu-latest
|
|
17
37
|
permissions:
|
|
@@ -22,7 +42,7 @@ jobs:
|
|
|
22
42
|
|
|
23
43
|
steps:
|
|
24
44
|
- name: Checkout repository
|
|
25
|
-
uses: actions/checkout@
|
|
45
|
+
uses: actions/checkout@v7
|
|
26
46
|
with:
|
|
27
47
|
fetch-depth: 1
|
|
28
48
|
|
|
@@ -32,6 +52,13 @@ jobs:
|
|
|
32
52
|
with:
|
|
33
53
|
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
34
54
|
|
|
55
|
+
# Primary model precedence: the workflow_dispatch "model" input (manual
|
|
56
|
+
# runs) -> the CLAUDE_MODEL repo variable -> this built-in default.
|
|
57
|
+
# Pinning a current id avoids the action's frozen default, which 404s
|
|
58
|
+
# ("model: claude-sonnet-4-20250514"). Fall back to Sonnet on overload.
|
|
59
|
+
model: "${{ inputs.model || vars.CLAUDE_MODEL || 'claude-opus-4-6' }}"
|
|
60
|
+
fallback_model: "${{ vars.CLAUDE_FALLBACK_MODEL || 'claude-sonnet-4-6' }}"
|
|
61
|
+
|
|
35
62
|
# Optional: Use sticky comments to make Claude reuse the same comment on subsequent pushes to the same PR
|
|
36
63
|
use_sticky_comment: true
|
|
37
64
|
|
|
@@ -50,6 +77,3 @@ jobs:
|
|
|
50
77
|
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
51
78
|
# or https://docs.anthropic.com/en/docs/claude-code/sdk#command-line for available options
|
|
52
79
|
allowed_tools: "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"
|
|
53
|
-
|
|
54
|
-
# Allow bot-initiated workflows (e.g., qodo, dependabot)
|
|
55
|
-
allowed_bots: "*"
|
|
@@ -26,16 +26,23 @@ jobs:
|
|
|
26
26
|
actions: read # Required for Claude to read CI results on PRs
|
|
27
27
|
steps:
|
|
28
28
|
- name: Checkout repository
|
|
29
|
-
uses: actions/checkout@
|
|
29
|
+
uses: actions/checkout@v7
|
|
30
30
|
with:
|
|
31
31
|
fetch-depth: 1
|
|
32
32
|
|
|
33
33
|
- name: Run Claude Code
|
|
34
34
|
id: claude
|
|
35
|
-
uses: anthropics/claude-code-action@
|
|
35
|
+
uses: anthropics/claude-code-action@beta
|
|
36
36
|
with:
|
|
37
37
|
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
38
38
|
|
|
39
|
+
# Primary model: set the CLAUDE_MODEL repo variable to override without
|
|
40
|
+
# editing this file. Pinning a current id avoids the action's frozen
|
|
41
|
+
# default, which 404s ("model: claude-sonnet-4-20250514"). Fall back to
|
|
42
|
+
# Sonnet if the primary is unavailable or overloaded.
|
|
43
|
+
model: "${{ vars.CLAUDE_MODEL || 'claude-opus-4-6' }}"
|
|
44
|
+
fallback_model: "${{ vars.CLAUDE_FALLBACK_MODEL || 'claude-sonnet-4-6' }}"
|
|
45
|
+
|
|
39
46
|
# This is an optional setting that allows Claude to read CI results on PRs
|
|
40
47
|
additional_permissions: |
|
|
41
48
|
actions: read
|
|
@@ -21,7 +21,7 @@ jobs:
|
|
|
21
21
|
|
|
22
22
|
steps:
|
|
23
23
|
- name: Checkout code
|
|
24
|
-
uses: actions/checkout@
|
|
24
|
+
uses: actions/checkout@v7
|
|
25
25
|
|
|
26
26
|
- name: Set up Ruby
|
|
27
27
|
uses: ruby/setup-ruby@v1
|
|
@@ -91,7 +91,7 @@ jobs:
|
|
|
91
91
|
|
|
92
92
|
steps:
|
|
93
93
|
- name: Checkout code
|
|
94
|
-
uses: actions/checkout@
|
|
94
|
+
uses: actions/checkout@v7
|
|
95
95
|
|
|
96
96
|
- name: Set up Ruby
|
|
97
97
|
uses: ruby/setup-ruby@v1
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# For most projects, this workflow file will not need changing; you simply need
|
|
2
|
+
# to commit it to your repository.
|
|
3
|
+
#
|
|
4
|
+
# You may wish to alter this file to override the set of languages analyzed,
|
|
5
|
+
# or to provide custom queries or build logic.
|
|
6
|
+
#
|
|
7
|
+
# ******** NOTE ********
|
|
8
|
+
# We have attempted to detect the languages in your repository. Please check
|
|
9
|
+
# the `language` matrix defined below to confirm you have the correct set of
|
|
10
|
+
# supported CodeQL languages.
|
|
11
|
+
#
|
|
12
|
+
name: "CodeQL Advanced"
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
push:
|
|
16
|
+
branches: [ "main" ]
|
|
17
|
+
pull_request:
|
|
18
|
+
branches: [ "main" ]
|
|
19
|
+
schedule:
|
|
20
|
+
- cron: '30 0 * * 6'
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
analyze:
|
|
24
|
+
name: Analyze (${{ matrix.language }})
|
|
25
|
+
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
|
26
|
+
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
|
27
|
+
# - https://gh.io/supported-runners-and-hardware-resources
|
|
28
|
+
# - https://gh.io/using-larger-runners (GitHub.com only)
|
|
29
|
+
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
|
30
|
+
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
|
31
|
+
permissions:
|
|
32
|
+
# required for all workflows
|
|
33
|
+
security-events: write
|
|
34
|
+
|
|
35
|
+
# required to fetch internal or private CodeQL packs
|
|
36
|
+
packages: read
|
|
37
|
+
|
|
38
|
+
# only required for workflows in private repositories
|
|
39
|
+
actions: read
|
|
40
|
+
contents: read
|
|
41
|
+
|
|
42
|
+
strategy:
|
|
43
|
+
fail-fast: false
|
|
44
|
+
matrix:
|
|
45
|
+
include:
|
|
46
|
+
- language: actions
|
|
47
|
+
build-mode: none
|
|
48
|
+
- language: ruby
|
|
49
|
+
build-mode: none
|
|
50
|
+
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
|
|
51
|
+
# Use `c-cpp` to analyze code written in C, C++ or both
|
|
52
|
+
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
|
53
|
+
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
|
54
|
+
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
|
55
|
+
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
|
56
|
+
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
|
57
|
+
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
|
58
|
+
steps:
|
|
59
|
+
- name: Checkout repository
|
|
60
|
+
uses: actions/checkout@v7
|
|
61
|
+
|
|
62
|
+
# Add any setup steps before running the `github/codeql-action/init` action.
|
|
63
|
+
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
|
64
|
+
# or others). This is typically only required for manual builds.
|
|
65
|
+
# - name: Setup runtime (example)
|
|
66
|
+
# uses: actions/setup-example@v1
|
|
67
|
+
|
|
68
|
+
# Initializes the CodeQL tools for scanning.
|
|
69
|
+
- name: Initialize CodeQL
|
|
70
|
+
uses: github/codeql-action/init@v4
|
|
71
|
+
with:
|
|
72
|
+
languages: ${{ matrix.language }}
|
|
73
|
+
build-mode: ${{ matrix.build-mode }}
|
|
74
|
+
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
75
|
+
# By default, queries listed here will override any specified in a config file.
|
|
76
|
+
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
77
|
+
|
|
78
|
+
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
|
79
|
+
# queries: security-extended,security-and-quality
|
|
80
|
+
|
|
81
|
+
# If the analyze step fails for one of the languages you are analyzing with
|
|
82
|
+
# "We were unable to automatically build your code", modify the matrix above
|
|
83
|
+
# to set the build mode to "manual" for that language. Then modify this step
|
|
84
|
+
# to build your code.
|
|
85
|
+
# ℹ️ Command-line programs to run using the OS shell.
|
|
86
|
+
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
|
87
|
+
- name: Run manual build steps
|
|
88
|
+
if: matrix.build-mode == 'manual'
|
|
89
|
+
shell: bash
|
|
90
|
+
run: |
|
|
91
|
+
echo 'If you are using a "manual" build mode for one or more of the' \
|
|
92
|
+
'languages you are analyzing, replace this with the commands to build' \
|
|
93
|
+
'your code, for example:'
|
|
94
|
+
echo ' make bootstrap'
|
|
95
|
+
echo ' make release'
|
|
96
|
+
exit 1
|
|
97
|
+
|
|
98
|
+
- name: Perform CodeQL Analysis
|
|
99
|
+
uses: github/codeql-action/analyze@v4
|
|
100
|
+
with:
|
|
101
|
+
category: "/language:${{matrix.language}}"
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Release Gem Workflow
|
|
2
|
+
#
|
|
3
|
+
# Automatically builds and publishes the familia gem to RubyGems.org when a
|
|
4
|
+
# GitHub release is published with a semantic version tag (vMAJOR.MINOR.PATCH,
|
|
5
|
+
# e.g. v2.10.1, v3.0.0).
|
|
6
|
+
#
|
|
7
|
+
# Follows the canonical RubyGems Trusted Publishing workflow:
|
|
8
|
+
# https://guides.rubygems.org/trusted-publishing/
|
|
9
|
+
#
|
|
10
|
+
# ============================================================================
|
|
11
|
+
# SETUP INSTRUCTIONS (one-time)
|
|
12
|
+
# ============================================================================
|
|
13
|
+
#
|
|
14
|
+
# This workflow uses RubyGems Trusted Publishing (OIDC). No long-lived API
|
|
15
|
+
# key needs to be stored in GitHub.
|
|
16
|
+
#
|
|
17
|
+
# ----------------------------------------------------------------------------
|
|
18
|
+
# 1. Configure the trusted publisher on RubyGems.org
|
|
19
|
+
# ----------------------------------------------------------------------------
|
|
20
|
+
#
|
|
21
|
+
# a. Sign in to https://rubygems.org and enable MFA on your account
|
|
22
|
+
# (required for trusted publishing).
|
|
23
|
+
#
|
|
24
|
+
# b. Open the gem's trusted publisher page (works for both existing gems
|
|
25
|
+
# and the first-ever publish):
|
|
26
|
+
# Existing gem: https://rubygems.org/gems/familia/trusted_publishers
|
|
27
|
+
# First publish: https://rubygems.org/profile/oidc/pending_trusted_publishers/new
|
|
28
|
+
#
|
|
29
|
+
# c. Click "Create" and fill in:
|
|
30
|
+
# Publisher type: GitHub Actions
|
|
31
|
+
# Repository owner: delano
|
|
32
|
+
# Repository name: familia
|
|
33
|
+
# Workflow filename: release-gem.yml
|
|
34
|
+
# Environment: rubygems.org (must match `environment.name` below)
|
|
35
|
+
#
|
|
36
|
+
# d. Save. RubyGems will now accept short-lived OIDC tokens issued by this
|
|
37
|
+
# workflow running in this repository.
|
|
38
|
+
#
|
|
39
|
+
# ----------------------------------------------------------------------------
|
|
40
|
+
# 2. Configure the GitHub environment
|
|
41
|
+
# ----------------------------------------------------------------------------
|
|
42
|
+
#
|
|
43
|
+
# a. In GitHub: Settings -> Environments -> New environment
|
|
44
|
+
# Name: `rubygems.org` (must match `environment.name` below)
|
|
45
|
+
#
|
|
46
|
+
# b. Under "Deployment branches and tags", restrict to tags matching:
|
|
47
|
+
# v*.*.*
|
|
48
|
+
# This prevents the environment (and its OIDC token) from being used
|
|
49
|
+
# from any branch or non-release tag.
|
|
50
|
+
#
|
|
51
|
+
# c. Optional but recommended: add required reviewers to gate publishes
|
|
52
|
+
# behind manual approval.
|
|
53
|
+
#
|
|
54
|
+
# No GitHub secrets are required - OIDC handles authentication.
|
|
55
|
+
#
|
|
56
|
+
# ----------------------------------------------------------------------------
|
|
57
|
+
# 3. Cutting a release
|
|
58
|
+
# ----------------------------------------------------------------------------
|
|
59
|
+
#
|
|
60
|
+
# a. Bump Familia::VERSION in lib/familia/version.rb (semver: MAJOR.MINOR.PATCH).
|
|
61
|
+
# b. Update CHANGELOG.rst and commit on main.
|
|
62
|
+
# c. On GitHub, draft a Release with tag `vX.Y.Z` (matching the constant)
|
|
63
|
+
# and publish it. This workflow runs automatically: it verifies the tag
|
|
64
|
+
# matches the gemspec version, builds the gem, and pushes it.
|
|
65
|
+
#
|
|
66
|
+
# ----------------------------------------------------------------------------
|
|
67
|
+
# Fallback: API key (only if Trusted Publishing isn't an option)
|
|
68
|
+
# ----------------------------------------------------------------------------
|
|
69
|
+
#
|
|
70
|
+
# 1. Create an API key at https://rubygems.org/profile/api_keys with scope
|
|
71
|
+
# "Push rubygem" restricted to the `familia` gem.
|
|
72
|
+
# 2. Store it as a repo secret named `RUBYGEMS_API_KEY`.
|
|
73
|
+
# 3. Drop the `id-token: write` permission and `environment:` block, and
|
|
74
|
+
# replace the `rubygems/release-gem` step with:
|
|
75
|
+
#
|
|
76
|
+
# - name: Publish to RubyGems
|
|
77
|
+
# env:
|
|
78
|
+
# GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
|
79
|
+
# run: gem push familia-*.gem
|
|
80
|
+
#
|
|
81
|
+
# ----------------------------------------------------------------------------
|
|
82
|
+
# Action provenance (SHA pins)
|
|
83
|
+
# ----------------------------------------------------------------------------
|
|
84
|
+
#
|
|
85
|
+
# All third-party actions below are pinned to a full commit SHA, per GitHub's
|
|
86
|
+
# secure-use guidance for release-critical workflows. The accompanying
|
|
87
|
+
# comment records the tag the SHA was resolved from so renovate/dependabot
|
|
88
|
+
# can keep them current.
|
|
89
|
+
#
|
|
90
|
+
# actions/checkout - official GitHub action
|
|
91
|
+
# ruby/setup-ruby - official Ruby org action (GitHub-verified creator)
|
|
92
|
+
# rubygems/release-gem - official RubyGems action for trusted publishing
|
|
93
|
+
#
|
|
94
|
+
# ============================================================================
|
|
95
|
+
|
|
96
|
+
name: Release Gem
|
|
97
|
+
|
|
98
|
+
on:
|
|
99
|
+
release:
|
|
100
|
+
types: [published]
|
|
101
|
+
|
|
102
|
+
# Coarse default. Each job re-declares the narrowest permissions it needs.
|
|
103
|
+
permissions:
|
|
104
|
+
contents: read
|
|
105
|
+
|
|
106
|
+
# Don't allow two release runs to race - one published tag, one publish.
|
|
107
|
+
concurrency:
|
|
108
|
+
group: release-gem-${{ github.event.release.tag_name }}
|
|
109
|
+
cancel-in-progress: false
|
|
110
|
+
|
|
111
|
+
jobs:
|
|
112
|
+
release:
|
|
113
|
+
name: Build and push gem
|
|
114
|
+
runs-on: ubuntu-latest
|
|
115
|
+
timeout-minutes: 10
|
|
116
|
+
|
|
117
|
+
environment:
|
|
118
|
+
name: rubygems.org
|
|
119
|
+
url: https://rubygems.org/gems/familia
|
|
120
|
+
|
|
121
|
+
permissions:
|
|
122
|
+
contents: write # rubygems/release-gem attaches built .gem to the GitHub release
|
|
123
|
+
id-token: write # OIDC token for RubyGems Trusted Publishing
|
|
124
|
+
|
|
125
|
+
steps:
|
|
126
|
+
- name: Checkout
|
|
127
|
+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
128
|
+
with:
|
|
129
|
+
persist-credentials: false
|
|
130
|
+
|
|
131
|
+
- name: Set up Ruby
|
|
132
|
+
uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1.310.0
|
|
133
|
+
with:
|
|
134
|
+
bundler-cache: true
|
|
135
|
+
# Pinned to 3.3 to match the otto/rhales release builds. The gem is
|
|
136
|
+
# pure Ruby, so the build Ruby is independent of the supported range
|
|
137
|
+
# declared in the gemspec (>= 3.2).
|
|
138
|
+
ruby-version: "3.3"
|
|
139
|
+
|
|
140
|
+
- name: Verify release tag matches Familia::VERSION
|
|
141
|
+
env:
|
|
142
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
143
|
+
run: |
|
|
144
|
+
set -euo pipefail
|
|
145
|
+
case "${RELEASE_TAG}" in
|
|
146
|
+
v[0-9]*.[0-9]*.[0-9]*) ;;
|
|
147
|
+
*)
|
|
148
|
+
echo "Release tag '${RELEASE_TAG}' is not a vMAJOR.MINOR.PATCH semver tag." >&2
|
|
149
|
+
exit 1
|
|
150
|
+
;;
|
|
151
|
+
esac
|
|
152
|
+
tag_version="${RELEASE_TAG#v}"
|
|
153
|
+
gem_version="$(ruby -r ./lib/familia/version.rb -e 'print Familia::VERSION')"
|
|
154
|
+
if [ "${tag_version}" != "${gem_version}" ]; then
|
|
155
|
+
echo "Release tag ${RELEASE_TAG} (${tag_version}) != Familia::VERSION (${gem_version})." >&2
|
|
156
|
+
exit 1
|
|
157
|
+
fi
|
|
158
|
+
echo "Releasing familia ${gem_version} from tag ${RELEASE_TAG}"
|
|
159
|
+
|
|
160
|
+
- name: Build and push gem to RubyGems
|
|
161
|
+
uses: rubygems/release-gem@6317d8d1f7e28c24d28f6eff169ea854948bd9f7 # v1.2.0
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# .github/workflows/ruby-lint.yml
|
|
2
|
+
|
|
3
|
+
name: Ruby Lint
|
|
4
|
+
|
|
5
|
+
# Runs RuboCop across the supported Ruby versions. Linting is informational:
|
|
6
|
+
# every dependency/lint step is continue-on-error, so style findings surface as
|
|
7
|
+
# annotations without blocking merges.
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
branches:
|
|
12
|
+
- fix/*
|
|
13
|
+
- rel/*
|
|
14
|
+
pull_request:
|
|
15
|
+
branches:
|
|
16
|
+
- main
|
|
17
|
+
- develop
|
|
18
|
+
- feature/*
|
|
19
|
+
workflow_dispatch:
|
|
20
|
+
inputs:
|
|
21
|
+
debug_enabled:
|
|
22
|
+
type: boolean
|
|
23
|
+
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
|
|
24
|
+
required: false
|
|
25
|
+
default: false
|
|
26
|
+
|
|
27
|
+
permissions:
|
|
28
|
+
contents: read
|
|
29
|
+
|
|
30
|
+
jobs:
|
|
31
|
+
lint:
|
|
32
|
+
timeout-minutes: 10 # prevent hung jobs
|
|
33
|
+
|
|
34
|
+
runs-on: ubuntu-24.04
|
|
35
|
+
|
|
36
|
+
strategy:
|
|
37
|
+
fail-fast: true
|
|
38
|
+
matrix:
|
|
39
|
+
ruby: ['3.2', '3.3', '3.4', '3.5', '4.0']
|
|
40
|
+
continue-on-error: [true]
|
|
41
|
+
|
|
42
|
+
steps:
|
|
43
|
+
- name: Checkout code
|
|
44
|
+
uses: actions/checkout@v7
|
|
45
|
+
|
|
46
|
+
- uses: ruby/setup-ruby@v1
|
|
47
|
+
with:
|
|
48
|
+
ruby-version: ${{ matrix.ruby }}
|
|
49
|
+
bundler-cache: true
|
|
50
|
+
|
|
51
|
+
- name: Setup tmate session
|
|
52
|
+
uses: mxschmitt/action-tmate@7b6a61a73bbb9793cb80ad69b8dd8ac19261834c # v3
|
|
53
|
+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
|
|
54
|
+
with:
|
|
55
|
+
detached: true
|
|
56
|
+
|
|
57
|
+
- name: Install dependencies
|
|
58
|
+
continue-on-error: ${{ matrix.continue-on-error }}
|
|
59
|
+
run: |
|
|
60
|
+
bundle config path vendor/bundle
|
|
61
|
+
bundle install --jobs 4 --retry 3
|
|
62
|
+
|
|
63
|
+
- name: Run Rubocop
|
|
64
|
+
continue-on-error: ${{ matrix.continue-on-error }}
|
|
65
|
+
run: |
|
|
66
|
+
bundle exec rubocop --config .rubocop.yml --format json --fail-level warning
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
name: Generate and Deploy YARD Documentation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
paths:
|
|
8
|
+
- 'lib/**/*'
|
|
9
|
+
- 'docs/**/*'
|
|
10
|
+
- 'README.md'
|
|
11
|
+
- 'CHANGELOG.md'
|
|
12
|
+
- 'CHANGELOG.rst'
|
|
13
|
+
- 'LICENSE.txt'
|
|
14
|
+
- '.yardopts'
|
|
15
|
+
- '.github/workflows/yardoc.yml'
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
pages: write
|
|
21
|
+
id-token: write
|
|
22
|
+
|
|
23
|
+
# Allow only one concurrent deployment, skipping runs queued between the run
|
|
24
|
+
# in-progress and latest queued. Do NOT cancel in-progress runs so production
|
|
25
|
+
# deployments can complete.
|
|
26
|
+
concurrency:
|
|
27
|
+
group: "pages"
|
|
28
|
+
cancel-in-progress: false
|
|
29
|
+
|
|
30
|
+
jobs:
|
|
31
|
+
build-docs:
|
|
32
|
+
timeout-minutes: 10
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
name: Generate YARD Documentation
|
|
35
|
+
|
|
36
|
+
steps:
|
|
37
|
+
- name: Checkout repository
|
|
38
|
+
uses: actions/checkout@v7
|
|
39
|
+
with:
|
|
40
|
+
fetch-depth: 0
|
|
41
|
+
|
|
42
|
+
- name: Set up Ruby environment
|
|
43
|
+
uses: ruby/setup-ruby@v1
|
|
44
|
+
with:
|
|
45
|
+
ruby-version: '3.4'
|
|
46
|
+
bundler-cache: true
|
|
47
|
+
|
|
48
|
+
- name: Generate documentation
|
|
49
|
+
# Uses the repository's committed .yardopts as the single source of
|
|
50
|
+
# truth for the output dir, includes/excludes, markup, and tags.
|
|
51
|
+
run: |
|
|
52
|
+
echo "::group::YARD Documentation Generation"
|
|
53
|
+
bundle exec yard stats --list-undoc || true
|
|
54
|
+
bundle exec yard doc
|
|
55
|
+
echo "::endgroup::"
|
|
56
|
+
|
|
57
|
+
- name: Disable Jekyll processing
|
|
58
|
+
# YARD emits files and directories that begin with underscores; the
|
|
59
|
+
# .nojekyll marker stops GitHub Pages from stripping them.
|
|
60
|
+
run: touch doc/.nojekyll
|
|
61
|
+
|
|
62
|
+
- name: Validate documentation output
|
|
63
|
+
run: |
|
|
64
|
+
echo "::group::Documentation Validation"
|
|
65
|
+
if [ ! -d "doc" ]; then
|
|
66
|
+
echo "Error: documentation directory 'doc' was not generated." >&2
|
|
67
|
+
echo "Ensure .yardopts sets '--output-dir doc'." >&2
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
if [ ! -f "doc/index.html" ]; then
|
|
71
|
+
echo "Warning: doc/index.html not found"
|
|
72
|
+
fi
|
|
73
|
+
echo "Generated HTML files: $(find doc -name '*.html' | wc -l)"
|
|
74
|
+
echo "Total documentation size: $(du -sh doc/ | cut -f1)"
|
|
75
|
+
echo "::endgroup::"
|
|
76
|
+
|
|
77
|
+
- name: Setup GitHub Pages configuration
|
|
78
|
+
uses: actions/configure-pages@v4
|
|
79
|
+
|
|
80
|
+
- name: Upload documentation artifact
|
|
81
|
+
uses: actions/upload-pages-artifact@v5
|
|
82
|
+
with:
|
|
83
|
+
path: './doc'
|
|
84
|
+
|
|
85
|
+
deploy-pages:
|
|
86
|
+
timeout-minutes: 10
|
|
87
|
+
environment:
|
|
88
|
+
name: github-pages
|
|
89
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
90
|
+
runs-on: ubuntu-latest
|
|
91
|
+
needs: build-docs
|
|
92
|
+
outputs:
|
|
93
|
+
page_url: ${{ steps.deployment.outputs.page_url }}
|
|
94
|
+
|
|
95
|
+
steps:
|
|
96
|
+
- name: Deploy to GitHub Pages
|
|
97
|
+
id: deployment
|
|
98
|
+
uses: actions/deploy-pages@v5
|
|
99
|
+
|
|
100
|
+
notify-completion:
|
|
101
|
+
timeout-minutes: 5
|
|
102
|
+
runs-on: ubuntu-latest
|
|
103
|
+
needs: [build-docs, deploy-pages]
|
|
104
|
+
if: success()
|
|
105
|
+
|
|
106
|
+
steps:
|
|
107
|
+
- name: Documentation deployment summary
|
|
108
|
+
run: |
|
|
109
|
+
echo "::notice title=Documentation Deployed::YARD documentation successfully deployed to GitHub Pages"
|
|
110
|
+
echo "::notice title=Access URL::Documentation available at: ${{ needs.deploy-pages.outputs.page_url }}"
|
data/.rubocop.yml
CHANGED
|
@@ -135,9 +135,11 @@ Style/StringLiterals:
|
|
|
135
135
|
Enabled: true
|
|
136
136
|
EnforcedStyle: single_quotes
|
|
137
137
|
|
|
138
|
+
# Every lib/ file carries `# frozen_string_literal: true`; require it rather
|
|
139
|
+
# than flag it (the previous `never` contradicted the entire codebase).
|
|
138
140
|
Style/FrozenStringLiteralComment:
|
|
139
141
|
Enabled: true
|
|
140
|
-
EnforcedStyle:
|
|
142
|
+
EnforcedStyle: always_true
|
|
141
143
|
|
|
142
144
|
Naming/MemoizedInstanceVariableName:
|
|
143
145
|
Enabled: false
|