diff --git a/.github/workflows/test_deinit.yml b/.github/workflows/test_deinit.yml new file mode 100644 index 00000000..851b1c91 --- /dev/null +++ b/.github/workflows/test_deinit.yml @@ -0,0 +1,108 @@ +name: Test deinit + +on: + push: + branches: + - main + pull_request: null + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test_deinit: + name: Test deinit - ${{ matrix.os }} ${{ matrix.rcfile }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + # .bash_profile gets initialized in p-w-m ubuntu-latest instead of .bashrc like in micromamba + - { os: ubuntu-latest, rcfile: ~/.bash_profile } + - { os: ubuntu-latest, rcfile: ~/.zshrc } + # TODO zshrc not yet initialized in macos p-w-m + # https://github.com/mamba-org/mamba/issues/925 + - { os: macos-latest, rcfile: ~/.bash_profile } + - { os: windows-latest, rcfile: ~/.bash_profile } + - { os: windows-latest, rcfile: ~/Documents/WindowsPowerShell/profile.ps1 } + steps: + - uses: actions/checkout@v3 + + # This is a fork of webiny/action-post-run. If webiny/action-post-run is updated, this should be updated as well. + - uses: lisanna-dettwyler/action-post-run@d053b9b43d788b87a409f6cdb3b6fc87c6c8a4fe + with: + # assert that mamba initialize is not in rcfile + # if the rcfile is not found, the test will still succeed + run: | + ! grep -F "mamba initialize" ${{ matrix.rcfile }} + + - uses: ./ + with: + environment-file: false + + - run: grep -F "mamba initialize" ${{ matrix.rcfile }} + + test_deinit_false: + name: Test deinit false - ${{ matrix.os }} ${{ matrix.rcfile }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + # .bash_profile gets initialized in p-w-m ubuntu-latest instead of .bashrc like in micromamba + - { os: ubuntu-latest, rcfile: ~/.bash_profile } + - { os: ubuntu-latest, rcfile: ~/.zshrc } + # TODO zshrc not yet initialized in macos p-w-m + # https://github.com/mamba-org/mamba/issues/925 + - { os: macos-latest, rcfile: ~/.bash_profile } + - { os: windows-latest, rcfile: ~/.bash_profile } + - { os: windows-latest, rcfile: ~/Documents/WindowsPowerShell/profile.ps1 } + steps: + - uses: actions/checkout@v3 + + # This is a fork of webiny/action-post-run. If webiny/action-post-run is updated, this should be updated as well. + - uses: lisanna-dettwyler/action-post-run@d053b9b43d788b87a409f6cdb3b6fc87c6c8a4fe + with: + # assert that mamba initialize is in rcfile + run: | + grep -F "mamba initialize" ${{ matrix.rcfile }} + + - uses: ./ + with: + environment-file: false + post-deinit: false + + - run: grep -F "mamba initialize" ${{ matrix.rcfile }} + + test_deinit_auto_old_micromamba: + name: Test deinit auto old micromamba - ${{ matrix.os }} ${{ matrix.rcfile }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + # .bash_profile gets initialized in p-w-m ubuntu-latest instead of .bashrc like in micromamba + - { os: ubuntu-latest, rcfile: ~/.bash_profile } + - { os: ubuntu-latest, rcfile: ~/.zshrc } + # TODO zshrc not yet initialized in macos p-w-m + # https://github.com/mamba-org/mamba/issues/925 + - { os: macos-latest, rcfile: ~/.bash_profile } + - { os: windows-latest, rcfile: ~/.bash_profile } + - { os: windows-latest, rcfile: ~/Documents/WindowsPowerShell/profile.ps1 } + steps: + - uses: actions/checkout@v3 + + # This is a fork of webiny/action-post-run. If webiny/action-post-run is updated, this should be updated as well. + - uses: lisanna-dettwyler/action-post-run@d053b9b43d788b87a409f6cdb3b6fc87c6c8a4fe + with: + # assert that mamba initialize is in rcfile + run: | + grep -F "mamba initialize" ${{ matrix.rcfile }} + + - uses: ./ + with: + environment-file: false + micromamba-version: "0.23.0" + + - run: grep -F "mamba initialize" ${{ matrix.rcfile }} diff --git a/README.md b/README.md index c62a32bd..2b4e9641 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,15 @@ condarc-options: | http: ... ``` +### `post-deinit` + +Attempt to undo any modifications done to `.bashrc` etc. in the post action of the workflow. +This is useful for self-hosted runners that keep the state of the system. +One of `"auto"`, `"true"` or `"false"`. +If set to `"auto"`, behaves like `"true"` if the micromamba version used supports `micromamba shell deinit` (i.e. `micromamba>=0.25.0`). + +Default value: `auto` + ## Example usage diff --git a/action.yml b/action.yml index 1f3df240..ec514366 100644 --- a/action.yml +++ b/action.yml @@ -109,6 +109,14 @@ inputs: proxy_servers: http: ... ``` + post-deinit: + description: |- + Attempt to undo any modifications done to `.bashrc` etc. in the post action of the workflow. + This is useful for self-hosted runners that keep the state of the system. + One of `"auto"`, `"true"` or `"false"`. + If set to `"auto"`, behaves like `"true"` if the micromamba version used supports `micromamba shell deinit` (i.e. `micromamba>=0.25.0`). + required: true + default: auto # setup-miniconda options that are not (yet) supported: # diff --git a/dist/main/index.js b/dist/main/index.js index 9f2a1c6a..c537ea23 100644 --- a/dist/main/index.js +++ b/dist/main/index.js @@ -1397,6 +1397,7 @@ const file_command_1 = __nccwpck_require__(717); const utils_1 = __nccwpck_require__(5278); const os = __importStar(__nccwpck_require__(2037)); const path = __importStar(__nccwpck_require__(1017)); +const uuid_1 = __nccwpck_require__(8974); const oidc_utils_1 = __nccwpck_require__(8041); /** * The code to exit an action @@ -1426,7 +1427,14 @@ function exportVariable(name, val) { process.env[name] = convertedVal; const filePath = process.env['GITHUB_ENV'] || ''; if (filePath) { - const delimiter = '_GitHubActionsFileCommandDelimeter_'; + const delimiter = `ghadelimiter_${uuid_1.v4()}`; + // These should realistically never happen, but just in case someone finds a way to exploit uuid generation let's not allow keys or values that contain the delimiter. + if (name.includes(delimiter)) { + throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`); + } + if (convertedVal.includes(delimiter)) { + throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`); + } const commandValue = `${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter}`; file_command_1.issueCommand('ENV', commandValue); } @@ -2228,6 +2236,652 @@ exports.toCommandProperties = toCommandProperties; /***/ }), +/***/ 8974: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "v1", ({ + enumerable: true, + get: function () { + return _v.default; + } +})); +Object.defineProperty(exports, "v3", ({ + enumerable: true, + get: function () { + return _v2.default; + } +})); +Object.defineProperty(exports, "v4", ({ + enumerable: true, + get: function () { + return _v3.default; + } +})); +Object.defineProperty(exports, "v5", ({ + enumerable: true, + get: function () { + return _v4.default; + } +})); +Object.defineProperty(exports, "NIL", ({ + enumerable: true, + get: function () { + return _nil.default; + } +})); +Object.defineProperty(exports, "version", ({ + enumerable: true, + get: function () { + return _version.default; + } +})); +Object.defineProperty(exports, "validate", ({ + enumerable: true, + get: function () { + return _validate.default; + } +})); +Object.defineProperty(exports, "stringify", ({ + enumerable: true, + get: function () { + return _stringify.default; + } +})); +Object.defineProperty(exports, "parse", ({ + enumerable: true, + get: function () { + return _parse.default; + } +})); + +var _v = _interopRequireDefault(__nccwpck_require__(1595)); + +var _v2 = _interopRequireDefault(__nccwpck_require__(6993)); + +var _v3 = _interopRequireDefault(__nccwpck_require__(1472)); + +var _v4 = _interopRequireDefault(__nccwpck_require__(6217)); + +var _nil = _interopRequireDefault(__nccwpck_require__(2381)); + +var _version = _interopRequireDefault(__nccwpck_require__(427)); + +var _validate = _interopRequireDefault(__nccwpck_require__(2609)); + +var _stringify = _interopRequireDefault(__nccwpck_require__(1458)); + +var _parse = _interopRequireDefault(__nccwpck_require__(6385)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/***/ }), + +/***/ 5842: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function md5(bytes) { + if (Array.isArray(bytes)) { + bytes = Buffer.from(bytes); + } else if (typeof bytes === 'string') { + bytes = Buffer.from(bytes, 'utf8'); + } + + return _crypto.default.createHash('md5').update(bytes).digest(); +} + +var _default = md5; +exports["default"] = _default; + +/***/ }), + +/***/ 2381: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _default = '00000000-0000-0000-0000-000000000000'; +exports["default"] = _default; + +/***/ }), + +/***/ 6385: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _validate = _interopRequireDefault(__nccwpck_require__(2609)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function parse(uuid) { + if (!(0, _validate.default)(uuid)) { + throw TypeError('Invalid UUID'); + } + + let v; + const arr = new Uint8Array(16); // Parse ########-....-....-....-............ + + arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24; + arr[1] = v >>> 16 & 0xff; + arr[2] = v >>> 8 & 0xff; + arr[3] = v & 0xff; // Parse ........-####-....-....-............ + + arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8; + arr[5] = v & 0xff; // Parse ........-....-####-....-............ + + arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8; + arr[7] = v & 0xff; // Parse ........-....-....-####-............ + + arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8; + arr[9] = v & 0xff; // Parse ........-....-....-....-############ + // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) + + arr[10] = (v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000 & 0xff; + arr[11] = v / 0x100000000 & 0xff; + arr[12] = v >>> 24 & 0xff; + arr[13] = v >>> 16 & 0xff; + arr[14] = v >>> 8 & 0xff; + arr[15] = v & 0xff; + return arr; +} + +var _default = parse; +exports["default"] = _default; + +/***/ }), + +/***/ 6230: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i; +exports["default"] = _default; + +/***/ }), + +/***/ 9784: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = rng; + +var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const rnds8Pool = new Uint8Array(256); // # of random values to pre-allocate + +let poolPtr = rnds8Pool.length; + +function rng() { + if (poolPtr > rnds8Pool.length - 16) { + _crypto.default.randomFillSync(rnds8Pool); + + poolPtr = 0; + } + + return rnds8Pool.slice(poolPtr, poolPtr += 16); +} + +/***/ }), + +/***/ 8844: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function sha1(bytes) { + if (Array.isArray(bytes)) { + bytes = Buffer.from(bytes); + } else if (typeof bytes === 'string') { + bytes = Buffer.from(bytes, 'utf8'); + } + + return _crypto.default.createHash('sha1').update(bytes).digest(); +} + +var _default = sha1; +exports["default"] = _default; + +/***/ }), + +/***/ 1458: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _validate = _interopRequireDefault(__nccwpck_require__(2609)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Convert array of 16 byte values to UUID string format of the form: + * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + */ +const byteToHex = []; + +for (let i = 0; i < 256; ++i) { + byteToHex.push((i + 0x100).toString(16).substr(1)); +} + +function stringify(arr, offset = 0) { + // Note: Be careful editing this code! It's been tuned for performance + // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 + const uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one + // of the following: + // - One or more input array values don't map to a hex octet (leading to + // "undefined" in the uuid) + // - Invalid input values for the RFC `version` or `variant` fields + + if (!(0, _validate.default)(uuid)) { + throw TypeError('Stringified UUID is invalid'); + } + + return uuid; +} + +var _default = stringify; +exports["default"] = _default; + +/***/ }), + +/***/ 1595: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _rng = _interopRequireDefault(__nccwpck_require__(9784)); + +var _stringify = _interopRequireDefault(__nccwpck_require__(1458)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// **`v1()` - Generate time-based UUID** +// +// Inspired by https://github.com/LiosK/UUID.js +// and http://docs.python.org/library/uuid.html +let _nodeId; + +let _clockseq; // Previous uuid creation time + + +let _lastMSecs = 0; +let _lastNSecs = 0; // See https://github.com/uuidjs/uuid for API details + +function v1(options, buf, offset) { + let i = buf && offset || 0; + const b = buf || new Array(16); + options = options || {}; + let node = options.node || _nodeId; + let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; // node and clockseq need to be initialized to random values if they're not + // specified. We do this lazily to minimize issues related to insufficient + // system entropy. See #189 + + if (node == null || clockseq == null) { + const seedBytes = options.random || (options.rng || _rng.default)(); + + if (node == null) { + // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) + node = _nodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]]; + } + + if (clockseq == null) { + // Per 4.2.2, randomize (14 bit) clockseq + clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff; + } + } // UUID timestamps are 100 nano-second units since the Gregorian epoch, + // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so + // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' + // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. + + + let msecs = options.msecs !== undefined ? options.msecs : Date.now(); // Per 4.2.1.2, use count of uuid's generated during the current clock + // cycle to simulate higher resolution clock + + let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; // Time since last uuid creation (in msecs) + + const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; // Per 4.2.1.2, Bump clockseq on clock regression + + if (dt < 0 && options.clockseq === undefined) { + clockseq = clockseq + 1 & 0x3fff; + } // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new + // time interval + + + if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { + nsecs = 0; + } // Per 4.2.1.2 Throw error if too many uuids are requested + + + if (nsecs >= 10000) { + throw new Error("uuid.v1(): Can't create more than 10M uuids/sec"); + } + + _lastMSecs = msecs; + _lastNSecs = nsecs; + _clockseq = clockseq; // Per 4.1.4 - Convert from unix epoch to Gregorian epoch + + msecs += 12219292800000; // `time_low` + + const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; + b[i++] = tl >>> 24 & 0xff; + b[i++] = tl >>> 16 & 0xff; + b[i++] = tl >>> 8 & 0xff; + b[i++] = tl & 0xff; // `time_mid` + + const tmh = msecs / 0x100000000 * 10000 & 0xfffffff; + b[i++] = tmh >>> 8 & 0xff; + b[i++] = tmh & 0xff; // `time_high_and_version` + + b[i++] = tmh >>> 24 & 0xf | 0x10; // include version + + b[i++] = tmh >>> 16 & 0xff; // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) + + b[i++] = clockseq >>> 8 | 0x80; // `clock_seq_low` + + b[i++] = clockseq & 0xff; // `node` + + for (let n = 0; n < 6; ++n) { + b[i + n] = node[n]; + } + + return buf || (0, _stringify.default)(b); +} + +var _default = v1; +exports["default"] = _default; + +/***/ }), + +/***/ 6993: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _v = _interopRequireDefault(__nccwpck_require__(5920)); + +var _md = _interopRequireDefault(__nccwpck_require__(5842)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const v3 = (0, _v.default)('v3', 0x30, _md.default); +var _default = v3; +exports["default"] = _default; + +/***/ }), + +/***/ 5920: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = _default; +exports.URL = exports.DNS = void 0; + +var _stringify = _interopRequireDefault(__nccwpck_require__(1458)); + +var _parse = _interopRequireDefault(__nccwpck_require__(6385)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function stringToBytes(str) { + str = unescape(encodeURIComponent(str)); // UTF8 escape + + const bytes = []; + + for (let i = 0; i < str.length; ++i) { + bytes.push(str.charCodeAt(i)); + } + + return bytes; +} + +const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; +exports.DNS = DNS; +const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'; +exports.URL = URL; + +function _default(name, version, hashfunc) { + function generateUUID(value, namespace, buf, offset) { + if (typeof value === 'string') { + value = stringToBytes(value); + } + + if (typeof namespace === 'string') { + namespace = (0, _parse.default)(namespace); + } + + if (namespace.length !== 16) { + throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)'); + } // Compute hash of namespace and value, Per 4.3 + // Future: Use spread syntax when supported on all platforms, e.g. `bytes = + // hashfunc([...namespace, ... value])` + + + let bytes = new Uint8Array(16 + value.length); + bytes.set(namespace); + bytes.set(value, namespace.length); + bytes = hashfunc(bytes); + bytes[6] = bytes[6] & 0x0f | version; + bytes[8] = bytes[8] & 0x3f | 0x80; + + if (buf) { + offset = offset || 0; + + for (let i = 0; i < 16; ++i) { + buf[offset + i] = bytes[i]; + } + + return buf; + } + + return (0, _stringify.default)(bytes); + } // Function#name is not settable on some platforms (#270) + + + try { + generateUUID.name = name; // eslint-disable-next-line no-empty + } catch (err) {} // For CommonJS default export support + + + generateUUID.DNS = DNS; + generateUUID.URL = URL; + return generateUUID; +} + +/***/ }), + +/***/ 1472: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _rng = _interopRequireDefault(__nccwpck_require__(9784)); + +var _stringify = _interopRequireDefault(__nccwpck_require__(1458)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function v4(options, buf, offset) { + options = options || {}; + + const rnds = options.random || (options.rng || _rng.default)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` + + + rnds[6] = rnds[6] & 0x0f | 0x40; + rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided + + if (buf) { + offset = offset || 0; + + for (let i = 0; i < 16; ++i) { + buf[offset + i] = rnds[i]; + } + + return buf; + } + + return (0, _stringify.default)(rnds); +} + +var _default = v4; +exports["default"] = _default; + +/***/ }), + +/***/ 6217: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _v = _interopRequireDefault(__nccwpck_require__(5920)); + +var _sha = _interopRequireDefault(__nccwpck_require__(8844)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const v5 = (0, _v.default)('v5', 0x50, _sha.default); +var _default = v5; +exports["default"] = _default; + +/***/ }), + +/***/ 2609: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _regex = _interopRequireDefault(__nccwpck_require__(6230)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function validate(uuid) { + return typeof uuid === 'string' && _regex.default.test(uuid); +} + +var _default = validate; +exports["default"] = _default; + +/***/ }), + +/***/ 427: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _validate = _interopRequireDefault(__nccwpck_require__(2609)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function version(uuid) { + if (!(0, _validate.default)(uuid)) { + throw TypeError('Invalid UUID'); + } + + return parseInt(uuid.substr(14, 1), 16); +} + +var _default = version; +exports["default"] = _default; + +/***/ }), + /***/ 1514: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { @@ -62662,6 +63316,112 @@ module.exports.implForWrapper = function (wrapper) { }).call(this); +/***/ }), + +/***/ 4962: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const fs = __nccwpck_require__(7147) +const os = __nccwpck_require__(2037) +const path = __nccwpck_require__(1017) + +const exec = __nccwpck_require__(1514) +const core = __nccwpck_require__(2186) +const io = __nccwpck_require__(7436) + +const PATHS = { + condarc: path.join(os.homedir(), '.condarc'), + bashprofile: path.join(os.homedir(), '.bash_profile'), + micromambaBinFolder: path.join(os.homedir(), 'micromamba-bin'), + micromambaExe: path.join(os.homedir(), 'micromamba-bin', 'micromamba'), + // Without the "-root" suffix it causes problems, why? + // xref https://github.com/mamba-org/mamba/issues/1751 + micromambaRoot: path.join(os.homedir(), 'micromamba-root'), + micromambaPkgs: path.join(os.homedir(), 'micromamba-root', 'pkgs'), + micromambaEnvs: path.join(os.homedir(), 'micromamba-root', 'envs') +} + +async function withMkdtemp (callback) { + const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'micromamba-')) + let res + try { + res = await callback(tmpdir) + } catch (e) { + io.rmRF(tmpdir) + throw e + } + io.rmRF(tmpdir) + return res +} + +async function executeSubproc (...args) { + core.debug(`Running shell command ${JSON.stringify(args)}`) + try { + return await exec.getExecOutput(...args) + } catch (error) { + throw Error(`Failed to execute ${JSON.stringify(args)}: ${error}`) + } +} + +async function executeMicromambaShell (command, shell, logLevel) { + const cmd = micromambaCmd(`shell ${command} -s ${shell} -p ${PATHS.micromambaRoot} -y`, logLevel, PATHS.micromambaExe) + const cmd2 = cmd.split(' ') + return await executeSubproc(cmd2[0], cmd2.slice(1)) +} + +function micromambaCmd (command, logLevel, micromambaExe = 'micromamba') { + return `${micromambaExe} ${command}` + (logLevel ? ` --log-level ${logLevel}` : '') +} + +async function setupProfile (command, os, logLevel) { + switch (os) { + case 'darwin': + await executeMicromambaShell(command, 'bash', logLevel) + // TODO need to fix a check in micromamba so that this works + // https://github.com/mamba-org/mamba/issues/925 + // await executeMicromambaShell(command, 'zsh', logLevel) + break; + case 'linux': + await executeMicromambaShell(command, 'zsh', logLevel) + // On Linux, Micromamba modifies .bashrc but we want the modifications to be in .bash_profile. + if (command === 'init') { + await withMkdtemp(async tmpdir => { + const oldHome = process.env.HOME + process.env.HOME = tmpdir + await executeMicromambaShell(command, 'bash', logLevel) + process.env.HOME = oldHome + fs.appendFileSync(PATHS.bashprofile, '\n' + fs.readFileSync(path.join(tmpdir, '.bashrc'))) + }) + } else { + // we still need to deinit for the regular .bashrc since `micromamba shell init` also changes other files, not only .bashrc + await executeMicromambaShell(command, 'bash', logLevel) + // remove mamba initialize block from .bash_profile + const regexBlock = "\n# >>> mamba initialize >>>(?:\n|\r\n)?([\\s\\S]*?)# <<< mamba initialize <<<(?:\n|\r\n)?" + const bashProfile = fs.readFileSync(PATHS.bashprofile, 'utf8') + const newBashProfile = bashProfile.replace(new RegExp(regexBlock, 'g'), '') + fs.writeFileSync(PATHS.bashprofile, newBashProfile) + } + break; + case 'win32': + if (await haveBash()) { + await executeMicromambaShell(command, 'bash', logLevel) + } + // https://github.com/mamba-org/mamba/issues/1756 + await executeMicromambaShell(command, 'cmd.exe', logLevel) + await executeMicromambaShell(command, 'powershell', logLevel) + break; + } +} + +async function haveBash () { + return !!(await io.which('bash')) +} + +module.exports = { + PATHS, withMkdtemp, executeSubproc, executeMicromambaShell, micromambaCmd, setupProfile, haveBash +} + + /***/ }), /***/ 2877: @@ -62931,7 +63691,6 @@ __nccwpck_require__.r(__webpack_exports__); /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); const fs = __nccwpck_require__(7147) -const os = __nccwpck_require__(2037) const path = __nccwpck_require__(1017) const process = __nccwpck_require__(7282) const crypto = __nccwpck_require__(6113) @@ -62940,22 +63699,9 @@ const yaml = __nccwpck_require__(1917) const cache = __nccwpck_require__(7799) const core = __nccwpck_require__(2186) -const exec = __nccwpck_require__(1514) const io = __nccwpck_require__(7436) -const PATHS = { - condarc: path.join(os.homedir(), '.condarc'), - bashprofile: path.join(os.homedir(), '.bash_profile'), - micromambaBinFolder: path.join(os.homedir(), 'micromamba-bin'), - micromambaExe: path.join(os.homedir(), 'micromamba-bin', 'micromamba'), - // Without the "-root" suffix it causes problems, why? - // xref https://github.com/mamba-org/mamba/issues/1751 - micromambaRoot: path.join(os.homedir(), 'micromamba-root'), - micromambaPkgs: path.join(os.homedir(), 'micromamba-root', 'pkgs'), - micromambaEnvs: path.join(os.homedir(), 'micromamba-root', 'envs') -} - -// --- OS utils --- +const { PATHS, withMkdtemp, executeSubproc, setupProfile, micromambaCmd, haveBash } = __nccwpck_require__(4962) function getInputAsArray (name) { // From https://github.com/actions/cache/blob/main/src/utils/actionUtils.ts @@ -62966,14 +63712,7 @@ function getInputAsArray (name) { .filter(x => x !== '') } -async function executeSubproc (...args) { - core.debug(`Running shell command ${JSON.stringify(args)}`) - try { - return await exec.getExecOutput(...args) - } catch (error) { - throw Error(`Failed to execute ${JSON.stringify(args)}: ${error}`) - } -} +// --- OS utils --- async function executeBashFlags (flags, command) { return await executeSubproc('bash', ['-eo', 'pipefail', ...flags, '-c', command]) @@ -63011,27 +63750,6 @@ function sha256Short (s) { return sha256(s).substr(0, 8) } -function rmRf (dir) { - try { - fs.rmSync(dir, { recursive: true }) - } catch (e) { - core.warning(`Error removing directory ${dir}: ${e}`) - } -} - -async function withMkdtemp (callback) { - const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'micromamba-')) - let res - try { - res = await callback(tmpdir) - } catch (e) { - rmRf(tmpdir) - throw e - } - rmRf(tmpdir) - return res -} - function today () { return new Date().toDateString() } @@ -63059,10 +63777,6 @@ async function retry (callback, backoffTimes = [2000, 5000, 10000]) { } } -async function haveBash () { - return !!(await io.which('bash')) -} - function dumpFileContents (path) { core.info(`--- Contents of ${path} ---\n${fs.readFileSync(path)}\n--- End contents of ${path} ---`) } @@ -63099,46 +63813,8 @@ function getCondaArch () { return arch } -function micromambaCmd (command, logLevel, micromambaExe = 'micromamba') { - return `${micromambaExe} ${command}` + (logLevel ? ` --log-level ${logLevel}` : '') -} - -async function executeMicromambaShellInit (shell, logLevel) { - const cmd = micromambaCmd(`shell init -s ${shell} -p ${PATHS.micromambaRoot} -y`, logLevel, PATHS.micromambaExe) - const cmd2 = cmd.split(' ') - return await executeSubproc(cmd2[0], cmd2.slice(1)) -} - // --- Micromamba download + installation --- -const setupProfile = { - darwin: async logLevel => { - await executeMicromambaShellInit('bash', logLevel) - // TODO need to fix a check in micromamba so that this works - // https://github.com/mamba-org/mamba/issues/925 - // await executeMicromambaShellInit('zsh', logLevel) - }, - linux: async logLevel => { - await executeMicromambaShellInit('zsh', logLevel) - // On Linux, Micromamba modifies .bashrc but we want the modifications to be in .bash_profile. - await withMkdtemp(async tmpdir => { - const oldHome = process.env.HOME - process.env.HOME = tmpdir - await executeMicromambaShellInit('bash', logLevel) - process.env.HOME = oldHome - fs.appendFileSync(PATHS.bashprofile, '\n' + fs.readFileSync(path.join(tmpdir, '.bashrc'))) - }) - }, - win32: async logLevel => { - if (await haveBash()) { - await executeMicromambaShellInit('bash', logLevel) - } - // https://github.com/mamba-org/mamba/issues/1756 - await executeMicromambaShellInit('cmd.exe', logLevel) - await executeMicromambaShellInit('powershell', logLevel) - } -} - async function downloadMicromamba (micromambaUrl) { fs.mkdirSync(PATHS.micromambaBinFolder) const curlOpts = `${micromambaUrl} -Ls --retry 5 --retry-delay 1` @@ -63204,7 +63880,7 @@ async function installMicromamba (inputs) { await downloadMicromamba(micromambaUrl) saveCacheOnPost(...cacheArgs) } - await setupProfile[process.platform](inputs.logLevel) + await setupProfile('init', process.platform, inputs.logLevel) core.exportVariable('MAMBA_ROOT_PREFIX', PATHS.micromambaRoot) core.exportVariable('MAMBA_EXE', PATHS.micromambaExe) core.addPath(PATHS.micromambaBinFolder) @@ -63348,6 +64024,8 @@ async function installEnvironment (inputs, envFilePath, envYaml) { // --- Main --- async function main () { + // Using getInput is not safe in a post action for templated inputs. + // Therefore, we need to save the input values beforehand to the state. const inputs = { // Basic options envFile: core.getInput('environment-file'), @@ -63370,8 +64048,10 @@ async function main () { // Advanced options logLevel: core.getInput('log-level'), condaRcOptions: core.getInput('condarc-options'), - installerUrl: core.getInput('installer-url') + installerUrl: core.getInput('installer-url'), + postDeinit: core.getInput('post-deinit') } + core.saveState('inputs', JSON.stringify(inputs)) // Read environment file let envFilePath, envYaml diff --git a/dist/main/licenses.txt b/dist/main/licenses.txt index 9a3e28cc..ee06305b 100644 --- a/dist/main/licenses.txt +++ b/dist/main/licenses.txt @@ -976,25 +976,13 @@ uuid MIT The MIT License (MIT) -Copyright (c) 2010-2016 Robert Kieffer and other contributors +Copyright (c) 2010-2020 Robert Kieffer and other contributors -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. webidl-conversions diff --git a/dist/post/index.js b/dist/post/index.js index d3440365..68d9dbf6 100644 --- a/dist/post/index.js +++ b/dist/post/index.js @@ -1397,6 +1397,7 @@ const file_command_1 = __nccwpck_require__(717); const utils_1 = __nccwpck_require__(5278); const os = __importStar(__nccwpck_require__(2037)); const path = __importStar(__nccwpck_require__(1017)); +const uuid_1 = __nccwpck_require__(8974); const oidc_utils_1 = __nccwpck_require__(8041); /** * The code to exit an action @@ -1426,7 +1427,14 @@ function exportVariable(name, val) { process.env[name] = convertedVal; const filePath = process.env['GITHUB_ENV'] || ''; if (filePath) { - const delimiter = '_GitHubActionsFileCommandDelimeter_'; + const delimiter = `ghadelimiter_${uuid_1.v4()}`; + // These should realistically never happen, but just in case someone finds a way to exploit uuid generation let's not allow keys or values that contain the delimiter. + if (name.includes(delimiter)) { + throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`); + } + if (convertedVal.includes(delimiter)) { + throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`); + } const commandValue = `${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter}`; file_command_1.issueCommand('ENV', commandValue); } @@ -2228,6 +2236,652 @@ exports.toCommandProperties = toCommandProperties; /***/ }), +/***/ 8974: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "v1", ({ + enumerable: true, + get: function () { + return _v.default; + } +})); +Object.defineProperty(exports, "v3", ({ + enumerable: true, + get: function () { + return _v2.default; + } +})); +Object.defineProperty(exports, "v4", ({ + enumerable: true, + get: function () { + return _v3.default; + } +})); +Object.defineProperty(exports, "v5", ({ + enumerable: true, + get: function () { + return _v4.default; + } +})); +Object.defineProperty(exports, "NIL", ({ + enumerable: true, + get: function () { + return _nil.default; + } +})); +Object.defineProperty(exports, "version", ({ + enumerable: true, + get: function () { + return _version.default; + } +})); +Object.defineProperty(exports, "validate", ({ + enumerable: true, + get: function () { + return _validate.default; + } +})); +Object.defineProperty(exports, "stringify", ({ + enumerable: true, + get: function () { + return _stringify.default; + } +})); +Object.defineProperty(exports, "parse", ({ + enumerable: true, + get: function () { + return _parse.default; + } +})); + +var _v = _interopRequireDefault(__nccwpck_require__(1595)); + +var _v2 = _interopRequireDefault(__nccwpck_require__(6993)); + +var _v3 = _interopRequireDefault(__nccwpck_require__(1472)); + +var _v4 = _interopRequireDefault(__nccwpck_require__(6217)); + +var _nil = _interopRequireDefault(__nccwpck_require__(2381)); + +var _version = _interopRequireDefault(__nccwpck_require__(427)); + +var _validate = _interopRequireDefault(__nccwpck_require__(2609)); + +var _stringify = _interopRequireDefault(__nccwpck_require__(1458)); + +var _parse = _interopRequireDefault(__nccwpck_require__(6385)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/***/ }), + +/***/ 5842: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function md5(bytes) { + if (Array.isArray(bytes)) { + bytes = Buffer.from(bytes); + } else if (typeof bytes === 'string') { + bytes = Buffer.from(bytes, 'utf8'); + } + + return _crypto.default.createHash('md5').update(bytes).digest(); +} + +var _default = md5; +exports["default"] = _default; + +/***/ }), + +/***/ 2381: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _default = '00000000-0000-0000-0000-000000000000'; +exports["default"] = _default; + +/***/ }), + +/***/ 6385: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _validate = _interopRequireDefault(__nccwpck_require__(2609)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function parse(uuid) { + if (!(0, _validate.default)(uuid)) { + throw TypeError('Invalid UUID'); + } + + let v; + const arr = new Uint8Array(16); // Parse ########-....-....-....-............ + + arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24; + arr[1] = v >>> 16 & 0xff; + arr[2] = v >>> 8 & 0xff; + arr[3] = v & 0xff; // Parse ........-####-....-....-............ + + arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8; + arr[5] = v & 0xff; // Parse ........-....-####-....-............ + + arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8; + arr[7] = v & 0xff; // Parse ........-....-....-####-............ + + arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8; + arr[9] = v & 0xff; // Parse ........-....-....-....-############ + // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) + + arr[10] = (v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000 & 0xff; + arr[11] = v / 0x100000000 & 0xff; + arr[12] = v >>> 24 & 0xff; + arr[13] = v >>> 16 & 0xff; + arr[14] = v >>> 8 & 0xff; + arr[15] = v & 0xff; + return arr; +} + +var _default = parse; +exports["default"] = _default; + +/***/ }), + +/***/ 6230: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i; +exports["default"] = _default; + +/***/ }), + +/***/ 9784: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = rng; + +var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const rnds8Pool = new Uint8Array(256); // # of random values to pre-allocate + +let poolPtr = rnds8Pool.length; + +function rng() { + if (poolPtr > rnds8Pool.length - 16) { + _crypto.default.randomFillSync(rnds8Pool); + + poolPtr = 0; + } + + return rnds8Pool.slice(poolPtr, poolPtr += 16); +} + +/***/ }), + +/***/ 8844: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function sha1(bytes) { + if (Array.isArray(bytes)) { + bytes = Buffer.from(bytes); + } else if (typeof bytes === 'string') { + bytes = Buffer.from(bytes, 'utf8'); + } + + return _crypto.default.createHash('sha1').update(bytes).digest(); +} + +var _default = sha1; +exports["default"] = _default; + +/***/ }), + +/***/ 1458: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _validate = _interopRequireDefault(__nccwpck_require__(2609)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Convert array of 16 byte values to UUID string format of the form: + * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + */ +const byteToHex = []; + +for (let i = 0; i < 256; ++i) { + byteToHex.push((i + 0x100).toString(16).substr(1)); +} + +function stringify(arr, offset = 0) { + // Note: Be careful editing this code! It's been tuned for performance + // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 + const uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one + // of the following: + // - One or more input array values don't map to a hex octet (leading to + // "undefined" in the uuid) + // - Invalid input values for the RFC `version` or `variant` fields + + if (!(0, _validate.default)(uuid)) { + throw TypeError('Stringified UUID is invalid'); + } + + return uuid; +} + +var _default = stringify; +exports["default"] = _default; + +/***/ }), + +/***/ 1595: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _rng = _interopRequireDefault(__nccwpck_require__(9784)); + +var _stringify = _interopRequireDefault(__nccwpck_require__(1458)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// **`v1()` - Generate time-based UUID** +// +// Inspired by https://github.com/LiosK/UUID.js +// and http://docs.python.org/library/uuid.html +let _nodeId; + +let _clockseq; // Previous uuid creation time + + +let _lastMSecs = 0; +let _lastNSecs = 0; // See https://github.com/uuidjs/uuid for API details + +function v1(options, buf, offset) { + let i = buf && offset || 0; + const b = buf || new Array(16); + options = options || {}; + let node = options.node || _nodeId; + let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; // node and clockseq need to be initialized to random values if they're not + // specified. We do this lazily to minimize issues related to insufficient + // system entropy. See #189 + + if (node == null || clockseq == null) { + const seedBytes = options.random || (options.rng || _rng.default)(); + + if (node == null) { + // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) + node = _nodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]]; + } + + if (clockseq == null) { + // Per 4.2.2, randomize (14 bit) clockseq + clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff; + } + } // UUID timestamps are 100 nano-second units since the Gregorian epoch, + // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so + // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' + // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. + + + let msecs = options.msecs !== undefined ? options.msecs : Date.now(); // Per 4.2.1.2, use count of uuid's generated during the current clock + // cycle to simulate higher resolution clock + + let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; // Time since last uuid creation (in msecs) + + const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; // Per 4.2.1.2, Bump clockseq on clock regression + + if (dt < 0 && options.clockseq === undefined) { + clockseq = clockseq + 1 & 0x3fff; + } // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new + // time interval + + + if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { + nsecs = 0; + } // Per 4.2.1.2 Throw error if too many uuids are requested + + + if (nsecs >= 10000) { + throw new Error("uuid.v1(): Can't create more than 10M uuids/sec"); + } + + _lastMSecs = msecs; + _lastNSecs = nsecs; + _clockseq = clockseq; // Per 4.1.4 - Convert from unix epoch to Gregorian epoch + + msecs += 12219292800000; // `time_low` + + const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; + b[i++] = tl >>> 24 & 0xff; + b[i++] = tl >>> 16 & 0xff; + b[i++] = tl >>> 8 & 0xff; + b[i++] = tl & 0xff; // `time_mid` + + const tmh = msecs / 0x100000000 * 10000 & 0xfffffff; + b[i++] = tmh >>> 8 & 0xff; + b[i++] = tmh & 0xff; // `time_high_and_version` + + b[i++] = tmh >>> 24 & 0xf | 0x10; // include version + + b[i++] = tmh >>> 16 & 0xff; // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) + + b[i++] = clockseq >>> 8 | 0x80; // `clock_seq_low` + + b[i++] = clockseq & 0xff; // `node` + + for (let n = 0; n < 6; ++n) { + b[i + n] = node[n]; + } + + return buf || (0, _stringify.default)(b); +} + +var _default = v1; +exports["default"] = _default; + +/***/ }), + +/***/ 6993: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _v = _interopRequireDefault(__nccwpck_require__(5920)); + +var _md = _interopRequireDefault(__nccwpck_require__(5842)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const v3 = (0, _v.default)('v3', 0x30, _md.default); +var _default = v3; +exports["default"] = _default; + +/***/ }), + +/***/ 5920: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = _default; +exports.URL = exports.DNS = void 0; + +var _stringify = _interopRequireDefault(__nccwpck_require__(1458)); + +var _parse = _interopRequireDefault(__nccwpck_require__(6385)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function stringToBytes(str) { + str = unescape(encodeURIComponent(str)); // UTF8 escape + + const bytes = []; + + for (let i = 0; i < str.length; ++i) { + bytes.push(str.charCodeAt(i)); + } + + return bytes; +} + +const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; +exports.DNS = DNS; +const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'; +exports.URL = URL; + +function _default(name, version, hashfunc) { + function generateUUID(value, namespace, buf, offset) { + if (typeof value === 'string') { + value = stringToBytes(value); + } + + if (typeof namespace === 'string') { + namespace = (0, _parse.default)(namespace); + } + + if (namespace.length !== 16) { + throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)'); + } // Compute hash of namespace and value, Per 4.3 + // Future: Use spread syntax when supported on all platforms, e.g. `bytes = + // hashfunc([...namespace, ... value])` + + + let bytes = new Uint8Array(16 + value.length); + bytes.set(namespace); + bytes.set(value, namespace.length); + bytes = hashfunc(bytes); + bytes[6] = bytes[6] & 0x0f | version; + bytes[8] = bytes[8] & 0x3f | 0x80; + + if (buf) { + offset = offset || 0; + + for (let i = 0; i < 16; ++i) { + buf[offset + i] = bytes[i]; + } + + return buf; + } + + return (0, _stringify.default)(bytes); + } // Function#name is not settable on some platforms (#270) + + + try { + generateUUID.name = name; // eslint-disable-next-line no-empty + } catch (err) {} // For CommonJS default export support + + + generateUUID.DNS = DNS; + generateUUID.URL = URL; + return generateUUID; +} + +/***/ }), + +/***/ 1472: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _rng = _interopRequireDefault(__nccwpck_require__(9784)); + +var _stringify = _interopRequireDefault(__nccwpck_require__(1458)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function v4(options, buf, offset) { + options = options || {}; + + const rnds = options.random || (options.rng || _rng.default)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` + + + rnds[6] = rnds[6] & 0x0f | 0x40; + rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided + + if (buf) { + offset = offset || 0; + + for (let i = 0; i < 16; ++i) { + buf[offset + i] = rnds[i]; + } + + return buf; + } + + return (0, _stringify.default)(rnds); +} + +var _default = v4; +exports["default"] = _default; + +/***/ }), + +/***/ 6217: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _v = _interopRequireDefault(__nccwpck_require__(5920)); + +var _sha = _interopRequireDefault(__nccwpck_require__(8844)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const v5 = (0, _v.default)('v5', 0x50, _sha.default); +var _default = v5; +exports["default"] = _default; + +/***/ }), + +/***/ 2609: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _regex = _interopRequireDefault(__nccwpck_require__(6230)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function validate(uuid) { + return typeof uuid === 'string' && _regex.default.test(uuid); +} + +var _default = validate; +exports["default"] = _default; + +/***/ }), + +/***/ 427: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _validate = _interopRequireDefault(__nccwpck_require__(2609)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function version(uuid) { + if (!(0, _validate.default)(uuid)) { + throw TypeError('Invalid UUID'); + } + + return parseInt(uuid.substr(14, 1), 16); +} + +var _default = version; +exports["default"] = _default; + +/***/ }), + /***/ 1514: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { @@ -58495,6 +59149,112 @@ module.exports.implForWrapper = function (wrapper) { }).call(this); +/***/ }), + +/***/ 4962: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const fs = __nccwpck_require__(7147) +const os = __nccwpck_require__(2037) +const path = __nccwpck_require__(1017) + +const exec = __nccwpck_require__(1514) +const core = __nccwpck_require__(2186) +const io = __nccwpck_require__(7436) + +const PATHS = { + condarc: path.join(os.homedir(), '.condarc'), + bashprofile: path.join(os.homedir(), '.bash_profile'), + micromambaBinFolder: path.join(os.homedir(), 'micromamba-bin'), + micromambaExe: path.join(os.homedir(), 'micromamba-bin', 'micromamba'), + // Without the "-root" suffix it causes problems, why? + // xref https://github.com/mamba-org/mamba/issues/1751 + micromambaRoot: path.join(os.homedir(), 'micromamba-root'), + micromambaPkgs: path.join(os.homedir(), 'micromamba-root', 'pkgs'), + micromambaEnvs: path.join(os.homedir(), 'micromamba-root', 'envs') +} + +async function withMkdtemp (callback) { + const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'micromamba-')) + let res + try { + res = await callback(tmpdir) + } catch (e) { + io.rmRF(tmpdir) + throw e + } + io.rmRF(tmpdir) + return res +} + +async function executeSubproc (...args) { + core.debug(`Running shell command ${JSON.stringify(args)}`) + try { + return await exec.getExecOutput(...args) + } catch (error) { + throw Error(`Failed to execute ${JSON.stringify(args)}: ${error}`) + } +} + +async function executeMicromambaShell (command, shell, logLevel) { + const cmd = micromambaCmd(`shell ${command} -s ${shell} -p ${PATHS.micromambaRoot} -y`, logLevel, PATHS.micromambaExe) + const cmd2 = cmd.split(' ') + return await executeSubproc(cmd2[0], cmd2.slice(1)) +} + +function micromambaCmd (command, logLevel, micromambaExe = 'micromamba') { + return `${micromambaExe} ${command}` + (logLevel ? ` --log-level ${logLevel}` : '') +} + +async function setupProfile (command, os, logLevel) { + switch (os) { + case 'darwin': + await executeMicromambaShell(command, 'bash', logLevel) + // TODO need to fix a check in micromamba so that this works + // https://github.com/mamba-org/mamba/issues/925 + // await executeMicromambaShell(command, 'zsh', logLevel) + break; + case 'linux': + await executeMicromambaShell(command, 'zsh', logLevel) + // On Linux, Micromamba modifies .bashrc but we want the modifications to be in .bash_profile. + if (command === 'init') { + await withMkdtemp(async tmpdir => { + const oldHome = process.env.HOME + process.env.HOME = tmpdir + await executeMicromambaShell(command, 'bash', logLevel) + process.env.HOME = oldHome + fs.appendFileSync(PATHS.bashprofile, '\n' + fs.readFileSync(path.join(tmpdir, '.bashrc'))) + }) + } else { + // we still need to deinit for the regular .bashrc since `micromamba shell init` also changes other files, not only .bashrc + await executeMicromambaShell(command, 'bash', logLevel) + // remove mamba initialize block from .bash_profile + const regexBlock = "\n# >>> mamba initialize >>>(?:\n|\r\n)?([\\s\\S]*?)# <<< mamba initialize <<<(?:\n|\r\n)?" + const bashProfile = fs.readFileSync(PATHS.bashprofile, 'utf8') + const newBashProfile = bashProfile.replace(new RegExp(regexBlock, 'g'), '') + fs.writeFileSync(PATHS.bashprofile, newBashProfile) + } + break; + case 'win32': + if (await haveBash()) { + await executeMicromambaShell(command, 'bash', logLevel) + } + // https://github.com/mamba-org/mamba/issues/1756 + await executeMicromambaShell(command, 'cmd.exe', logLevel) + await executeMicromambaShell(command, 'powershell', logLevel) + break; + } +} + +async function haveBash () { + return !!(await io.which('bash')) +} + +module.exports = { + PATHS, withMkdtemp, executeSubproc, executeMicromambaShell, micromambaCmd, setupProfile, haveBash +} + + /***/ }), /***/ 2877: @@ -58755,13 +59515,15 @@ __nccwpck_require__.r(__webpack_exports__); /* harmony export */ __nccwpck_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); +const fs = __nccwpck_require__(7147) +const os = __nccwpck_require__(2037) +const path = __nccwpck_require__(1017) + const cache = __nccwpck_require__(7799) const core = __nccwpck_require__(2186) const io = __nccwpck_require__(7436) -const fs = __nccwpck_require__(7147) -const os = __nccwpck_require__(2037) -const path = __nccwpck_require__(1017) +const { setupProfile } = __nccwpck_require__(4962) // From https://github.com/conda-incubator/setup-miniconda (MIT license) async function trimPkgsCacheFolder (cacheFolder) { @@ -58791,7 +59553,22 @@ async function trimPkgsCacheFolder (cacheFolder) { core.endGroup() } +function useDeinit (inputs) { + // debug output values + core.debug(`inputs.postDeinit: ${inputs.postDeinit}`) + core.debug(`inputs.micromambaVersion: ${inputs.micromambaVersion}`) + // since 'latest' >= '0.25.0', this works for all expected values + return (inputs.postDeinit === 'auto' && inputs.micromambaVersion >= '0.25.0') || inputs.postDeinit === 'true' +} + async function main () { + const inputs = JSON.parse(core.getState('inputs')) + + if (useDeinit(inputs)) { + core.startGroup(`Deinitializing micromamba ...`) + await setupProfile('deinit', process.platform, inputs.logLevel) + core.endGroup() + } if (!core.getState('mainRanSuccessfully')) { core.notice('Conda environment setup failed. Cache will not be saved.') return diff --git a/dist/post/licenses.txt b/dist/post/licenses.txt index 21d4dfef..4b791fd3 100644 --- a/dist/post/licenses.txt +++ b/dist/post/licenses.txt @@ -951,25 +951,13 @@ uuid MIT The MIT License (MIT) -Copyright (c) 2010-2016 Robert Kieffer and other contributors +Copyright (c) 2010-2020 Robert Kieffer and other contributors -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. webidl-conversions diff --git a/index.js b/index.js index ecf11d81..75839e9a 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,4 @@ const fs = require('fs') -const os = require('os') const path = require('path') const process = require('process') const crypto = require('crypto') @@ -8,22 +7,9 @@ const yaml = require('js-yaml') const cache = require('@actions/cache') const core = require('@actions/core') -const exec = require('@actions/exec') const io = require('@actions/io') -const PATHS = { - condarc: path.join(os.homedir(), '.condarc'), - bashprofile: path.join(os.homedir(), '.bash_profile'), - micromambaBinFolder: path.join(os.homedir(), 'micromamba-bin'), - micromambaExe: path.join(os.homedir(), 'micromamba-bin', 'micromamba'), - // Without the "-root" suffix it causes problems, why? - // xref https://github.com/mamba-org/mamba/issues/1751 - micromambaRoot: path.join(os.homedir(), 'micromamba-root'), - micromambaPkgs: path.join(os.homedir(), 'micromamba-root', 'pkgs'), - micromambaEnvs: path.join(os.homedir(), 'micromamba-root', 'envs') -} - -// --- OS utils --- +const { PATHS, withMkdtemp, executeSubproc, setupProfile, micromambaCmd, haveBash } = require('./util') function getInputAsArray (name) { // From https://github.com/actions/cache/blob/main/src/utils/actionUtils.ts @@ -34,14 +20,7 @@ function getInputAsArray (name) { .filter(x => x !== '') } -async function executeSubproc (...args) { - core.debug(`Running shell command ${JSON.stringify(args)}`) - try { - return await exec.getExecOutput(...args) - } catch (error) { - throw Error(`Failed to execute ${JSON.stringify(args)}: ${error}`) - } -} +// --- OS utils --- async function executeBashFlags (flags, command) { return await executeSubproc('bash', ['-eo', 'pipefail', ...flags, '-c', command]) @@ -79,27 +58,6 @@ function sha256Short (s) { return sha256(s).substr(0, 8) } -function rmRf (dir) { - try { - fs.rmSync(dir, { recursive: true }) - } catch (e) { - core.warning(`Error removing directory ${dir}: ${e}`) - } -} - -async function withMkdtemp (callback) { - const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'micromamba-')) - let res - try { - res = await callback(tmpdir) - } catch (e) { - rmRf(tmpdir) - throw e - } - rmRf(tmpdir) - return res -} - function today () { return new Date().toDateString() } @@ -127,10 +85,6 @@ async function retry (callback, backoffTimes = [2000, 5000, 10000]) { } } -async function haveBash () { - return !!(await io.which('bash')) -} - function dumpFileContents (path) { core.info(`--- Contents of ${path} ---\n${fs.readFileSync(path)}\n--- End contents of ${path} ---`) } @@ -167,46 +121,8 @@ function getCondaArch () { return arch } -function micromambaCmd (command, logLevel, micromambaExe = 'micromamba') { - return `${micromambaExe} ${command}` + (logLevel ? ` --log-level ${logLevel}` : '') -} - -async function executeMicromambaShellInit (shell, logLevel) { - const cmd = micromambaCmd(`shell init -s ${shell} -p ${PATHS.micromambaRoot} -y`, logLevel, PATHS.micromambaExe) - const cmd2 = cmd.split(' ') - return await executeSubproc(cmd2[0], cmd2.slice(1)) -} - // --- Micromamba download + installation --- -const setupProfile = { - darwin: async logLevel => { - await executeMicromambaShellInit('bash', logLevel) - // TODO need to fix a check in micromamba so that this works - // https://github.com/mamba-org/mamba/issues/925 - // await executeMicromambaShellInit('zsh', logLevel) - }, - linux: async logLevel => { - await executeMicromambaShellInit('zsh', logLevel) - // On Linux, Micromamba modifies .bashrc but we want the modifications to be in .bash_profile. - await withMkdtemp(async tmpdir => { - const oldHome = process.env.HOME - process.env.HOME = tmpdir - await executeMicromambaShellInit('bash', logLevel) - process.env.HOME = oldHome - fs.appendFileSync(PATHS.bashprofile, '\n' + fs.readFileSync(path.join(tmpdir, '.bashrc'))) - }) - }, - win32: async logLevel => { - if (await haveBash()) { - await executeMicromambaShellInit('bash', logLevel) - } - // https://github.com/mamba-org/mamba/issues/1756 - await executeMicromambaShellInit('cmd.exe', logLevel) - await executeMicromambaShellInit('powershell', logLevel) - } -} - async function downloadMicromamba (micromambaUrl) { fs.mkdirSync(PATHS.micromambaBinFolder) const curlOpts = `${micromambaUrl} -Ls --retry 5 --retry-delay 1` @@ -272,7 +188,7 @@ async function installMicromamba (inputs) { await downloadMicromamba(micromambaUrl) saveCacheOnPost(...cacheArgs) } - await setupProfile[process.platform](inputs.logLevel) + await setupProfile('init', process.platform, inputs.logLevel) core.exportVariable('MAMBA_ROOT_PREFIX', PATHS.micromambaRoot) core.exportVariable('MAMBA_EXE', PATHS.micromambaExe) core.addPath(PATHS.micromambaBinFolder) @@ -416,6 +332,8 @@ async function installEnvironment (inputs, envFilePath, envYaml) { // --- Main --- async function main () { + // Using getInput is not safe in a post action for templated inputs. + // Therefore, we need to save the input values beforehand to the state. const inputs = { // Basic options envFile: core.getInput('environment-file'), @@ -438,8 +356,10 @@ async function main () { // Advanced options logLevel: core.getInput('log-level'), condaRcOptions: core.getInput('condarc-options'), - installerUrl: core.getInput('installer-url') + installerUrl: core.getInput('installer-url'), + postDeinit: core.getInput('post-deinit') } + core.saveState('inputs', JSON.stringify(inputs)) // Read environment file let envFilePath, envYaml diff --git a/package-lock.json b/package-lock.json index 44774776..b9c9b2cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,11 +39,20 @@ } }, "node_modules/@actions/core": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.0.tgz", - "integrity": "sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", + "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", "dependencies": { - "@actions/http-client": "^2.0.1" + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "node_modules/@actions/core/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/@actions/exec": { @@ -2966,11 +2975,19 @@ } }, "@actions/core": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.0.tgz", - "integrity": "sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", + "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", "requires": { - "@actions/http-client": "^2.0.1" + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } } }, "@actions/exec": { diff --git a/post.js b/post.js index cfd877e5..dad04211 100644 --- a/post.js +++ b/post.js @@ -1,10 +1,12 @@ +const fs = require('fs') +const os = require('os') +const path = require('path') + const cache = require('@actions/cache') const core = require('@actions/core') const io = require('@actions/io') -const fs = require('fs') -const os = require('os') -const path = require('path') +const { setupProfile } = require('./util') // From https://github.com/conda-incubator/setup-miniconda (MIT license) async function trimPkgsCacheFolder (cacheFolder) { @@ -34,7 +36,22 @@ async function trimPkgsCacheFolder (cacheFolder) { core.endGroup() } +function useDeinit (inputs) { + // debug output values + core.debug(`inputs.postDeinit: ${inputs.postDeinit}`) + core.debug(`inputs.micromambaVersion: ${inputs.micromambaVersion}`) + // since 'latest' >= '0.25.0', this works for all expected values + return (inputs.postDeinit === 'auto' && inputs.micromambaVersion >= '0.25.0') || inputs.postDeinit === 'true' +} + async function main () { + const inputs = JSON.parse(core.getState('inputs')) + + if (useDeinit(inputs)) { + core.startGroup(`Deinitializing micromamba ...`) + await setupProfile('deinit', process.platform, inputs.logLevel) + core.endGroup() + } if (!core.getState('mainRanSuccessfully')) { core.notice('Conda environment setup failed. Cache will not be saved.') return diff --git a/util.js b/util.js new file mode 100644 index 00000000..2ce30e7f --- /dev/null +++ b/util.js @@ -0,0 +1,99 @@ +const fs = require('fs') +const os = require('os') +const path = require('path') + +const exec = require('@actions/exec') +const core = require('@actions/core') +const io = require('@actions/io') + +const PATHS = { + condarc: path.join(os.homedir(), '.condarc'), + bashprofile: path.join(os.homedir(), '.bash_profile'), + micromambaBinFolder: path.join(os.homedir(), 'micromamba-bin'), + micromambaExe: path.join(os.homedir(), 'micromamba-bin', 'micromamba'), + // Without the "-root" suffix it causes problems, why? + // xref https://github.com/mamba-org/mamba/issues/1751 + micromambaRoot: path.join(os.homedir(), 'micromamba-root'), + micromambaPkgs: path.join(os.homedir(), 'micromamba-root', 'pkgs'), + micromambaEnvs: path.join(os.homedir(), 'micromamba-root', 'envs') +} + +async function withMkdtemp (callback) { + const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'micromamba-')) + let res + try { + res = await callback(tmpdir) + } catch (e) { + io.rmRF(tmpdir) + throw e + } + io.rmRF(tmpdir) + return res +} + +async function executeSubproc (...args) { + core.debug(`Running shell command ${JSON.stringify(args)}`) + try { + return await exec.getExecOutput(...args) + } catch (error) { + throw Error(`Failed to execute ${JSON.stringify(args)}: ${error}`) + } +} + +async function executeMicromambaShell (command, shell, logLevel) { + const cmd = micromambaCmd(`shell ${command} -s ${shell} -p ${PATHS.micromambaRoot} -y`, logLevel, PATHS.micromambaExe) + const cmd2 = cmd.split(' ') + return await executeSubproc(cmd2[0], cmd2.slice(1)) +} + +function micromambaCmd (command, logLevel, micromambaExe = 'micromamba') { + return `${micromambaExe} ${command}` + (logLevel ? ` --log-level ${logLevel}` : '') +} + +async function setupProfile (command, os, logLevel) { + switch (os) { + case 'darwin': + await executeMicromambaShell(command, 'bash', logLevel) + // TODO need to fix a check in micromamba so that this works + // https://github.com/mamba-org/mamba/issues/925 + // await executeMicromambaShell(command, 'zsh', logLevel) + break; + case 'linux': + await executeMicromambaShell(command, 'zsh', logLevel) + // On Linux, Micromamba modifies .bashrc but we want the modifications to be in .bash_profile. + if (command === 'init') { + await withMkdtemp(async tmpdir => { + const oldHome = process.env.HOME + process.env.HOME = tmpdir + await executeMicromambaShell(command, 'bash', logLevel) + process.env.HOME = oldHome + fs.appendFileSync(PATHS.bashprofile, '\n' + fs.readFileSync(path.join(tmpdir, '.bashrc'))) + }) + } else { + // we still need to deinit for the regular .bashrc since `micromamba shell init` also changes other files, not only .bashrc + await executeMicromambaShell(command, 'bash', logLevel) + // remove mamba initialize block from .bash_profile + const regexBlock = "\n# >>> mamba initialize >>>(?:\n|\r\n)?([\\s\\S]*?)# <<< mamba initialize <<<(?:\n|\r\n)?" + const bashProfile = fs.readFileSync(PATHS.bashprofile, 'utf8') + const newBashProfile = bashProfile.replace(new RegExp(regexBlock, 'g'), '') + fs.writeFileSync(PATHS.bashprofile, newBashProfile) + } + break; + case 'win32': + if (await haveBash()) { + await executeMicromambaShell(command, 'bash', logLevel) + } + // https://github.com/mamba-org/mamba/issues/1756 + await executeMicromambaShell(command, 'cmd.exe', logLevel) + await executeMicromambaShell(command, 'powershell', logLevel) + break; + } +} + +async function haveBash () { + return !!(await io.which('bash')) +} + +module.exports = { + PATHS, withMkdtemp, executeSubproc, executeMicromambaShell, micromambaCmd, setupProfile, haveBash +}