forked from Mirror/attic-action
feat: auto push store paths and add CI (#1)
* feat: add flake devShell * chore: add prettier * fix: respect cache input during configure * feat: auto push paths * feat: add test workflow * chore: update index.js * refactors * pnpm lock * more refactors * remove copying --------- Co-authored-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com>
This commit is contained in:
parent
6e24bce2f4
commit
5dc7b671af
17 changed files with 231 additions and 36 deletions
32
.github/workflows/test.yml
vendored
Normal file
32
.github/workflows/test.yml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
name: "Test"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
pull_request:
|
||||||
|
workflow_call:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-cache:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest]
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Nix
|
||||||
|
uses: cachix/install-nix-action@v22
|
||||||
|
|
||||||
|
- name: Setup Attic Cache
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
endpoint: ${{ secrets.ATTIC_ENDPOINT }}
|
||||||
|
cache: ${{ secrets.ATTIC_CACHE }}
|
||||||
|
token: ${{ secrets.ATTIC_TOKEN }}
|
||||||
|
|
||||||
|
- run: nix-build test.nix
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,3 +1,7 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
|
# nix
|
||||||
|
.direnv/
|
||||||
|
.envrc
|
||||||
|
|
4
.prettierignore
Normal file
4
.prettierignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.direnv/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
pnpm-lock.yaml
|
|
@ -8,9 +8,9 @@ inputs:
|
||||||
cache:
|
cache:
|
||||||
description: "attic cache"
|
description: "attic cache"
|
||||||
required: true
|
required: true
|
||||||
paths:
|
skip-push:
|
||||||
description: "paths to upload"
|
description: "set to true to disable pushing to the cache"
|
||||||
required: true
|
required: false
|
||||||
token:
|
token:
|
||||||
description: "attic token"
|
description: "attic token"
|
||||||
required: false
|
required: false
|
||||||
|
@ -18,3 +18,4 @@ inputs:
|
||||||
runs:
|
runs:
|
||||||
using: "node16"
|
using: "node16"
|
||||||
main: "dist/index.js"
|
main: "dist/index.js"
|
||||||
|
post: "dist/post.js"
|
||||||
|
|
11
dist/index.js
vendored
11
dist/index.js
vendored
File diff suppressed because one or more lines are too long
9
dist/post.js
vendored
Normal file
9
dist/post.js
vendored
Normal file
File diff suppressed because one or more lines are too long
26
flake.lock
Normal file
26
flake.lock
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1689534811,
|
||||||
|
"narHash": "sha256-jnSUdzD/414d94plCyNlvTJJtiTogTep6t7ZgIKIHiE=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "6cee3b5893090b0f5f0a06b4cf42ca4e60e5d222",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
36
flake.nix
Normal file
36
flake.nix
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
description = "";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "nixpkgs/nixos-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{ nixpkgs
|
||||||
|
, ...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
mkSystems = sys: builtins.map (arch: "${arch}-${sys}") [ "x86_64" "aarch64" ];
|
||||||
|
systems =
|
||||||
|
mkSystems "linux"
|
||||||
|
++ mkSystems "darwin";
|
||||||
|
|
||||||
|
forAllSystems = nixpkgs.lib.genAttrs systems;
|
||||||
|
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
|
||||||
|
|
||||||
|
forEachSystem = fn:
|
||||||
|
forAllSystems (s: fn nixpkgsFor.${s});
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells = forEachSystem (pkgs: {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
actionlint
|
||||||
|
nodePackages.pnpm
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
formatter = forEachSystem (p: p.nixpkgs-fmt);
|
||||||
|
};
|
||||||
|
}
|
|
@ -3,7 +3,8 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Cache Nix derivations with attic",
|
"description": "Cache Nix derivations with attic",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "esbuild src/index.ts --outdir=dist --platform=node --format=cjs --bundle --minify-whitespace --minify-syntax"
|
"build": "esbuild src/index.ts src/post.ts --outdir=dist --platform=node --format=cjs --bundle --minify-whitespace --minify-syntax",
|
||||||
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Ryan Cao <hello@ryanccn.dev>",
|
"author": "Ryan Cao <hello@ryanccn.dev>",
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^16.18.38",
|
"@types/node": "^16.18.38",
|
||||||
"esbuild": "^0.18.14",
|
"esbuild": "^0.18.14",
|
||||||
|
"prettier": "3.0.0",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "^5.1.6"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@8.6.9"
|
"packageManager": "pnpm@8.6.9"
|
||||||
|
|
|
@ -22,6 +22,9 @@ devDependencies:
|
||||||
esbuild:
|
esbuild:
|
||||||
specifier: ^0.18.14
|
specifier: ^0.18.14
|
||||||
version: 0.18.14
|
version: 0.18.14
|
||||||
|
prettier:
|
||||||
|
specifier: 3.0.0
|
||||||
|
version: 3.0.0
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.1.6
|
specifier: ^5.1.6
|
||||||
version: 5.1.6
|
version: 5.1.6
|
||||||
|
@ -299,6 +302,12 @@ packages:
|
||||||
ufo: 1.1.2
|
ufo: 1.1.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/prettier@3.0.0:
|
||||||
|
resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/tunnel@0.0.6:
|
/tunnel@0.0.6:
|
||||||
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
|
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
|
||||||
engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
|
engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
|
||||||
|
|
11
src/index.ts
11
src/index.ts
|
@ -1,12 +1,9 @@
|
||||||
import { install } from "./stages/install";
|
import { install } from "./stages/install";
|
||||||
import { configure } from "./stages/configure";
|
import { configure } from "./stages/configure";
|
||||||
import { push } from "./stages/push";
|
|
||||||
|
|
||||||
(async () => {
|
const main = async () => {
|
||||||
await install();
|
await install();
|
||||||
await configure();
|
await configure();
|
||||||
await push();
|
};
|
||||||
})().catch((e) => {
|
|
||||||
console.error(e);
|
main();
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|
7
src/post.ts
Normal file
7
src/post.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { push } from "./stages/push";
|
||||||
|
|
||||||
|
const main = async () => {
|
||||||
|
await push();
|
||||||
|
};
|
||||||
|
|
||||||
|
main();
|
|
@ -1,11 +1,24 @@
|
||||||
import { getInput, startGroup, endGroup } from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import { exec } from "@actions/exec";
|
import { exec } from "@actions/exec";
|
||||||
|
import { getStorePaths } from "../utils";
|
||||||
|
|
||||||
export const configure = async () => {
|
export const configure = async () => {
|
||||||
startGroup("Configure attic");
|
core.startGroup("Configure attic");
|
||||||
const endpoint = getInput("endpoint");
|
|
||||||
const token = getInput("token");
|
|
||||||
|
|
||||||
await exec("attic", ["login", "--set-default", "ci", endpoint, token]);
|
try {
|
||||||
endGroup();
|
const endpoint = core.getInput("endpoint");
|
||||||
|
const cache = core.getInput("cache");
|
||||||
|
const token = core.getInput("token");
|
||||||
|
|
||||||
|
core.info("Logging in to attic cache");
|
||||||
|
await exec("attic", ["login", "--set-default", cache, endpoint, token]);
|
||||||
|
|
||||||
|
core.info("Collecting store paths before build");
|
||||||
|
const paths = await getStorePaths();
|
||||||
|
core.saveState("initial-paths", JSON.stringify(paths));
|
||||||
|
} catch (e) {
|
||||||
|
core.setFailed(`Action failed with error: ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.endGroup();
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { startGroup, endGroup } from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import { exec } from "@actions/exec";
|
import { exec } from "@actions/exec";
|
||||||
import { fetch } from "ofetch";
|
import { fetch } from "ofetch";
|
||||||
|
|
||||||
|
@ -7,23 +7,31 @@ import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
|
|
||||||
export const install = async () => {
|
export const install = async () => {
|
||||||
startGroup("Install attic");
|
core.startGroup("Install attic");
|
||||||
|
|
||||||
|
core.info("Installing attic");
|
||||||
const installScript = await fetch(
|
const installScript = await fetch(
|
||||||
"https://raw.githubusercontent.com/zhaofengli/attic/main/.github/install-attic-ci.sh"
|
"https://raw.githubusercontent.com/zhaofengli/attic/main/.github/install-attic-ci.sh"
|
||||||
).then((r) => {
|
).then((r) => {
|
||||||
if (!r.ok)
|
if (!r.ok) {
|
||||||
throw new Error(
|
core.setFailed(`Action failed with error: ${r.statusText}`);
|
||||||
`Failed to fetch install script: ${r.status} ${r.statusText}`
|
core.endGroup();
|
||||||
);
|
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
return r.text();
|
return r.text();
|
||||||
});
|
});
|
||||||
|
|
||||||
const installScriptPath = join(tmpdir(), "install-attic-ci.sh");
|
try {
|
||||||
|
const installScriptPath = join(tmpdir(), "install-attic-ci.sh");
|
||||||
|
|
||||||
await writeFile(installScriptPath, installScript);
|
await writeFile(installScriptPath, installScript);
|
||||||
await exec("bash", [installScriptPath]);
|
core.info("Running install script");
|
||||||
|
await exec("bash", [installScriptPath]);
|
||||||
|
} catch (e) {
|
||||||
|
core.setFailed(`Action failed with error: ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
endGroup();
|
core.endGroup();
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,27 @@
|
||||||
import { getInput, getMultilineInput } from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import { exec } from "@actions/exec";
|
import { exec } from "@actions/exec";
|
||||||
|
import { getStorePaths } from "../utils";
|
||||||
|
|
||||||
export const push = async () => {
|
export const push = async () => {
|
||||||
const cache = getInput("cache");
|
core.startGroup("Push to Attic");
|
||||||
const paths = getMultilineInput("paths");
|
|
||||||
|
|
||||||
await exec("attic", ["push", cache, ...paths]);
|
try {
|
||||||
|
const skipPush = core.getInput("skip-push");
|
||||||
|
if (skipPush === "true") {
|
||||||
|
core.info("Pushing to cache is disabled by skip-push");
|
||||||
|
} else {
|
||||||
|
const cache = core.getInput("cache");
|
||||||
|
|
||||||
|
core.info("Pushing to cache");
|
||||||
|
const oldPaths = JSON.parse(core.getState("initial-paths")) as string[];
|
||||||
|
const newPaths = await getStorePaths();
|
||||||
|
const addedPaths = newPaths.filter((p) => !oldPaths.includes(p));
|
||||||
|
|
||||||
|
await exec("attic", ["push", cache, ...addedPaths]);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
core.setFailed(`Action failed with error: ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.endGroup();
|
||||||
};
|
};
|
||||||
|
|
21
src/utils.ts
Normal file
21
src/utils.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { exec } from "@actions/exec";
|
||||||
|
import { Writable } from "node:stream";
|
||||||
|
|
||||||
|
const streamToString = (stream: Writable): Promise<string> => {
|
||||||
|
const chunks: Buffer[] = [];
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
||||||
|
stream.on("error", (err) => reject(err));
|
||||||
|
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStorePaths = async () => {
|
||||||
|
const outStream = new Writable();
|
||||||
|
await exec("nix", ["path-info", "--all"], { outStream });
|
||||||
|
const paths = await streamToString(outStream)
|
||||||
|
.then((res) => res.split("\n"))
|
||||||
|
.then((paths) => paths.filter(Boolean));
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
};
|
7
test.nix
Normal file
7
test.nix
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
let
|
||||||
|
pkgs = import <nixpkgs> { };
|
||||||
|
time = with builtins; toString currentTime;
|
||||||
|
in
|
||||||
|
pkgs.runCommand "${time}-test" { } ''
|
||||||
|
echo "${time}" > $out
|
||||||
|
''
|
Loading…
Reference in a new issue