├── .0pdd.yml
├── .gitattributes
├── .github
└── workflows
│ ├── actionlint.yml
│ ├── codecov.yml
│ ├── copyrights.yml
│ ├── license.yml
│ ├── markdown-lint.yml
│ ├── pdd.yml
│ ├── rake.yml
│ ├── reuse.yml
│ ├── typos.yml
│ ├── xcop.yml
│ └── yamllint.yml
├── .gitignore
├── .pdd
├── .rubocop.yml
├── .rultor.yml
├── .simplecov
├── Gemfile
├── Gemfile.lock
├── LICENSE.txt
├── LICENSES
└── MIT.txt
├── README.md
├── REUSE.toml
├── Rakefile
├── bin
└── texqc
├── cucumber.yml
├── features
├── cli.feature
├── gem_package.feature
├── step_definitions
│ └── steps.rb
└── support
│ └── env.rb
├── logo.svg
├── renovate.json
└── texqc.gemspec
/.0pdd.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2020 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | errors:
5 | - yegor256@gmail.com
6 | # alerts:
7 | # github:
8 | # - yegor256
9 |
10 | tags:
11 | - pdd
12 | - bug
13 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Check out all text files in UNIX format, with LF as end of line
2 | # Don't change this file. If you have any ideas about it, please
3 | # submit a separate issue about it and we'll discuss.
4 |
5 | * text=auto eol=lf
6 | *.rb ident
7 | *.xml ident
8 |
--------------------------------------------------------------------------------
/.github/workflows/actionlint.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | # yamllint disable rule:line-length
5 | name: actionlint
6 | 'on':
7 | push:
8 | branches:
9 | - master
10 | pull_request:
11 | branches:
12 | - master
13 | jobs:
14 | actionlint:
15 | timeout-minutes: 15
16 | runs-on: ubuntu-24.04
17 | steps:
18 | - uses: actions/checkout@v4
19 | - name: Download actionlint
20 | id: get_actionlint
21 | run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
22 | shell: bash
23 | - name: Check workflow files
24 | run: ${{ steps.get_actionlint.outputs.executable }} -color
25 | shell: bash
26 |
--------------------------------------------------------------------------------
/.github/workflows/codecov.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | # yamllint disable rule:line-length
5 | name: codecov
6 | 'on':
7 | push:
8 | branches:
9 | - master
10 | jobs:
11 | codecov:
12 | timeout-minutes: 15
13 | runs-on: ubuntu-24.04
14 | steps:
15 | - uses: actions/checkout@v4
16 | - uses: ruby/setup-ruby@v1
17 | with:
18 | ruby-version: 3.3
19 | bundler-cache: true
20 | - run: bundle config set --global path "$(pwd)/vendor/bundle"
21 | - run: bundle install --no-color
22 | - run: bundle exec rake
23 | - uses: codecov/codecov-action@v5
24 | with:
25 | token: ${{ secrets.CODECOV_TOKEN }}
26 |
--------------------------------------------------------------------------------
/.github/workflows/copyrights.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | # yamllint disable rule:line-length
5 | name: copyrights
6 | 'on':
7 | push:
8 | branches:
9 | - master
10 | pull_request:
11 | branches:
12 | - master
13 | jobs:
14 | copyrights:
15 | timeout-minutes: 15
16 | runs-on: ubuntu-24.04
17 | steps:
18 | - uses: actions/checkout@v4
19 | - uses: yegor256/copyrights-action@0.0.8
20 |
--------------------------------------------------------------------------------
/.github/workflows/license.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | # yamllint disable rule:line-length
5 | name: license
6 | 'on':
7 | push:
8 | branches:
9 | - master
10 | pull_request:
11 | branches:
12 | - master
13 | jobs:
14 | license:
15 | timeout-minutes: 15
16 | runs-on: ubuntu-24.04
17 | steps:
18 | - uses: actions/checkout@v4
19 | - shell: bash
20 | run: |
21 | header="Copyright (c) $(date +%Y) Yegor Bugayenko"
22 | failed="false"
23 | while IFS= read -r file; do
24 | if ! grep -q "${header}" "${file}"; then
25 | failed="true"
26 | echo "⚠️ Copyright header is not found in: ${file}"
27 | else
28 | echo "File looks good: ${file}"
29 | fi
30 | done < <(find . -type f \( \
31 | -name "Dockerfile" -o \
32 | -name "LICENSE.txt" -o \
33 | -name "Makefile" -o \
34 | -name "Rakefile" -o \
35 | -name "*.sh" -o \
36 | -name "*.rb" -o \
37 | -name "*.fe" -o \
38 | -name "*.yml" \
39 | \) -print)
40 | if [ "${failed}" = "true" ]; then
41 | exit 1
42 | fi
43 |
--------------------------------------------------------------------------------
/.github/workflows/markdown-lint.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | # yamllint disable rule:line-length
5 | name: markdown-lint
6 | 'on':
7 | push:
8 | branches:
9 | - master
10 | pull_request:
11 | branches:
12 | - master
13 | paths-ignore: ['paper/**', 'sandbox/**']
14 | concurrency:
15 | group: markdown-lint-${{ github.ref }}
16 | cancel-in-progress: true
17 | jobs:
18 | markdown-lint:
19 | timeout-minutes: 15
20 | runs-on: ubuntu-24.04
21 | steps:
22 | - uses: actions/checkout@v4
23 | - uses: DavidAnson/markdownlint-cli2-action@v20.0.0
24 |
--------------------------------------------------------------------------------
/.github/workflows/pdd.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | # yamllint disable rule:line-length
5 | name: pdd
6 | 'on':
7 | push:
8 | branches:
9 | - master
10 | pull_request:
11 | branches:
12 | - master
13 | jobs:
14 | pdd:
15 | timeout-minutes: 15
16 | runs-on: ubuntu-24.04
17 | steps:
18 | - uses: actions/checkout@v4
19 | - uses: volodya-lombrozo/pdd-action@master
20 |
--------------------------------------------------------------------------------
/.github/workflows/rake.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | # yamllint disable rule:line-length
5 | name: rake
6 | 'on':
7 | push:
8 | branches:
9 | - master
10 | pull_request:
11 | branches:
12 | - master
13 | jobs:
14 | rake:
15 | strategy:
16 | matrix:
17 | os: [ubuntu-24.04, macos-15, windows-2022]
18 | ruby: [3.3]
19 | runs-on: ${{ matrix.os }}
20 | steps:
21 | - uses: actions/checkout@v4
22 | - uses: ruby/setup-ruby@v1
23 | with:
24 | ruby-version: ${{ matrix.ruby }}
25 | bundler-cache: true
26 | - run: bundle config set --global path "$(pwd)/vendor/bundle"
27 | - run: bundle install --no-color
28 | - run: bundle exec rake
29 |
--------------------------------------------------------------------------------
/.github/workflows/reuse.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | # yamllint disable rule:line-length
5 | name: reuse
6 | 'on':
7 | push:
8 | branches:
9 | - master
10 | pull_request:
11 | branches:
12 | - master
13 | jobs:
14 | reuse:
15 | timeout-minutes: 15
16 | runs-on: ubuntu-24.04
17 | steps:
18 | - uses: actions/checkout@v4
19 | - uses: fsfe/reuse-action@v5
20 |
--------------------------------------------------------------------------------
/.github/workflows/typos.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2020 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | # yamllint disable rule:line-length
5 | name: typos
6 | 'on':
7 | push:
8 | branches:
9 | - master
10 | pull_request:
11 | branches:
12 | - master
13 | jobs:
14 | typos:
15 | timeout-minutes: 15
16 | runs-on: ubuntu-24.04
17 | steps:
18 | - uses: actions/checkout@v4
19 | - uses: crate-ci/typos@v1.32.0
20 |
--------------------------------------------------------------------------------
/.github/workflows/xcop.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | # yamllint disable rule:line-length
5 | name: xcop
6 | 'on':
7 | push:
8 | pull_request:
9 | jobs:
10 | xcop:
11 | timeout-minutes: 15
12 | runs-on: ubuntu-24.04
13 | steps:
14 | - uses: actions/checkout@v4
15 | - uses: g4s8/xcop-action@master
16 |
--------------------------------------------------------------------------------
/.github/workflows/yamllint.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | # yamllint disable rule:line-length
5 | name: yamllint
6 | 'on':
7 | push:
8 | branches:
9 | - master
10 | pull_request:
11 | branches:
12 | - master
13 | jobs:
14 | yamllint:
15 | timeout-minutes: 15
16 | runs-on: ubuntu-24.04
17 | steps:
18 | - uses: actions/checkout@v4
19 | - uses: ibiqlik/action-yamllint@v3
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle/
2 | .DS_Store
3 | .idea/
4 | .yardoc/
5 | *.gem
6 | coverage/
7 | doc/
8 | node_modules/
9 | rdoc/
10 | tmp/
11 | vendor/
12 |
--------------------------------------------------------------------------------
/.pdd:
--------------------------------------------------------------------------------
1 | --source=.
2 | --verbose
3 | --exclude target/**/*
4 | --exclude coverage/**/*
5 | --rule min-words:20
6 | --rule min-estimate:15
7 | --rule max-estimate:90
8 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2020 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | AllCops:
5 | Exclude:
6 | - 'assets/**/*'
7 | - 'vendor/**/*'
8 | DisplayCopNames: true
9 | TargetRubyVersion: 2.3
10 | Metrics/LineLength:
11 | Max: 100
12 | Layout/EndOfLine:
13 | EnforcedStyle: lf
14 | Style/MultilineTernaryOperator:
15 | Enabled: false
16 | Metrics/BlockLength:
17 | Max: 50
18 | Layout/AlignParameters:
19 | Enabled: false
20 | Layout/EmptyLineAfterGuardClause:
21 | Enabled: false
22 | require:
23 | - rubocop-rake
24 | - rubocop-minitest
25 | - rubocop-performance
26 |
--------------------------------------------------------------------------------
/.rultor.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2020 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | # yamllint disable rule:line-length
5 | assets:
6 | rubygems.yml: yegor256/home#assets/rubygems.yml
7 | install: |
8 | export PATH=$PATH:/usr/local/texlive/2021/bin/x86_64-linux/
9 | sudo apt install -y aspell
10 | pdd -f /dev/null
11 | sudo bundle install --no-color "--gemfile=$(pwd)/Gemfile"
12 | release:
13 | pre: false
14 | script: |-
15 | bundle exec rake
16 | sed -i "s/0\.0\.0/${tag}/g" bin/texqc
17 | sed -i "s/0\.0\.0/${tag}/g" texqc.gemspec
18 | git add bin/texqc
19 | git add texqc.gemspec
20 | git commit -m "version set to ${tag}"
21 | gem build texqc.gemspec
22 | chmod 0600 ../rubygems.yml
23 | gem push *.gem --config-file ../rubygems.yml
24 | merge:
25 | script: |-
26 | bundle exec rake
27 | deploy:
28 | script: |-
29 | echo "There is nothing to deploy"
30 | exit -1
31 |
--------------------------------------------------------------------------------
/.simplecov:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2020-2025 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 |
4 | if Gem.win_platform? then
5 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6 | SimpleCov::Formatter::HTMLFormatter
7 | ]
8 | SimpleCov.start do
9 | add_filter "/test/"
10 | add_filter "/features/"
11 | end
12 | else
13 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
14 | [SimpleCov::Formatter::HTMLFormatter]
15 | )
16 | SimpleCov.start do
17 | add_filter "/test/"
18 | add_filter "/features/"
19 | minimum_coverage 60
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # SPDX-FileCopyrightText: Copyright (c) 2020-2025 Yegor Bugayenko
4 | # SPDX-License-Identifier: MIT
5 |
6 | source 'https://rubygems.org'
7 | gemspec
8 |
9 | gem 'rubocop-minitest', '>0', require: false
10 | gem 'rubocop-performance', '>0', require: false
11 | gem 'rubocop-rake', '>0', require: false
12 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | texqc (0.0.0)
5 | backtrace (~> 0.3)
6 | loog (~> 0.2)
7 | slop (~> 4.8.2)
8 |
9 | GEM
10 | remote: https://rubygems.org/
11 | specs:
12 | ast (2.4.3)
13 | backtrace (0.4.0)
14 | builder (3.3.0)
15 | codecov (0.2.6)
16 | colorize
17 | json
18 | simplecov
19 | colorize (1.1.0)
20 | cucumber (1.3.20)
21 | builder (>= 2.1.2)
22 | diff-lcs (>= 1.1.3)
23 | gherkin (~> 2.12)
24 | multi_json (>= 1.7.5, < 2.0)
25 | multi_test (>= 0.1.2)
26 | diff-lcs (1.6.2)
27 | docile (1.4.1)
28 | gherkin (2.12.2)
29 | multi_json (~> 1.3)
30 | jaro_winkler (1.5.6)
31 | json (2.12.0)
32 | logger (1.7.0)
33 | loog (0.6.1)
34 | logger (~> 1.0)
35 | multi_json (1.15.0)
36 | multi_test (1.1.0)
37 | parallel (1.27.0)
38 | parser (3.3.8.0)
39 | ast (~> 2.4.1)
40 | racc
41 | powerpack (0.1.3)
42 | racc (1.8.1)
43 | rainbow (3.1.1)
44 | rake (12.3.3)
45 | rubocop (0.61.0)
46 | jaro_winkler (~> 1.5.1)
47 | parallel (~> 1.10)
48 | parser (>= 2.5, != 2.5.1.1)
49 | powerpack (~> 0.1)
50 | rainbow (>= 2.2.2, < 4.0)
51 | ruby-progressbar (~> 1.7)
52 | unicode-display_width (~> 1.4.0)
53 | rubocop-minitest (0.0.1)
54 | rubocop-performance (1.0.0)
55 | rubocop (>= 0.58.0)
56 | rubocop-rake (0.5.1)
57 | rubocop
58 | rubocop-rspec (1.31.0)
59 | rubocop (>= 0.60.0)
60 | ruby-progressbar (1.13.0)
61 | simplecov (0.22.0)
62 | docile (~> 1.1)
63 | simplecov-html (~> 0.11)
64 | simplecov_json_formatter (~> 0.1)
65 | simplecov-html (0.13.1)
66 | simplecov_json_formatter (0.1.4)
67 | slop (4.8.2)
68 | unicode-display_width (1.4.1)
69 |
70 | PLATFORMS
71 | arm64-darwin-23
72 | arm64-darwin-24
73 | ruby
74 | x64-mingw-ucrt
75 | x86_64-linux
76 |
77 | DEPENDENCIES
78 | codecov (= 0.2.6)
79 | cucumber (~> 1.3.17)
80 | rake (= 12.3.3)
81 | rubocop (= 0.61.0)
82 | rubocop-minitest (> 0)
83 | rubocop-performance (> 0)
84 | rubocop-rake (> 0)
85 | rubocop-rspec (= 1.31.0)
86 | texqc!
87 |
88 | BUNDLED WITH
89 | 2.5.16
90 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright (c) 2020 Yegor Bugayenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the 'Software'), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LICENSES/MIT.txt:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright (c) 2020 Yegor Bugayenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the 'Software'), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [](https://badge.fury.io/rb/texqc)
4 | [](https://github.com/yegor256/takes/texqc/master/LICENSE.txt)
5 |
6 | This tool helps you make sure your LaTeX document compiles without issues.
7 |
8 | First, you install it:
9 |
10 | ```bash
11 | $ gem install texqc
12 | ```
13 |
14 | Then, you just run it after the LaTeX document is compiled:
15 |
16 | ```bash
17 | $ latexmk -pdf article
18 | $ texqc article
19 | ```
20 |
21 | If any warnings were reported by LaTeX, you will get a short list of them
22 | and the exit code will be non-zero (very convenient for your CI/CD scripts).
23 |
24 | To make configuration easier, you can create `.texqc` file next to your
25 | `.tex` file and place all your command line configuration options over there,
26 | each one on its own line. You can also have a global configuration file
27 | at `~/.texqc`, which will be read first.
28 |
29 | ## How to contribute
30 |
31 | Read [these guidelines](https://www.yegor256.com/2014/04/15/github-guidelines.html).
32 | Make sure your build is green before you contribute
33 | your pull request. You will need to have [Ruby](https://www.ruby-lang.org/en/) 2.3+ and
34 | [Bundler](https://bundler.io/) installed. Then:
35 |
36 | ```
37 | $ bundle update
38 | $ bundle exec rake
39 | ```
40 |
41 | If it's clean and you don't see any error messages, submit your pull request.
42 |
--------------------------------------------------------------------------------
/REUSE.toml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2025 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 |
4 | version = 1
5 | [[annotations]]
6 | path = [
7 | ".DS_Store",
8 | ".gitattributes",
9 | ".gitignore",
10 | ".pdd",
11 | "**.json",
12 | "**.md",
13 | "**.svg",
14 | "**.txt",
15 | "**/.DS_Store",
16 | "**/.gitignore",
17 | "**/.pdd",
18 | "**/*.csv",
19 | "**/*.jpg",
20 | "**/*.json",
21 | "**/*.md",
22 | "**/*.pdf",
23 | "**/*.png",
24 | "**/*.svg",
25 | "**/*.txt",
26 | "**/*.vm",
27 | "**/CNAME",
28 | "**/Gemfile.lock",
29 | "Gemfile.lock",
30 | "README.md",
31 | "renovate.json",
32 | ]
33 | precedence = "override"
34 | SPDX-FileCopyrightText = "Copyright (c) 2025 Yegor Bugayenko"
35 | SPDX-License-Identifier = "MIT"
36 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # SPDX-FileCopyrightText: Copyright (c) 2020-2025 Yegor Bugayenko
4 | # SPDX-License-Identifier: MIT
5 |
6 | require 'rubygems'
7 | require 'rake'
8 | require 'rdoc'
9 | require 'rake/clean'
10 |
11 | def name
12 | @name ||= File.basename(Dir['*.gemspec'].first, '.*')
13 | end
14 |
15 | def version
16 | Gem::Specification.load(Dir['*.gemspec'].first).version
17 | end
18 |
19 | task default: %i[clean features rubocop]
20 |
21 | require 'rubocop/rake_task'
22 | RuboCop::RakeTask.new(:rubocop) do |task|
23 | task.fail_on_error = true
24 | task.requires << 'rubocop-rspec'
25 | end
26 |
27 | require 'cucumber/rake/task'
28 | Cucumber::Rake::Task.new(:features) do
29 | Rake::Cleaner.cleanup_files(['coverage'])
30 | end
31 | Cucumber::Rake::Task.new(:'features:html') do |t|
32 | t.profile = 'html_report'
33 | end
34 |
--------------------------------------------------------------------------------
/bin/texqc:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | # SPDX-FileCopyrightText: Copyright (c) 2020-2025 Yegor Bugayenko
5 | # SPDX-License-Identifier: MIT
6 |
7 | VERSION = '0.0.0'
8 |
9 | STDOUT.sync = true
10 |
11 | require 'backtrace'
12 | require 'loog'
13 | require 'open3'
14 | require 'slop'
15 |
16 | # Errors counter.
17 | class Errors
18 | attr_reader :count
19 | def initialize
20 | @count = 0
21 | end
22 |
23 | def report(pos, txt)
24 | puts "[#{pos}] #{txt}"
25 | @count += 1
26 | end
27 | end
28 |
29 | def config(path)
30 | f = File.expand_path(path)
31 | args = []
32 | if File.exist?(f)
33 | args += File.readlines(f).map(&:strip)
34 | puts "Found #{args.length} lines in #{File.absolute_path(f)}"
35 | end
36 | args
37 | end
38 |
39 | begin
40 | log = Loog::REGULAR
41 | args = config('~/.texqc') + config('.texqc') + ARGV
42 |
43 | begin
44 | opts = Slop.parse(args, strict: true, help: true) do |o|
45 | o.banner = "Usage (#{VERSION}): texqc [options] file...
46 | Options are:"
47 | o.bool '--dry', 'Don\'t fail the build on errors'
48 | o.array '--ignore', 'Ignore given regexp'
49 | o.bool '--version', 'Print current version' do
50 | puts VERSION
51 | exit
52 | end
53 | o.bool '--verbose', 'Make it more verbose than usual' do
54 | log = Loog::VERBOSE
55 | end
56 | o.bool '--help', 'Read this: https://github.com/yegor256/texqc' do
57 | puts o
58 | exit
59 | end
60 | end
61 | rescue Slop::Error => ex
62 | raise ex.message
63 | end
64 | candidates = opts.arguments
65 | candidates += Dir['*.tex'] if candidates.empty?
66 | puts "Args: #{args}" if opts[:verbose]
67 | puts "Ignore: #{opts[:ignore]}" if opts[:verbose]
68 | puts "Candidates: #{candidates}" if opts[:verbose]
69 | candidates.each do |doc|
70 | if doc.end_with?('.tex')
71 | log.info("File extention removed from #{doc.inspect}")
72 | doc = doc.gsub(/\.tex$/, '')
73 | end
74 | f = "#{doc}.log"
75 | e = Errors.new
76 | matches = [
77 | /^Underfull /,
78 | /^Overfull /,
79 | /^LaTeX Warning: /,
80 | /^pdfTeX warning: /,
81 | /^Class [a-zA-Z0-9\*]+ Warning: /,
82 | /^Package [a-zA-Z0-9\*]+ Warning: /,
83 | /^LaTeX Font Warning: /
84 | ]
85 | File.readlines(f).each_with_index do |t, i|
86 | matches.each do |p|
87 | next unless p.match?(t)
88 | next if opts[:ignore].any? { |re| /.*#{re}.*/.match?(t) }
89 | e.report(i, t)
90 | end
91 | end
92 | unless e.count.zero?
93 | log.info("#{e.count} LaTeX processing errors found in #{f.inspect}")
94 | exit 1
95 | end
96 | log.info("No LaTeX processing errors found in #{f.inspect}")
97 | end
98 | rescue StandardError => ex
99 | if opts[:verbose]
100 | puts Backtrace.new(ex).to_s
101 | else
102 | puts "ERROR: #{ex.message}"
103 | end
104 | exit(255)
105 | end
106 |
--------------------------------------------------------------------------------
/cucumber.yml:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2020 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | ---
4 | default: --format pretty
5 | travis: --format progress
6 | html_report: --format progress --format html --out=features_report.html
7 |
--------------------------------------------------------------------------------
/features/cli.feature:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2020 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | Feature: Command Line Processing
4 | As a author of LaTeX document I want to check spelling
5 |
6 | Scenario: Help can be printed
7 | When I run bin/texqc with "--help"
8 | Then Exit code is zero
9 | And Stdout contains "--help"
10 |
11 | Scenario: Good LaTeX log output can be checked
12 | Given I have a "article.tex" file with content:
13 | """
14 | \documentclass{article}
15 | \begin{document}
16 | How are you, my dear friend?
17 | \end{document}
18 | """
19 | When I run bash with "pdflatex article.tex"
20 | Then I run bin/texqc with ""
21 | Then Exit code is zero
22 | And Stdout contains "No LaTeX processing errors found"
23 |
24 | Scenario: Bad LaTeX log output checked
25 | Given I have a "article.tex" file with content:
26 | """
27 | \documentclass{article}
28 | \begin{document}
29 | HowareyouHowareyouHowareyouHowareyouHowareyouHowareyouHowareyouHowareyouHowareyouHowareyouHowareyouHowareyouHowareyou
30 | \end{document}
31 | """
32 | When I run bash with "pdflatex article.tex"
33 | Then I run bin/texqc with "article"
34 | Then Exit code is not zero
35 | And Stdout contains "1 LaTeX processing errors"
36 |
37 | Scenario: Bad LaTeX log output checked with LaTeX warning
38 | Given I have a "article.tex" file with content:
39 | """
40 | \documentclass{article}
41 | \begin{document}
42 | test\label{xxx}test\label{xxx}
43 | \end{document}
44 | """
45 | When I run bash with "pdflatex article.tex"
46 | Then I run bin/texqc with "article.tex"
47 | Then Exit code is not zero
48 | And Stdout contains "1 LaTeX processing errors"
49 |
50 | Scenario: Bad LaTeX log output checked with LaTeX warning, but ignored
51 | Given I have a "article.tex" file with content:
52 | """
53 | \documentclass{article}
54 | \begin{document}
55 | test\label{xxx}test\label{xxx}
56 | \end{document}
57 | """
58 | When I run bash with "pdflatex article.tex"
59 | Then I run bin/texqc with "--ignore 'may have changed' article.tex"
60 | Then Exit code is zero
61 |
62 | Scenario: Bad LaTeX log output checked with LaTeX warning, but ignored with .texqc
63 | Given I have a "article.tex" file with content:
64 | """
65 | \documentclass{article}
66 | \begin{document}
67 | test\label{xxx}test\label{xxx}
68 | \end{document}
69 | """
70 | And I have a ".texqc" file with content:
71 | """
72 | --verbose
73 |
74 | --ignore='may have changed'
75 | """
76 | When I run bash with "pdflatex article.tex"
77 | Then I run bin/texqc
78 | Then Exit code is zero
79 |
--------------------------------------------------------------------------------
/features/gem_package.feature:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2020 Yegor Bugayenko
2 | # SPDX-License-Identifier: MIT
3 | Feature: Gem Package
4 | As a source code writer I want to be able to
5 | package the Gem into .gem file
6 |
7 | Scenario: Gem can be packaged
8 | Given It is Unix
9 | Given I have a "execs.rb" file with content:
10 | """
11 | #!/usr/bin/env ruby
12 | require 'rubygems'
13 | spec = Gem::Specification::load('./spec.rb')
14 | if spec.executables.empty?
15 | fail 'no executables: ' + File.read('./spec.rb')
16 | end
17 | """
18 | When I run bash with:
19 | """
20 | cd texqc
21 | gem build texqc.gemspec
22 | gem specification --ruby texqc-*.gem > ../spec.rb
23 | cd ..
24 | ruby execs.rb
25 | """
26 | Then Exit code is zero
27 |
--------------------------------------------------------------------------------
/features/step_definitions/steps.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # SPDX-FileCopyrightText: Copyright (c) 2020-2025 Yegor Bugayenko
4 | # SPDX-License-Identifier: MIT
5 |
6 | require 'tmpdir'
7 | require 'English'
8 |
9 | Before do
10 | @cwd = Dir.pwd
11 | @dir = Dir.mktmpdir('test')
12 | FileUtils.mkdir_p(@dir) unless File.exist?(@dir)
13 | Dir.chdir(@dir)
14 | end
15 |
16 | After do
17 | Dir.chdir(@cwd)
18 | FileUtils.rm_rf(@dir) if File.exist?(@dir)
19 | end
20 |
21 | Given(/^I have a "([^"]*)" file with content:$/) do |file, text|
22 | FileUtils.mkdir_p(File.dirname(file)) unless File.exist?(file)
23 | File.open(file, 'w') do |f|
24 | f.write(text.gsub(/\\xFF/, 0xFF.chr))
25 | end
26 | end
27 |
28 | When(%r{^I run bin/texqc with "([^"]*)"$}) do |arg|
29 | home = File.join(File.dirname(__FILE__), '../..')
30 | @stdout = `ruby -I#{home}/lib #{home}/bin/texqc #{arg}`
31 | @exitstatus = $CHILD_STATUS.exitstatus
32 | end
33 |
34 | Then(/^Stdout contains "([^"]*)"$/) do |txt|
35 | raise "STDOUT doesn't contain '#{txt}':\n#{@stdout}" unless @stdout.include?(txt)
36 | end
37 |
38 | Then(/^Stdout is empty$/) do
39 | raise "STDOUT is not empty:\n#{@stdout}" unless @stdout == ''
40 | end
41 |
42 | Then(/^Exit code is zero$/) do
43 | raise "Non-zero exit #{@exitstatus}:\n#{@stdout}" unless @exitstatus.zero?
44 | end
45 |
46 | Then(/^Exit code is not zero$/) do
47 | raise 'Zero exit code' if @exitstatus.zero?
48 | end
49 |
50 | When(/^I run bash with "([^"]*)"$/) do |text|
51 | FileUtils.copy_entry(@cwd, File.join(@dir, 'texqc'))
52 | @stdout = `#{text}`
53 | @exitstatus = $CHILD_STATUS.exitstatus
54 | end
55 |
56 | When(/^I run bash with:$/) do |text|
57 | FileUtils.copy_entry(@cwd, File.join(@dir, 'texqc'))
58 | @stdout = `#{text}`
59 | @exitstatus = $CHILD_STATUS.exitstatus
60 | end
61 |
62 | Given(/^It is Unix$/) do
63 | pending if Gem.win_platform?
64 | end
65 |
66 | Given(/^It is Windows$/) do
67 | pending unless Gem.win_platform?
68 | end
69 |
--------------------------------------------------------------------------------
/features/support/env.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # SPDX-FileCopyrightText: Copyright (c) 2020-2025 Yegor Bugayenko
4 | # SPDX-License-Identifier: MIT
5 |
6 | require 'simplecov'
7 |
--------------------------------------------------------------------------------
/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
15 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/texqc.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # SPDX-FileCopyrightText: Copyright (c) 2020-2025 Yegor Bugayenko
4 | # SPDX-License-Identifier: MIT
5 |
6 | require 'English'
7 |
8 | lib = File.expand_path('lib', __dir__)
9 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
10 | Gem::Specification.new do |s|
11 | s.specification_version = 2 if s.respond_to? :specification_version=
12 | if s.respond_to? :required_rubygems_version=
13 | s.required_rubygems_version = Gem::Requirement.new('>= 0')
14 | end
15 | s.rubygems_version = '2.2'
16 | s.required_ruby_version = '>= 2.3'
17 | s.name = 'texqc'
18 | s.version = '0.0.0'
19 | s.license = 'MIT'
20 | s.summary = 'Quality Control of Your LaTeX Build'
21 | s.description = 'Run it after you compile your LaTeX document'
22 | s.authors = ['Yegor Bugayenko']
23 | s.email = 'yegor256@gmail.com'
24 | s.homepage = 'http://github.com/yegor256/texqc'
25 | s.files = `git ls-files`.split($RS)
26 | s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
27 | s.test_files = s.files.grep(%r{^(test|spec|features)/})
28 | s.rdoc_options = ['--charset=UTF-8']
29 | s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
30 | s.add_runtime_dependency 'backtrace', '~> 0.3'
31 | s.add_runtime_dependency 'loog', '~> 0.2'
32 | s.add_runtime_dependency 'slop', '~> 4.8.2'
33 | s.add_development_dependency 'codecov', '0.2.6'
34 | s.add_development_dependency 'cucumber', '~> 1.3.17'
35 | s.add_development_dependency 'rake', '12.3.3'
36 | s.add_development_dependency 'rubocop', '0.61.0'
37 | s.add_development_dependency 'rubocop-rspec', '1.31.0'
38 | end
39 |
--------------------------------------------------------------------------------