-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathsetup-haskell.js
147 lines (147 loc) · 7.06 KB
/
setup-haskell.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const io = __importStar(require("@actions/io"));
const ensure_error_1 = __importDefault(require("ensure-error"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os_1 = require("os");
const opts_1 = require("./opts");
const installer_1 = require("./installer");
const exec_1 = require("@actions/exec");
async function cabalConfig() {
let out = Buffer.from('');
const append = (b) => (out = Buffer.concat([out, b]));
await (0, exec_1.exec)('cabal', ['--help'], {
silent: true,
listeners: { stdout: append, stderr: append }
});
return configFileFromHelpText(out.toString());
}
// The end of the cabal help text is printing the config file, e.g.:
//
// > You can edit the cabal configuration file to set defaults:
// > <<HOME>>/.cabal/config
// > This file will be generated with sensible defaults if you run 'cabal update'.
//
// The last line here is only printed if the file does not exist yet.
//
// So trimming last following "You can edit..." will give us the name of the config file.
//
// Needless to say this is very brittle, but we secure this by a test
// in Cabal's testsuite: https://github.com/haskell/cabal/pull/9614
//
function configFileFromHelpText(txt) {
const marker = 'You can edit the cabal configuration file to set defaults:';
const lines = txt.split('\n').map(line => line.trim());
const foundIndex = lines.findLastIndex(line => line === marker);
if (foundIndex !== -1 && foundIndex + 1 < lines.length) {
return lines[foundIndex + 1];
}
else {
return '';
}
}
async function run(inputs) {
try {
core.info('Preparing to setup a Haskell environment');
const os = process.platform;
const arch = process.arch;
const opts = (0, opts_1.getOpts)((0, opts_1.getDefaults)(os), os, inputs);
core.debug(`run: inputs = ${JSON.stringify(inputs)}`);
core.debug(`run: os = ${JSON.stringify(os)}`);
core.debug(`run: opts = ${JSON.stringify(opts)}`);
if (opts.ghcup.releaseChannel) {
await core.group(`Preparing ghcup environment`, async () => (0, installer_1.addGhcupReleaseChannel)(opts.ghcup.releaseChannel, os, arch));
}
for (const [t, { resolved }] of Object.entries(opts).filter(o => o[1].enable)) {
await core.group(`Preparing ${t} environment`, async () => (0, installer_1.resetTool)(t, resolved, os, arch));
await core.group(`Installing ${t} version ${resolved}`, async () => (0, installer_1.installTool)(t, resolved, os, arch));
}
if (opts.stack.setup)
await core.group('Pre-installing GHC with stack', async () => (0, exec_1.exec)('stack', ['setup', opts.ghc.resolved]));
if (opts.cabal.enable)
await core.group('Setting up cabal', async () => {
// Andreas, 2023-03-16, issue #210.
// Create .cabal/bin to activate non-XDG mode of cabal.
if (process.platform !== 'win32')
io.mkdirP(`${process.env.HOME}/.cabal/bin`);
// Create config only if it doesn't exist.
await (0, exec_1.exec)('cabal', ['user-config', 'init'], {
silent: true,
ignoreReturnCode: true
});
// Set the 'store-dir' in the cabal configuration.
// Blindly appending is fine.
// Cabal merges these and picks the last defined option.
const configFile = await cabalConfig();
const storeDir = process.platform === 'win32'
? 'C:\\sr'
: `${process.env.HOME}/.cabal/store`;
fs.appendFileSync(configFile, `store-dir: ${storeDir}${os_1.EOL}`);
core.setOutput('cabal-store', storeDir);
if (process.platform === 'win32') {
// Some Windows version cannot symlink, so we need to switch to 'install-method: copy'.
// Choco does this for us, but not GHCup: https://github.com/haskell/ghcup-hs/issues/808
// However, here we do not know whether we installed with choco or not, so do it always:
fs.appendFileSync(configFile, `install-method: copy${os_1.EOL}`);
fs.appendFileSync(configFile, `overwrite-policy: always${os_1.EOL}`);
}
else {
// Issue #130: for non-choco installs, add ~/.cabal/bin to PATH
const installdir = `${process.env.HOME}/.cabal/bin`;
core.info(`Adding ${installdir} to PATH`);
core.addPath(installdir);
}
// Workaround the GHC nopie linking errors for ancient GHC versions
// NB: Is this _just_ for GHC 7.10.3?
if (opts.ghc.resolved === '7.10.3' && os !== 'win32') {
fs.appendFileSync(configFile, ['program-default-options', ' ghc-options: -optl-no-pie'].join(os_1.EOL) + os_1.EOL);
// We cannot use cabal user-config to normalize the config because of:
// https://github.com/haskell/cabal/issues/6823
// await exec('cabal user-config update');
}
if (opts.cabal.update && !opts.stack.enable)
await (0, exec_1.exec)('cabal update');
});
core.info(`##[add-matcher]${path.join(__dirname, '..', 'matcher.json')}`);
}
catch (_error) {
const error = (0, ensure_error_1.default)(_error);
if (core.isDebug()) {
// we don't fail here so that the error path can be tested in CI
core.setOutput('failed', true);
core.debug(error.message);
}
else {
core.setFailed(error.message);
}
}
}
exports.default = run;