fasti 1.0.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 +7 -0
- data/.mcp.json +19 -0
- data/.rspec +3 -0
- data/.rubocop.yml +82 -0
- data/.rubocop_todo.yml +89 -0
- data/.serena/project.yml +68 -0
- data/.simplecov +31 -0
- data/.yardopts +9 -0
- data/AGENTS.md +60 -0
- data/CHANGELOG.md +25 -0
- data/CLAUDE.md +1 -0
- data/LICENSE.txt +21 -0
- data/README.md +416 -0
- data/RELEASING.md +202 -0
- data/Rakefile +34 -0
- data/TODO.md +11 -0
- data/benchmark/holiday_cache_benchmark.rb +111 -0
- data/benchmark/memory_benchmark.rb +86 -0
- data/docs/agents/git-pr.md +298 -0
- data/docs/agents/languages.md +388 -0
- data/docs/agents/rubocop.md +55 -0
- data/docs/plans/positional-arguments.md +303 -0
- data/docs/plans/structured-config.md +232 -0
- data/examples/config.rb +80 -0
- data/exe/fasti +6 -0
- data/lib/fasti/calendar.rb +292 -0
- data/lib/fasti/calendar_transition.rb +266 -0
- data/lib/fasti/cli.rb +550 -0
- data/lib/fasti/config/schema.rb +36 -0
- data/lib/fasti/config/types.rb +66 -0
- data/lib/fasti/config.rb +125 -0
- data/lib/fasti/error.rb +6 -0
- data/lib/fasti/formatter.rb +234 -0
- data/lib/fasti/style_parser.rb +211 -0
- data/lib/fasti/version.rb +6 -0
- data/lib/fasti.rb +21 -0
- data/mise.toml +5 -0
- data/sig/fasti.rbs +4 -0
- metadata +181 -0
data/README.md
ADDED
@@ -0,0 +1,416 @@
|
|
1
|
+
# :spiral_calendar: Fasti
|
2
|
+
|
3
|
+
[](https://badge.fury.io/rb/fasti)
|
4
|
+
[](https://github.com/sakuro/fasti/actions/workflows/ci.yml)
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
6
|
+
[](https://rubygems.org/gems/fasti)
|
7
|
+
[](https://depfu.com/github/sakuro/fasti)
|
8
|
+
|
9
|
+
A flexible calendar application with multi-country holiday support, written in Ruby. Fasti provides beautiful, colorful calendar displays with support for multiple output formats, configurable week start days, and country-specific holiday highlighting.
|
10
|
+
|
11
|
+
## Features
|
12
|
+
|
13
|
+
- **Multiple Display Formats**: Month, quarter (3 months), and full year views
|
14
|
+
- **Configurable Week Start**: Start weeks on any day of the week
|
15
|
+
- **Holiday Support**: Country-specific holiday highlighting using the holidays gem
|
16
|
+
- **Historical Calendar Transitions**: Julian to Gregorian calendar transitions with country-specific gap handling
|
17
|
+
- **Color Coding**: ANSI color support for holidays, weekends, and today's date
|
18
|
+
- **Configuration File**: XDG-compliant Ruby-based configuration file support (`~/.config/fasti/config.rb`)
|
19
|
+
- **Locale Detection**: Automatic country detection from `LC_ALL` and `LANG` environment variables
|
20
|
+
- **Command Line Interface**: Full-featured CLI with comprehensive options
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
|
24
|
+
Install the gem by executing:
|
25
|
+
|
26
|
+
```bash
|
27
|
+
gem install fasti
|
28
|
+
```
|
29
|
+
|
30
|
+
Or add it to your Gemfile:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
gem 'fasti'
|
34
|
+
```
|
35
|
+
|
36
|
+
Then execute:
|
37
|
+
|
38
|
+
```bash
|
39
|
+
bundle install
|
40
|
+
```
|
41
|
+
|
42
|
+
## Usage
|
43
|
+
|
44
|
+
### Basic Usage
|
45
|
+
|
46
|
+
Display the current month:
|
47
|
+
```bash
|
48
|
+
fasti
|
49
|
+
```
|
50
|
+
|
51
|
+
Display a specific month and year:
|
52
|
+
```bash
|
53
|
+
fasti 6 2024
|
54
|
+
```
|
55
|
+
|
56
|
+
### Display Formats
|
57
|
+
|
58
|
+
**Month View** (default):
|
59
|
+
```bash
|
60
|
+
fasti 6 2024 --format month
|
61
|
+
# Or using shortcuts:
|
62
|
+
fasti 6 2024 --month
|
63
|
+
fasti 6 2024 -m
|
64
|
+
```
|
65
|
+
|
66
|
+
**Quarter View** (3 months side by side):
|
67
|
+
```bash
|
68
|
+
fasti 6 2024 --format quarter
|
69
|
+
# Or using shortcuts:
|
70
|
+
fasti 6 2024 --quarter
|
71
|
+
fasti 6 2024 -q
|
72
|
+
```
|
73
|
+
|
74
|
+
**Year View** (all 12 months):
|
75
|
+
```bash
|
76
|
+
fasti 2024 --format year
|
77
|
+
# Or using shortcuts:
|
78
|
+
fasti 2024 --year
|
79
|
+
fasti 2024 -y
|
80
|
+
```
|
81
|
+
|
82
|
+
### Week Start Configuration
|
83
|
+
|
84
|
+
You can start the week on any day of the week:
|
85
|
+
|
86
|
+
```bash
|
87
|
+
fasti --start-of-week sunday # Default
|
88
|
+
fasti --start-of-week monday # Common international standard
|
89
|
+
fasti --start-of-week tuesday
|
90
|
+
fasti --start-of-week wednesday
|
91
|
+
fasti --start-of-week thursday
|
92
|
+
fasti --start-of-week friday
|
93
|
+
fasti --start-of-week saturday
|
94
|
+
```
|
95
|
+
|
96
|
+
### Country and Holiday Support
|
97
|
+
|
98
|
+
Specify country for holiday highlighting:
|
99
|
+
```bash
|
100
|
+
fasti --country JP # Japan
|
101
|
+
fasti --country US # United States
|
102
|
+
fasti --country GB # United Kingdom
|
103
|
+
fasti --country DE # Germany
|
104
|
+
```
|
105
|
+
|
106
|
+
Fasti automatically detects your country from environment variables (`LC_ALL`, `LANG`), but you can override this with the `--country` option.
|
107
|
+
|
108
|
+
### Historical Calendar Transitions
|
109
|
+
|
110
|
+
For historical dates, Fasti supports country-specific Julian to Gregorian calendar transitions with proper gap handling:
|
111
|
+
|
112
|
+
```bash
|
113
|
+
# Show October 1582 (Italian transition) - compressed display
|
114
|
+
fasti 10 1582 --country IT
|
115
|
+
|
116
|
+
# British transition - September 3-13, 1752 never existed in Britain
|
117
|
+
fasti 9 1752 --country GB
|
118
|
+
|
119
|
+
# Asian countries transitioned from lunisolar calendars
|
120
|
+
# Japan transitioned in 1873 (Meiji era)
|
121
|
+
fasti 12 1872 --country JP
|
122
|
+
|
123
|
+
# Korea transitioned in 1896 (Korean Empire era)
|
124
|
+
fasti 12 1895 --country KR
|
125
|
+
|
126
|
+
# France had different transition date than Italy
|
127
|
+
fasti 12 1582 --country FR
|
128
|
+
|
129
|
+
# Sweden's complex transition in 1753
|
130
|
+
fasti 3 1753 --country SE
|
131
|
+
```
|
132
|
+
|
133
|
+
Calendar transitions are displayed in compressed format (like UNIX `cal` command) for continuous display without confusing gaps.
|
134
|
+
|
135
|
+
Supported countries with calendar transition dates:
|
136
|
+
|
137
|
+
**Early Catholic adoption (1582-1583):**
|
138
|
+
- Italy, Spain, Portugal (1582-10-15)
|
139
|
+
- France (1582-12-20)
|
140
|
+
- Belgium/Spanish Netherlands (1583-01-06)
|
141
|
+
|
142
|
+
**Protestant countries (1700-1753):**
|
143
|
+
- Netherlands (1700-07-12), Switzerland (1700-01-23), Denmark/Norway (1700-03-01)
|
144
|
+
- Great Britain & colonies: US, Canada, Australia, etc. (1752-09-14)
|
145
|
+
- Sweden (1753-03-01)
|
146
|
+
|
147
|
+
**Eastern Europe (1916-1927):**
|
148
|
+
- Bulgaria (1916), Russia (1918), Romania/Yugoslavia (1919), Greece (1923), Turkey (1927)
|
149
|
+
|
150
|
+
**Asian countries (1873-1967):**
|
151
|
+
- Japan (1873), China/Taiwan (1912), Korea (1896), Vietnam (1967), Thailand (1888)
|
152
|
+
|
153
|
+
Note: Asian countries transitioned from lunisolar calendars to Gregorian; pre-transition dates use proleptic Gregorian calendar for computational consistency.
|
154
|
+
|
155
|
+
### Command Line Options
|
156
|
+
|
157
|
+
```
|
158
|
+
Usage: fasti [month] [year] [options]
|
159
|
+
|
160
|
+
Arguments:
|
161
|
+
month Month (1-12, optional)
|
162
|
+
year Year (optional)
|
163
|
+
|
164
|
+
Format options:
|
165
|
+
-f, --format FORMAT Output format (month, quarter, year)
|
166
|
+
-m, --month Display month format (equivalent to --format month)
|
167
|
+
-q, --quarter Display quarter format (equivalent to --format quarter)
|
168
|
+
-y, --year Display year format (equivalent to --format year)
|
169
|
+
|
170
|
+
Calendar display options:
|
171
|
+
-w, --start-of-week WEEKDAY Week start day (any day of the week)
|
172
|
+
-c, --country COUNTRY Country code for holidays (e.g., JP, US, GB, DE)
|
173
|
+
-s, --style STYLE Custom styling (e.g., "sunday:bold holiday:foreground=red today:inverse")
|
174
|
+
|
175
|
+
Other options:
|
176
|
+
-v, --version Show version
|
177
|
+
-h, --help Show this help
|
178
|
+
```
|
179
|
+
|
180
|
+
### Positional Arguments
|
181
|
+
|
182
|
+
**No arguments** - Display current month:
|
183
|
+
```bash
|
184
|
+
fasti
|
185
|
+
```
|
186
|
+
|
187
|
+
**One argument** - Interpreted based on value:
|
188
|
+
- 1-12: Month for current year
|
189
|
+
- 13+: Year for current month
|
190
|
+
|
191
|
+
```bash
|
192
|
+
fasti 6 # June current year
|
193
|
+
fasti 2024 # Current month 2024
|
194
|
+
```
|
195
|
+
|
196
|
+
**Two arguments** - Month and year:
|
197
|
+
```bash
|
198
|
+
fasti 6 2024 # June 2024
|
199
|
+
```
|
200
|
+
|
201
|
+
### Configuration File
|
202
|
+
|
203
|
+
Create a configuration file at `~/.config/fasti/config.rb` (or `$XDG_CONFIG_HOME/fasti/config.rb`) using Ruby syntax to set default options:
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
# Example configuration
|
207
|
+
Fasti.configure do |config|
|
208
|
+
config.format = :quarter
|
209
|
+
config.start_of_week = :monday
|
210
|
+
config.country = :US
|
211
|
+
# Custom styling (optional)
|
212
|
+
config.style = {
|
213
|
+
sunday: { bold: true },
|
214
|
+
holiday: { foreground: :red, bold: true },
|
215
|
+
today: { inverse: true }
|
216
|
+
}
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
See `examples/config.rb` for a complete configuration example.
|
221
|
+
|
222
|
+
Command line options override configuration file settings.
|
223
|
+
|
224
|
+
### Styling
|
225
|
+
|
226
|
+
By default, Fasti displays all days with normal text styling. You can customize the appearance of different day types using the `style` configuration option:
|
227
|
+
|
228
|
+
- **sunday**: Configurable styling for Sundays
|
229
|
+
- **holiday**: Configurable styling for holidays
|
230
|
+
- **today**: Configurable styling for today's date
|
231
|
+
- **monday, tuesday, etc.**: Configurable styling for individual weekdays
|
232
|
+
|
233
|
+
You can apply styling using the `--style` option with space-separated target:attribute pairs:
|
234
|
+
|
235
|
+
```bash
|
236
|
+
# Bold Sundays and red holidays
|
237
|
+
fasti --style "sunday:bold holiday:foreground=red"
|
238
|
+
|
239
|
+
# Multiple attributes for the same target
|
240
|
+
fasti --style "today:bold,inverse sunday:foreground=blue"
|
241
|
+
|
242
|
+
# Mix different styling attributes
|
243
|
+
fasti --style "sunday:bold holiday:foreground=red,background=yellow today:inverse"
|
244
|
+
|
245
|
+
# Override config file settings with no- prefix (boolean attributes only)
|
246
|
+
fasti --style "sunday:no-bold,no-italic holiday:foreground=red" # Remove boolean styling
|
247
|
+
```
|
248
|
+
|
249
|
+
### Examples
|
250
|
+
|
251
|
+
Display current month with Monday start:
|
252
|
+
```bash
|
253
|
+
fasti --start-of-week monday
|
254
|
+
```
|
255
|
+
|
256
|
+
Display current month with Wednesday start:
|
257
|
+
```bash
|
258
|
+
fasti --start-of-week wednesday
|
259
|
+
```
|
260
|
+
|
261
|
+
Show quarter view for summer 2024 in Japan:
|
262
|
+
```bash
|
263
|
+
fasti 7 2024 --format quarter --country JP
|
264
|
+
```
|
265
|
+
|
266
|
+
Full year 2024 with US holidays:
|
267
|
+
```bash
|
268
|
+
fasti 2024 --format year --country US
|
269
|
+
```
|
270
|
+
|
271
|
+
## Environment Variables
|
272
|
+
|
273
|
+
Fasti respects the following environment variables for automatic country detection:
|
274
|
+
|
275
|
+
- `LC_ALL` (highest priority)
|
276
|
+
- `LANG` (fallback)
|
277
|
+
|
278
|
+
Note: Only `LC_ALL` and `LANG` are used for country detection. Other `LC_*` variables (such as `LC_MESSAGES`, `LC_TIME`) are not used as they represent specific locale categories rather than the user's preferred country for holiday context.
|
279
|
+
|
280
|
+
Example:
|
281
|
+
```bash
|
282
|
+
export LANG=ja_JP.UTF-8 # Automatically uses Japan (JP) holidays
|
283
|
+
fasti
|
284
|
+
```
|
285
|
+
|
286
|
+
## Development
|
287
|
+
|
288
|
+
After checking out the repo, run `bin/setup` to install dependencies:
|
289
|
+
|
290
|
+
```bash
|
291
|
+
git clone https://github.com/sakuro/fasti.git
|
292
|
+
cd fasti
|
293
|
+
bin/setup
|
294
|
+
```
|
295
|
+
|
296
|
+
Run the tests (includes coverage measurement):
|
297
|
+
```bash
|
298
|
+
bundle exec rake spec
|
299
|
+
```
|
300
|
+
|
301
|
+
Run linting:
|
302
|
+
```bash
|
303
|
+
bundle exec rake rubocop
|
304
|
+
```
|
305
|
+
|
306
|
+
Generate API documentation:
|
307
|
+
```bash
|
308
|
+
bundle exec rake doc
|
309
|
+
```
|
310
|
+
|
311
|
+
Clean temporary files:
|
312
|
+
```bash
|
313
|
+
bundle exec rake clean
|
314
|
+
```
|
315
|
+
|
316
|
+
Remove all generated files:
|
317
|
+
```bash
|
318
|
+
bundle exec rake clobber
|
319
|
+
```
|
320
|
+
|
321
|
+
Run all checks:
|
322
|
+
```bash
|
323
|
+
bundle exec rake
|
324
|
+
```
|
325
|
+
|
326
|
+
To install this gem onto your local machine:
|
327
|
+
```bash
|
328
|
+
bundle exec rake install
|
329
|
+
```
|
330
|
+
|
331
|
+
For an interactive prompt that allows you to experiment:
|
332
|
+
```bash
|
333
|
+
bin/console
|
334
|
+
```
|
335
|
+
|
336
|
+
## Dependencies
|
337
|
+
|
338
|
+
- **dry-configurable** (~> 1.0): Configuration management
|
339
|
+
- **dry-schema** (~> 1.13): Configuration validation
|
340
|
+
- **dry-types** (~> 1.7): Type system for configuration
|
341
|
+
- **holidays** (~> 8.0): Country-specific holiday data
|
342
|
+
- **locale** (~> 2.1): Locale parsing for country detection
|
343
|
+
- **tint_me** (~> 1.0): ANSI color support
|
344
|
+
- **zeitwerk** (~> 2.6): Code autoloading
|
345
|
+
|
346
|
+
## Development Dependencies
|
347
|
+
|
348
|
+
- **rspec** (~> 3.0): Testing framework
|
349
|
+
- **rubocop** (~> 1.21): Code linting
|
350
|
+
- **simplecov** (~> 0.22): Code coverage measurement
|
351
|
+
- **yard** (from GitHub): API documentation generation with Data class support
|
352
|
+
|
353
|
+
## Releasing
|
354
|
+
|
355
|
+
This project uses automated GitHub Actions workflows for releases. Maintainers can create new releases with a simple workflow execution.
|
356
|
+
|
357
|
+
### Quick Release Process
|
358
|
+
|
359
|
+
1. Go to **Actions** → **Release Preparation** → **Run workflow**
|
360
|
+
2. Enter version number (e.g., `1.0.0`)
|
361
|
+
3. Review and merge the generated PR
|
362
|
+
4. Release is automatically published to RubyGems and GitHub
|
363
|
+
|
364
|
+
**For detailed instructions, troubleshooting, and setup requirements, see [`.github/RELEASING.md`](.github/RELEASING.md).**
|
365
|
+
|
366
|
+
## Known Limitations
|
367
|
+
|
368
|
+
### Historical Calendar Support
|
369
|
+
|
370
|
+
Fasti properly handles historical calendar transitions with country-specific transition dates. Each country switched from Julian to Gregorian calendar at different times, and these transition periods contain non-existent dates that are handled correctly.
|
371
|
+
|
372
|
+
Historical transition periods display correctly with proper gap handling:
|
373
|
+
|
374
|
+
```bash
|
375
|
+
# Italian transition works correctly - shows compressed display
|
376
|
+
fasti 10 1582 --country IT
|
377
|
+
|
378
|
+
# Asian countries show transitions from lunisolar calendars
|
379
|
+
fasti 12 1872 --country JP
|
380
|
+
```
|
381
|
+
|
382
|
+
**Note**: Standard UNIX calendar tools like `cal` and `gcal` correctly handle these historical transitions by appropriately skipping non-existent dates during calendar reforms.
|
383
|
+
|
384
|
+
**Technical Details**: Fasti uses country-specific Julian to Gregorian calendar transition dates with proper gap handling. Ruby's `Date` class uses Italy's transition date (`Date::ITALY`) by default, but Fasti's `CalendarTransition` class supports transitions for different countries, properly handling non-existent dates during calendar reforms.
|
385
|
+
|
386
|
+
**Available Countries**: Fasti supports **25+ countries** with calendar transitions:
|
387
|
+
- **Catholic countries**: Italy/Spain/Portugal (1582), France (1582), Belgium (1583)
|
388
|
+
- **Protestant countries**: Netherlands (1700), Switzerland (1700), Denmark/Norway (1700), Great Britain & colonies (1752), Sweden (1753)
|
389
|
+
- **Eastern Europe**: Bulgaria (1916), Russia (1918), Romania/Yugoslavia (1919), Greece (1923), Turkey (1927)
|
390
|
+
- **Asian countries**: Japan (1873), China/Taiwan (1912), Korea (1896), Vietnam (1967), Thailand (1888)
|
391
|
+
|
392
|
+
Asian countries used lunisolar calendars before adoption; pre-transition dates use proleptic Gregorian calendar for computational consistency.
|
393
|
+
|
394
|
+
## Contributing
|
395
|
+
|
396
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sakuro/fasti.
|
397
|
+
|
398
|
+
1. Fork the repository
|
399
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
400
|
+
3. Write tests for your changes
|
401
|
+
4. Ensure all tests pass (`bundle exec rake spec`)
|
402
|
+
5. Ensure code passes linting (`bundle exec rake rubocop`)
|
403
|
+
6. Commit your changes (`git commit -am 'Add some feature'`)
|
404
|
+
7. Push to the branch (`git push origin my-new-feature`)
|
405
|
+
8. Create a Pull Request
|
406
|
+
|
407
|
+
## License
|
408
|
+
|
409
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
410
|
+
|
411
|
+
## Acknowledgments
|
412
|
+
|
413
|
+
- Built with Ruby 3.2+ for broad compatibility and modern features
|
414
|
+
- Uses the excellent [holidays](https://github.com/holidays/holidays) gem for accurate holiday data
|
415
|
+
- Color support provided by the [tint_me](https://github.com/ddfreyne/tint-me) gem
|
416
|
+
- Follows XDG Base Directory Specification for configuration files
|
data/RELEASING.md
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
# Releasing
|
2
|
+
|
3
|
+
This document describes the release process for Ruby gems used in this project, which is fully automated through GitHub Actions workflows.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
The release process consists of three automated workflows:
|
8
|
+
|
9
|
+
1. **Release Preparation** - Creates a release branch with version updates
|
10
|
+
2. **Release Validation** - Validates the release branch on PR creation
|
11
|
+
3. **Release Publish** - Publishes the gem after PR merge
|
12
|
+
|
13
|
+
## Prerequisites
|
14
|
+
|
15
|
+
Before initiating a release, ensure:
|
16
|
+
|
17
|
+
- [ ] All desired features and fixes are merged to `main`
|
18
|
+
- [ ] CI is passing on `main` branch
|
19
|
+
- [ ] `CHANGELOG.md` has entries under `## [Unreleased]` section
|
20
|
+
- [ ] `RUBYGEMS_API_KEY` secret is configured in repository settings
|
21
|
+
|
22
|
+
## Release Process
|
23
|
+
|
24
|
+
### Step 1: Initiate Release
|
25
|
+
|
26
|
+
1. Go to the [Actions tab](../../actions) in the GitHub repository
|
27
|
+
2. Select "Release Preparation" workflow
|
28
|
+
3. Click "Run workflow"
|
29
|
+
4. Enter the version number (e.g., `1.0.0`)
|
30
|
+
5. Click "Run workflow"
|
31
|
+
|
32
|
+
The workflow will automatically:
|
33
|
+
- Create a new branch `release-v{version}`
|
34
|
+
- Update `lib/[gem_name]/version.rb` with the new version
|
35
|
+
- Update `CHANGELOG.md`:
|
36
|
+
- Replace `## [Unreleased]` with `## [{version}] - {date}`
|
37
|
+
- Add a new `## [Unreleased]` section for future changes
|
38
|
+
- Create a git tag `v{version}` on the release branch
|
39
|
+
- Push the branch and tag to GitHub
|
40
|
+
- Create a Pull Request to `main`
|
41
|
+
|
42
|
+
### Step 2: Review and Merge
|
43
|
+
|
44
|
+
Once the PR is created, the Release Validation workflow automatically:
|
45
|
+
- Validates version format (must be `x.y.z`)
|
46
|
+
- Verifies version consistency between branch name and `version.rb`
|
47
|
+
- Checks that the git tag doesn't already exist (or can be updated)
|
48
|
+
- Confirms the version isn't already published on RubyGems
|
49
|
+
- Verifies `RUBYGEMS_API_KEY` secret is configured
|
50
|
+
- Validates `CHANGELOG.md` has an entry for this version
|
51
|
+
|
52
|
+
**Note**: Quality checks (tests and RuboCop) are handled by the CI workflow to avoid duplication.
|
53
|
+
|
54
|
+
**Important**: If you push additional commits to the release PR (e.g., bug fixes, workflow updates, documentation changes):
|
55
|
+
- The validation workflow automatically moves the release tag to the latest commit
|
56
|
+
- This ensures the tag always points to the final reviewed code
|
57
|
+
- No manual intervention required
|
58
|
+
- All changes will be included in the final release
|
59
|
+
|
60
|
+
If all checks pass, merge the PR.
|
61
|
+
|
62
|
+
### Step 3: Automatic Publishing
|
63
|
+
|
64
|
+
After the PR is merged, the Release Publish workflow automatically:
|
65
|
+
- Checks out the exact git tag created on the release branch
|
66
|
+
- Builds the gem from the tagged commit
|
67
|
+
- Publishes the gem to RubyGems
|
68
|
+
- Creates a GitHub Release with:
|
69
|
+
- Release notes extracted from `CHANGELOG.md`
|
70
|
+
- The built gem file as an attachment
|
71
|
+
- Cleans up the release branch
|
72
|
+
|
73
|
+
## Workflow Architecture
|
74
|
+
|
75
|
+
### Key Design Decisions
|
76
|
+
|
77
|
+
#### Tag-Based Deployment
|
78
|
+
|
79
|
+
The release workflow uses a **tag-based deployment strategy** to ensure the released gem contains exactly the code that was reviewed and approved in the release PR, without any subsequent changes from `main`.
|
80
|
+
|
81
|
+
```mermaid
|
82
|
+
graph LR
|
83
|
+
A[main branch] -->|Create release branch| B[release-v1.0.0]
|
84
|
+
B -->|Update version & tag| C[Tagged: v1.0.0]
|
85
|
+
B -->|Create PR| D[Pull Request]
|
86
|
+
A -->|Other PRs merged| E[main with new commits]
|
87
|
+
D -->|Merge PR| E
|
88
|
+
C -->|Checkout tag| F[Build & Publish gem]
|
89
|
+
F -->|Contains only| G[Code from release branch]
|
90
|
+
```
|
91
|
+
|
92
|
+
#### Automatic Tag Movement
|
93
|
+
|
94
|
+
When additional commits are pushed to a release PR, the tag automatically moves to the latest commit:
|
95
|
+
|
96
|
+
```mermaid
|
97
|
+
graph LR
|
98
|
+
A[Initial commit] -->|Tag v1.0.0| B[Tagged commit]
|
99
|
+
B -->|Fix typo| C[New commit]
|
100
|
+
C -->|Auto-move tag| D[Tag v1.0.0 on latest]
|
101
|
+
D -->|Final review| E[Ready to merge]
|
102
|
+
```
|
103
|
+
|
104
|
+
This ensures:
|
105
|
+
- The tag always points to the final reviewed code
|
106
|
+
- No manual tag management required
|
107
|
+
- The published gem matches exactly what was approved
|
108
|
+
|
109
|
+
## Manual Release (Emergency Only)
|
110
|
+
|
111
|
+
If automation fails, you can release manually using our tag-based strategy:
|
112
|
+
|
113
|
+
```bash
|
114
|
+
# 1. Create release branch
|
115
|
+
git checkout -b release-v1.0.0
|
116
|
+
|
117
|
+
# 2. Update version
|
118
|
+
vim lib/[gem_name]/version.rb
|
119
|
+
|
120
|
+
# 3. Update CHANGELOG (move content from [Unreleased] to [1.0.0])
|
121
|
+
vim CHANGELOG.md
|
122
|
+
|
123
|
+
# 4. Commit changes and create tag
|
124
|
+
git add -A
|
125
|
+
git commit -m ":bookmark: Release v1.0.0"
|
126
|
+
git tag -a v1.0.0 -m "Release v1.0.0"
|
127
|
+
|
128
|
+
# 5. Push release branch and tag
|
129
|
+
git push origin release-v1.0.0
|
130
|
+
git push origin v1.0.0
|
131
|
+
|
132
|
+
# 6. Create PR and merge after review
|
133
|
+
gh pr create --title "Release v1.0.0" --body "Release v1.0.0"
|
134
|
+
# (Review and merge PR)
|
135
|
+
|
136
|
+
# 7. Checkout the tag and build gem
|
137
|
+
git checkout v1.0.0
|
138
|
+
bundle exec rake build
|
139
|
+
|
140
|
+
# 8. Push to RubyGems
|
141
|
+
gem push pkg/[gem_name]-1.0.0.gem
|
142
|
+
|
143
|
+
# 9. Create GitHub release
|
144
|
+
gh release create v1.0.0 --title "[gem_name] v1.0.0" --generate-notes pkg/[gem_name]-1.0.0.gem
|
145
|
+
```
|
146
|
+
|
147
|
+
## Troubleshooting
|
148
|
+
|
149
|
+
### Release Validation Fails
|
150
|
+
|
151
|
+
**Version already exists on RubyGems**
|
152
|
+
- Solution: Increment the version number and try again
|
153
|
+
|
154
|
+
**RUBYGEMS_API_KEY not configured**
|
155
|
+
1. Generate an API key at https://rubygems.org/profile/edit
|
156
|
+
2. Go to repository Settings → Secrets and variables → Actions
|
157
|
+
3. Add new secret: `RUBYGEMS_API_KEY` with your API key
|
158
|
+
|
159
|
+
**Tests or RuboCop failing**
|
160
|
+
- These are checked by the CI workflow, not in release validation
|
161
|
+
- Fix the issues on the release branch or `main` as appropriate
|
162
|
+
- Release validation focuses on version consistency and format
|
163
|
+
|
164
|
+
### Release Publish Fails
|
165
|
+
|
166
|
+
**Tag not found**
|
167
|
+
- The release preparation workflow may have failed to create the tag
|
168
|
+
- Check if the tag exists: `git tag | grep v1.0.0`
|
169
|
+
- If missing, the release validation workflow will recreate it on PR updates
|
170
|
+
- For manual fix: create tag on release branch with `git tag -a v1.0.0 -m "Release v1.0.0"`
|
171
|
+
|
172
|
+
**Gem push fails**
|
173
|
+
- Verify RubyGems API key is valid
|
174
|
+
- Check if you have push permissions for the gem
|
175
|
+
- Ensure the version doesn't already exist on RubyGems
|
176
|
+
|
177
|
+
## Version Numbering
|
178
|
+
|
179
|
+
Follow [Semantic Versioning](https://semver.org/):
|
180
|
+
|
181
|
+
- **MAJOR** version for incompatible API changes
|
182
|
+
- **MINOR** version for backwards-compatible functionality additions
|
183
|
+
- **PATCH** version for backwards-compatible bug fixes
|
184
|
+
|
185
|
+
Examples:
|
186
|
+
- `0.1.0` → `0.1.1`: Bug fixes only
|
187
|
+
- `0.1.1` → `0.2.0`: New features added
|
188
|
+
- `0.2.0` → `1.0.0`: Breaking changes or stable release
|
189
|
+
|
190
|
+
## Workflow Files
|
191
|
+
|
192
|
+
The release automation is implemented in:
|
193
|
+
|
194
|
+
- `.github/workflows/release-preparation.yml` - Creates release branch and PR
|
195
|
+
- `.github/workflows/release-validation.yml` - Validates release PR
|
196
|
+
- `.github/workflows/release-publish.yml` - Publishes gem after merge
|
197
|
+
|
198
|
+
## Security Notes
|
199
|
+
|
200
|
+
- The `RUBYGEMS_API_KEY` secret is only accessible to workflows running on the default branch
|
201
|
+
- Release branches are automatically deleted after successful release
|
202
|
+
- All releases are tagged for audit trail and rollback capability
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
|
5
|
+
require "rake/clean"
|
6
|
+
CLEAN.include("coverage", ".rspec_status", ".yardoc")
|
7
|
+
CLOBBER.include("docs/api", "pkg")
|
8
|
+
|
9
|
+
require "rspec/core/rake_task"
|
10
|
+
RSpec::Core::RakeTask.new(:spec)
|
11
|
+
|
12
|
+
require "rubocop/rake_task"
|
13
|
+
RuboCop::RakeTask.new
|
14
|
+
|
15
|
+
require "gemoji"
|
16
|
+
require "yard"
|
17
|
+
YARD::Rake::YardocTask.new(:doc)
|
18
|
+
Rake::Task[:doc].enhance do
|
19
|
+
# Convert GitHub emoji shortcodes to actual emojis in generated HTML
|
20
|
+
Dir["docs/api/**/*.html"].each do |file|
|
21
|
+
content = File.read(file)
|
22
|
+
|
23
|
+
# Replace :emoji_name: patterns with actual unicode emojis
|
24
|
+
content.gsub!(/:([a-z0-9+\-_]+):/) do |match|
|
25
|
+
alias_name = $1
|
26
|
+
emoji = Emoji.find_by_alias(alias_name)
|
27
|
+
emoji ? emoji.raw : match # Keep original if emoji not found
|
28
|
+
end
|
29
|
+
|
30
|
+
File.write(file, content)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
task default: %i[spec rubocop]
|
data/TODO.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# TODO
|
2
|
+
|
3
|
+
**Tasks were previously managed in this file, but have been migrated to GitHub Issues for better tracking and collaboration.**
|
4
|
+
|
5
|
+
## Quick Links
|
6
|
+
- [Open Issues](https://github.com/sakuro/fasti/issues)
|
7
|
+
- [Current Milestone](https://github.com/sakuro/fasti/milestones)
|
8
|
+
- [All Milestones](https://github.com/sakuro/fasti/milestones?state=closed)
|
9
|
+
- [Project Board](https://github.com/sakuro/fasti/projects) (planned)
|
10
|
+
|
11
|
+
For ongoing maintenance guidelines, see the [AI Development Guide](CLAUDE.md).
|