Merge pull request #321 from stencilproject/feature/pr-automation
PR checks automation
This commit is contained in:
7
.github/dependabot.yml
vendored
Normal file
7
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
# Maintain dependencies for GitHub Actions
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
25
.github/workflows/danger.yml
vendored
Normal file
25
.github/workflows/danger.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: Danger
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Danger Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
-
|
||||||
|
name: Setup Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
-
|
||||||
|
name: Run Danger
|
||||||
|
run: bundle exec danger --verbose --dangerfile=rakelib/Dangerfile
|
||||||
|
env:
|
||||||
|
DANGER_GITHUB_API_TOKEN: ${{ secrets.danger_github_api_token }}
|
||||||
23
.github/workflows/lint-cocoapods.yml
vendored
Normal file
23
.github/workflows/lint-cocoapods.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: Lint Cocoapods
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: Pod Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
-
|
||||||
|
name: Setup Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
-
|
||||||
|
name: Lint podspec
|
||||||
|
run: bundle exec rake pod:lint
|
||||||
23
.github/workflows/release-check-versions.yml
vendored
Normal file
23
.github/workflows/release-check-versions.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: Check Versions
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'release/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check_versions:
|
||||||
|
name: Check Versions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
-
|
||||||
|
name: Setup Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
-
|
||||||
|
name: Check versions
|
||||||
|
run: bundle exec rake release:check_versions
|
||||||
26
.github/workflows/swiftlint.yml
vendored
Normal file
26
.github/workflows/swiftlint.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
name: SwiftLint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: SwiftLint
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
-
|
||||||
|
name: Setup Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
-
|
||||||
|
name: Lint source code
|
||||||
|
run: bundle exec rake lint:code
|
||||||
|
-
|
||||||
|
name: Lint tests source code
|
||||||
|
run: bundle exec rake lint:tests
|
||||||
44
.github/workflows/tag-publish.yml
vendored
Normal file
44
.github/workflows/tag-publish.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
name: Publish on Tag
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cocoapods:
|
||||||
|
name: Push To CocoaPods
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
-
|
||||||
|
name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
-
|
||||||
|
name: Push to CocoaPods
|
||||||
|
run: bundle exec rake release:cocoapods
|
||||||
|
env:
|
||||||
|
COCOAPODS_TRUNK_TOKEN: ${{secrets.COCOAPODS_TRUNK_TOKEN}}
|
||||||
|
|
||||||
|
github:
|
||||||
|
name: GitHub Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
-
|
||||||
|
name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
-
|
||||||
|
name: Create release on GitHub
|
||||||
|
run: bundle exec rake release:github
|
||||||
|
env:
|
||||||
|
DANGER_GITHUB_API_TOKEN: ${{ secrets.danger_github_api_token }}
|
||||||
66
.github/workflows/test-spm.yml
vendored
Normal file
66
.github/workflows/test-spm.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
name: Test SPM
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linux:
|
||||||
|
name: Test SPM Linux
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: swiftgen/swift:5.6
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
-
|
||||||
|
# Note: we can't use `ruby/setup-ruby` on custom docker images, so we
|
||||||
|
# have to do our own caching
|
||||||
|
name: Cache gems
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: vendor/bundle
|
||||||
|
key: ${{ runner.os }}-gems-${{ hashFiles('Gemfile.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gems-
|
||||||
|
-
|
||||||
|
name: Cache SPM
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: .build
|
||||||
|
key: ${{ runner.os }}-spm-${{ hashFiles('Package.resolved') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-spm-
|
||||||
|
-
|
||||||
|
name: Bundle install
|
||||||
|
run: |
|
||||||
|
bundle config path vendor/bundle
|
||||||
|
bundle install --jobs 4 --retry 3
|
||||||
|
-
|
||||||
|
name: Run tests
|
||||||
|
run: bundle exec rake spm:test
|
||||||
|
|
||||||
|
macos:
|
||||||
|
name: Test SPM macOS
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
-
|
||||||
|
name: Setup Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
-
|
||||||
|
name: Cache SPM
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: .build
|
||||||
|
key: ${{ runner.os }}-spm-${{ hashFiles('Package.resolved') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-spm-
|
||||||
|
-
|
||||||
|
name: Run tests
|
||||||
|
run: bundle exec rake spm:test
|
||||||
77
.gitignore
vendored
77
.gitignore
vendored
@@ -1,6 +1,75 @@
|
|||||||
.conche/
|
# Xcode
|
||||||
|
#
|
||||||
|
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||||
|
|
||||||
|
## Build generated
|
||||||
|
build/
|
||||||
|
DerivedData/
|
||||||
|
Fixtures/stub-env/**/*.swiftmodule
|
||||||
|
|
||||||
|
## Various settings
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
xcuserdata/
|
||||||
|
|
||||||
|
## Other
|
||||||
|
*.moved-aside
|
||||||
|
*.xcuserstate
|
||||||
|
*.xcscmblueprint
|
||||||
|
|
||||||
|
## Obj-C/Swift specific
|
||||||
|
*.hmap
|
||||||
|
*.ipa
|
||||||
|
*.dSYM.zip
|
||||||
|
*.dSYM
|
||||||
|
|
||||||
|
## Playgrounds
|
||||||
|
timeline.xctimeline
|
||||||
|
playground.xcworkspace
|
||||||
|
|
||||||
|
# Swift Package Manager
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||||
|
Packages/
|
||||||
.build/
|
.build/
|
||||||
.swiftpm/
|
.swiftpm/
|
||||||
Packages/
|
|
||||||
Package.pins
|
# CocoaPods
|
||||||
*.xcodeproj
|
#
|
||||||
|
# We recommend against adding the Pods directory to your .gitignore. However
|
||||||
|
# you should judge for yourself, the pros and cons are mentioned at:
|
||||||
|
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||||
|
#
|
||||||
|
# Pods/
|
||||||
|
|
||||||
|
# Carthage
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||||
|
Carthage/Checkouts
|
||||||
|
Carthage/Build
|
||||||
|
|
||||||
|
# fastlane
|
||||||
|
#
|
||||||
|
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||||
|
# screenshots whenever they are needed.
|
||||||
|
# For more information about the recommended setup visit:
|
||||||
|
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
|
||||||
|
|
||||||
|
fastlane/report.xml
|
||||||
|
fastlane/Preview.html
|
||||||
|
fastlane/screenshots
|
||||||
|
fastlane/test_output
|
||||||
|
|
||||||
|
# Other stuff
|
||||||
|
.apitoken
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
bin/
|
||||||
|
Frameworks/
|
||||||
|
Rome/
|
||||||
|
|||||||
1
.ruby-version
Normal file
1
.ruby-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.0.4
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
swiftlint_version: 0.39.2
|
swiftlint_version: 0.48.0
|
||||||
|
|
||||||
disabled_rules:
|
disabled_rules:
|
||||||
# Remove this once we remove old swift support
|
# Remove this once we remove old swift support
|
||||||
|
|||||||
22
.travis.yml
22
.travis.yml
@@ -1,22 +0,0 @@
|
|||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: osx
|
|
||||||
osx_image: xcode11.4
|
|
||||||
env: SWIFT_VERSION=4.2
|
|
||||||
- os: osx
|
|
||||||
osx_image: xcode11.4
|
|
||||||
env: SWIFT_VERSION=5.0
|
|
||||||
- os: linux
|
|
||||||
env: SWIFT_VERSION=4.2
|
|
||||||
- os: linux
|
|
||||||
env: SWIFT_VERSION=5.0
|
|
||||||
language: generic
|
|
||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
install:
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then eval "$(curl -sL https://swiftenv.fuller.li/install.sh)"; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then wget --output-document /tmp/SwiftLint.pkg https://github.com/realm/SwiftLint/releases/download/0.39.2/SwiftLint.pkg &&
|
|
||||||
sudo installer -pkg /tmp/SwiftLint.pkg -target /; fi
|
|
||||||
script:
|
|
||||||
- swift test
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then swiftlint; fi
|
|
||||||
12
CHANGELOG.md
12
CHANGELOG.md
@@ -20,7 +20,9 @@ _None_
|
|||||||
|
|
||||||
### Internal Changes
|
### Internal Changes
|
||||||
|
|
||||||
_None_
|
- Updated internal maintenance scripts, and switched to GitHub actions.
|
||||||
|
[David Jennes](https://github.com/djbe)
|
||||||
|
[#321](https://github.com/stencilproject/Stencil/pull/321)
|
||||||
|
|
||||||
## 0.14.2
|
## 0.14.2
|
||||||
|
|
||||||
@@ -179,7 +181,7 @@ _None_
|
|||||||
- The `{% for %}` tag can now iterate over tuples, structures and classes via
|
- The `{% for %}` tag can now iterate over tuples, structures and classes via
|
||||||
their stored properties.
|
their stored properties.
|
||||||
[Ilya Puchka](https://github.com/ilyapuchka)
|
[Ilya Puchka](https://github.com/ilyapuchka)
|
||||||
[#172](https://github.com/stencilproject/Stencil/pull/173)
|
[#173](https://github.com/stencilproject/Stencil/pull/173)
|
||||||
- Added `split` filter.
|
- Added `split` filter.
|
||||||
[Ilya Puchka](https://github.com/ilyapuchka)
|
[Ilya Puchka](https://github.com/ilyapuchka)
|
||||||
[#187](https://github.com/stencilproject/Stencil/pull/187)
|
[#187](https://github.com/stencilproject/Stencil/pull/187)
|
||||||
@@ -288,7 +290,7 @@ _None_
|
|||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- You can now use literal filter arguments which contain quotes.
|
- You can now use literal filter arguments which contain quotes.
|
||||||
[#98](https://github.com/kylef/Stencil/pull/98)
|
[#98](https://github.com/stencilproject/Stencil/pull/98)
|
||||||
|
|
||||||
|
|
||||||
## 0.8.0
|
## 0.8.0
|
||||||
@@ -432,10 +434,10 @@ _None_
|
|||||||
|
|
||||||
- Variables (`{{ variable.5 }}`) that reference an array index at an unknown
|
- Variables (`{{ variable.5 }}`) that reference an array index at an unknown
|
||||||
index will now resolve to `nil` instead of causing a crash.
|
index will now resolve to `nil` instead of causing a crash.
|
||||||
[#72](https://github.com/kylef/Stencil/issues/72)
|
[#72](https://github.com/stencilproject/Stencil/issues/72)
|
||||||
|
|
||||||
- Templates can now extend templates that extend other templates.
|
- Templates can now extend templates that extend other templates.
|
||||||
[#60](https://github.com/kylef/Stencil/issues/60)
|
[#60](https://github.com/stencilproject/Stencil/issues/60)
|
||||||
|
|
||||||
- If comparisons will now treat 0 and below numbers as negative.
|
- If comparisons will now treat 0 and below numbers as negative.
|
||||||
|
|
||||||
|
|||||||
22
Gemfile
22
Gemfile
@@ -1,7 +1,21 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
source "https://rubygems.org"
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
gem "octokit"
|
# The bare minimum for building, e.g. in Homebrew
|
||||||
gem "cocoapods"
|
group :build do
|
||||||
gem "rake"
|
gem 'rake', '~> 13.0'
|
||||||
|
gem 'xcpretty', '~> 0.3'
|
||||||
|
end
|
||||||
|
|
||||||
|
# In addition to :build, for contributing
|
||||||
|
group :development do
|
||||||
|
gem 'cocoapods', '~> 1.11'
|
||||||
|
gem 'danger', '~> 8.4'
|
||||||
|
gem 'rubocop', '~> 1.22'
|
||||||
|
end
|
||||||
|
|
||||||
|
# For releasing to GitHub
|
||||||
|
group :release do
|
||||||
|
gem 'octokit', '~> 4.7'
|
||||||
|
end
|
||||||
|
|||||||
122
Gemfile.lock
122
Gemfile.lock
@@ -1,9 +1,9 @@
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (3.0.4)
|
CFPropertyList (3.0.5)
|
||||||
rexml
|
rexml
|
||||||
activesupport (6.1.4.1)
|
activesupport (6.1.6.1)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
@@ -14,12 +14,17 @@ GEM
|
|||||||
algoliasearch (1.27.5)
|
algoliasearch (1.27.5)
|
||||||
httpclient (~> 2.8, >= 2.8.3)
|
httpclient (~> 2.8, >= 2.8.3)
|
||||||
json (>= 1.5.1)
|
json (>= 1.5.1)
|
||||||
|
ast (2.4.2)
|
||||||
atomos (0.1.3)
|
atomos (0.1.3)
|
||||||
claide (1.0.3)
|
claide (1.1.0)
|
||||||
cocoapods (1.11.2)
|
claide-plugins (0.9.2)
|
||||||
|
cork
|
||||||
|
nap
|
||||||
|
open4 (~> 1.3)
|
||||||
|
cocoapods (1.11.3)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
cocoapods-core (= 1.11.2)
|
cocoapods-core (= 1.11.3)
|
||||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||||
cocoapods-downloader (>= 1.4.0, < 2.0)
|
cocoapods-downloader (>= 1.4.0, < 2.0)
|
||||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||||
@@ -34,7 +39,7 @@ GEM
|
|||||||
nap (~> 1.0)
|
nap (~> 1.0)
|
||||||
ruby-macho (>= 1.0, < 3.0)
|
ruby-macho (>= 1.0, < 3.0)
|
||||||
xcodeproj (>= 1.21.0, < 2.0)
|
xcodeproj (>= 1.21.0, < 2.0)
|
||||||
cocoapods-core (1.11.2)
|
cocoapods-core (1.11.3)
|
||||||
activesupport (>= 5.0, < 7)
|
activesupport (>= 5.0, < 7)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
algoliasearch (~> 1.0)
|
algoliasearch (~> 1.0)
|
||||||
@@ -45,7 +50,7 @@ GEM
|
|||||||
public_suffix (~> 4.0)
|
public_suffix (~> 4.0)
|
||||||
typhoeus (~> 1.0)
|
typhoeus (~> 1.0)
|
||||||
cocoapods-deintegrate (1.0.5)
|
cocoapods-deintegrate (1.0.5)
|
||||||
cocoapods-downloader (1.5.1)
|
cocoapods-downloader (1.6.3)
|
||||||
cocoapods-plugins (1.0.0)
|
cocoapods-plugins (1.0.0)
|
||||||
nap
|
nap
|
||||||
cocoapods-search (1.0.1)
|
cocoapods-search (1.0.1)
|
||||||
@@ -54,74 +59,131 @@ GEM
|
|||||||
netrc (~> 0.11)
|
netrc (~> 0.11)
|
||||||
cocoapods-try (1.2.0)
|
cocoapods-try (1.2.0)
|
||||||
colored2 (3.1.2)
|
colored2 (3.1.2)
|
||||||
concurrent-ruby (1.1.9)
|
concurrent-ruby (1.1.10)
|
||||||
|
cork (0.3.0)
|
||||||
|
colored2 (~> 3.1)
|
||||||
|
danger (8.6.1)
|
||||||
|
claide (~> 1.0)
|
||||||
|
claide-plugins (>= 0.9.2)
|
||||||
|
colored2 (~> 3.1)
|
||||||
|
cork (~> 0.1)
|
||||||
|
faraday (>= 0.9.0, < 2.0)
|
||||||
|
faraday-http-cache (~> 2.0)
|
||||||
|
git (~> 1.7)
|
||||||
|
kramdown (~> 2.3)
|
||||||
|
kramdown-parser-gfm (~> 1.0)
|
||||||
|
no_proxy_fix
|
||||||
|
octokit (~> 4.7)
|
||||||
|
terminal-table (>= 1, < 4)
|
||||||
escape (0.0.4)
|
escape (0.0.4)
|
||||||
ethon (0.15.0)
|
ethon (0.15.0)
|
||||||
ffi (>= 1.15.0)
|
ffi (>= 1.15.0)
|
||||||
faraday (1.8.0)
|
faraday (1.10.0)
|
||||||
faraday-em_http (~> 1.0)
|
faraday-em_http (~> 1.0)
|
||||||
faraday-em_synchrony (~> 1.0)
|
faraday-em_synchrony (~> 1.0)
|
||||||
faraday-excon (~> 1.1)
|
faraday-excon (~> 1.1)
|
||||||
faraday-httpclient (~> 1.0.1)
|
faraday-httpclient (~> 1.0)
|
||||||
|
faraday-multipart (~> 1.0)
|
||||||
faraday-net_http (~> 1.0)
|
faraday-net_http (~> 1.0)
|
||||||
faraday-net_http_persistent (~> 1.1)
|
faraday-net_http_persistent (~> 1.0)
|
||||||
faraday-patron (~> 1.0)
|
faraday-patron (~> 1.0)
|
||||||
faraday-rack (~> 1.0)
|
faraday-rack (~> 1.0)
|
||||||
multipart-post (>= 1.2, < 3)
|
faraday-retry (~> 1.0)
|
||||||
ruby2_keywords (>= 0.0.4)
|
ruby2_keywords (>= 0.0.4)
|
||||||
faraday-em_http (1.0.0)
|
faraday-em_http (1.0.0)
|
||||||
faraday-em_synchrony (1.0.0)
|
faraday-em_synchrony (1.0.0)
|
||||||
faraday-excon (1.1.0)
|
faraday-excon (1.1.0)
|
||||||
|
faraday-http-cache (2.4.0)
|
||||||
|
faraday (>= 0.8)
|
||||||
faraday-httpclient (1.0.1)
|
faraday-httpclient (1.0.1)
|
||||||
|
faraday-multipart (1.0.4)
|
||||||
|
multipart-post (~> 2)
|
||||||
faraday-net_http (1.0.1)
|
faraday-net_http (1.0.1)
|
||||||
faraday-net_http_persistent (1.2.0)
|
faraday-net_http_persistent (1.2.0)
|
||||||
faraday-patron (1.0.0)
|
faraday-patron (1.0.0)
|
||||||
faraday-rack (1.0.0)
|
faraday-rack (1.0.0)
|
||||||
ffi (1.15.4)
|
faraday-retry (1.0.3)
|
||||||
|
ffi (1.15.5)
|
||||||
fourflusher (2.3.1)
|
fourflusher (2.3.1)
|
||||||
fuzzy_match (2.0.4)
|
fuzzy_match (2.0.4)
|
||||||
gh_inspector (1.1.3)
|
gh_inspector (1.1.3)
|
||||||
|
git (1.11.0)
|
||||||
|
rchardet (~> 1.8)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (1.8.11)
|
i18n (1.12.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
json (2.6.1)
|
json (2.6.2)
|
||||||
minitest (5.14.4)
|
kramdown (2.4.0)
|
||||||
|
rexml
|
||||||
|
kramdown-parser-gfm (1.1.0)
|
||||||
|
kramdown (~> 2.0)
|
||||||
|
minitest (5.16.2)
|
||||||
molinillo (0.8.0)
|
molinillo (0.8.0)
|
||||||
multipart-post (2.1.1)
|
multipart-post (2.2.3)
|
||||||
nanaimo (0.3.0)
|
nanaimo (0.3.0)
|
||||||
nap (1.1.0)
|
nap (1.1.0)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
octokit (4.21.0)
|
no_proxy_fix (0.1.2)
|
||||||
faraday (>= 0.9)
|
octokit (4.25.1)
|
||||||
sawyer (~> 0.8.0, >= 0.5.3)
|
faraday (>= 1, < 3)
|
||||||
public_suffix (4.0.6)
|
sawyer (~> 0.9)
|
||||||
|
open4 (1.3.4)
|
||||||
|
parallel (1.22.1)
|
||||||
|
parser (3.1.2.0)
|
||||||
|
ast (~> 2.4.1)
|
||||||
|
public_suffix (4.0.7)
|
||||||
|
rainbow (3.1.1)
|
||||||
rake (13.0.6)
|
rake (13.0.6)
|
||||||
|
rchardet (1.8.0)
|
||||||
|
regexp_parser (2.5.0)
|
||||||
rexml (3.2.5)
|
rexml (3.2.5)
|
||||||
|
rouge (2.0.7)
|
||||||
|
rubocop (1.32.0)
|
||||||
|
json (~> 2.3)
|
||||||
|
parallel (~> 1.10)
|
||||||
|
parser (>= 3.1.0.0)
|
||||||
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
|
rexml (>= 3.2.5, < 4.0)
|
||||||
|
rubocop-ast (>= 1.19.1, < 2.0)
|
||||||
|
ruby-progressbar (~> 1.7)
|
||||||
|
unicode-display_width (>= 1.4.0, < 3.0)
|
||||||
|
rubocop-ast (1.19.1)
|
||||||
|
parser (>= 3.1.1.0)
|
||||||
ruby-macho (2.5.1)
|
ruby-macho (2.5.1)
|
||||||
|
ruby-progressbar (1.11.0)
|
||||||
ruby2_keywords (0.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
sawyer (0.8.2)
|
sawyer (0.9.2)
|
||||||
addressable (>= 2.3.5)
|
addressable (>= 2.3.5)
|
||||||
faraday (> 0.8, < 2.0)
|
faraday (>= 0.17.3, < 3)
|
||||||
|
terminal-table (3.0.2)
|
||||||
|
unicode-display_width (>= 1.1.1, < 3)
|
||||||
typhoeus (1.4.0)
|
typhoeus (1.4.0)
|
||||||
ethon (>= 0.9.0)
|
ethon (>= 0.9.0)
|
||||||
tzinfo (2.0.4)
|
tzinfo (2.0.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
xcodeproj (1.21.0)
|
unicode-display_width (2.2.0)
|
||||||
|
xcodeproj (1.22.0)
|
||||||
CFPropertyList (>= 2.3.3, < 4.0)
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
atomos (~> 0.1.3)
|
atomos (~> 0.1.3)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
colored2 (~> 3.1)
|
colored2 (~> 3.1)
|
||||||
nanaimo (~> 0.3.0)
|
nanaimo (~> 0.3.0)
|
||||||
rexml (~> 3.2.4)
|
rexml (~> 3.2.4)
|
||||||
zeitwerk (2.5.1)
|
xcpretty (0.3.0)
|
||||||
|
rouge (~> 2.0.7)
|
||||||
|
zeitwerk (2.6.0)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
cocoapods
|
cocoapods (~> 1.11)
|
||||||
octokit
|
danger (~> 8.4)
|
||||||
rake
|
octokit (~> 4.7)
|
||||||
|
rake (~> 13.0)
|
||||||
|
rubocop (~> 1.22)
|
||||||
|
xcpretty (~> 0.3)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.1.4
|
2.2.33
|
||||||
|
|||||||
50
Rakefile
50
Rakefile
@@ -1,10 +1,50 @@
|
|||||||
|
#!/usr/bin/rake
|
||||||
|
|
||||||
PODSPEC_FILE = 'Stencil.podspec.json'
|
unless defined?(Bundler)
|
||||||
CHANGELOG_FILE = 'CHANGELOG.md'
|
puts 'Please use bundle exec to run the rake command'
|
||||||
|
|
||||||
if ENV['BUNDLE_GEMFILE'].nil?
|
|
||||||
puts "\u{274C} Please use bundle exec"
|
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
require 'English'
|
||||||
|
|
||||||
|
## [ Constants ] ##############################################################
|
||||||
|
|
||||||
|
POD_NAME = 'Stencil'
|
||||||
|
MIN_XCODE_VERSION = 13.0
|
||||||
|
BUILD_DIR = File.absolute_path('./.build')
|
||||||
|
|
||||||
|
## [ Build Tasks ] ############################################################
|
||||||
|
|
||||||
|
namespace :files do
|
||||||
|
desc 'Update all files containing a version'
|
||||||
|
task :update, [:version] do |_, args|
|
||||||
|
version = args[:version]
|
||||||
|
|
||||||
|
Utils.print_header "Updating files for version #{version}"
|
||||||
|
|
||||||
|
podspec = Utils.podspec(POD_NAME)
|
||||||
|
podspec['version'] = version
|
||||||
|
podspec['source']['tag'] = version
|
||||||
|
File.write("#{POD_NAME}.podspec.json", JSON.pretty_generate(podspec) + "\n")
|
||||||
|
|
||||||
|
replace('CHANGELOG.md', '## Master' => "\#\# #{version}")
|
||||||
|
replace("docs/conf.py",
|
||||||
|
/^version = .*/ => %Q(version = '#{version}'),
|
||||||
|
/^release = .*/ => %Q(release = '#{version}')
|
||||||
|
)
|
||||||
|
replace("docs/installation.rst",
|
||||||
|
/pod 'Stencil', '.*'/ => %Q(pod 'Stencil', '~> #{version}'),
|
||||||
|
/github "stencilproject\/Stencil" ~> .*/ => %Q(github "stencilproject/Stencil" ~> #{version})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def replace(file, replacements)
|
||||||
|
content = File.read(file)
|
||||||
|
replacements.each do |match, replacement|
|
||||||
|
content.gsub!(match, replacement)
|
||||||
|
end
|
||||||
|
File.write(file, content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
task :default => 'release:new'
|
task :default => 'release:new'
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ open class Extension {
|
|||||||
|
|
||||||
/// Registers a template filter with the given name
|
/// Registers a template filter with the given name
|
||||||
public func registerFilter(_ name: String, filter: @escaping (Any?, [Any?]) throws -> Any?) {
|
public func registerFilter(_ name: String, filter: @escaping (Any?, [Any?]) throws -> Any?) {
|
||||||
filters[name] = .arguments({ value, args, _ in try filter(value, args) })
|
filters[name] = .arguments { value, args, _ in try filter(value, args) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers a template filter with the given name
|
/// Registers a template filter with the given name
|
||||||
|
|||||||
@@ -35,10 +35,8 @@ class BlockContext {
|
|||||||
|
|
||||||
extension Collection {
|
extension Collection {
|
||||||
func any(_ closure: (Iterator.Element) -> Bool) -> Iterator.Element? {
|
func any(_ closure: (Iterator.Element) -> Bool) -> Iterator.Element? {
|
||||||
for element in self {
|
for element in self where closure(element) {
|
||||||
if closure(element) {
|
return element
|
||||||
return element
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
public func until(_ tags: [String]) -> ((TokenParser, Token) -> Bool) {
|
public func until(_ tags: [String]) -> ((TokenParser, Token) -> Bool) {
|
||||||
return { parser, token in
|
return { _, token in
|
||||||
if let name = token.components.first {
|
if let name = token.components.first {
|
||||||
for tag in tags where name == tag {
|
for tag in tags where name == tag {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ public struct Variable: Equatable, Resolvable {
|
|||||||
|
|
||||||
/// Resolve the variable in the given context
|
/// Resolve the variable in the given context
|
||||||
public func resolve(_ context: Context) throws -> Any? {
|
public func resolve(_ context: Context) throws -> Any? {
|
||||||
if variable.count > 1 && ((variable.hasPrefix("'") && variable.hasSuffix("'")) || (variable.hasPrefix("\"") && variable.hasSuffix("\""))) {
|
if variable.count > 1 &&
|
||||||
|
((variable.hasPrefix("'") && variable.hasSuffix("'")) || (variable.hasPrefix("\"") && variable.hasSuffix("\""))) {
|
||||||
// String literal
|
// String literal
|
||||||
return String(variable[variable.index(after: variable.startIndex) ..< variable.index(before: variable.endIndex)])
|
return String(variable[variable.index(after: variable.startIndex) ..< variable.index(before: variable.endIndex)])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
parent_config: ../../.swiftlint.yml
|
||||||
|
|
||||||
disabled_rules: # rule identifiers to exclude from running
|
disabled_rules: # rule identifiers to exclude from running
|
||||||
- type_body_length
|
- type_body_length
|
||||||
- file_length
|
- file_length
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ final class LexerTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testPerformance() throws {
|
func testPerformance() throws {
|
||||||
let path = Path(#file as String) + ".." + "fixtures" + "huge.html"
|
let path = Path(#file as String) + ".." + "fixtures" + "huge.html"
|
||||||
let content: String = try path.read()
|
let content: String = try path.read()
|
||||||
|
|
||||||
measure {
|
measure {
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ final class VariableTests: XCTestCase {
|
|||||||
let result = try variable.resolve(self.context) as? String
|
let result = try variable.resolve(self.context) as? String
|
||||||
try expect(result) == "name"
|
try expect(result) == "name"
|
||||||
}
|
}
|
||||||
|
|
||||||
it("can resolve a string literal with one double quote") {
|
it("can resolve a string literal with one double quote") {
|
||||||
let variable = Variable("\"")
|
let variable = Variable("\"")
|
||||||
let result = try variable.resolve(self.context) as? String
|
let result = try variable.resolve(self.context) as? String
|
||||||
@@ -75,13 +75,13 @@ final class VariableTests: XCTestCase {
|
|||||||
let result = try variable.resolve(self.context) as? String
|
let result = try variable.resolve(self.context) as? String
|
||||||
try expect(result) == "name"
|
try expect(result) == "name"
|
||||||
}
|
}
|
||||||
|
|
||||||
it("can resolve a string literal with one single quote") {
|
it("can resolve a string literal with one single quote") {
|
||||||
let variable = Variable("'")
|
let variable = Variable("'")
|
||||||
let result = try variable.resolve(self.context) as? String
|
let result = try variable.resolve(self.context) as? String
|
||||||
try expect(result).to.beNil()
|
try expect(result).to.beNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
it("can resolve an integer literal") {
|
it("can resolve an integer literal") {
|
||||||
let variable = Variable("5")
|
let variable = Variable("5")
|
||||||
let result = try variable.resolve(self.context) as? Int
|
let result = try variable.resolve(self.context) as? Int
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ dependencies inside ``Package.swift``.
|
|||||||
let package = Package(
|
let package = Package(
|
||||||
name: "MyApplication",
|
name: "MyApplication",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.Package(url: "https://github.com/stencilproject/Stencil.git", majorVersion: 0, minor: 13),
|
.package(url: "https://github.com/stencilproject/Stencil.git", from: "0.14.2"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
97
rakelib/Dangerfile
Normal file
97
rakelib/Dangerfile
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative 'check_changelog'
|
||||||
|
|
||||||
|
is_release = github.branch_for_head.start_with?('release/')
|
||||||
|
is_hotfix = github.branch_for_head.start_with?('hotfix/')
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Welcome message
|
||||||
|
markdown [
|
||||||
|
"Hey 👋 I'm Eve, the friendly bot watching over Stencil 🤖",
|
||||||
|
'Thanks a lot for your contribution!',
|
||||||
|
'', '---', ''
|
||||||
|
]
|
||||||
|
|
||||||
|
need_fixes = []
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Make it more obvious that a PR is a work in progress and shouldn't be merged yet
|
||||||
|
warn('PR is classed as Work in Progress') if github.pr_title.include? '[WIP]'
|
||||||
|
|
||||||
|
# Note when there is a big PR
|
||||||
|
message('Big PR') if git.lines_of_code > 500 && !is_release
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Check for correct base branch
|
||||||
|
if is_release
|
||||||
|
message('This is a Release PR')
|
||||||
|
|
||||||
|
require 'open3'
|
||||||
|
|
||||||
|
stdout, _, status = Open3.capture3('bundle', 'exec', 'rake', 'changelog:check')
|
||||||
|
markdown [
|
||||||
|
'',
|
||||||
|
'### ChangeLog check',
|
||||||
|
'',
|
||||||
|
stdout
|
||||||
|
]
|
||||||
|
need_fixes << fail('Please fix the CHANGELOG errors') unless status.success?
|
||||||
|
|
||||||
|
stdout, _, status = Open3.capture3('bundle', 'exec', 'rake', 'release:check_versions')
|
||||||
|
markdown [
|
||||||
|
'',
|
||||||
|
'### Release version check',
|
||||||
|
'',
|
||||||
|
stdout
|
||||||
|
]
|
||||||
|
need_fixes << fail('Please fix the versions inconsistencies') unless status.success?
|
||||||
|
elsif is_hotfix
|
||||||
|
message('This is a Hotfix PR')
|
||||||
|
end
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Check for a CHANGELOG entry
|
||||||
|
declared_trivial = github.pr_title.include? '#trivial'
|
||||||
|
has_changelog = git.modified_files.include?('CHANGELOG.md')
|
||||||
|
changelog_msg = ''
|
||||||
|
unless has_changelog || declared_trivial
|
||||||
|
repo_url = github.pr_json['head']['repo']['html_url']
|
||||||
|
pr_title = github.pr_title
|
||||||
|
pr_title += '.' unless pr_title.end_with?('.')
|
||||||
|
pr_number = github.pr_json['number']
|
||||||
|
pr_url = github.pr_json['html_url']
|
||||||
|
pr_author = github.pr_author
|
||||||
|
pr_author_url = "https://github.com/#{pr_author}"
|
||||||
|
|
||||||
|
need_fixes = fail("Please include a CHANGELOG entry to credit your work. \nYou can find it at [CHANGELOG.md](#{repo_url}/blob/#{github.branch_for_head}/CHANGELOG.md).")
|
||||||
|
|
||||||
|
changelog_msg = <<-CHANGELOG_FORMAT.gsub(/^ *\|/, '')
|
||||||
|
|📝 We use the following format for CHANGELOG entries:
|
||||||
|
|```
|
||||||
|
|* #{pr_title}
|
||||||
|
| [##{pr_number}](#{pr_url})
|
||||||
|
| [@#{pr_author}](#{pr_author_url})
|
||||||
|
|```
|
||||||
|
|:bulb: Don't forget to end the line describing your changes by a period and two spaces.
|
||||||
|
CHANGELOG_FORMAT
|
||||||
|
# changelog_msg is printed during the "Encouragement message" section, see below
|
||||||
|
end
|
||||||
|
|
||||||
|
changelog_warnings = check_changelog
|
||||||
|
unless changelog_warnings.empty?
|
||||||
|
need_fixes << warn('Found some warnings in CHANGELOG.md')
|
||||||
|
changelog_warnings.each do |warning|
|
||||||
|
warn(warning[:message], file: 'CHANGELOG.md', line: warning[:line])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Encouragement message
|
||||||
|
if need_fixes.empty?
|
||||||
|
markdown('Seems like everything is in order 👍 You did a good job here! 🤝')
|
||||||
|
else
|
||||||
|
markdown('Once you fix those tiny nitpickings above, we should be good to go! 🙌')
|
||||||
|
markdown(changelog_msg) unless changelog_msg.empty?
|
||||||
|
markdown('ℹ️ _I will update this comment as you add new commits_')
|
||||||
|
end
|
||||||
@@ -1,34 +1,56 @@
|
|||||||
NEW_CHANGELOG_SECTION = "## Master\n" + ['Breaking', 'Enhancements', 'Deprecations', 'Bug Fixes', 'Internal Changes'].map do |s|
|
# frozen_string_literal: true
|
||||||
<<~MARKDOWN
|
|
||||||
|
|
||||||
### #{s}
|
# Used constants:
|
||||||
|
# _none_
|
||||||
_None_
|
|
||||||
MARKDOWN
|
|
||||||
end.join
|
|
||||||
|
|
||||||
def changelog_first_section
|
require_relative 'check_changelog'
|
||||||
content = []
|
|
||||||
section_count = 0
|
|
||||||
File.foreach(CHANGELOG_FILE) do |line|
|
|
||||||
section_count += 1 if line.start_with?('## ')
|
|
||||||
break if section_count > 1
|
|
||||||
content.append(line) if section_count == 1
|
|
||||||
end
|
|
||||||
content[1..].join
|
|
||||||
end
|
|
||||||
|
|
||||||
namespace :changelog do
|
namespace :changelog do
|
||||||
# rake changelog:reset
|
desc 'Add the empty CHANGELOG entries after a new release'
|
||||||
desc "Add a new empty section at the top of the changelog and git push it"
|
|
||||||
task :reset do
|
task :reset do
|
||||||
header "Reset CHANGELOG"
|
changelog = File.read('CHANGELOG.md')
|
||||||
content = File.read(CHANGELOG_FILE)
|
abort('A Master entry already exists') if changelog =~ /^##\s*Master$/
|
||||||
new_content = NEW_CHANGELOG_SECTION + "\n" + content
|
changelog.sub!(/^##[^#]/, "#{header}\\0")
|
||||||
File.write(CHANGELOG_FILE, new_content)
|
File.write('CHANGELOG.md', changelog)
|
||||||
|
end
|
||||||
|
|
||||||
sh("git", "add", CHANGELOG_FILE)
|
def header
|
||||||
sh("git", "commit", "-m", "Reset CHANGELOG")
|
<<-HEADER.gsub(/^\s*\|/, '')
|
||||||
sh("git", "push")
|
|## Master
|
||||||
|
|
|
||||||
|
|### Breaking
|
||||||
|
|
|
||||||
|
|_None_
|
||||||
|
|
|
||||||
|
|### Enhancements
|
||||||
|
|
|
||||||
|
|_None_
|
||||||
|
|
|
||||||
|
|### Deprecations
|
||||||
|
|
|
||||||
|
|_None_
|
||||||
|
|
|
||||||
|
|### Bug Fixes
|
||||||
|
|
|
||||||
|
|_None_
|
||||||
|
|
|
||||||
|
|### Internal Changes
|
||||||
|
|
|
||||||
|
|_None_
|
||||||
|
|
|
||||||
|
HEADER
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Check if links to issues and PRs use matching numbers between text & link'
|
||||||
|
task :check do
|
||||||
|
warnings = check_changelog
|
||||||
|
if warnings.empty?
|
||||||
|
puts "\u{2705} All entries seems OK (end with period + 2 spaces, correct links)"
|
||||||
|
else
|
||||||
|
puts "\u{274C} Some warnings were found:\n" + Array(warnings.map do |warning|
|
||||||
|
" - Line #{warning[:line]}: #{warning[:message]}"
|
||||||
|
end).join("\n")
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
61
rakelib/check_changelog.rb
Normal file
61
rakelib/check_changelog.rb
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# This analyze the CHANGELOG.md file and report warnings on its content
|
||||||
|
#
|
||||||
|
# It checks:
|
||||||
|
# - if the description part of each entry ends with a period and two spaces
|
||||||
|
# - that all links to PRs & issues with format [#nn](repo_url/nn) are consistent
|
||||||
|
# (use the same number in the link title and URL)
|
||||||
|
#
|
||||||
|
# @return Array of Hashes with keys `:line` & `:message` for each element
|
||||||
|
#
|
||||||
|
def check_changelog
|
||||||
|
current_repo = File.basename(`git remote get-url origin`.chomp, '.git').freeze
|
||||||
|
slug_re = '([a-zA-Z]*/[a-zA-Z]*)'
|
||||||
|
links = %r{\[#{slug_re}?\#([0-9]+)\]\(https://github.com/#{slug_re}/(issues|pull)/([0-9]+)\)}
|
||||||
|
links_typos = %r{https://github.com/#{slug_re}/(issue|pulls)/([0-9]+)}
|
||||||
|
|
||||||
|
all_warnings = []
|
||||||
|
inside_entry = false
|
||||||
|
last_line_has_correct_ending = false
|
||||||
|
|
||||||
|
File.readlines('CHANGELOG.md').each_with_index do |line, idx|
|
||||||
|
line.chomp! # Remove \n the end, it's easier for checks below
|
||||||
|
was_inside_entry = inside_entry
|
||||||
|
just_started_new_entry = line.start_with?('* ')
|
||||||
|
inside_entry = true if just_started_new_entry
|
||||||
|
inside_entry = false if /^ \[.*\]\(.*\)$/ =~ line # link-only line
|
||||||
|
|
||||||
|
if was_inside_entry && !inside_entry && !last_line_has_correct_ending
|
||||||
|
# We just ended an entry's description by starting the links, but description didn't end with '. '
|
||||||
|
# Note: entry descriptions can be on multiple lines, hence the need to wait for the next line
|
||||||
|
# to not be inside an entry to be able to consider the previous line as the end of entry description.
|
||||||
|
all_warnings.concat [
|
||||||
|
{ line: idx, message: 'Line describing your entry should end with a period and 2 spaces.' }
|
||||||
|
]
|
||||||
|
end
|
||||||
|
# Store if current line has correct ending, for next iteration, so that if the next line isn't
|
||||||
|
# part of the entry description, we can check if previous line ends description correctly.
|
||||||
|
# Also, lines just linking to CHANGELOG to other repositories (StencilSwiftKit & Stencil mainly)
|
||||||
|
# should be considered as not needing the '. ' ending.
|
||||||
|
last_line_has_correct_ending = line.end_with?('. ') || line.end_with?('/CHANGELOG.md)')
|
||||||
|
|
||||||
|
# Now, check that links [#nn](.../nn) have matching numbers in link title & URL
|
||||||
|
wrong_links = line.scan(links).reject do |m|
|
||||||
|
slug = m[0] || "stencilproject/#{current_repo}"
|
||||||
|
(slug == m[2]) && (m[1] == m[4])
|
||||||
|
end
|
||||||
|
all_warnings.concat Array(wrong_links.map do |m|
|
||||||
|
link_text = "#{m[0]}##{m[1]}"
|
||||||
|
link_url = "#{m[2]}##{m[4]}"
|
||||||
|
{ line: idx + 1, message: "Link text is #{link_text} but links points to #{link_url}." }
|
||||||
|
end)
|
||||||
|
|
||||||
|
# Flag common typos in GitHub issue/PR URLs
|
||||||
|
typo_links = line.scan(links_typos)
|
||||||
|
all_warnings.concat Array(typo_links.map do |_|
|
||||||
|
{ line: idx + 1, message: 'This looks like a GitHub link URL with a typo. Issue links should use `/issues/123` (plural) and PR links should use `/pull/123` (singular).' }
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
all_warnings
|
||||||
|
end
|
||||||
50
rakelib/lint.rake
Normal file
50
rakelib/lint.rake
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Used constants:
|
||||||
|
# - BUILD_DIR
|
||||||
|
|
||||||
|
namespace :lint do
|
||||||
|
SWIFTLINT = 'rakelib/lint.sh'
|
||||||
|
SWIFTLINT_VERSION = '0.48.0'
|
||||||
|
|
||||||
|
task :install do |task|
|
||||||
|
next if check_version
|
||||||
|
|
||||||
|
if OS.mac?
|
||||||
|
url = "https://github.com/realm/SwiftLint/releases/download/#{SWIFTLINT_VERSION}/portable_swiftlint.zip"
|
||||||
|
else
|
||||||
|
url = "https://github.com/realm/SwiftLint/releases/download/#{SWIFTLINT_VERSION}/swiftlint_linux.zip"
|
||||||
|
end
|
||||||
|
tmppath = '/tmp/swiftlint.zip'
|
||||||
|
destination = "#{BUILD_DIR}/swiftlint"
|
||||||
|
|
||||||
|
Utils.run([
|
||||||
|
%(curl -Lo #{tmppath} #{url}),
|
||||||
|
%(rm -rf #{destination}),
|
||||||
|
%(mkdir -p #{destination}),
|
||||||
|
%(unzip #{tmppath} -d #{destination})
|
||||||
|
], task)
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Lint the code'
|
||||||
|
task :code => :install do |task|
|
||||||
|
Utils.print_header 'Linting the code'
|
||||||
|
Utils.run(%(#{SWIFTLINT} sources), task)
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Lint the tests'
|
||||||
|
task :tests => :install do |task|
|
||||||
|
Utils.print_header 'Linting the unit test code'
|
||||||
|
Utils.run(%(#{SWIFTLINT} tests), task)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_version
|
||||||
|
swiftlint = "#{BUILD_DIR}/swiftlint/swiftlint"
|
||||||
|
return false unless File.executable?(swiftlint)
|
||||||
|
|
||||||
|
current = `#{swiftlint} version`.chomp
|
||||||
|
required = SWIFTLINT_VERSION.chomp
|
||||||
|
|
||||||
|
current == required
|
||||||
|
end
|
||||||
|
end
|
||||||
35
rakelib/lint.sh
Executable file
35
rakelib/lint.sh
Executable file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
PROJECT_DIR="${PROJECT_DIR:-`cd "$(dirname $0)/..";pwd`}"
|
||||||
|
SWIFTLINT="${PROJECT_DIR}/.build/swiftlint/swiftlint"
|
||||||
|
CONFIG="${PROJECT_DIR}/.swiftlint.yml"
|
||||||
|
if [ $CI ]; then
|
||||||
|
REPORTER="--reporter github-actions-logging"
|
||||||
|
else
|
||||||
|
REPORTER=
|
||||||
|
fi
|
||||||
|
|
||||||
|
# possible paths
|
||||||
|
paths_sources="Sources"
|
||||||
|
paths_tests="Tests/StencilTests"
|
||||||
|
|
||||||
|
# load selected group
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
key="$1"
|
||||||
|
else
|
||||||
|
echo "error: need group to lint."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
selected_path=`eval echo '$'paths_$key`
|
||||||
|
if [ -z "$selected_path" ]; then
|
||||||
|
echo "error: need a valid group to lint."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SUB_CONFIG="${PROJECT_DIR}/${selected_path}/.swiftlint.yml"
|
||||||
|
if [ -f "$SUB_CONFIG" ]; then
|
||||||
|
"$SWIFTLINT" lint --strict --config "$SUB_CONFIG" $REPORTER "${PROJECT_DIR}/${selected_path}"
|
||||||
|
else
|
||||||
|
"$SWIFTLINT" lint --strict --config "$CONFIG" $REPORTER "${PROJECT_DIR}/${selected_path}"
|
||||||
|
fi
|
||||||
@@ -1,21 +1,12 @@
|
|||||||
require 'json'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
def current_pod_version
|
# Used constants:
|
||||||
JSON.parse(File.read(PODSPEC_FILE))['version']
|
# - POD_NAME
|
||||||
end
|
|
||||||
|
|
||||||
namespace :pod do
|
namespace :pod do
|
||||||
# rake pod:lint
|
desc 'Lint the Pod'
|
||||||
desc "Lint the podspec"
|
task :lint do |task|
|
||||||
task :lint do
|
Utils.print_header 'Linting the pod spec'
|
||||||
header "Linting podspec"
|
Utils.run(%(bundle exec pod lib lint "#{POD_NAME}.podspec.json" --quick), task)
|
||||||
sh("pod", "lib", "lint", PODSPEC_FILE)
|
|
||||||
end
|
|
||||||
|
|
||||||
# rake pod:push
|
|
||||||
desc "Push the podspec to trunk"
|
|
||||||
task :push do
|
|
||||||
header "Pushing podspec to trunk"
|
|
||||||
sh("pod", "trunk", "push", PODSPEC_FILE)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,67 +1,95 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Used constants:
|
||||||
|
# - BUILD_DIR
|
||||||
|
|
||||||
require 'json'
|
require 'json'
|
||||||
|
|
||||||
namespace :release do
|
namespace :release do
|
||||||
|
desc 'Create a new release'
|
||||||
|
task :new => [:check_versions, :check_tag_and_ask_to_release, 'spm:test', :github, :cocoapods]
|
||||||
|
|
||||||
# rake release:new
|
desc 'Check if all versions from the podspecs and CHANGELOG match'
|
||||||
desc "Ask for a version number and prepare a release PR for that version"
|
task :check_versions do
|
||||||
task :new do
|
results = []
|
||||||
info "Current version is: #{current_pod_version}"
|
|
||||||
print "What version do you want to release? "
|
|
||||||
new_version = STDIN.gets.chomp
|
|
||||||
|
|
||||||
Rake::Task['release:start'].invoke(new_version)
|
# Check if bundler is installed first, as we'll need it for the cocoapods task (and we prefer to fail early)
|
||||||
end
|
`which bundler`
|
||||||
|
results << Utils.table_result(
|
||||||
# rake release:start[version]
|
$CHILD_STATUS.success?,
|
||||||
desc "Start a release by creating a PR with the required changes to bump the version"
|
'Bundler installed',
|
||||||
task :start, [:version] => ['release:create_branch', 'release:update_files', 'pod:lint', 'release:push_branch', 'github:create_release_pr', 'github:pull_master']
|
'Install bundler using `gem install bundler` and run `bundle install` first.'
|
||||||
|
|
||||||
# rake release:finish[version]
|
|
||||||
desc "Finish a release after the PR has been merged, by tagging master and pushing to trunk"
|
|
||||||
task :finish => ['github:pull_master', 'github:tag', 'pod:push', 'github:create_release', 'changelog:reset']
|
|
||||||
|
|
||||||
|
|
||||||
### Helper tasks ###
|
|
||||||
|
|
||||||
# rake release:create_branch[version]
|
|
||||||
task :create_branch, [:version] do |_, args|
|
|
||||||
branch = release_branch(args[:version])
|
|
||||||
|
|
||||||
header "Creating release branch"
|
|
||||||
sh("git", "checkout", "-b", branch)
|
|
||||||
end
|
|
||||||
|
|
||||||
# rake release:update_files[version]
|
|
||||||
task :update_files, [:version] do |_, args|
|
|
||||||
version = args[:version]
|
|
||||||
|
|
||||||
header "Updating files for version #{version}"
|
|
||||||
|
|
||||||
podspec = JSON.parse(File.read(PODSPEC_FILE))
|
|
||||||
podspec['version'] = version
|
|
||||||
podspec['source']['tag'] = version
|
|
||||||
File.write(PODSPEC_FILE, JSON.pretty_generate(podspec) + "\n")
|
|
||||||
|
|
||||||
replace(CHANGELOG_FILE, '## Master' => "\#\# #{version}")
|
|
||||||
replace("docs/conf.py",
|
|
||||||
/^version = .*/ => %Q(version = '#{version}'),
|
|
||||||
/^release = .*/ => %Q(release = '#{version}')
|
|
||||||
)
|
|
||||||
replace("docs/installation.rst",
|
|
||||||
/pod 'Stencil', '.*'/ => %Q(pod 'Stencil', '~> #{version}'),
|
|
||||||
/github "stencilproject\/Stencil" ~> .*/ => %Q(github "stencilproject/Stencil" ~> #{version})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
## Commit Changes
|
# Extract version from podspec
|
||||||
sh("git", "add", PODSPEC_FILE, CHANGELOG_FILE, "docs/*")
|
podspec = Utils::podspec(POD_NAME)
|
||||||
sh("git", "commit", "-m", "Version #{version}")
|
v = podspec['version']
|
||||||
|
Utils.table_info("#{POD_NAME}.podspec", v)
|
||||||
|
|
||||||
|
# Check podspec tag
|
||||||
|
podspec_tag = podspec['source']['tag']
|
||||||
|
results << Utils.table_result(podspec_tag == v, 'Podspec version & tag equal', 'Update the `tag` in podspec')
|
||||||
|
|
||||||
|
# Check docs config
|
||||||
|
docs_version = Utils.first_match_in_file('docs/conf.py', /version = '(.+)'/, 1)
|
||||||
|
docs_release = Utils.first_match_in_file('docs/conf.py', /release = '(.+)'/, 1)
|
||||||
|
results << Utils.table_result(docs_version == v,'Docs, version updated', 'Update the `version` in docs/conf.py')
|
||||||
|
results << Utils.table_result(docs_release == v, 'Docs, release updated', 'Update the `release` in docs/conf.py')
|
||||||
|
|
||||||
|
# Check docs installation
|
||||||
|
docs_package = Utils.first_match_in_file('docs/installation.rst', /\.package\(url: .+ from: "(.+)"/, 1)
|
||||||
|
docs_cocoapods = Utils.first_match_in_file('docs/installation.rst', /pod 'Stencil', '~> (.+)'/, 1)
|
||||||
|
docs_carthage = Utils.first_match_in_file('docs/installation.rst', /github ".+\/Stencil" ~> (.+)/, 1)
|
||||||
|
results << Utils.table_result(docs_package == v, 'Docs, package updated', 'Update the package version in docs/installation.rst')
|
||||||
|
results << Utils.table_result(docs_cocoapods == v, 'Docs, cocoapods updated', 'Update the cocoapods version in docs/installation.rst')
|
||||||
|
results << Utils.table_result(docs_carthage == v, 'Docs, carthage updated', 'Update the carthage version in docs/installation.rst')
|
||||||
|
|
||||||
|
# Check if entry present in CHANGELOG
|
||||||
|
changelog_entry = Utils.first_match_in_file('CHANGELOG.md', /^## #{Regexp.quote(v)}$/)
|
||||||
|
results << Utils.table_result(changelog_entry, 'CHANGELOG, Entry added', "Add an entry for #{v} in CHANGELOG.md")
|
||||||
|
|
||||||
|
changelog_has_stable = system("grep -qi '^## Master' CHANGELOG.md")
|
||||||
|
results << Utils.table_result(!changelog_has_stable, 'CHANGELOG, No master', 'Remove section for master branch in CHANGELOG')
|
||||||
|
|
||||||
|
exit 1 unless results.all?
|
||||||
end
|
end
|
||||||
|
|
||||||
# rake release:push_branch[version]
|
desc "Check tag and ask to release"
|
||||||
task :push_branch, [:version] do |_, args|
|
task :check_tag_and_ask_to_release do
|
||||||
branch = release_branch(args[:version])
|
results = []
|
||||||
|
podspec_version = Utils.podspec_version(POD_NAME)
|
||||||
|
|
||||||
header "Pushing #{branch} to origin"
|
tag_set = !`git ls-remote --tags . refs/tags/#{podspec_version}`.empty?
|
||||||
sh("git", "push", "-u", "origin", branch)
|
results << Utils.table_result(
|
||||||
|
tag_set,
|
||||||
|
'Tag pushed',
|
||||||
|
'Please create a tag and push it'
|
||||||
|
)
|
||||||
|
|
||||||
|
exit 1 unless results.all?
|
||||||
|
|
||||||
|
print "Release version #{podspec_version} [Y/n]? "
|
||||||
|
exit 2 unless STDIN.gets.chomp == 'Y'
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Create a new GitHub release"
|
||||||
|
task :github do
|
||||||
|
require 'octokit'
|
||||||
|
|
||||||
|
client = Utils.octokit_client
|
||||||
|
tag = Utils.top_changelog_version
|
||||||
|
body = Utils.top_changelog_entry
|
||||||
|
|
||||||
|
raise 'Must be a valid version' if tag == 'Master'
|
||||||
|
|
||||||
|
repo_name = File.basename(`git remote get-url origin`.chomp, '.git').freeze
|
||||||
|
puts "Pushing release notes for tag #{tag}"
|
||||||
|
client.create_release("stencilproject/#{repo_name}", tag, name: tag, body: body)
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "pod trunk push #{POD_NAME} to CocoaPods"
|
||||||
|
task :cocoapods do
|
||||||
|
Utils.print_header 'Pushing pod to CocoaPods Trunk'
|
||||||
|
sh "bundle exec pod trunk push #{POD_NAME}.podspec.json"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
18
rakelib/spm.rake
Normal file
18
rakelib/spm.rake
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Used constants:
|
||||||
|
# _none_
|
||||||
|
|
||||||
|
namespace :spm do
|
||||||
|
desc 'Build using SPM'
|
||||||
|
task :build do |task|
|
||||||
|
Utils.print_header 'Compile using SPM'
|
||||||
|
Utils.run('swift build', task, xcrun: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Run SPM Unit Tests'
|
||||||
|
task :test => :build do |task|
|
||||||
|
Utils.print_header 'Run the unit tests using SPM'
|
||||||
|
Utils.run('swift test --parallel', task, xcrun: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,28 +1,266 @@
|
|||||||
def colorize(string, *codes)
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Used constants:
|
||||||
|
# - MIN_XCODE_VERSION
|
||||||
|
|
||||||
|
require 'json'
|
||||||
|
require 'open3'
|
||||||
|
require 'pathname'
|
||||||
|
|
||||||
|
# Utility functions to run Xcode commands, extract versionning info and logs messages
|
||||||
|
#
|
||||||
|
class Utils
|
||||||
|
COLUMN_WIDTHS = [45, 12].freeze
|
||||||
|
|
||||||
|
## [ Run commands ] #########################################################
|
||||||
|
|
||||||
|
# formatter types
|
||||||
|
# :xcpretty : through xcpretty and store in artifacts
|
||||||
|
# :raw : store in artifacts
|
||||||
|
# :to_string : run using backticks and return output
|
||||||
|
|
||||||
|
# run a command using xcrun and xcpretty if applicable
|
||||||
|
def self.run(command, task, subtask = '', xcrun: false, formatter: :raw)
|
||||||
|
commands = if xcrun and OS.mac?
|
||||||
|
Array(command).map { |cmd| "#{version_select} xcrun #{cmd}" }
|
||||||
|
else
|
||||||
|
Array(command)
|
||||||
|
end
|
||||||
|
case formatter
|
||||||
|
when :xcpretty then xcpretty(commands, task, subtask)
|
||||||
|
when :raw then plain(commands, task, subtask)
|
||||||
|
when :to_string then `#{commands.join(' && ')}`
|
||||||
|
else raise "Unknown formatter '#{formatter}'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
## [ Convenience Helpers ] ##################################################
|
||||||
|
|
||||||
|
def self.podspec(file)
|
||||||
|
JSON.parse(File.read("#{file}.podspec.json"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.podspec_version(file)
|
||||||
|
podspec_as_json(file)['version']
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.pod_trunk_last_version(pod)
|
||||||
|
require 'yaml'
|
||||||
|
stdout, _, _ = Open3.capture3('bundle', 'exec', 'pod', 'trunk', 'info', pod)
|
||||||
|
stdout.sub!("\n#{pod}\n", '')
|
||||||
|
last_version_line = YAML.safe_load(stdout).first['Versions'].last
|
||||||
|
/^[0-9.]*/.match(last_version_line)[0] # Just the 'x.y.z' part
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.spm_own_version(dep)
|
||||||
|
dependencies = JSON.load(File.new('Package.resolved'))['object']['pins']
|
||||||
|
dependencies.find { |d| d['package'] == dep }['state']['version']
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.spm_resolved_version(dep)
|
||||||
|
dependencies = JSON.load(File.new('Package.resolved'))['object']['pins']
|
||||||
|
dependencies.find { |d| d['package'] == dep }['state']['version']
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.last_git_tag_version
|
||||||
|
`git describe --tags --abbrev=0`.strip
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.octokit_client
|
||||||
|
token = ENV['DANGER_GITHUB_API_TOKEN']
|
||||||
|
token ||= File.exist?('.apitoken') && File.read('.apitoken')
|
||||||
|
token ||= File.exist?('../.apitoken') && File.read('../.apitoken')
|
||||||
|
Utils.print_error('No .apitoken file found') unless token
|
||||||
|
require 'octokit'
|
||||||
|
Octokit::Client.new(access_token: token)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.top_changelog_version(changelog_file = 'CHANGELOG.md')
|
||||||
|
header, _, _ = Open3.capture3('grep', '-m', '1', '^## ', changelog_file)
|
||||||
|
header.gsub('## ', '').strip
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.top_changelog_entry(changelog_file = 'CHANGELOG.md')
|
||||||
|
tag = top_changelog_version
|
||||||
|
stdout, _, _ = Open3.capture3('sed', '-n', "/^## #{tag}$/,/^## /p", changelog_file)
|
||||||
|
stdout.gsub(/^## .*$/, '').strip
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.first_match_in_file(file, regexp, index = 0)
|
||||||
|
File.foreach(file) do |line|
|
||||||
|
m = regexp.match(line)
|
||||||
|
return m[index] if m
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
## [ Print info/errors ] ####################################################
|
||||||
|
|
||||||
|
# print an info header
|
||||||
|
def self.print_header(str)
|
||||||
|
puts "== #{str.chomp} ==".format(:yellow, :bold)
|
||||||
|
end
|
||||||
|
|
||||||
|
# print an info message
|
||||||
|
def self.print_info(str)
|
||||||
|
puts str.chomp.format(:green)
|
||||||
|
end
|
||||||
|
|
||||||
|
# print an error message
|
||||||
|
def self.print_error(str)
|
||||||
|
puts str.chomp.format(:red)
|
||||||
|
end
|
||||||
|
|
||||||
|
# format an info message in a 2 column table
|
||||||
|
def self.table_header(col1, col2)
|
||||||
|
puts "| #{col1.ljust(COLUMN_WIDTHS[0])} | #{col2.ljust(COLUMN_WIDTHS[1])} |"
|
||||||
|
puts "| #{'-' * COLUMN_WIDTHS[0]} | #{'-' * COLUMN_WIDTHS[1]} |"
|
||||||
|
end
|
||||||
|
|
||||||
|
# format an info message in a 2 column table
|
||||||
|
def self.table_info(label, msg)
|
||||||
|
puts "| #{label.ljust(COLUMN_WIDTHS[0])} | 👉 #{msg.ljust(COLUMN_WIDTHS[1] - 4)} |"
|
||||||
|
end
|
||||||
|
|
||||||
|
# format a result message in a 2 column table
|
||||||
|
def self.table_result(result, label, error_msg)
|
||||||
|
if result
|
||||||
|
puts "| #{label.ljust(COLUMN_WIDTHS[0])} | #{'✅'.ljust(COLUMN_WIDTHS[1] - 1)} |"
|
||||||
|
else
|
||||||
|
puts "| #{label.ljust(COLUMN_WIDTHS[0])} | ❌ - #{error_msg.ljust(COLUMN_WIDTHS[1] - 6)} |"
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
## [ Private helper functions ] ##################################################
|
||||||
|
|
||||||
|
# run a command, pipe output through 'xcpretty' and store the output in CI artifacts
|
||||||
|
def self.xcpretty(cmd, task, subtask)
|
||||||
|
command = Array(cmd).join(' && \\' + "\n")
|
||||||
|
|
||||||
|
if ENV['CI']
|
||||||
|
Rake.sh %(set -o pipefail && (\\\n#{command} \\\n) | bundle exec xcpretty --color --report junit)
|
||||||
|
elsif system('which xcpretty > /dev/null')
|
||||||
|
Rake.sh %(set -o pipefail && (\\\n#{command} \\\n) | bundle exec xcpretty --color)
|
||||||
|
else
|
||||||
|
Rake.sh command
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private_class_method :xcpretty
|
||||||
|
|
||||||
|
# run a command and store the output in CI artifacts
|
||||||
|
def self.plain(cmd, task, subtask)
|
||||||
|
command = Array(cmd).join(' && \\' + "\n")
|
||||||
|
|
||||||
|
if ENV['CI']
|
||||||
|
if OS.mac?
|
||||||
|
Rake.sh %(set -o pipefail && (#{command}))
|
||||||
|
else
|
||||||
|
# dash on linux doesn't support `set -o`
|
||||||
|
Rake.sh %(/bin/bash -eo pipefail -c "#{command}")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Rake.sh command
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private_class_method :plain
|
||||||
|
|
||||||
|
# select the xcode version we want/support
|
||||||
|
def self.version_select
|
||||||
|
@version_select ||= compute_developer_dir(MIN_XCODE_VERSION)
|
||||||
|
end
|
||||||
|
private_class_method :version_select
|
||||||
|
|
||||||
|
# Return the "DEVELOPER_DIR=..." prefix to use in order to point to the best Xcode version
|
||||||
|
#
|
||||||
|
# @param [String|Float|Gem::Requirement] version_req
|
||||||
|
# The Xcode version requirement.
|
||||||
|
# - If it's a Float, it's converted to a "~> x.y" requirement
|
||||||
|
# - If it's a String, it's converted to a Gem::Requirement as is
|
||||||
|
# @note If you pass a String, be sure to use "~> " in the string unless you really want
|
||||||
|
# to point to an exact, very specific version
|
||||||
|
#
|
||||||
|
def self.compute_developer_dir(version_req)
|
||||||
|
version_req = Gem::Requirement.new("~> #{version_req}") if version_req.is_a?(Float)
|
||||||
|
version_req = Gem::Requirement.new(version_req) unless version_req.is_a?(Gem::Requirement)
|
||||||
|
# if current Xcode already fulfills min version don't force DEVELOPER_DIR=...
|
||||||
|
current_xcode_version = `xcodebuild -version`.split("\n").first.match(/[0-9.]+/).to_s
|
||||||
|
return '' if version_req.satisfied_by? Gem::Version.new(current_xcode_version)
|
||||||
|
|
||||||
|
supported_versions = all_xcode_versions.select { |app| version_req.satisfied_by?(app[:vers]) }
|
||||||
|
latest_supported_xcode = supported_versions.sort_by { |app| app[:vers] }.last
|
||||||
|
|
||||||
|
# Check if it's at least the right version
|
||||||
|
if latest_supported_xcode.nil?
|
||||||
|
raise "\n[!!!] Requires Xcode #{version_req}, but we were not able to find it. " \
|
||||||
|
"If it's already installed, either `xcode-select -s` to it, or update your Spotlight index " \
|
||||||
|
"with 'mdimport /Applications/Xcode*'\n\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
%(DEVELOPER_DIR="#{latest_supported_xcode[:path]}/Contents/Developer")
|
||||||
|
end
|
||||||
|
private_class_method :compute_developer_dir
|
||||||
|
|
||||||
|
# @return [Array<Hash>] A list of { :vers => ... , :path => ... } hashes
|
||||||
|
# of all Xcodes found on the machine using Spotlight
|
||||||
|
def self.all_xcode_versions
|
||||||
|
xcodes = `mdfind "kMDItemCFBundleIdentifier = 'com.apple.dt.Xcode'"`.chomp.split("\n")
|
||||||
|
xcodes.map do |path|
|
||||||
|
{ vers: Gem::Version.new(`mdls -name kMDItemVersion -raw "#{path}"`), path: path }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private_class_method :all_xcode_versions
|
||||||
|
end
|
||||||
|
|
||||||
|
# OS detection
|
||||||
|
#
|
||||||
|
module OS
|
||||||
|
def OS.mac?
|
||||||
|
(/darwin/ =~ RUBY_PLATFORM) != nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def OS.linux?
|
||||||
|
OS.unix? and not OS.mac?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Colorization support for Strings
|
||||||
|
#
|
||||||
|
class String
|
||||||
|
# colorization
|
||||||
|
FORMATTING = {
|
||||||
|
# text styling
|
||||||
|
bold: 1,
|
||||||
|
faint: 2,
|
||||||
|
italic: 3,
|
||||||
|
underline: 4,
|
||||||
|
# foreground colors
|
||||||
|
black: 30,
|
||||||
|
red: 31,
|
||||||
|
green: 32,
|
||||||
|
yellow: 33,
|
||||||
|
blue: 34,
|
||||||
|
magenta: 35,
|
||||||
|
cyan: 36,
|
||||||
|
white: 37,
|
||||||
|
# background colors
|
||||||
|
bg_black: 40,
|
||||||
|
bg_red: 41,
|
||||||
|
bg_green: 42,
|
||||||
|
bg_yellow: 43,
|
||||||
|
bg_blue: 44,
|
||||||
|
bg_magenta: 45,
|
||||||
|
bg_cyan: 46,
|
||||||
|
bg_white: 47
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
# only enable formatting if terminal supports it
|
||||||
if `tput colors`.chomp.to_i >= 8
|
if `tput colors`.chomp.to_i >= 8
|
||||||
code = codes.join(';')
|
def format(*styles)
|
||||||
puts "\e[#{code}m" + string + "\e[0m"
|
styles.map { |s| "\e[#{FORMATTING[s]}m" }.join + self + "\e[0m"
|
||||||
|
end
|
||||||
else
|
else
|
||||||
puts string
|
def format(*_styles)
|
||||||
|
self
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def header(title)
|
|
||||||
puts colorize("==> #{title}...", 1, 32) # bold, green
|
|
||||||
end
|
|
||||||
|
|
||||||
def info(string)
|
|
||||||
puts colorize(string, 34) # blue
|
|
||||||
end
|
|
||||||
|
|
||||||
def release_branch(version)
|
|
||||||
"release/#{version}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def replace(file, replacements)
|
|
||||||
content = File.read(file)
|
|
||||||
replacements.each do |match, replacement|
|
|
||||||
content.gsub!(match, replacement)
|
|
||||||
end
|
|
||||||
File.write(file, content)
|
|
||||||
end
|
|
||||||
|
|||||||
Reference in New Issue
Block a user