Skip to content

Init #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 9, 2025
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
7 changes: 7 additions & 0 deletions .bundle/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
BUNDLE_BIN: "bin"
BUNDLE_PATH: "vendor/gems"
BUNDLE_CACHE_PATH: "vendor/cache"
BUNDLE_CACHE_ALL: "true"
BUNDLE_SPECIFIC_PLATFORM: "true"
BUNDLE_NO_INSTALL: "true"
12 changes: 12 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
docs/
bin/
README.md
.gitignore
tmp/
.git
coverage/
vendor/gems/
.github/
.devcontainer/
.ruby-lsp/
docker-compose.yml
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @GrantBirki
98 changes: 98 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copilot Instructions

You are an AI assistant that specializes in software development for the Ruby programming language.

## Environment Setup

Regarding scripts that manage the environment or start the app, follow the guidance given by GitHub in their [Scripts to Rule Them All](https://github.blog/engineering/scripts-to-rule-them-all/) blog post. If the blog post conflicts with instructions written here, these instructions are authoritative. For example:

Bootstrap the Ruby project by running:

```bash
script/bootstrap
```

## Testing

Ensure all unit tests pass by running the following:

```bash
script/test
```

This project **requires 100% test coverage** of code, not including:

- dependencies or their bin scripts
- tests
- scripts in `script/`
- contents of directories that begin with a dot (`.`)

Tests are powered by Ruby's `rspec`. By running `script/test`, the tool `simplecov` will be automatically used and will exit with a non-zero code if the coverage is below 100%.

## Linting

Ensure the linter passes by running:

```bash
script/lint
```

The linter is powered by `rubocop` with its config file located at `.rubocop.yml`. The linter will exit with a non-zero code if any issues are found. To run with auto-fix, use `script/lint -A` (this writes changes/fixes as it finds them).

## Project Guidelines

- Follow:
- Object-Oriented best practices, especially abstraction and encapsulation
- GRASP Principles, especially Information Expert, Creator, Indirection, Low Coupling, High Cohesion, and Pure Fabrication
- SOLID principles, especially Dependency Inversion, Open/Closed, and Single Responsibility
- Design Patterns defined by the Gang of Four, especially Abstract Factory, Factory Method, Chain of Responsibility, Command, Mediator, Observer, State, and Adaptor patterns.
- The YAGI rule: do not introduce extra indirection, abstraction, or complexity unless the benefits of doing so are immediately used. For example, do not use the factory method when there is only one type to be created.
- Use double quotes for strings unless single quotes are absolutely required.
- Base new work on latest `main` branch.
- Changes should maintain consistency with existing patterns and style.
- Document changes clearly and thoroughly, including updates to existing comments when appropriate. Try to use the same "voice" as the other comments, mimicking their tone and style.
- When responding to code refactoring suggestions, function suggestions, or other code changes, please keep your responses as concise as possible. We are capable engineers and can understand the code changes without excessive explanation. If you feel that a more detailed explanation is necessary, you can provide it, but keep it concise.
- When suggesting code changes, always opt for the most maintainable approach. Try your best to keep the code clean and follow DRY principles. Avoid unnecessary complexity and always consider the long-term maintainability of the code.
- When writing unit tests, try to consider edge cases as well as the main path of success. This will help ensure that the code is robust and can handle unexpected inputs or situations.
- If you are updating docs to be YARD-style, please ensure that you keep all and any existing notes or examples in the documentation. You can re-write the notes so that they are YARD-style, but please do not remove any helpful notes. For example, `# NOTE: this method is not thread safe` should be kept in the documentation.
- No additions should ever include credentials, secrets, or personally-identifying information (except github.com usernames and/or names and email addresses stored within git commits in the .git directory).
- Hard-coded strings should almost always be constant variables.
- In general, avoid introducing new dependencies. Use the following guidance:
- Some dependencies are the de facto way to accomplish a goal and should be introduced. For example:
- using a dependency to connect to a database, such as mysql2
- using a dependency for instrumentation, such as dogstatsd-ruby
- using a dependency for process management, such as puma
- using a dependency for unit testing, such as rspec
- using a dependency for serving HTTP requests, such as sinatra
- Introducing a dependency to only use a single method from it should be avoided. Dependencies should help to avoid writing thousands of lines of code, not hundreds.
- Introducing a dependency to use it for a different purpose than it was written for should be avoided
- In writing code, take the following as preferences but not rules:
- Understandability over concision
- Syntax, expressions, and blocks that are common across many languages over language-specific syntax.
- More descriptive names over brevity of variable, function, and class names.
- The use of whitespace (newlines) over compactness of files.
- Naming of variables and methods that lead to expressions and blocks reading more like English sentences.
- Less lines of code over more. Keep changes minimal and focused.

## Pull Request Requirements

- All tests must pass.
- The linter must pass.
- Documentation must be up-to-date.
- Any new dependencies must be vendored.
- All new code must have YARD-style documentation.
- The body of the Pull Request should:
- Contain a summary of the changes.
- Make special note of any changes to dependencies.
- Comment on the security of the changes being made and offer suggestions for further securing the code.

## Repository Organization

- `.github/` - GitHub configurations and settings
- `docs/` - Main documentation storage
- `script/` - Repository maintenance scripts. Includes things like `script/bootstrap`, `script/test`, and `script/lint`.
- `config/` - Configuration files for the project.
- `lib/` - Main code for the project. This is where the main application/service code lives.
- `spec/` - Tests for the project. This is where the unit tests and acceptance tests live.
- `vendor/cache` - Vendored dependencies (Ruby Gems).
- `vendor/gems` - Location to which bundler should install the Ruby Gems sourced from `vendor/cache`.
29 changes: 29 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
version: 2
updates:
- package-ecosystem: bundler
vendor: true
directory: "/"
schedule:
interval: weekly
day: "monday"
time: "21:00"
groups:
prod-ruby-dependencies:
dependency-type: "production"
patterns:
- "*"
dev-ruby-dependencies:
dependency-type: "development"
patterns:
- "*"
- package-ecosystem: github-actions
directory: "/"
groups:
github-actions:
patterns:
- "*"
schedule:
interval: weekly
day: "tuesday"
time: "21:00"
35 changes: 35 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: build

on:
push:
branches:
- main
pull_request:
branches:
- main

permissions:
contents: read

jobs:
build:
name: build

strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}

steps:
- name: checkout
uses: actions/checkout@v4

- uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # [email protected]
with:
bundler-cache: true

- name: bootstrap
run: script/bootstrap

- name: build
run: script/build
22 changes: 22 additions & 0 deletions .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: "Copilot Setup Steps"

# Allows you to test the setup steps from your repository's "Actions" tab
on: workflow_dispatch

jobs:
copilot-setup-steps:
runs-on: ubuntu-latest
# Set the permissions to the lowest permissions possible needed for *your steps*. Copilot will be given its own token for its operations.
permissions:
# If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete.
contents: read
steps:
- name: checkout
uses: actions/checkout@v4

- uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # [email protected]
with:
bundler-cache: true

- name: bootstrap
run: script/bootstrap
29 changes: 29 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: lint

on:
push:
branches:
- main
pull_request:

permissions:
contents: read

jobs:
lint:
name: lint
runs-on: ubuntu-latest

steps:
- name: checkout
uses: actions/checkout@v4

- uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # [email protected]
with:
bundler-cache: true

- name: bootstrap
run: script/bootstrap

- name: lint
run: script/lint
156 changes: 156 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
name: release

on:
workflow_dispatch:
push:
branches:
- main
paths:
- lib/hooks/version.rb

permissions: {}

jobs:
build:
if: github.repository == 'github/hooks'
permissions:
contents: read
runs-on: ubuntu-latest
outputs:
artifact-id: ${{ steps.upload-artifact.outputs.artifact-id }}
gem_name: ${{ steps.build.outputs.gem_name }}
gem_version: ${{ steps.build.outputs.gem_version }}
gem_path: ${{ steps.build.outputs.gem_path }}

steps:
- name: checkout
uses: actions/checkout@v4
with:
persist-credentials: false

- uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # [email protected]
with:
bundler-cache: false

- name: bootstrap
run: script/bootstrap

# IMPORTANT: this step MUST export for the following outputs:
# gem_name: the name of the gem - ex: "my-cool-gem"
# gem_version: the version of the gem - ex: "1.0.0"
# gem_path: the path/filename of the gem - ex: "my-cool-gem-1.0.0.gem"
- name: build
id: build
run: script/build

- name: upload artifact
uses: actions/[email protected]
id: upload-artifact
with:
path: "${{ steps.build.outputs.gem_path }}"

release:
needs: build
environment: release
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false

- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
artifact-ids: ${{ needs.build.outputs.artifact-id }}

- name: Publish to GitHub Packages
env:
OWNER: ${{ github.repository_owner }}
GEM_NAME: ${{ needs.build.outputs.gem_name }}
GEM_VERSION: ${{ needs.build.outputs.gem_version }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ARTIFACT_PATH: "artifact"
run: |
GEM_HOST_API_KEY=${GITHUB_TOKEN} gem push --key github --host https://rubygems.pkg.github.com/${OWNER} $ARTIFACT_PATH/${GEM_NAME}-${GEM_VERSION}.gem

- uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # [email protected]
with:
bundler-cache: false

- name: bootstrap
run: script/bootstrap

- name: Configure RubyGems Credentials
uses: rubygems/configure-rubygems-credentials@e3f5097339179e0d4c7321ab44209e7e02446746 # pin@main

- name: sign ruby gem
env:
GEM_NAME: ${{ needs.build.outputs.gem_name }}
GEM_VERSION: ${{ needs.build.outputs.gem_version }}
ARTIFACT_PATH: "artifact"
run: bundle exec sigstore-cli sign ${ARTIFACT_PATH}/${GEM_NAME}-${GEM_VERSION}.gem --bundle ${GEM_NAME}-${GEM_VERSION}.sigstore.json

- name: Publish to RubyGems
env:
GEM_NAME: ${{ needs.build.outputs.gem_name }}
GEM_VERSION: ${{ needs.build.outputs.gem_version }}
ARTIFACT_PATH: "artifact"
run: gem push ${ARTIFACT_PATH}/${GEM_NAME}-${GEM_VERSION}.gem --attestation ${GEM_NAME}-${GEM_VERSION}.sigstore.json

- name: await gem
env:
GEM_NAME: ${{ needs.build.outputs.gem_name }}
GEM_VERSION: ${{ needs.build.outputs.gem_version }}
run: bundle exec rubygems-await "${GEM_NAME}:${GEM_VERSION}" --timeout 120

- name: GitHub Release
env:
GEM_NAME: ${{ needs.build.outputs.gem_name }}
GEM_VERSION: ${{ needs.build.outputs.gem_version }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ARTIFACT_PATH: "artifact"
run: |
gh release create "v${GEM_VERSION}" \
"${ARTIFACT_PATH}/${GEM_NAME}-${GEM_VERSION}.gem" \
"${GEM_NAME}-${GEM_VERSION}.sigstore.json" \
--title "v${GEM_VERSION}" \
--generate-notes

sign:
needs: [build, release]
runs-on: ubuntu-latest
permissions:
id-token: write
attestations: write
contents: read

steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
artifact-ids: ${{ needs.build.outputs.artifact-id }}

- name: attest build provenance
uses: actions/[email protected]
with:
subject-path: "artifact/${{ needs.build.outputs.gem_path }}"

verify:
permissions: {}
needs: [build, release, sign]
runs-on: ubuntu-latest

steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
artifact-ids: ${{ needs.build.outputs.artifact-id }}

- name: verify
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
ARTIFACT_PATH: "artifact/${{ needs.build.outputs.gem_path }}"
run: gh attestation verify "$ARTIFACT_PATH" --repo ${OWNER}/${REPO} --signer-workflow ${OWNER}/${REPO}/.github/workflows/release.yml
Loading