Skip to content

Commit

Permalink
Merge pull request #178 from cachix/refactor
Browse files Browse the repository at this point in the history
Clean up the logic that determines which push mode to use
  • Loading branch information
domenkozar authored Apr 4, 2024
2 parents 60ef706 + cee104e commit fb5cb83
Show file tree
Hide file tree
Showing 2 changed files with 289 additions and 215 deletions.
246 changes: 138 additions & 108 deletions dist/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7750,73 +7750,95 @@ const cachixBinInput = core.getInput('cachixBin');
const installCommand = core.getInput('installCommand') ||
"nix-env --quiet -j8 -iA cachix -f https://cachix.org/api/v1/install";
const ENV_CACHIX_DAEMON_DIR = 'CACHIX_DAEMON_DIR';
var PushMode;
(function (PushMode) {
// Disable pushing entirely.
PushMode["None"] = "None";
// Scans the entire store during the pre- and post-hooks and uploads the difference.
// This is a very simple method and is likely to work in any environment.
// There are two downsides:
// 1. The final set of paths to push is computed in the post-build hook, so paths are not pushed during builds.
// 2. It is not safe to use in a multi-user environment, as it may leak store paths built by other users.
PushMode["StoreScan"] = "StoreScan";
// Uses the Cachix Daemon to register a post-build hook with the Nix Daemon.
// Very efficient as it can upload paths as they are built.
// May not be supported in all environment (e.g. NixOS Containers) and inherits all of the implementation deficiencies of Nix's post-build hook.
PushMode["Daemon"] = "Daemon";
})(PushMode || (PushMode = {}));
async function setup() {
try {
let cachixBin = cachixBinInput;
if (cachixBin !== "") {
core.debug(`Using Cachix executable from input: ${cachixBin}`);
let cachixBin = cachixBinInput;
if (cachixBin !== "") {
core.debug(`Using Cachix executable from input: ${cachixBin}`);
}
else {
// Find the Cachix executable in PATH
let resolvedCachixBin = which_1.default.sync('cachix', { nothrow: true });
if (resolvedCachixBin) {
core.debug(`Found Cachix executable: ${cachixBin}`);
cachixBin = resolvedCachixBin;
}
else {
// Find the Cachix executable in PATH
let resolvedCachixBin = which_1.default.sync('cachix', { nothrow: true });
if (resolvedCachixBin) {
core.debug(`Found Cachix executable: ${cachixBin}`);
cachixBin = resolvedCachixBin;
}
else {
core.startGroup('Cachix: installing');
await exec.exec('bash', ['-c', installCommand]);
cachixBin = which_1.default.sync('cachix');
core.debug(`Installed Cachix executable: ${cachixBin}`);
core.endGroup();
}
core.startGroup('Cachix: installing');
await exec.exec('bash', ['-c', installCommand]);
cachixBin = which_1.default.sync('cachix');
core.debug(`Installed Cachix executable: ${cachixBin}`);
core.endGroup();
}
core.saveState('cachixBin', cachixBin);
// Print the executable version.
// Also verifies that the binary exists and is executable.
core.startGroup('Cachix: checking version');
let cachixVersion = await execToVariable(cachixBin, ['--version'])
.then((res) => semver_1.default.coerce(res.split(" ")[1]));
}
core.saveState('cachixBin', cachixBin);
// Print the executable version.
// Also verifies that the binary exists and is executable.
core.startGroup('Cachix: checking version');
let cachixVersion = await execToVariable(cachixBin, ['--version'])
.then((res) => semver_1.default.coerce(res.split(" ")[1]));
core.endGroup();
// For managed signing key and private caches
if (authToken !== "") {
await exec.exec(cachixBin, ['authtoken', authToken]);
}
if (signingKey !== "") {
core.exportVariable('CACHIX_SIGNING_KEY', signingKey);
}
let hasPushTokens = signingKey !== "" || authToken !== "";
core.saveState('hasPushTokens', hasPushTokens);
if (skipAddingSubstituter) {
core.info('Not adding Cachix cache to substituters as skipAddingSubstituter is set to true');
}
else {
core.startGroup(`Cachix: using cache ` + name);
await exec.exec(cachixBin, ['use', name]);
core.endGroup();
// for managed signing key and private caches
if (authToken !== "") {
await exec.exec(cachixBin, ['authtoken', authToken]);
}
if (skipAddingSubstituter) {
core.info('Not adding Cachix cache to substituters as skipAddingSubstituter is set to true');
}
else {
core.startGroup(`Cachix: using cache ` + name);
await exec.exec(cachixBin, ['use', name]);
core.endGroup();
}
if (extraPullNames != "") {
core.startGroup(`Cachix: using extra caches ` + extraPullNames);
const extraPullNameList = extraPullNames.split(',');
for (let itemName of extraPullNameList) {
const trimmedItemName = itemName.trim();
await exec.exec(cachixBin, ['use', trimmedItemName]);
}
if (extraPullNames != "") {
core.startGroup(`Cachix: using extra caches ` + extraPullNames);
const extraPullNameList = extraPullNames.split(',');
for (let itemName of extraPullNameList) {
const trimmedItemName = itemName.trim();
await exec.exec(cachixBin, ['use', trimmedItemName]);
core.endGroup();
}
// Determine the push mode to use
let pushMode = PushMode.None;
if (hasPushTokens && !skipPush) {
if (useDaemon) {
let supportsDaemonInterface = (cachixVersion) ? semver_1.default.gte(cachixVersion, '1.7.0') : false;
let supportsPostBuildHook = await isTrustedUser();
if (!supportsDaemonInterface) {
core.warning(`Cachix Daemon is not supported by this version of Cachix (${cachixVersion}). Ignoring the 'useDaemon' option.`);
}
core.endGroup();
}
if (signingKey !== "") {
core.exportVariable('CACHIX_SIGNING_KEY', signingKey);
}
let supportsDaemonInterface = (cachixVersion) ? semver_1.default.gte(cachixVersion, '1.7.0') : false;
let supportsPostBuildHook = await isTrustedUser();
let hasPushCredentials = signingKey !== "" || authToken !== "";
if (useDaemon && !supportsDaemonInterface) {
core.warning(`Cachix Daemon is not supported by this version of Cachix (${cachixVersion}). Ignoring the 'useDaemon' option.`);
}
if (useDaemon && !supportsPostBuildHook) {
core.warning("This user is not allowed to set the post-build-hook. Ignoring the 'useDaemon' option.");
if (!supportsPostBuildHook) {
core.warning("This user is not allowed to set the post-build-hook. Ignoring the 'useDaemon' option.");
}
pushMode = (supportsDaemonInterface && supportsPostBuildHook) ? PushMode.Daemon : PushMode.StoreScan;
}
if (useDaemon && !hasPushCredentials) {
core.warning("No push credentials found. Ignoring the 'useDaemon' option.");
else {
pushMode = PushMode.StoreScan;
}
let supportsDaemon = supportsDaemonInterface && supportsPostBuildHook && hasPushCredentials;
core.saveState('supportsDaemon', supportsDaemon);
if (useDaemon && supportsDaemon) {
}
core.saveState('pushMode', pushMode);
switch (pushMode) {
case PushMode.Daemon: {
const tmpdir = process.env['RUNNER_TEMP'] ?? os.tmpdir();
const daemonDir = await fs.mkdtemp(path.join(tmpdir, 'cachix-daemon-'));
const daemonLog = (0, node_fs_1.openSync)(`${daemonDir}/daemon.log`, 'a');
Expand Down Expand Up @@ -7846,59 +7868,62 @@ async function setup() {
core.exportVariable(ENV_CACHIX_DAEMON_DIR, daemonDir);
// Detach the daemon process from the current process
daemon.unref();
break;
}
else {
case PushMode.StoreScan: {
// Remember existing store paths
await exec.exec("sh", ["-c", `${__dirname}/list-nix-store.sh > /tmp/store-path-pre-build`]);
break;
}
}
catch (error) {
core.setFailed(`Action failed with error: ${error}`);
default:
break;
}
}
async function upload() {
core.startGroup('Cachix: push');
const cachixBin = core.getState('cachixBin');
const supportsDaemon = core.getState('supportsDaemon') === 'true';
try {
if (skipPush) {
core.info('Pushing is disabled as skipPush is set to true');
}
else if (signingKey !== "" || authToken !== "") {
if (useDaemon && supportsDaemon) {
const daemonDir = process.env[ENV_CACHIX_DAEMON_DIR];
if (!daemonDir) {
core.error('Cachix Daemon not started. Skipping push');
return;
}
const daemonPid = parseInt(await fs.readFile(pidFilePath(daemonDir), { encoding: 'utf8' }));
if (!daemonPid) {
core.error('Failed to find PID of Cachix Daemon. Skipping push.');
return;
}
core.debug(`Found Cachix daemon with pid ${daemonPid}`);
let daemonLog = new tail_1.Tail(`${daemonDir}/daemon.log`, { fromBeginning: true });
daemonLog.on('line', (line) => core.info(line));
try {
core.debug('Waiting for Cachix daemon to exit...');
await exec.exec(cachixBin, ["daemon", "stop", "--socket", `${daemonDir}/daemon.sock`]);
}
finally {
// Wait a bit for the logs to flush through
await new Promise((resolve) => setTimeout(resolve, 1000));
daemonLog.unwatch();
}
const pushMode = core.getState('pushMode');
switch (pushMode) {
case PushMode.None: {
core.info("Pushing is disabled.");
const hasPushTokens = !!core.getState('hasPushTokens');
if (skipPush) {
core.info('skipPush is enabled.');
}
else {
await exec.exec(`${__dirname}/push-paths.sh`, [cachixBin, cachixArgs, name, pathsToPush, pushFilter]);
else if (!hasPushTokens) {
core.info('Missing a Cachix auth token. Provide an authToken and/or signingKey to enable pushing to the cache.');
}
break;
}
else {
core.info('Pushing is disabled because neither signingKey nor authToken are set (or are empty?) in your YAML file.');
case PushMode.Daemon: {
const daemonDir = process.env[ENV_CACHIX_DAEMON_DIR];
if (!daemonDir) {
core.error('Cachix Daemon not started. Skipping push');
break;
}
const daemonPid = parseInt(await fs.readFile(pidFilePath(daemonDir), { encoding: 'utf8' }));
if (!daemonPid) {
core.error('Failed to find PID of Cachix Daemon. Skipping push.');
break;
}
core.debug(`Found Cachix daemon with pid ${daemonPid}`);
let daemonLog = new tail_1.Tail(`${daemonDir}/daemon.log`, { fromBeginning: true });
daemonLog.on('line', (line) => core.info(line));
try {
core.debug('Waiting for Cachix daemon to exit...');
await exec.exec(cachixBin, ["daemon", "stop", "--socket", `${daemonDir}/daemon.sock`]);
}
finally {
// Wait a bit for the logs to flush through
await new Promise((resolve) => setTimeout(resolve, 1000));
daemonLog.unwatch();
}
break;
}
case PushMode.StoreScan: {
await exec.exec(`${__dirname}/push-paths.sh`, [cachixBin, cachixArgs, name, pathsToPush, pushFilter]);
break;
}
}
catch (error) {
core.setFailed(`Action failed with error: ${error}`);
}
core.endGroup();
}
Expand Down Expand Up @@ -8036,16 +8061,21 @@ function partitionUsersAndGroups(mixedUsers) {
}
const isPost = !!core.getState('isPost');
// Main
if (!isPost) {
// Publish a variable so that when the POST action runs, it can determine it should run the cleanup logic.
// This is necessary since we don't have a separate entry point.
core.saveState('isPost', 'true');
setup();
core.debug('Setup done');
}
else {
// Post
upload();
try {
if (!isPost) {
// Publish a variable so that when the POST action runs, it can determine it should run the cleanup logic.
// This is necessary since we don't have a separate entry point.
core.saveState('isPost', 'true');
setup();
core.debug('Setup done');
}
else {
// Post
upload();
}
}
catch (error) {
core.setFailed(`Action failed with error: ${error}`);
}


Expand Down
Loading

0 comments on commit fb5cb83

Please sign in to comment.