├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── action.yml ├── etc ├── cwtools_gitlab_pr_example.png └── cwtools_pr_example.png ├── examples ├── GitHub_CWToolsCI.yml └── GitLab_CWToolsCI.yml └── lib ├── .reviewdog.yml ├── cwtools.rb ├── entrypoint.sh └── gitlab_setup.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /spec/examples.txt 9 | /test/tmp/ 10 | /test/version_tmp/ 11 | /tmp/ 12 | 13 | # Used by dotenv library to load environment variables. 14 | # .env 15 | 16 | ## Specific to RubyMotion: 17 | .dat* 18 | .repl_history 19 | build/ 20 | *.bridgesupport 21 | build-iPhoneOS/ 22 | build-iPhoneSimulator/ 23 | 24 | ## Specific to RubyMotion (use of CocoaPods): 25 | # 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 29 | # 30 | # vendor/Pods/ 31 | 32 | ## Documentation cache and generated files: 33 | /.yardoc/ 34 | /_yardoc/ 35 | /doc/ 36 | /rdoc/ 37 | 38 | ## Environment normalization: 39 | /.bundle/ 40 | /vendor/bundle 41 | /lib/bundler/man/ 42 | 43 | # for a library or gem, you might want to ignore these files since the code is 44 | # intended to run in multiple environments; otherwise, check them in: 45 | # Gemfile.lock 46 | # .ruby-version 47 | # .ruby-gemset 48 | 49 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 50 | .rvmrc 51 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM cwtools/cwtools-action:latest 2 | 3 | COPY lib /action/lib 4 | 5 | ENTRYPOINT ["/action/lib/entrypoint.sh"] 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Antoni Baum 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 | # CWTools Action 2 | 3 | Run CWTools on your Clausewitz mod PDXScript code in parallel to your builds. 4 | 5 | If CWTools finds errors, warnings or suggestions in the mod code then they will be output. 6 | 7 | It will also insert them as inline feedback into your PRs ("Files changed" tab): 8 | 9 | ![pr_example](./etc/cwtools_pr_example.png) 10 | 11 | ## Setup 12 | 13 | **GitHub:** Can't use GitHub? Click [here](#gitlab) for GitLab installation instructions . 14 | 15 | In most cases, no setup is required beyond adding the following workflow yml file to your project (`.github/workflows` folder) and setting the correct game. See below for advanced configuration and an explanation of the tools used. 16 | 17 | The following games require no further setup: 18 | 19 | - HOI4 20 | - Stellaris 21 | 22 | ### Example workflow yml 23 | 24 | ```yml 25 | name: CWTools CI 26 | 27 | on: [pull_request, push] # other events may work but are not supported 28 | 29 | jobs: 30 | cwtools_job: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v1 # required 34 | - uses: cwtools/cwtools-action@v1.1.0 35 | with: 36 | game: hoi4 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # required, secret is automatically set by github 39 | 40 | ``` 41 | 42 | This action will create a new job called "CWTools", which will be used to annotate your code. Its success or failure state depends on the CWTools output. 43 | 44 | The full `output.json` log is saved to `$GITHUB_WORKSPACE`, and can be recovered with [actions/upload-artifact](https://github.com/actions/upload-artifact). 45 | 46 | ```yml 47 | - uses: cwtools/cwtools-action@v1.1.0 48 | with: 49 | game: hoi4 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | - name: Upload artifact 53 | if: always() # so even if the check fails, the log is uploaded 54 | uses: actions/upload-artifact@v1.1.0 55 | with: 56 | name: cwtools_output 57 | path: output.json 58 | ``` 59 | 60 | ## Configuration 61 | 62 | ### game (required) 63 | 64 | What game to use. Allowed values: `hoi4`, `ck2`, `eu4`, `ir`, `stellaris`, `vic2`. 65 | 66 | ```yml 67 | - uses: cwtools/cwtools-action@v1.1.0 68 | with: 69 | game: hoi4 70 | env: 71 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 72 | ``` 73 | 74 | ### modPath (optional) 75 | 76 | Path to the mod folder in `$GITHUB_WORKSPACE` (root of repository). (Default: "" - root of repository itself) 77 | 78 | ```yml 79 | - uses: cwtools/cwtools-action@v1.1.0 80 | with: 81 | game: hoi4 82 | modPath: "mod_folder" 83 | env: 84 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 85 | ``` 86 | 87 | ### cache (optional) 88 | 89 | Path to the full cache file (`cwb.bz2`) in `$GITHUB_WORKSPACE` (root of repository). Use an empty string to use metadata from cwtools/cwtools-cache-files (Default: use metadata) 90 | 91 | ```yml 92 | - uses: cwtools/cwtools-action@v1.1.0 93 | with: 94 | game: hoi4 95 | cache: "cache/hoi4.cwb.bz2" 96 | env: 97 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 98 | ``` 99 | 100 | ### locLanguages (optional) 101 | 102 | Which languages to check localisation for, space separated, lowercase (eg. `english spanish russian`). Note: May be different from game to game. (Default: `english`) 103 | 104 | ```yml 105 | - uses: cwtools/cwtools-action@v1.1.0 106 | with: 107 | game: hoi4 108 | locLanguages: "english spanish russian" 109 | env: 110 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 111 | ``` 112 | 113 | ### vanillaMode (optional) 114 | 115 | Whether to not use cache, and instead treat the project as a vanilla game installation folder - if you are a modder, you probably should not be using this. If True, cache input will be ignored (Default: False, set to anything other than 0 or blank for True) 116 | 117 | ```yml 118 | - uses: cwtools/cwtools-action@v1.1.0 119 | with: 120 | game: hoi4 121 | vanillaMode: "1" 122 | env: 123 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 124 | ``` 125 | 126 | ### rules (optional) 127 | 128 | What rules repository to use (Default: `https://github.com/cwtools/cwtools-$INPUT_GAME-config.git`) 129 | 130 | ```yml 131 | - uses: cwtools/cwtools-action@v1.1.0 132 | with: 133 | game: hoi4 134 | rules: "https://github.com/Yard1/cwtools-hoi4-config.git" 135 | env: 136 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 137 | ``` 138 | 139 | ### rulesRef (optional) 140 | 141 | What ref on rules repo to checkout (Default: `master`) 142 | 143 | ```yml 144 | - uses: cwtools/cwtools-action@v1.1.0 145 | with: 146 | game: hoi4 147 | rulesRef: "1.0.0" 148 | env: 149 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 150 | ``` 151 | 152 | ### changedFilesOnly (optional) 153 | 154 | By default will only annotate changed files in a push or a pull request. In order to annotate all files set `changedFilesOnly` input to `"0"`. 155 | 156 | ```yml 157 | - uses: cwtools/cwtools-action@v1.1.0 158 | with: 159 | game: hoi4 160 | changedFilesOnly: "0" 161 | env: 162 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 163 | ``` 164 | 165 | ### suppressedOffenceCategories (optional) 166 | 167 | You can choose to suppress annotations with chosen CWTools offence category IDs (`CW###`) per GitHub severity type (failure, warning, notice). 168 | 169 | ```yml 170 | - uses: cwtools/cwtools-action@v1.1.0 171 | with: 172 | game: hoi4 173 | suppressedOffenceCategories: '{"failure":["CW110", "CW210"], "warning":[], "notice":[]}' # will suppress CW110 and CW210 category failures, but will show those for warnings and notices 174 | env: 175 | default: ${{ secrets.GITHUB_TOKEN }} 176 | ``` 177 | 178 | ### suppressedFiles (optional) 179 | 180 | You can choose to suppress annotations completely in certain files. Use paths from root of repository, make sure to have no trailing whitespace. Globbing is not supported. 181 | 182 | ```yml 183 | - uses: cwtools/cwtools-action@v1.1.0 184 | with: 185 | game: hoi4 186 | suppressedFiles: '["common/scripted_effects/my_effects.txt", "events/EventFile.txt"]' # will completely suppress any annotations in those two files 187 | env: 188 | default: ${{ secrets.GITHUB_TOKEN }} 189 | ``` 190 | 191 | ### CWToolsCLIVersion (optional) 192 | 193 | Which CWTools.CLI version to use (Default: latest stable). 194 | 195 | ```yml 196 | - uses: cwtools/cwtools-action@v1.1.0 197 | with: 198 | game: hoi4 199 | CWToolsCLIVersion: '0.0.7' 200 | env: 201 | default: ${{ secrets.GITHUB_TOKEN }} 202 | ``` 203 | 204 | ## GitLab 205 | 206 | **Due to limitations with GitLab, this currently only works for merge requests to master** 207 | 208 | [](gitlab)Running this action on GitLab is a bit more involved, requiring the creation of a bot account. It is also limited to providing comments on pull requests as shown here: 209 | 210 | ![GitLab example](etc/cwtools_gitlab_pr_example.png) 211 | 212 | ### Setting up the bot account 213 | 214 | 1. Create a new GitLab account for this "bot" and give it "Reporter" access to your project. 215 | 2. Log into the account, browse to [the Personal Access Token page](https://gitlab.com/profile/personal_access_tokens) and generate a PAT with "api" scope. Make a note of the token. 216 | 3. Log back into your primary account. 217 | 4. Browse to your Project and go to "Settings", "CI / CD", and open the section "Variables". 218 | 5. Create a variable called "REVIEWDOG_GITLAB_API_TOKEN". Put the PAT generated above as the Value, then set it as "Masked" but **not** Protected. 219 | 6. Press "Save variables". 220 | 221 | **Please note:** This PAT gives access to all projects the bot can access. If somebody gets access to your pipeline logs, it's possible (although not likely) that they could access the token. 222 | 223 | ### Configuring gitlab-ci 224 | 225 | 1. In the root of your project create a file called `.gitlab-ci.yml` 226 | 2. Copy the contents of the example file [GitLab_CWToolsCI.yml](examples/GitLab_CWToolsCI.yml), found in /examples, into it. 227 | 3. Configure the variables if desired (see above). 228 | 4. Create a merge request and check it works! 229 | 230 | #### GitLab self-hosted 231 | 232 | If you're running your own instance of GitLab, you'll need to set the following two variables in addition to those in the default template: 233 | 234 | ``` 235 | - GITLAB_API: "https://example.gitlab.com/api/v4" 236 | - REVIEWDOG_INSECURE_SKIP_VERIFY: true 237 | ``` 238 | 239 | ## How this works 240 | 241 | [CWTools](https://github.com/tboby/cwtools) is a .NET library that provides features to analyse and manipulate the scripting language used in Paradox Development Studio's games (PDXScript). This is mainly used in a VS Code extension, [cwtools-vscode](https://marketplace.visualstudio.com/items?itemName=tboby.cwtools-vscode). CWTools also provides a CLI tool [CWTools.CLI](https://www.nuget.org/packages/CWTools.CLI/) to allow automated anaylsis, which is what this action relies on. 242 | 243 | This action relies on two things: 244 | 245 | 1. A set of valiation rules written for the game your mod is for 246 | 2. A cache file containing key information from vanilla files 247 | 248 | ### Validation rules 249 | 250 | The validation rules are taken from the master branch of the public repository for the game, e.g. [https://github.com/cwtools/cwtools-hoi4-config](https://github.com/cwtools/cwtools-hoi4-config). The settings `rules` and `rulesRef` can be used to specify an alternative repo, or to stay on an old version of the rules. 251 | 252 | ### Vanilla cache file 253 | 254 | In order to validate correctly, CWTools requires certain data from the vanilla game files such as defined localisation, variables, etc. There are two formats of cache file: 255 | 256 | #### Metadata only 257 | 258 | The metadata format contains a limited set of information from vanilla, enough to run the main validator. For convenience CWTools automatically generates these metadata cache files for the latest public rules and latest version of each game. These are found [here](https://github.com/cwtools/cwtools-cache-files) and are used by default in this action (please note that not all games may be supported yet). 259 | 260 | #### Full 261 | 262 | The full format contains a more details, processed, version of the vanilla script files. This is required in order to provide accurate validation taking load order and file overrides into account. This is the same format used by cwtools-vscode. We do not provide these files, however this action can be configured to use them. 263 | 264 | ## Credits 265 | 266 | Created by Antoni Baum ([Yard1](https://github.com/Yard1)). 267 | 268 | Using [tboby/cwtools](https://github.com/tboby/cwtools). 269 | 270 | Based on [gimenete/rubocop-action](https://github.com/gimenete/rubocop-action) by Alberto Gimeno. 271 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | # action.yml 2 | name: 'CWTools Action' 3 | description: 'Run CWTools on your Clausewitz mod PDXScript code in parallel to your builds' 4 | branding: 5 | icon: 'check' 6 | color: 'purple' 7 | inputs: 8 | game: 9 | description: 'What game to use. Supported values: hoi4, ck2, eu4, ir, stellaris, vic2.' 10 | required: true 11 | modPath: 12 | description: 'Path to the mod folder in $GITHUB_WORKSPACE (root of repository). (Default: "" - root of repository itself)' 13 | required: false 14 | default: '' 15 | cache: 16 | description: 'Path to the full cache file (cwb.bz2) in $GITHUB_WORKSPACE (root of repository). Use an empty string to use metadata from cwtools/cwtools-cache-files (Default: use metadata)' 17 | required: false 18 | default: '' 19 | vanillaMode: 20 | description: 'Whether to not use cache, and instead treat the project as a vanilla game installation folder - if you are a modder, you probably should not be using this. If True, cache input will be ignored (Default: False, set to anything other than 0 or blank for True)' 21 | required: false 22 | default: '0' 23 | locLanguages: 24 | description: 'Which languages to check localisation for, space separated, lowercase (eg. "english spanish russian"). Note: May be different from game to game. (Default: english)' 25 | required: false 26 | default: 'english' 27 | rules: 28 | description: 'What rules repository to use (Default: https://github.com/cwtools/cwtools-$INPUT_GAME-config.git)' 29 | required: false 30 | default: '' #if empty, use cwtools/cwtools-$INPUT_GAME-config.git 31 | rulesRef: 32 | description: 'What ref on rules repo to checkout (Default: master)' 33 | required: false 34 | default: 'master' 35 | changedFilesOnly: 36 | description: 'Whether to only push offences in changed files (Default: True, set to 0 or blank for False)' 37 | required: false 38 | default: '1' 39 | suppressedOffenceCategories: 40 | description: 'What CWTools offence categories to suppress, in JSON format: {"failure":[], "warning":[], "notice":[]}' 41 | required: false 42 | default: '{"failure":[], "warning":[], "notice":[]}' 43 | suppressedFiles: 44 | description: 'Which files to completely ignore, in JSON format: ["path/from/root/repo/folder", "path/from/root/repo/folder"]' 45 | required: false 46 | default: '[]' 47 | CWToolsCLIVersion: 48 | description: 'Which CWTools.CLI version to use (Default: latest stable)' 49 | required: false 50 | default: '' 51 | runs: 52 | using: 'docker' 53 | image: 'Dockerfile' 54 | -------------------------------------------------------------------------------- /etc/cwtools_gitlab_pr_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwtools/cwtools-action/a729d740ba1fbb71c799b65850730328a7bacb5c/etc/cwtools_gitlab_pr_example.png -------------------------------------------------------------------------------- /etc/cwtools_pr_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwtools/cwtools-action/a729d740ba1fbb71c799b65850730328a7bacb5c/etc/cwtools_pr_example.png -------------------------------------------------------------------------------- /examples/GitHub_CWToolsCI.yml: -------------------------------------------------------------------------------- 1 | # cwtools-action 2 | # https://github.com/cwtools/cwtools-action 3 | # Example GitHub Actions workflow file for a Hearts of Iron IV project 4 | # Will trigger on pushes to master, or when a pull request is made or updated 5 | # Will only trigger if files that can be checked by CWTools are pushed 6 | # Put in .github/workflows (create folders if they are not there) 7 | 8 | name: CWTools CI 9 | 10 | on: 11 | push: 12 | branches: 13 | - master 14 | paths: 15 | - '**/*.txt' 16 | - '**/*.yml' 17 | - '**/*.gfx' 18 | - '**/*.gui' 19 | - '!.**' 20 | - '!tutorial/**' 21 | - '!changelog.txt' 22 | - '!interface/credits.txt' 23 | pull_request: 24 | paths: 25 | - '**/*.txt' 26 | - '**/*.yml' 27 | - '**/*.gfx' 28 | - '**/*.gui' 29 | - '!.**' 30 | - '!tutorial/**' 31 | - '!changelog.txt' 32 | - '!interface/credits.txt' 33 | 34 | jobs: 35 | cwtools_job: 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v1 39 | - uses: cwtools/cwtools-action@v1.1.0 40 | with: 41 | game: hoi4 42 | locLanguages: "english russian" # change this to what localisation languages your mod supports 43 | env: 44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 45 | - name: Upload output.json 46 | if: always() 47 | uses: actions/upload-artifact@v1.1.0 48 | with: 49 | name: cwtools_output 50 | path: output.json 51 | -------------------------------------------------------------------------------- /examples/GitLab_CWToolsCI.yml: -------------------------------------------------------------------------------- 1 | # cwtools-action v1.0, 2019-12-02 2 | # Please occasionally check below for updates to this template 3 | # https://github.com/cwtools/cwtools-action 4 | # Example GitLab CI workflow file for a Stellaris project 5 | # Will trigger when a merge request to master is created 6 | # Rename to .gitlab-ci.yml and put in root of your repository 7 | 8 | image: mcr.microsoft.com/dotnet/core/sdk:3.0 9 | 10 | variables: 11 | DOCKER_DRIVER: overlay2 12 | GIT_STRATEGY: clone # Please see https://github.com/cwtools/cwtools-action/issues/3 for details as to why this is needed 13 | INPUT_GAME: "stellaris" # Change to the game used in your project 14 | # Variables below are optional and set to their default values - uncomment and change them if you wish so 15 | #INPUT_MODPATH: '' 16 | #INPUT_CACHE: '' 17 | #INPUT_LOCLANGUAGES: 'english' 18 | #INPUT_RULES: '' 19 | #INPUT_RULESREF: 'master' 20 | #INPUT_VANILLAMODE: '0' 21 | #INPUT_SUPPRESSEDOFFENCECATEGORIES: '{"failure":[], "warning":[], "notice":[]}' 22 | #INPUT_SUPPRESSEDFILES: '[]' 23 | #INPUT_CWTOOLSCLIVERSION: '' 24 | 25 | stages: 26 | - CWTools_CI 27 | 28 | CWTools_CI: 29 | stage: CWTools_CI 30 | only: [merge_requests] 31 | script: 32 | - wget -O - -q https://raw.githubusercontent.com/cwtools/cwtools-action/v1.1.0/lib/gitlab_setup.sh | sh -s 33 | 34 | # Optional, expose the CWTools errors in JSON 35 | artifacts: 36 | expose_as: 'CWTools output' 37 | paths: 38 | - output.json 39 | when: always -------------------------------------------------------------------------------- /lib/.reviewdog.yml: -------------------------------------------------------------------------------- 1 | runner: 2 | cwtools: 3 | cmd: cat errors.txt 4 | errorformat: 5 | - "%Z%f:%l:%c:%t:%E%m" 6 | - "%+C%m" 7 | -------------------------------------------------------------------------------- /lib/cwtools.rb: -------------------------------------------------------------------------------- 1 | # Based on https://github.com/gimenete/rubocop-action by Alberto Gimeno published under MIT License. 2 | # 3 | # MIT License 4 | # 5 | # Copyright (c) 2019 Alberto Gimeno 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | require 'net/http' 26 | require 'json' 27 | require 'time' 28 | require 'set' 29 | 30 | @CW_EVENT_PATH = ENV["CW_EVENT"] 31 | @CW_TOKEN = ENV["CW_TOKEN"] 32 | @CW_WORKSPACE = ENV["CW_WORKSPACE"] 33 | @CW_SHA = ENV["CW_SHA"] 34 | @check_name = ENV["CW_CHECKNAME"] 35 | @CW_CI_ENV = ENV["CW_CI_ENV"] 36 | 37 | @SUPPRESSED_OFFENCE_CATEGORIES = JSON.parse(ENV["INPUT_SUPPRESSEDOFFENCECATEGORIES"]) 38 | @SUPPRESSED_FILES = JSON.parse(ENV["INPUT_SUPPRESSEDFILES"]) 39 | @GAME = ENV["INPUT_GAME"] 40 | @LOC_LANGUAGES = ENV["INPUT_LOCLANGUAGES"] 41 | @MOD_PATH = ENV["INPUT_MODPATH"] 42 | @CHANGED_ONLY = ENV["INPUT_CHANGEDFILESONLY"] 43 | @CACHE_FULL = ENV["INPUT_CACHE"] 44 | @VANILLA_MODE = ENV["INPUT_VANILLAMODE"] 45 | 46 | if @CW_CI_ENV == "github" 47 | @CHANGED_ONLY = !(@CHANGED_ONLY == '0' || @CHANGED_ONLY == '') 48 | elsif @CW_CI_ENV == "gitlab" 49 | @CHANGED_ONLY = false 50 | end 51 | 52 | @CACHE_FULL = !(@CACHE_FULL == '') 53 | @VANILLA_MODE = !(@VANILLA_MODE == '0' || @VANILLA_MODE == '') 54 | 55 | if @MOD_PATH != '' 56 | @MOD_PATH = "/" + @MOD_PATH 57 | end 58 | 59 | @changed_files = [] 60 | 61 | @annotation_levels = { 62 | "error" => 'failure', 63 | "warning" => 'warning', 64 | "information" => 'notice', 65 | "hint" => 'notice' 66 | } 67 | 68 | @reviewdog_annotation_levels = { 69 | "failure" => '❌ Failure: ', 70 | "warning" => '⚠️ Warning: ', 71 | "notice" => 'ℹ️ Notice: ', 72 | } 73 | 74 | @reviewdog_error_types = { 75 | "failure" => 'E', 76 | "warning" => 'W', 77 | "notice" => 'I', 78 | } 79 | 80 | @is_pull_request = false 81 | 82 | if @CW_CI_ENV == "github" 83 | @event = JSON.parse(File.read(@CW_EVENT_PATH)) 84 | @repository = @event["repository"] 85 | @owner = @repository["owner"]["login"] 86 | @repo = @repository["name"] 87 | unless @event["pull_request"].nil? 88 | @CW_SHA = @event["pull_request"]["head"]["sha"] 89 | @is_pull_request = [@event["pull_request"]["base"]["ref"], @event["pull_request"]["head"]["ref"]] 90 | end 91 | @headers = { 92 | "Content-Type": 'application/json', 93 | "Accept": 'application/vnd.github.antiope-preview+json', 94 | "Authorization": "Bearer #{@CW_TOKEN}", 95 | "User-Agent": 'cwtools-action' 96 | } 97 | end 98 | 99 | def get_changed_files 100 | diff_output = nil 101 | Dir.chdir(@CW_WORKSPACE) do 102 | if @CW_CI_ENV == "github" 103 | if @is_pull_request 104 | diff_output = `git log --name-only --pretty="" origin/#{@is_pull_request[0]}..origin/#{@is_pull_request[1]}` 105 | else 106 | before_commit = @event["before"] 107 | diff_output = `git diff --name-only #{before_commit} #{@CW_SHA}` 108 | end 109 | end 110 | end 111 | unless diff_output.nil? 112 | diff_output = diff_output.split("\n") 113 | diff_output.collect(&:strip) 114 | else 115 | diff_output = [] 116 | end 117 | diff_output = diff_output.to_set 118 | p diff_output 119 | @changed_files = diff_output 120 | end 121 | 122 | def create_github_check 123 | body = { 124 | "name" => @check_name, 125 | "head_sha" => @CW_SHA, 126 | "status" => "in_progress", 127 | "started_at" => Time.now.iso8601 128 | } 129 | 130 | http = Net::HTTP.new('api.github.com', 443) 131 | http.use_ssl = true 132 | path = "/repos/#{@owner}/#{@repo}/check-runs" 133 | 134 | resp = http.post(path, body.to_json, @headers) 135 | 136 | if resp.code.to_i >= 300 137 | $stderr.puts JSON.pretty_generate(resp.body) 138 | raise resp.message 139 | end 140 | 141 | data = JSON.parse(resp.body) 142 | return data["id"] 143 | end 144 | 145 | def update_github_check(id, conclusion, output) 146 | if conclusion.nil? 147 | body = { 148 | "name" => @check_name, 149 | "head_sha" => @CW_SHA, 150 | "output" => output 151 | } 152 | else 153 | body = { 154 | "name" => @check_name, 155 | "head_sha" => @CW_SHA, 156 | "status" => 'completed', 157 | "completed_at" => Time.now.iso8601, 158 | "conclusion" => conclusion 159 | } 160 | end 161 | http = Net::HTTP.new('api.github.com', 443) 162 | http.use_ssl = true 163 | path = "/repos/#{@owner}/#{@repo}/check-runs/#{id}" 164 | 165 | resp = http.patch(path, body.to_json, @headers) 166 | 167 | if resp.code.to_i >= 300 168 | $stderr.puts JSON.pretty_generate(resp.body) 169 | raise resp.message 170 | end 171 | end 172 | 173 | def return_reviewdog_check(file, output) 174 | output["annotations"].each do |annotation| 175 | startCol = annotation["start_column"].nil? ? 1 : annotation["start_column"] 176 | file.puts "#{annotation["path"]}:#{annotation["start_line"]}:#{startCol}:#{@reviewdog_error_types[annotation["annotation_level"]]}:#{@reviewdog_annotation_levels[annotation["annotation_level"]]}#{annotation["message"]}" 177 | end 178 | end 179 | 180 | def run_cwtools 181 | annotations = [] 182 | errors = nil 183 | $stderr.puts "Running CWToolsCLI now..." 184 | Dir.chdir(@CW_WORKSPACE) do 185 | if @VANILLA_MODE 186 | $stderr.puts "Vanilla mode..." 187 | $stderr.puts "cwtools --game #{(@GAME == "stellaris") ? "stl" : @GAME} --directory \"#{@CW_WORKSPACE}#{@MOD_PATH}\" --rulespath \"/src/cwtools-#{@GAME}-config\" validate --reporttype json --scope vanilla --outputfile output.json --languages #{@LOC_LANGUAGES} all" 188 | `cwtools --game #{(@GAME == "stellaris") ? "stl" : @GAME} --directory "#{@CW_WORKSPACE}#{@MOD_PATH}" --rulespath "/src/cwtools-#{@GAME}-config" validate --reporttype json --scope vanilla --outputfile output.json --languages #{@LOC_LANGUAGES} all` 189 | elsif !@CACHE_FULL 190 | $stderr.puts "Metadata cache mode..." 191 | $stderr.puts "cwtools --game #{(@GAME == "stellaris") ? "stl" : @GAME} --directory \"#{@CW_WORKSPACE}#{@MOD_PATH}\" --cachefile \"/#{(@GAME == "stellaris") ? "stl" : @GAME}.cwv.bz2\" --rulespath \"/src/cwtools-#{@GAME}-config\" validate --cachetype metadata --reporttype json --scope mods --outputfile output.json --languages #{@LOC_LANGUAGES} all" 192 | `cwtools --game #{(@GAME == "stellaris") ? "stl" : @GAME} --directory "#{@CW_WORKSPACE}#{@MOD_PATH}" --cachefile "/#{(@GAME == "stellaris") ? "stl" : @GAME}.cwv.bz2" --rulespath "/src/cwtools-#{@GAME}-config" validate --cachetype metadata --reporttype json --scope mods --outputfile output.json --languages #{@LOC_LANGUAGES} all` 193 | else 194 | $stderr.puts "Full cache mode..." 195 | $stderr.puts "cwtools --game #{(@GAME == "stellaris") ? "stl" : @GAME} --directory \"#{@CW_WORKSPACE}#{@MOD_PATH}\" --cachefile \"/#{(@GAME == "stellaris") ? "stl" : @GAME}.cwb.bz2\" --rulespath \"/src/cwtools-#{@GAME}-config\" validate --cachetype full --reporttype json --scope mods --outputfile output.json --languages #{@LOC_LANGUAGES} all" 196 | `cwtools --game #{(@GAME == "stellaris") ? "stl" : @GAME} --directory "#{@CW_WORKSPACE}#{@MOD_PATH}" --cachefile "/#{(@GAME == "stellaris") ? "stl" : @GAME}.cwb.bz2" --rulespath "/src/cwtools-#{@GAME}-config" validate --cachetype full --reporttype json --scope mods --outputfile output.json --languages #{@LOC_LANGUAGES} all` 197 | end 198 | errors = JSON.parse(`cat output.json`) 199 | end 200 | $stderr.puts "Done running CWToolsCLI..." 201 | conclusion = "success" 202 | count = { "failure" => 0, "warning" => 0, "notice" => 0 } 203 | 204 | errors["files"].each do |file| 205 | path = file["file"] 206 | path = path.sub! @CW_WORKSPACE+"/", '' 207 | path = path.strip 208 | if @SUPPRESSED_FILES.include?(path) 209 | next 210 | end 211 | offenses = file["errors"] 212 | if !@CHANGED_ONLY || @changed_files.include?(path) 213 | offenses.each do |offense| 214 | severity = offense["severity"].downcase 215 | message = offense["category"] + ": " + offense["message"] 216 | location = offense["position"] 217 | annotation_level = @annotation_levels[severity] 218 | if annotation_level != "notice" && annotation_level != "warning" && annotation_level != "failure" 219 | annotation_level = "notice" 220 | end 221 | 222 | if @SUPPRESSED_OFFENCE_CATEGORIES[annotation_level].include?(offense["category"]) 223 | next 224 | end 225 | 226 | if annotation_level == "failure" 227 | conclusion = "failure" 228 | elsif conclusion != "failure" && annotation_level == "warning" 229 | conclusion = "neutral" 230 | end 231 | count[annotation_level] = count[annotation_level] + 1 232 | if location["startLine"] == location["endLine"] && location["startColumn"].to_i <= location["endColumn"].to_i 233 | annotations.push({ 234 | "path" => path, 235 | "title" => @check_name, 236 | "start_line" => location["startLine"], 237 | "end_line" => location["endLine"], 238 | "start_column" => location["startColumn"].to_i > 0 ? location["startColumn"] : 1, 239 | "end_column" => location["endColumn"].to_i > 0 ? location["endColumn"] : 1, 240 | "annotation_level" => annotation_level, 241 | "message" => message 242 | }) 243 | else 244 | annotations.push({ 245 | "path" => path, 246 | "title" => @check_name, 247 | "start_line" => location["startLine"], 248 | "end_line" => location["endLine"], 249 | "annotation_level" => annotation_level, 250 | "message" => message 251 | }) 252 | end 253 | end 254 | end 255 | end 256 | 257 | output = [] 258 | total_count = count["failure"]+count["warning"]+count["notice"] 259 | annotations.each_slice(50).to_a.each do |annotation| 260 | output.push({ 261 | "title": @check_name, 262 | "summary": "**#{total_count}** offense(s) found:\n* #{count["failure"]} failure(s)\n* #{count["warning"]} warning(s)\n* #{count["notice"]} notice(s)", 263 | "annotations" => annotation 264 | }) 265 | end 266 | 267 | return { "output" => output, "conclusion" => conclusion } 268 | end 269 | 270 | def run_gitlab 271 | begin 272 | results = run_cwtools() 273 | conclusion = results["conclusion"] 274 | output = results["output"] 275 | $stderr.puts "Updating checks..." 276 | Dir.chdir(@CW_WORKSPACE) do 277 | file = File.open("errors.txt", "w") 278 | output.each do |o| 279 | return_reviewdog_check(file, o) 280 | end 281 | end 282 | rescue => e 283 | $stderr.puts "Error during processing: #{$!}" 284 | $stderr.puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}" 285 | fail("There was an unhandled exception. Exiting with a non-zero error code...") 286 | end 287 | end 288 | 289 | def run_github 290 | unless defined?(@CW_TOKEN) 291 | raise "CW_TOKEN environment variable has not been defined!" 292 | end 293 | if @is_pull_request 294 | $stderr.puts "Is pull request..." 295 | else 296 | $stderr.puts "Is commit..." 297 | end 298 | if @CHANGED_ONLY 299 | $stderr.puts "Annotating only changed files..." 300 | else 301 | $stderr.puts "Annotating all files..." 302 | end 303 | id = create_github_check() 304 | begin 305 | get_changed_files() 306 | results = run_cwtools() 307 | conclusion = results["conclusion"] 308 | output = results["output"] 309 | $stderr.puts "Updating checks..." 310 | output.each do |o| 311 | update_github_check(id, nil, o) 312 | end 313 | update_github_check(id, conclusion, nil) 314 | rescue => e 315 | $stderr.puts "Error during processing: #{$!}" 316 | $stderr.puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}" 317 | update_github_check(id, "failure", nil) 318 | fail("There was an unhandled exception. Exiting with a non-zero error code...") 319 | end 320 | end 321 | 322 | def run 323 | $stderr.puts "CWTOOLS CHECK" 324 | $stderr.puts "CI ENVIROMENT: #{@CW_CI_ENV}" 325 | if @CW_CI_ENV == "github" 326 | run_github() 327 | elsif @CW_CI_ENV == "gitlab" 328 | run_gitlab() 329 | end 330 | $stderr.puts "RUBY SCRIPT FINISHED" 331 | end 332 | 333 | run() 334 | -------------------------------------------------------------------------------- /lib/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | export CW_CHECKNAME="CWTools" 6 | 7 | if [ -n "$GITHUB_SHA" ]; then 8 | export CW_CI_ENV="github" 9 | export CW_EVENT=$GITHUB_EVENT_PATH 10 | export CW_TOKEN=$GITHUB_TOKEN 11 | export CW_WORKSPACE=$GITHUB_WORKSPACE 12 | export CW_SHA=$GITHUB_SHA 13 | elif [ -n "$CI_PROJECT_DIR" ]; then 14 | export CW_CI_ENV="gitlab" 15 | export CW_TOKEN=$CI_JOB_TOKEN 16 | export CW_WORKSPACE=$CI_PROJECT_DIR 17 | if [ -z "$INPUT_MODPATH" ] || [ "$INPUT_MODPATH" = "" ]; then 18 | export INPUT_MODPATH='' 19 | fi 20 | if [ -z "$INPUT_CACHE" ] || [ "$INPUT_CACHE" = "" ]; then 21 | export INPUT_CACHE='' 22 | fi 23 | if [ -z "$INPUT_LOCLANGUAGES" ] || [ "$INPUT_LOCLANGUAGES" = "" ]; then 24 | export INPUT_LOCLANGUAGES='english' 25 | fi 26 | if [ -z "$INPUT_RULES" ] || [ "$INPUT_RULES" = "" ]; then 27 | export INPUT_RULES='' 28 | fi 29 | if [ -z "$INPUT_RULESREF" ] || [ "$INPUT_RULESREF" = "" ]; then 30 | export INPUT_RULESREF='master' 31 | fi 32 | if [ -z "$INPUT_CHANGEDFILESONLY" ] || [ "$INPUT_CHANGEDFILESONLY" = "" ]; then 33 | export INPUT_CHANGEDFILESONLY='1' # this is disabled for gitlab anyway 34 | fi 35 | if [ -z "$INPUT_SUPPRESSEDOFFENCECATEGORIES" ] || [ "$INPUT_SUPPRESSEDOFFENCECATEGORIES" = "" ]; then 36 | export INPUT_SUPPRESSEDOFFENCECATEGORIES='{"failure":[], "warning":[], "notice":[]}' 37 | fi 38 | if [ -z "$INPUT_SUPPRESSEDFILES" ] || [ "$INPUT_SUPPRESSEDFILES" = "" ]; then 39 | export INPUT_SUPPRESSEDFILES='[]' 40 | fi 41 | if [ -z "$INPUT_CWTOOLSCLIVERSION" ] || [ "$INPUT_CWTOOLSCLIVERSION" = "" ]; then 42 | export INPUT_CWTOOLSCLIVERSION='' 43 | fi 44 | fi 45 | 46 | echo "CI Enviroment detected as $CW_CI_ENV..." 47 | 48 | case $INPUT_GAME in 49 | "hoi4") echo "Game selected as $INPUT_GAME..." ;; 50 | "ck2") echo "Game selected as $INPUT_GAME..." ;; 51 | "eu4") echo "Game selected as $INPUT_GAME..." ;; 52 | "vic2") echo "Game selected as $INPUT_GAME..." ;; 53 | "ir") echo "Game selected as $INPUT_GAME..." ;; 54 | "stellaris") echo "Game selected as $INPUT_GAME..." ;; 55 | "ck3") echo "Game selected as $INPUT_GAME..." ;; 56 | *) echo "Wrong game, $INPUT_GAME is not valid!" 1>&2 ; exit 1 # terminate and indicate error 57 | esac 58 | 59 | if [ -z "$INPUT_CWTOOLSCLIVERSION" ] || [ "$INPUT_CWTOOLSCLIVERSION" = "" ]; then 60 | dotnet tool install --global -v m CWTools.CLI 61 | else 62 | dotnet tool install --global -v m CWTools.CLI --version $INPUT_CWTOOLSCLIVERSION 63 | fi 64 | 65 | if [ $CW_CI_ENV = "github" ]; then 66 | export PATH="$PATH:$HOME/.dotnet/tools" 67 | elif [ $CW_CI_ENV = "gitlab" ]; then 68 | export PATH="$PATH:/root/.dotnet/tools" 69 | fi 70 | 71 | cd / 72 | mkdir -p /src 73 | if [ -z "$INPUT_RULES" ] || [ "$INPUT_RULES" = "" ]; then 74 | git clone --depth 1 --single-branch --branch $INPUT_RULESREF https://github.com/cwtools/cwtools-$INPUT_GAME-config.git /src/cwtools-$INPUT_GAME-config 75 | else 76 | git clone --depth 1 --single-branch --branch $INPUT_RULESREF $INPUT_RULES /src/cwtools-$INPUT_GAME-config 77 | fi 78 | 79 | CWB_GAME=$INPUT_GAME 80 | if [ "$INPUT_GAME" = "stellaris" ]; then 81 | CWB_GAME="stl" 82 | fi 83 | 84 | cd / 85 | if [ -z "$INPUT_VANILLAMODE" ] || [ "$INPUT_VANILLAMODE" = "" ] || [ "$INPUT_VANILLAMODE" = 0 ]; then 86 | if [ -z "$INPUT_CACHE" ] || [ "$INPUT_CACHE" = "" ]; then 87 | echo "Using metadata cache from 'cwtools/cwtools-cache-files'..." 88 | echo "If git fails here, it is most likely because the selected game ($INPUT_GAME) is not yet supported in the 'cwtools/cwtools-cache-files'. In that case, use CWTools.CLI to generate a full cache of selected game and set it with the cache parameter. Consult README for more information." 89 | git clone --depth=1 --single-branch --branch $INPUT_GAME https://github.com/cwtools/cwtools-cache-files.git cwtools-cache-files 90 | mv -v cwtools-cache-files/$CWB_GAME.cwv.bz2 . 91 | else 92 | echo "Using full game cache from '$CW_WORKSPACE/$INPUT_CACHE'..." 93 | mv -v $CW_WORKSPACE/$INPUT_CACHE . 94 | 95 | if [ ! -f "$CWB_GAME.cwb.bz2" ]; then 96 | echo "$CWB_GAME.cwb.bz2 does not exist!" 97 | exit 1 98 | fi 99 | fi 100 | else 101 | echo "Vanilla mode, not using cache..." 102 | fi 103 | ruby /action/lib/cwtools.rb 104 | 105 | if [ $CW_CI_ENV = "gitlab" ]; then 106 | cd $CW_WORKSPACE 107 | if [ -f errors.txt ]; then 108 | echo "Running reviewdog on $PWD/errors.txt..." 109 | reviewdog -conf=/action/lib/.reviewdog.yml -name="$CW_CHECKNAME" -reporter=gitlab-mr-discussion | tee reviewdog_output.txt 110 | if grep -m 1 -q "❌" reviewdog_output.txt; then 111 | echo "At least one error in annotated files! Exiting with a non-zero error code..." 112 | exit 1 113 | fi 114 | else 115 | echo "errors.txt doesn't exist in $CW_WORKSPACE!" 116 | exit 1 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /lib/gitlab_setup.sh: -------------------------------------------------------------------------------- 1 | if [ -z "$INPUT_GAME" ] || [ "$INPUT_GAME" = "" ]; then 2 | echo "The required INPUT_GAME enviromental variable is not set!" 3 | exit 1 4 | fi 5 | 6 | if [ -z "$INPUT_CWTOOLSACTIONREF" ] || [ "$INPUT_CWTOOLSACTIONREF" = "" ]; then 7 | INPUT_CWTOOLSACTIONREF="v1.1.0" 8 | fi 9 | 10 | if [ -z "$INPUT_REVIEWDOGREF" ] || [ "$INPUT_REVIEWDOGREF" = "" ]; then 11 | INPUT_REVIEWDOGREF="master" 12 | fi 13 | 14 | apt-get update && apt-get -y install ruby bash git wget p7zip 15 | wget -O - -q https://raw.githubusercontent.com/reviewdog/reviewdog/$INPUT_REVIEWDOGREF/install.sh| sh -s -- -b /usr/local/bin/ 16 | 17 | git fetch origin $CI_DEFAULT_BRANCH 18 | 19 | cd / 20 | git clone --depth=1 --single-branch --branch $INPUT_CWTOOLSACTIONREF https://github.com/cwtools/cwtools-action.git action 21 | chmod +x /action/lib/entrypoint.sh 22 | /action/lib/entrypoint.sh 23 | --------------------------------------------------------------------------------