advanced pipeline
This commit is contained in:
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "rbconfig"
|
||||
|
||||
root = IO.popen([ "git", "rev-parse", "--show-toplevel" ], &:read).strip
|
||||
check = File.join(root, "script", "warp_engine_version_check.rb")
|
||||
|
||||
exit 0 unless File.exist?(check)
|
||||
|
||||
exec(RbConfig.ruby, check, "--staged")
|
||||
@@ -0,0 +1,117 @@
|
||||
# Éles deploy: master push -> rubocop -> host teszt -> pull + függőségek + restart.
|
||||
#
|
||||
# A woodpecker-agent ugyanabban a stackben fut, mint az api és a frontend, ezért
|
||||
# a deploy nem SSH-zik: a hoszt docker socketjét használja. A stack könyvtára a
|
||||
# konténerben UGYANARRA az útvonalra van mountolva, mint a hoszton — különben a
|
||||
# compose relatív bind mountjai (./data, ./apps) máshova oldódnának fel.
|
||||
#
|
||||
# Feltételek:
|
||||
# - Woodpecker: a repónál engedélyezni kell a "trusted: volumes" jelölést
|
||||
# (Repo settings -> Trusted, admin joggal), különben a mountok tiltottak.
|
||||
# - Woodpecker secret: forge_token — Forgejo token a services/teletypegames
|
||||
# olvasásához (ugyanaz, amit a warp_engine workflow használ).
|
||||
# - A szerveren /srv/stacks/teletype-games egy master-en álló git checkout,
|
||||
# benne az api és frontend konténerek futnak (container_name: api, frontend).
|
||||
|
||||
when:
|
||||
- event: push
|
||||
branch: master
|
||||
- event: manual
|
||||
|
||||
services:
|
||||
- name: mysql
|
||||
image: mysql:8
|
||||
environment:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
||||
MYSQL_DATABASE: softwares # a zeitwerk:check production modban bootol; a softwares_test-et a db:test:prepare csinalja
|
||||
|
||||
steps:
|
||||
rubocop:
|
||||
image: ruby:3.3
|
||||
environment:
|
||||
BUNDLE_PATH: .bundle-ci # a workspace-ben marad, a teszt step újrahasználja
|
||||
commands:
|
||||
- apt-get update && apt-get install -y --no-install-recommends default-libmysqlclient-dev
|
||||
- cd apps/api
|
||||
- bundle install --jobs 4
|
||||
- bundle exec rubocop
|
||||
|
||||
test-host:
|
||||
image: ruby:3.3
|
||||
environment:
|
||||
RAILS_ENV: test
|
||||
DB_HOST: mysql
|
||||
DB_USER: root
|
||||
DB_NAME: softwares
|
||||
FILE_CONTAINER_PATH: /tmp/softwares
|
||||
IMAGE_CONTAINER_PATH: /tmp/images
|
||||
UPDATE_SECRET: ci-test
|
||||
SECRET_KEY_BASE: ci-test
|
||||
BUNDLE_PATH: .bundle-ci
|
||||
commands:
|
||||
- apt-get update && apt-get install -y --no-install-recommends default-libmysqlclient-dev default-mysql-client
|
||||
- mkdir -p /tmp/softwares /tmp/images
|
||||
- cd apps/api
|
||||
- bundle install --jobs 4
|
||||
- |
|
||||
echo "==> Varakozas a mysql-re"
|
||||
for i in $(seq 1 60); do
|
||||
if mysqladmin ping -h mysql --silent 2>/dev/null; then break; fi
|
||||
sleep 2
|
||||
done
|
||||
- bundle exec rails db:test:prepare
|
||||
- bundle exec rspec
|
||||
# Production modu eager load: a sema-betoltott teszt adatbazisra mutatunk,
|
||||
# hogy a boot ne egy ures DB-n haljon el.
|
||||
- RAILS_ENV=production DB_NAME=softwares_test bundle exec rails zeitwerk:check
|
||||
|
||||
pull:
|
||||
image: alpine/git
|
||||
environment:
|
||||
FORGE_TOKEN:
|
||||
from_secret: forge_token
|
||||
volumes:
|
||||
- /srv/stacks/teletype-games:/srv/stacks/teletype-games
|
||||
commands:
|
||||
- git config --global --add safe.directory /srv/stacks/teletype-games
|
||||
- cd /srv/stacks/teletype-games
|
||||
# A szerver checkoutja detached HEAD-en is állhat: akkor a pull "sikeres",
|
||||
# de a futó kód nem mozdul. Inkább bukjunk el itt.
|
||||
- |
|
||||
BRANCH="$(git symbolic-ref --short -q HEAD || true)"
|
||||
if [ "$BRANCH" != "master" ]; then
|
||||
echo "A checkout nem master-en all (HEAD: $${BRANCH:-detached}), a deploy megall."
|
||||
exit 1
|
||||
fi
|
||||
- TOKEN="$$(printf '%s' "$${FORGE_TOKEN}" | tr -d '[:space:]')"
|
||||
- git fetch "https://ci:$${TOKEN}@git.teletypegames.org/services/teletypegames.git" master
|
||||
- git merge --ff-only FETCH_HEAD
|
||||
- |
|
||||
HEAD_SHA="$(git rev-parse HEAD)"
|
||||
echo "==> A szerver most itt all: $HEAD_SHA"
|
||||
if [ "$HEAD_SHA" != "$CI_COMMIT_SHA" ]; then
|
||||
echo "Megjegyzes: ez nem a pipeline commitja ($CI_COMMIT_SHA) — kozben ujabb push jott."
|
||||
fi
|
||||
when:
|
||||
- event: [ push, manual ]
|
||||
branch: master
|
||||
|
||||
restart:
|
||||
image: docker:cli
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
commands:
|
||||
# Csak az api és a frontend indul újra. A stack többi szolgáltatását
|
||||
# (traefik, gitea, woodpecker, mysql) nem bántjuk: a woodpecker-server
|
||||
# restartja épp ezt a pipeline-t vágná el.
|
||||
- docker exec api bundle install
|
||||
# A migracio a restart elott fut: a motor migraciois utvonala a hoszt
|
||||
# db:migrate-jaban van, tehat ez a warp_engine migraciot is elvegzi.
|
||||
- docker exec api bundle exec rails db:migrate
|
||||
- docker restart api
|
||||
- docker exec frontend npm install
|
||||
- docker restart frontend
|
||||
- docker ps --filter name=api --filter name=frontend --format '{{.Names}} {{.Status}}'
|
||||
when:
|
||||
- event: [ push, manual ]
|
||||
branch: master
|
||||
@@ -1,7 +1,8 @@
|
||||
# Read-only split mirror: a libs/ruby/warp_engine alkönyvtárat kitükrözi a
|
||||
# engines/warp_engine repóba (fejlesztés itt, a monorepóban történik; a tükör
|
||||
# csak publikálásra való). Tag-elt release (warp_engine-v*) esetén a gem a
|
||||
# Forgejo rubygems registry-be is felmegy.
|
||||
# WarpEngine gem: verzió -> rubocop -> teszt -> tükrözés -> gem kiadás.
|
||||
#
|
||||
# A fejlesztés ebben a monorepóban történik; a engines/warp_engine repó csak
|
||||
# publikálásra való read-only tükör, ezért CI-t oda nincs értelme tenni: a
|
||||
# subtree split minden alkalommal felülírja.
|
||||
#
|
||||
# Szükséges Woodpecker secret: forge_token — Forgejo access token
|
||||
# repository:write (engines/warp_engine) és package:write joggal.
|
||||
@@ -29,16 +30,41 @@ services:
|
||||
MYSQL_DATABASE: warp_engine_test
|
||||
|
||||
steps:
|
||||
# A motor tesztje kapuzza a tobbit: ha bukik, se a tukrozes, se a gem
|
||||
# kiadasa nem fut le. A mirror repoba nincs ertelme CI-t tenni, mert azt a
|
||||
# subtree split minden alkalommal feluliria.
|
||||
# Első kapu: a motor nem módosulhat verzióemelés nélkül. Ugyanaz a szkript
|
||||
# fut a .githooks/pre-commit hookban is, csak --staged módban.
|
||||
version-bumped:
|
||||
image: ruby:3.3
|
||||
commands:
|
||||
- ruby script/warp_engine_version_check.rb --range "$CI_PREV_COMMIT_SHA" "$CI_COMMIT_SHA"
|
||||
when:
|
||||
- event: [ push, manual ]
|
||||
|
||||
# Tag esetén a verzió már nem emelkedhet: a tagnek a VERSION-t kell hirdetnie.
|
||||
tag-matches-version:
|
||||
image: ruby:3.3
|
||||
commands:
|
||||
- ruby script/warp_engine_version_check.rb --tag "$CI_COMMIT_REF"
|
||||
when:
|
||||
- event: tag
|
||||
|
||||
rubocop:
|
||||
image: ruby:3.2
|
||||
environment:
|
||||
BUNDLE_PATH: .bundle-ci # a workspace-ben marad, a következő step újrahasználja
|
||||
commands:
|
||||
- apt-get update && apt-get install -y --no-install-recommends default-libmysqlclient-dev
|
||||
- cd libs/ruby/warp_engine
|
||||
- bundle install --jobs 4
|
||||
- bundle exec rubocop
|
||||
|
||||
test-engine:
|
||||
image: ruby:3.2
|
||||
environment:
|
||||
RAILS_ENV: test
|
||||
DB_HOST: mysql
|
||||
BUNDLE_PATH: .bundle-ci
|
||||
commands:
|
||||
- apt-get update && apt-get install -y --no-install-recommends default-libmysqlclient-dev
|
||||
- apt-get update && apt-get install -y --no-install-recommends default-libmysqlclient-dev default-mysql-client
|
||||
- cd libs/ruby/warp_engine
|
||||
- bundle install --jobs 4
|
||||
- |
|
||||
@@ -106,11 +106,14 @@ JSON contract baselines for `/api/software` and `/api/builds` live in
|
||||
### Backend (RuboCop)
|
||||
|
||||
```bash
|
||||
docker compose run --rm --no-deps api bundle exec rubocop # check
|
||||
docker compose run --rm --no-deps api bundle exec rubocop -A # autofix
|
||||
docker compose run --rm --no-deps api bundle exec rubocop # host app
|
||||
docker compose run --rm --no-deps api bundle exec rubocop -A # host app, autofix
|
||||
docker exec -w /libs/ruby/warp_engine api \
|
||||
env BUNDLE_GEMFILE=/app/Gemfile bundle exec rubocop # engine
|
||||
```
|
||||
|
||||
Config: `apps/api/.rubocop.yml` (rubocop-rails-omakase preset)
|
||||
Config: `apps/api/.rubocop.yml` and `libs/ruby/warp_engine/.rubocop.yml`
|
||||
(both rubocop-rails-omakase). Both are green, and CI keeps them that way.
|
||||
|
||||
### Frontend (ESLint)
|
||||
|
||||
@@ -120,3 +123,72 @@ docker compose run --rm --no-deps frontend npm run lint:fix # autofix
|
||||
```
|
||||
|
||||
Config: `apps/frontend/eslint.config.js` (ESLint 9 flat config, Vue + TypeScript)
|
||||
|
||||
## Pipelines
|
||||
|
||||
Woodpecker reads every file in `.woodpecker/` as its own workflow, each with its
|
||||
own trigger.
|
||||
|
||||
### `.woodpecker/warp_engine.yaml` — the gem
|
||||
|
||||
Runs when a push to master touches `libs/ruby/warp_engine/**`, on a manual run,
|
||||
and on a `warp_engine-v*` tag:
|
||||
|
||||
| Step | What it guards |
|
||||
|---|---|
|
||||
| `version-bumped` | the engine changed but `WarpEngine::VERSION` did not — fails first, before anything else runs |
|
||||
| `tag-matches-version` | tag events only: `warp_engine-v0.9.1` must find `VERSION = "0.9.1"` |
|
||||
| `rubocop` | `libs/ruby/warp_engine` against its own `.rubocop.yml` |
|
||||
| `test-engine` | the engine suite (dummy app, `warp_engine_test` DB) |
|
||||
| `split-mirror` | pushes the subtree split to `engines/warp_engine` (master pushes only) |
|
||||
| `publish-gem` | `gem push` to the Forgejo registry (tags only) |
|
||||
|
||||
Nothing is mirrored or published until the version check, RuboCop and the suite
|
||||
have all passed. The mirror repository deliberately has no CI of its own: the
|
||||
subtree split overwrites it on every run.
|
||||
|
||||
### `.woodpecker/deploy.yaml` — the deploy
|
||||
|
||||
Runs on every push to master (and manually): `rubocop` → `test-host` (host
|
||||
suite + a production-mode `zeitwerk:check`) → `pull` → `restart`.
|
||||
|
||||
The `pull` and `restart` steps reach the server through the host Docker socket
|
||||
rather than SSH — the `woodpecker-agent` runs in the same stack as `api` and
|
||||
`frontend`. Two things this depends on:
|
||||
|
||||
- The stack directory is bind-mounted **at the same path** it has on the host
|
||||
(`/srv/stacks/teletype-games`), so the compose file's relative bind mounts
|
||||
(`./data`, `./apps`) still resolve where they did.
|
||||
- Woodpecker only allows step volumes on a **trusted** repository: enable
|
||||
*Trusted → Volumes* in the repo settings (admin only), or the deploy steps
|
||||
are rejected.
|
||||
|
||||
`pull` fails if the server checkout is not on `master` (a detached HEAD would
|
||||
make the pull look successful while the running code never moves). `restart`
|
||||
installs dependencies, runs `db:migrate` and only then restarts — and it
|
||||
touches only `api` and `frontend`: restarting `woodpecker-server` would cut off
|
||||
the very pipeline doing the deploy.
|
||||
|
||||
`db:migrate` in the host app covers the engine too — WarpEngine appends its own
|
||||
`db/migrate` to the host's migration paths. A failing migration stops the
|
||||
deploy before the restart, so the old code keeps running.
|
||||
|
||||
Secret used by both workflows: `forge_token` — a Forgejo token with
|
||||
repository read/write and package:write.
|
||||
|
||||
## Version bump hook
|
||||
|
||||
The engine's version rule is enforced twice, by the same script:
|
||||
|
||||
```bash
|
||||
git config core.hooksPath .githooks # once per clone
|
||||
```
|
||||
|
||||
`.githooks/pre-commit` runs `script/warp_engine_version_check.rb --staged`: a
|
||||
commit that touches `libs/ruby/warp_engine/**` must also raise
|
||||
`WarpEngine::VERSION` above the one in `HEAD`. The pipeline runs the same script
|
||||
in `--range` mode over the pushed commits, so nothing slips through a
|
||||
`--no-verify`.
|
||||
|
||||
Escape hatch when a bump genuinely does not belong:
|
||||
`SKIP_WARP_ENGINE_VERSION_CHECK=1 git commit ...`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: ../libs/ruby/warp_engine
|
||||
specs:
|
||||
warp_engine (0.9.0)
|
||||
warp_engine (0.9.1)
|
||||
apipie-rails
|
||||
blueprinter
|
||||
rails (>= 8.0)
|
||||
|
||||
@@ -19,7 +19,6 @@ ActiveAdmin.register Store do
|
||||
end
|
||||
column :updated_at
|
||||
actions defaults: true do |store|
|
||||
|
||||
link_to store.active? ? "Hide" : "List",
|
||||
toggle_admin_store_path(store),
|
||||
method: :put
|
||||
|
||||
@@ -15,7 +15,6 @@ class Api::StoresController < ApiController
|
||||
returns code: 200, desc: "Array of stores" do
|
||||
property :name, String, desc: "Display name of the store"
|
||||
property :catalogUrl, String, desc: "Base URL of the WarpEngine catalog it serves"
|
||||
|
||||
end
|
||||
def index
|
||||
render json: StoreService.new.index
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
class StoreService
|
||||
|
||||
def index
|
||||
StoreSerializer.render_as_hash(Store.active.ordered)
|
||||
end
|
||||
|
||||
@@ -10,7 +10,7 @@ RSpec.describe Api::StoresController, type: :request do
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
json = JSON.parse(response.body)
|
||||
expect(json.map { |s| s["name"] }).to eq(["Apex Games", "Zed Games"])
|
||||
expect(json.map { |s| s["name"] }).to eq([ "Apex Games", "Zed Games" ])
|
||||
end
|
||||
|
||||
it "answers with the fields a client needs, camelCased" do
|
||||
|
||||
@@ -9,7 +9,7 @@ RSpec.describe Event, type: :model do
|
||||
future = create(:event, date: 1.week.from_now)
|
||||
create(:event, date: 1.week.ago)
|
||||
|
||||
expect(Event.upcoming).to eq([future])
|
||||
expect(Event.upcoming).to eq([ future ])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@ RSpec.describe Store, type: :model do
|
||||
later = create(:store, name: "Zed Games")
|
||||
first = create(:store, name: "Apex Games")
|
||||
|
||||
expect(Store.ordered).to eq([first, later])
|
||||
expect(Store.ordered).to eq([ first, later ])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
inherit_gem:
|
||||
rubocop-rails-omakase: rubocop.yml
|
||||
|
||||
AllCops:
|
||||
NewCops: enable
|
||||
TargetRubyVersion: 3.2
|
||||
Exclude:
|
||||
- "bin/**/*"
|
||||
- "db/**/*"
|
||||
- "config/**/*"
|
||||
- "examples/**/*"
|
||||
- "spec/dummy/**/*"
|
||||
- "vendor/**/*"
|
||||
@@ -10,4 +10,5 @@ group :development, :test do
|
||||
gem "shoulda-matchers", "~> 6.0"
|
||||
gem "webmock", "~> 3.0"
|
||||
gem "debug", platforms: %i[mri windows]
|
||||
gem "rubocop-rails-omakase", require: false
|
||||
end
|
||||
|
||||
@@ -761,6 +761,16 @@ bundle exec rspec
|
||||
## Development
|
||||
|
||||
This repository is a **read-only split mirror** — development happens in the
|
||||
[`tools/teletypegames`](https://git.teletypegames.org/tools/teletypegames)
|
||||
[`services/teletypegames`](https://git.teletypegames.org/services/teletypegames)
|
||||
monorepo under `libs/ruby/warp_engine`, and CI republishes the mirror on every
|
||||
change. Please do not open pull requests against the mirror.
|
||||
|
||||
Two rules the monorepo's pipeline enforces before anything is mirrored or
|
||||
published:
|
||||
|
||||
- **Every commit that touches the engine raises `WarpEngine::VERSION`.** A
|
||||
pre-commit hook checks it locally, the pipeline's first step checks it again
|
||||
over the pushed commits, and a `warp_engine-v*` tag must name the version the
|
||||
tree actually holds.
|
||||
- **RuboCop is green** (`.rubocop.yml`, rubocop-rails-omakase), and the suite
|
||||
passes — in that order, before the gem is built.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
module WarpEngine
|
||||
|
||||
module SubjectAuthentication
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
module WarpEngine
|
||||
|
||||
module UpdateAuthentication
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
module WarpEngine
|
||||
|
||||
class Api::Auth::DevicesController < ApiController
|
||||
before_action :ensure_identity_configured
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
module WarpEngine
|
||||
|
||||
class Api::Auth::TokensController < ApiController
|
||||
resource_description do
|
||||
short "Client tokens"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
module WarpEngine
|
||||
|
||||
class Api::ServiceController < ApiController
|
||||
resource_description do
|
||||
short "Service descriptor"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
module WarpEngine
|
||||
module Build
|
||||
|
||||
class ConfigsController < ApiController
|
||||
resource_description do
|
||||
short "CI pipeline configs"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
module WarpEngine
|
||||
|
||||
class DeviceGrant < ApplicationRecord
|
||||
self.table_name = "device_grants"
|
||||
|
||||
@@ -58,7 +57,6 @@ module WarpEngine
|
||||
end
|
||||
|
||||
def self.generate_user_code
|
||||
|
||||
10.times do
|
||||
candidate = Array.new(USER_CODE_LENGTH) { USER_CODE_ALPHABET.chars.sample }.join
|
||||
return candidate unless exists?(user_code: candidate)
|
||||
|
||||
@@ -16,7 +16,7 @@ module WarpEngine
|
||||
validate :c64_cannot_be_web_playable
|
||||
|
||||
def self.latest_non_dev(releases)
|
||||
sorted = releases.sort_by { |r| [r.created_at || Time.at(0), r.id] }.reverse
|
||||
sorted = releases.sort_by { |r| [ r.created_at || Time.at(0), r.id ] }.reverse
|
||||
candidates = sorted.reject { |r| r.version.to_s.start_with?("dev-") }
|
||||
candidates.empty? ? sorted.first : candidates.first
|
||||
end
|
||||
|
||||
-1
@@ -1,7 +1,6 @@
|
||||
module WarpEngine
|
||||
module Platforms
|
||||
module Builds
|
||||
|
||||
module BuildLinuxArm64
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
module WarpEngine
|
||||
|
||||
class DeviceGrantService
|
||||
class NotConfigured < StandardError; end
|
||||
class UnknownCode < StandardError; end
|
||||
|
||||
@@ -2,7 +2,6 @@ require "warp_engine/access_denied"
|
||||
|
||||
module WarpEngine
|
||||
class DownloadService
|
||||
|
||||
Denied = WarpEngine::AccessDenied
|
||||
|
||||
include AccessPolicyGuard
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
module WarpEngine
|
||||
class FileManagerService
|
||||
|
||||
def base_path
|
||||
@base_path ||= Pathname.new(WarpEngine.config.file_container_path)
|
||||
end
|
||||
|
||||
@@ -2,7 +2,6 @@ require "warp_engine/access_denied"
|
||||
|
||||
module WarpEngine
|
||||
class FileService
|
||||
|
||||
include AccessPolicyGuard
|
||||
|
||||
def show(input, subject: nil)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
module WarpEngine
|
||||
class PublishService
|
||||
|
||||
NOTIFICATION = "warp_engine.publish".freeze
|
||||
|
||||
def publish(input)
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace :warp_engine do
|
||||
checklist = WarpEngine::AssetCoverageChecklist.new(
|
||||
coverage, only_missing: ENV["ONLY"] == "missing"
|
||||
)
|
||||
puts checklist.to_s
|
||||
puts checklist
|
||||
|
||||
if ENV["STRICT"] == "1" && coverage.totals[:missing].positive?
|
||||
abort "\nSTRICT: #{coverage.totals[:missing]} expected assets are missing."
|
||||
|
||||
@@ -13,7 +13,6 @@ require "warp_engine/images"
|
||||
require "warp_engine/ci"
|
||||
|
||||
module WarpEngine
|
||||
|
||||
def self.table_name_prefix
|
||||
""
|
||||
end
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
module WarpEngine
|
||||
|
||||
module AccessPolicy
|
||||
|
||||
class Open
|
||||
def visible_software_scope(subject: nil)
|
||||
WarpEngine::Software.kept
|
||||
@@ -39,7 +37,6 @@ module WarpEngine
|
||||
end
|
||||
|
||||
class Access
|
||||
|
||||
attr_reader :gated, :entitled, :price_cents, :currency, :purchase_url, :web_url
|
||||
|
||||
def initialize(gated: false, entitled: true, price_cents: nil, currency: nil,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
module WarpEngine
|
||||
|
||||
module CI
|
||||
class Error < StandardError; end
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
module WarpEngine
|
||||
class Configuration
|
||||
|
||||
attr_accessor :site_url,
|
||||
:file_container_path,
|
||||
:update_secret,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
module WarpEngine
|
||||
|
||||
module Images
|
||||
|
||||
class HostModel
|
||||
URL_PREFIX = "/api/image".freeze
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
module WarpEngine
|
||||
|
||||
module Storage
|
||||
Location = Struct.new(:kind, :path, :url, keyword_init: true) do
|
||||
def file? = kind == :file
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module WarpEngine
|
||||
VERSION = "0.9.0"
|
||||
VERSION = "0.9.1"
|
||||
|
||||
VERSION_HEADER = "WarpEngine-Version".freeze
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@ RSpec.describe WarpEngine::CI::Gitlab::Client do
|
||||
|
||||
describe "repos" do
|
||||
it "lists repos" do
|
||||
repos = [{ "id" => 1, "path" => "game1" }]
|
||||
repos = [ { "id" => 1, "path" => "game1" } ]
|
||||
stub_gl(:get, "/api/v4/projects?membership=true&per_page=100&simple=true", body: repos)
|
||||
|
||||
expect(client.list_repos).to eq(repos)
|
||||
@@ -39,7 +39,7 @@ RSpec.describe WarpEngine::CI::Gitlab::Client do
|
||||
|
||||
describe "secrets (variables)" do
|
||||
it "lists variables" do
|
||||
vars = [{ "key" => "application_token" }]
|
||||
vars = [ { "key" => "application_token" } ]
|
||||
stub_gl(:get, "/api/v4/projects/1/variables", body: vars)
|
||||
|
||||
expect(client.list_secrets(1)).to eq(vars)
|
||||
@@ -68,7 +68,7 @@ RSpec.describe WarpEngine::CI::Gitlab::Client do
|
||||
|
||||
describe "pipelines" do
|
||||
it "lists pipelines" do
|
||||
pipelines = [{ "id" => 1, "status" => "success" }]
|
||||
pipelines = [ { "id" => 1, "status" => "success" } ]
|
||||
stub_gl(:get, "/api/v4/projects/42/pipelines?page=1&per_page=25", body: pipelines)
|
||||
|
||||
expect(client.list_pipelines(42)).to eq(pipelines)
|
||||
@@ -76,7 +76,7 @@ RSpec.describe WarpEngine::CI::Gitlab::Client do
|
||||
|
||||
it "gets latest pipeline" do
|
||||
pipeline = { "id" => 5, "status" => "running" }
|
||||
stub_gl(:get, "/api/v4/projects/42/pipelines?per_page=1&sort=desc", body: [pipeline])
|
||||
stub_gl(:get, "/api/v4/projects/42/pipelines?per_page=1&sort=desc", body: [ pipeline ])
|
||||
|
||||
expect(client.latest_pipeline(42)).to eq(pipeline)
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@ RSpec.describe WarpEngine::CI::Woodpecker::Client do
|
||||
|
||||
describe "repos" do
|
||||
it "lists repos" do
|
||||
repos = [{ "id" => 1, "name" => "game1" }]
|
||||
repos = [ { "id" => 1, "name" => "game1" } ]
|
||||
stub_wp(:get, "/api/repos", body: repos)
|
||||
|
||||
expect(client.list_repos).to eq(repos)
|
||||
@@ -37,7 +37,7 @@ RSpec.describe WarpEngine::CI::Woodpecker::Client do
|
||||
|
||||
describe "secrets" do
|
||||
it "lists secrets" do
|
||||
secrets = [{ "name" => "application_token" }]
|
||||
secrets = [ { "name" => "application_token" } ]
|
||||
stub_wp(:get, "/api/repos/1/secrets", body: secrets)
|
||||
|
||||
expect(client.list_secrets(1)).to eq(secrets)
|
||||
@@ -66,7 +66,7 @@ RSpec.describe WarpEngine::CI::Woodpecker::Client do
|
||||
|
||||
describe "pipelines" do
|
||||
it "lists pipelines" do
|
||||
pipelines = [{ "number" => 1, "status" => "success" }]
|
||||
pipelines = [ { "number" => 1, "status" => "success" } ]
|
||||
stub_wp(:get, "/api/repos/42/pipelines?page=1&perPage=25", body: pipelines)
|
||||
|
||||
expect(client.list_pipelines(42)).to eq(pipelines)
|
||||
|
||||
@@ -20,12 +20,13 @@ RSpec.describe WarpEngine::PlatformLink, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe "default scope" do
|
||||
it "excludes soft-deleted records" do
|
||||
active = create(:platform_link)
|
||||
create(:platform_link, deleted_at: Time.current)
|
||||
describe "scopes" do
|
||||
it "keeps soft-deleted records out of .kept, not out of .all" do
|
||||
active = create(:platform_link)
|
||||
deleted = create(:platform_link, deleted_at: Time.current)
|
||||
|
||||
expect(WarpEngine::PlatformLink.all).to eq([active])
|
||||
expect(WarpEngine::PlatformLink.kept).to eq([ active ])
|
||||
expect(WarpEngine::PlatformLink.all).to contain_exactly(active, deleted)
|
||||
end
|
||||
|
||||
it "does not order: ordering is asked for, not inherited" do
|
||||
@@ -52,7 +53,7 @@ RSpec.describe WarpEngine::PlatformLink, type: :model do
|
||||
create(:platform_link, platform: "love")
|
||||
|
||||
result = WarpEngine::PlatformLink.for_platform("tic80")
|
||||
expect(result).to eq([tic80_link])
|
||||
expect(result).to eq([ tic80_link ])
|
||||
end
|
||||
|
||||
it "returns empty array for platform without links" do
|
||||
|
||||
@@ -18,7 +18,7 @@ RSpec.describe WarpEngine::Software, type: :model do
|
||||
active = create(:software)
|
||||
create(:software, deleted_at: Time.current)
|
||||
|
||||
expect(WarpEngine::Software.kept).to eq([active])
|
||||
expect(WarpEngine::Software.kept).to eq([ active ])
|
||||
end
|
||||
|
||||
it "includes soft-deleted records without scope" do
|
||||
|
||||
@@ -11,7 +11,7 @@ RSpec.describe WarpEngine::BuildsService do
|
||||
end
|
||||
|
||||
it "returns c64 with only cartridge" do
|
||||
expect(result[:platforms]["c64"][:kinds]).to eq(["cartridge"])
|
||||
expect(result[:platforms]["c64"][:kinds]).to eq([ "cartridge" ])
|
||||
end
|
||||
|
||||
it "returns allKinds matching WarpEngine::ReleaseAsset::KINDS" do
|
||||
|
||||
Executable
+119
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "rubygems"
|
||||
|
||||
ENGINE_PREFIX = "libs/ruby/warp_engine/".freeze
|
||||
VERSION_PATH = "#{ENGINE_PREFIX}lib/warp_engine/version.rb".freeze
|
||||
TAG_PREFIX = "warp_engine-v".freeze
|
||||
SKIP_ENV = "SKIP_WARP_ENGINE_VERSION_CHECK".freeze
|
||||
|
||||
def die(message)
|
||||
warn "\e[31mwarp_engine verzióellenőrzés\e[0m: #{message}"
|
||||
exit 1
|
||||
end
|
||||
|
||||
def ok(message)
|
||||
puts "warp_engine verzióellenőrzés: #{message}"
|
||||
exit 0
|
||||
end
|
||||
|
||||
def git(*args)
|
||||
output = IO.popen([ "git", *args ], err: File::NULL, &:read)
|
||||
$?.success? ? output : nil
|
||||
end
|
||||
|
||||
def blank_rev?(rev)
|
||||
rev.nil? || rev.empty? || rev.match?(/\A0+\z/)
|
||||
end
|
||||
|
||||
def version_in(spec)
|
||||
source = git("show", spec)
|
||||
return nil if source.nil? || source.empty?
|
||||
|
||||
match = source.match(/VERSION\s*=\s*["']([^"']+)["']/)
|
||||
return nil if match.nil?
|
||||
|
||||
Gem::Version.new(match[1])
|
||||
rescue ArgumentError
|
||||
nil
|
||||
end
|
||||
|
||||
def engine_files(*diff_args)
|
||||
output = git("diff", *diff_args) or die("a git diff nem futott le (#{diff_args.join(' ')})")
|
||||
output.lines.map(&:chomp).select { |path| path.start_with?(ENGINE_PREFIX) }
|
||||
end
|
||||
|
||||
def bumped!(from, to, changed, how)
|
||||
die("a #{VERSION_PATH} nem olvasható #{how}") if to.nil?
|
||||
|
||||
if from.nil?
|
||||
ok("#{to} — korábbi verzió nem volt, elfogadva")
|
||||
end
|
||||
|
||||
if to > from
|
||||
puts "warp_engine verzióellenőrzés: #{from} -> #{to}, rendben (#{changed.size} érintett fájl)"
|
||||
return
|
||||
end
|
||||
|
||||
die(<<~MESSAGE)
|
||||
a warp_engine módosult, de a verzió nem emelkedett (#{from} -> #{to}).
|
||||
|
||||
Érintett fájlok:
|
||||
#{changed.first(10).map { |path| " #{path}" }.join("\n")}#{changed.size > 10 ? "\n ... és még #{changed.size - 10}" : ''}
|
||||
|
||||
Emeld a verziót itt: #{VERSION_PATH}
|
||||
A gem a #{TAG_PREFIX}<verzió> tagre kerül ki a registrybe.
|
||||
|
||||
Kihagyás (csak indokolt esetben): #{SKIP_ENV}=1, vagy git commit --no-verify
|
||||
MESSAGE
|
||||
end
|
||||
|
||||
def check_staged
|
||||
changed = engine_files("--cached", "--name-only", "--diff-filter=ACMRD")
|
||||
ok("a commit nem érinti a warp_engine-t") if changed.empty?
|
||||
ok("első commit, elfogadva") if git("rev-parse", "--verify", "HEAD").nil?
|
||||
|
||||
bumped!(version_in("HEAD:#{VERSION_PATH}"), version_in(":#{VERSION_PATH}"), changed, "az indexből")
|
||||
end
|
||||
|
||||
def check_range(from, to)
|
||||
ok("nincs korábbi commit a push-ban, elfogadva") if blank_rev?(from)
|
||||
to = "HEAD" if blank_rev?(to)
|
||||
|
||||
changed = engine_files("--name-only", from, to)
|
||||
ok("a push nem érinti a warp_engine-t") if changed.empty?
|
||||
|
||||
bumped!(version_in("#{from}:#{VERSION_PATH}"), version_in("#{to}:#{VERSION_PATH}"), changed, "a #{to} commitban")
|
||||
end
|
||||
|
||||
def check_tag(ref)
|
||||
tag = ref.to_s.sub(%r{\Arefs/tags/}, "")
|
||||
die("a(z) #{tag} tag neve nem #{TAG_PREFIX}<verzió> alakú") unless tag.start_with?(TAG_PREFIX)
|
||||
|
||||
tagged = Gem::Version.new(tag.delete_prefix(TAG_PREFIX))
|
||||
current = version_in("HEAD:#{VERSION_PATH}") or die("a #{VERSION_PATH} nem olvasható a HEAD-ben")
|
||||
|
||||
if tagged != current
|
||||
die("a tag #{tagged}-t hirdet, a #{VERSION_PATH} viszont #{current}-t tartalmaz")
|
||||
end
|
||||
|
||||
ok("a tag és a VERSION egyezik (#{current})")
|
||||
rescue ArgumentError
|
||||
die("a(z) #{tag} tagből nem olvasható verzió")
|
||||
end
|
||||
|
||||
ok("kihagyva (#{SKIP_ENV})") if ENV[SKIP_ENV] == "1"
|
||||
|
||||
case ARGV[0]
|
||||
when "--staged" then check_staged
|
||||
when "--range" then check_range(ARGV[1], ARGV[2])
|
||||
when "--tag" then check_tag(ARGV[1])
|
||||
else
|
||||
warn <<~USAGE
|
||||
Használat:
|
||||
script/warp_engine_version_check.rb --staged
|
||||
script/warp_engine_version_check.rb --range <elozo-sha> <mostani-sha>
|
||||
script/warp_engine_version_check.rb --tag <refs/tags/warp_engine-vX.Y.Z>
|
||||
USAGE
|
||||
exit 2
|
||||
end
|
||||
Reference in New Issue
Block a user