diff --git a/README.md b/README.md index 76582b8..d60d3ac 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,14 @@ jobs: path: renpy key: ${{ runner.os }}-renpy - name: Install Ren'Py - uses: Ayowel/renpy-setup-action@v2.0.0 + uses: Ayowel/renpy-setup-action@v2.0.1 if: steps.cache-renpy.outputs.cache-hit != 'true' with: action: install install_dir: renpy # Update/Replace the step below to do something different - name: Print help message - uses: Ayowel/renpy-setup-action@v2.0.0 + uses: Ayowel/renpy-setup-action@v2.0.1 with: action: exec install_dir: renpy @@ -53,7 +53,7 @@ After installing, easily build release packages for multiple platforms: ```yml - name: Generate game distribution files - uses: Ayowel/renpy-setup-action@v2.0.0 + uses: Ayowel/renpy-setup-action@v2.0.1 with: action: distribute install_dir: renpy @@ -65,7 +65,7 @@ After installing, easily build release packages for multiple platforms: Note that you may specify a file name after the package. If you do, the value of `out_dir` will be ignored for the package. Note however that the generated file will have an extension added to the path and will not match exactly the provided value: ```yml -- uses: Ayowel/renpy-setup-action@v2.0.0 +- uses: Ayowel/renpy-setup-action@v2.0.1 with: action: distribute install_dir: renpy @@ -81,7 +81,7 @@ After installing Ren'py, ensure that your code does not have structural issues: ```yml - name: Run Ren'Py linter - uses: Ayowel/renpy-setup-action@v2.0.0 + uses: Ayowel/renpy-setup-action@v2.0.1 with: action: lint install_dir: renpy @@ -93,7 +93,7 @@ After installing Ren'py, ensure that your code does not have structural issues: After installing Ren'Py, use the `translate` action to update the game's translation files: ```yml -- uses: Ayowel/renpy-setup-action@v2.0.0 +- uses: Ayowel/renpy-setup-action@v2.0.1 id: renpy with: action: translate @@ -107,7 +107,7 @@ After installing Ren'Py, use the `translate` action to update the game's transla After installing Ren'Py, use the `nothing` action if you just want to get one of the action's outputs, such as the Python installation's path : ```yml -- uses: Ayowel/renpy-setup-action@v2.0.0 +- uses: Ayowel/renpy-setup-action@v2.0.1 id: renpy with: action: nothing @@ -154,7 +154,7 @@ jobs: with: path: renpy key: ${{ runner.os }}-renpy - - uses: Ayowel/renpy-setup-action@v2.0.0 + - uses: Ayowel/renpy-setup-action@v2.0.1 if: steps.cache-renpy-android.outputs.cache-hit != 'true' with: action: install @@ -167,7 +167,7 @@ jobs: key.alias.password=${{ secrets.ANDROID_ALIAS_PASSWORD }} key.store=${{ github.workspace }}/android.keystore # The project must have a .android.json file - - uses: Ayowel/renpy-setup-action@v2.0.0 + - uses: Ayowel/renpy-setup-action@v2.0.1 with: action: android_build install_dir: renpy @@ -181,7 +181,7 @@ jobs: This action supports the following inputs: ```yml -- uses: Ayowel/renpy-setup-action@v2.0.0 +- uses: Ayowel/renpy-setup-action@v2.0.1 with: # What the action should do. Must be one of: # 'install', 'distribute', 'android_build', diff --git a/dist/setup/index.js b/dist/setup/index.js index fec3dcd..a047887 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -558,7 +558,7 @@ class OidcClient { .catch(error => { throw new Error(`Failed to get ID Token. \n Error Code : ${error.statusCode}\n - Error Message: ${error.result.message}`); + Error Message: ${error.message}`); }); const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value; if (!id_token) { @@ -1946,6 +1946,19 @@ class HttpClientResponse { })); }); } + readBodyBuffer() { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { + const chunks = []; + this.message.on('data', (chunk) => { + chunks.push(chunk); + }); + this.message.on('end', () => { + resolve(Buffer.concat(chunks)); + }); + })); + }); + } } exports.HttpClientResponse = HttpClientResponse; function isHttps(requestUrl) { @@ -2450,7 +2463,13 @@ function getProxyUrl(reqUrl) { } })(); if (proxyVar) { - return new URL(proxyVar); + try { + return new URL(proxyVar); + } + catch (_a) { + if (!proxyVar.startsWith('http://') && !proxyVar.startsWith('https://')) + return new URL(`http://${proxyVar}`); + } } else { return undefined; @@ -4020,8 +4039,11 @@ var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || // Max safe segment length for coercion. var MAX_SAFE_COMPONENT_LENGTH = 16 +var MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6 + // The actual regexps go on exports.re var re = exports.re = [] +var safeRe = exports.safeRe = [] var src = exports.src = [] var t = exports.tokens = {} var R = 0 @@ -4030,6 +4052,31 @@ function tok (n) { t[n] = R++ } +var LETTERDASHNUMBER = '[a-zA-Z0-9-]' + +// Replace some greedy regex tokens to prevent regex dos issues. These regex are +// used internally via the safeRe object since all inputs in this library get +// normalized first to trim and collapse all extra whitespace. The original +// regexes are exported for userland consumption and lower level usage. A +// future breaking change could export the safer regex only with a note that +// all input should have extra whitespace removed. +var safeRegexReplacements = [ + ['\\s', 1], + ['\\d', MAX_LENGTH], + [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH], +] + +function makeSafeRe (value) { + for (var i = 0; i < safeRegexReplacements.length; i++) { + var token = safeRegexReplacements[i][0] + var max = safeRegexReplacements[i][1] + value = value + .split(token + '*').join(token + '{0,' + max + '}') + .split(token + '+').join(token + '{1,' + max + '}') + } + return value +} + // The following Regular Expressions can be used for tokenizing, // validating, and parsing SemVer version strings. @@ -4039,14 +4086,14 @@ function tok (n) { tok('NUMERICIDENTIFIER') src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*' tok('NUMERICIDENTIFIERLOOSE') -src[t.NUMERICIDENTIFIERLOOSE] = '[0-9]+' +src[t.NUMERICIDENTIFIERLOOSE] = '\\d+' // ## Non-numeric Identifier // Zero or more digits, followed by a letter or hyphen, and then zero or // more letters, digits, or hyphens. tok('NONNUMERICIDENTIFIER') -src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' +src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-]' + LETTERDASHNUMBER + '*' // ## Main Version // Three dot-separated numeric identifiers. @@ -4088,7 +4135,7 @@ src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] + // Any combination of digits, letters, or hyphens. tok('BUILDIDENTIFIER') -src[t.BUILDIDENTIFIER] = '[0-9A-Za-z-]+' +src[t.BUILDIDENTIFIER] = LETTERDASHNUMBER + '+' // ## Build Metadata // Plus sign, followed by one or more period-separated build metadata @@ -4168,6 +4215,7 @@ src[t.COERCE] = '(^|[^\\d])' + '(?:$|[^\\d])' tok('COERCERTL') re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g') +safeRe[t.COERCERTL] = new RegExp(makeSafeRe(src[t.COERCE]), 'g') // Tilde ranges. // Meaning is "reasonably at or greater than" @@ -4177,6 +4225,7 @@ src[t.LONETILDE] = '(?:~>?)' tok('TILDETRIM') src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+' re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g') +safeRe[t.TILDETRIM] = new RegExp(makeSafeRe(src[t.TILDETRIM]), 'g') var tildeTrimReplace = '$1~' tok('TILDE') @@ -4192,6 +4241,7 @@ src[t.LONECARET] = '(?:\\^)' tok('CARETTRIM') src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+' re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g') +safeRe[t.CARETTRIM] = new RegExp(makeSafeRe(src[t.CARETTRIM]), 'g') var caretTrimReplace = '$1^' tok('CARET') @@ -4213,6 +4263,7 @@ src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] + // this one has to use the /g flag re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g') +safeRe[t.COMPARATORTRIM] = new RegExp(makeSafeRe(src[t.COMPARATORTRIM]), 'g') var comparatorTrimReplace = '$1$2$3' // Something like `1.2.3 - 1.2.4` @@ -4241,6 +4292,14 @@ for (var i = 0; i < R; i++) { debug(i, src[i]) if (!re[i]) { re[i] = new RegExp(src[i]) + + // Replace all greedy whitespace to prevent regex dos issues. These regex are + // used internally via the safeRe object since all inputs in this library get + // normalized first to trim and collapse all extra whitespace. The original + // regexes are exported for userland consumption and lower level usage. A + // future breaking change could export the safer regex only with a note that + // all input should have extra whitespace removed. + safeRe[i] = new RegExp(makeSafeRe(src[i])) } } @@ -4265,7 +4324,7 @@ function parse (version, options) { return null } - var r = options.loose ? re[t.LOOSE] : re[t.FULL] + var r = options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL] if (!r.test(version)) { return null } @@ -4320,7 +4379,7 @@ function SemVer (version, options) { this.options = options this.loose = !!options.loose - var m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]) + var m = version.trim().match(options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL]) if (!m) { throw new TypeError('Invalid Version: ' + version) @@ -4765,6 +4824,7 @@ function Comparator (comp, options) { return new Comparator(comp, options) } + comp = comp.trim().split(/\s+/).join(' ') debug('comparator', comp, options) this.options = options this.loose = !!options.loose @@ -4781,7 +4841,7 @@ function Comparator (comp, options) { var ANY = {} Comparator.prototype.parse = function (comp) { - var r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] + var r = this.options.loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR] var m = comp.match(r) if (!m) { @@ -4905,9 +4965,16 @@ function Range (range, options) { this.loose = !!options.loose this.includePrerelease = !!options.includePrerelease - // First, split based on boolean or || + // First reduce all whitespace as much as possible so we do not have to rely + // on potentially slow regexes like \s*. This is then stored and used for + // future error messages as well. this.raw = range - this.set = range.split(/\s*\|\|\s*/).map(function (range) { + .trim() + .split(/\s+/) + .join(' ') + + // First, split based on boolean or || + this.set = this.raw.split('||').map(function (range) { return this.parseRange(range.trim()) }, this).filter(function (c) { // throw out any that are not relevant for whatever reason @@ -4915,7 +4982,7 @@ function Range (range, options) { }) if (!this.set.length) { - throw new TypeError('Invalid SemVer Range: ' + range) + throw new TypeError('Invalid SemVer Range: ' + this.raw) } this.format() @@ -4934,20 +5001,19 @@ Range.prototype.toString = function () { Range.prototype.parseRange = function (range) { var loose = this.options.loose - range = range.trim() // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - var hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] + var hr = loose ? safeRe[t.HYPHENRANGELOOSE] : safeRe[t.HYPHENRANGE] range = range.replace(hr, hyphenReplace) debug('hyphen replace', range) // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) - debug('comparator trim', range, re[t.COMPARATORTRIM]) + range = range.replace(safeRe[t.COMPARATORTRIM], comparatorTrimReplace) + debug('comparator trim', range, safeRe[t.COMPARATORTRIM]) // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[t.TILDETRIM], tildeTrimReplace) + range = range.replace(safeRe[t.TILDETRIM], tildeTrimReplace) // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[t.CARETTRIM], caretTrimReplace) + range = range.replace(safeRe[t.CARETTRIM], caretTrimReplace) // normalize spaces range = range.split(/\s+/).join(' ') @@ -4955,7 +5021,7 @@ Range.prototype.parseRange = function (range) { // At this point, the range is completely trimmed and // ready to be split into comparators. - var compRe = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] + var compRe = loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR] var set = range.split(' ').map(function (comp) { return parseComparator(comp, this.options) }, this).join(' ').split(/\s+/) @@ -5055,7 +5121,7 @@ function replaceTildes (comp, options) { } function replaceTilde (comp, options) { - var r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE] + var r = options.loose ? safeRe[t.TILDELOOSE] : safeRe[t.TILDE] return comp.replace(r, function (_, M, m, p, pr) { debug('tilde', comp, _, M, m, p, pr) var ret @@ -5096,7 +5162,7 @@ function replaceCarets (comp, options) { function replaceCaret (comp, options) { debug('caret', comp, options) - var r = options.loose ? re[t.CARETLOOSE] : re[t.CARET] + var r = options.loose ? safeRe[t.CARETLOOSE] : safeRe[t.CARET] return comp.replace(r, function (_, M, m, p, pr) { debug('caret', comp, _, M, m, p, pr) var ret @@ -5155,7 +5221,7 @@ function replaceXRanges (comp, options) { function replaceXRange (comp, options) { comp = comp.trim() - var r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE] + var r = options.loose ? safeRe[t.XRANGELOOSE] : safeRe[t.XRANGE] return comp.replace(r, function (ret, gtlt, M, m, p, pr) { debug('xRange', comp, ret, gtlt, M, m, p, pr) var xM = isX(M) @@ -5230,7 +5296,7 @@ function replaceXRange (comp, options) { function replaceStars (comp, options) { debug('replaceStars', comp, options) // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[t.STAR], '') + return comp.trim().replace(safeRe[t.STAR], '') } // This function is passed to string.replace(re[t.HYPHENRANGE]) @@ -5556,7 +5622,7 @@ function coerce (version, options) { var match = null if (!options.rtl) { - match = version.match(re[t.COERCE]) + match = version.match(safeRe[t.COERCE]) } else { // Find the right-most coercible string that does not share // a terminus with a more left-ward coercible string. @@ -5567,17 +5633,17 @@ function coerce (version, options) { // Stop when we get a match that ends at the string end, since no // coercible string can be more right-ward without the same terminus. var next - while ((next = re[t.COERCERTL].exec(version)) && + while ((next = safeRe[t.COERCERTL].exec(version)) && (!match || match.index + match[0].length !== version.length) ) { if (!match || next.index + next[0].length !== match.index + match[0].length) { match = next } - re[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length + safeRe[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length } // leave it in a clean state - re[t.COERCERTL].lastIndex = -1 + safeRe[t.COERCERTL].lastIndex = -1 } if (match === null) { @@ -7027,6 +7093,7 @@ function getRenpyPythonPath(directory) { const ext = (0, utils_1.pickOsValue)('.exe', '', ''); const python_paths = [ `lib/py3-${os}-x86_64/python${ext}`, + `lib/py3-${os}-universal/python${ext}`, `lib/py2-${os}-x86_64/python${ext}`, `lib/${os}-x86_64/python${ext}` ]; @@ -7351,8 +7418,23 @@ class RenpyInstaller { yield this.installAndroidSdk(sdk_input); logger.info('Configure Android SDK build properties'); const project_path = path.join(this.install_dir, 'rapt', 'project'); - yield this.updateKeyValueConfig(path.join(project_path, 'bundle.properties'), opts.android_aab_properties); - yield this.updateKeyValueConfig(path.join(project_path, 'local.properties'), opts.android_apk_properties); + for (const target_pair of [ + ['bundle', 'bundle'], + ['local', 'android'] + ]) { + const default_properties = { + // Sets the default key values if none is set + 'key.alias': 'android', + 'key.store.password': 'android', + 'key.alias.password': 'android', + 'key.store': path.join(path.resolve(this.install_dir), 'rapt', `${target_pair[1]}.keystore`), + 'sdk.dir': path.join(path.resolve(this.install_dir), 'rapt', 'Sdk') + }; + const updated_keys = yield this.updateKeyValueConfig(path.join(project_path, `${target_pair[0]}.properties`), opts.android_aab_properties, default_properties); + if (!fs.existsSync(updated_keys['key.store'])) { + logger.warning(`The keystore path in ${target_pair[0]} does not appear to map to an existing keystore file (${updated_keys['key.store']}).`); + } + } } }); } @@ -7391,16 +7473,24 @@ class RenpyInstaller { 'rapt.install_sdk.install_sdk(rapt.interface.Interface())' ].join('\n') ]; - yield (0, system_1.renpyPythonExec)(this.install_dir, args, setupinfo); + return yield (0, system_1.renpyPythonExec)(this.install_dir, args, setupinfo); }); } - updateKeyValueConfig(file, pairs) { + updateKeyValueConfig(file, pairs, additional_pairs = {}) { return __awaiter(this, void 0, void 0, function* () { - const content = (0, renpy_1.stringToAndroidProperties)(fs.readFileSync(file).toString()); + const content = fs.existsSync(file) + ? (0, renpy_1.stringToAndroidProperties)(fs.readFileSync(file).toString()) + : {}; for (const k in pairs) { content[k] = pairs[k]; } + for (const k in additional_pairs) { + if (!(k in content)) { + content[k] = additional_pairs[k]; + } + } fs.writeFileSync(file, (0, renpy_1.androidPropertiesToString)(content)); + return content; }); } } @@ -7558,6 +7648,21 @@ function main() { break; case parameters_2.RenPyInputsSupportedAction.AndroidBuild: logger.startGroup('Build android project files'); + const android_config_file = path.join(opts.game_dir, '.android.json'); + if (fs.existsSync(android_config_file)) { + try { + const android_config = JSON.parse(fs.readFileSync(android_config_file, { encoding: 'utf-8' })); + if (android_config['update_keystores'] !== false) { + logger.warning("The file .android.json does not set 'update_keystores' to false. It is recommended that you set this key to false to avoid build issues."); + } + } + catch (_a) { + logger.error('Failed to verify .android.json file'); + } + } + else { + logger.error('Missing file .android.json in game directory'); + } yield executor.android_build(opts.game_dir, opts.android_build_opts); logger.endGroup(); break; diff --git a/package.json b/package.json index 05b7b27..ad61b58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "setup-renpy", - "version": "2.0.0", + "version": "2.0.1", "private": true, "description": "Setup renpy action", "author": "Ayowel",