mirror of
https://github.com/ryanccn/attic-action.git
synced 2024-11-19 22:16:35 +00:00
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
|
||||
|
||||
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:
|
||||
description: "attic cache"
|
||||
required: true
|
||||
paths:
|
||||
description: "paths to upload"
|
||||
required: true
|
||||
skip-push:
|
||||
description: "set to true to disable pushing to the cache"
|
||||
required: false
|
||||
token:
|
||||
description: "attic token"
|
||||
required: false
|
||||
|
@ -18,3 +18,4 @@ inputs:
|
|||
runs:
|
||||
using: "node16"
|
||||
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",
|
||||
"description": "Cache Nix derivations with attic",
|
||||
"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": [],
|
||||
"author": "Ryan Cao <hello@ryanccn.dev>",
|
||||
|
@ -16,6 +17,7 @@
|
|||
"devDependencies": {
|
||||
"@types/node": "^16.18.38",
|
||||
"esbuild": "^0.18.14",
|
||||
"prettier": "3.0.0",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"packageManager": "pnpm@8.6.9"
|
||||
|
|
|
@ -22,6 +22,9 @@ devDependencies:
|
|||
esbuild:
|
||||
specifier: ^0.18.14
|
||||
version: 0.18.14
|
||||
prettier:
|
||||
specifier: 3.0.0
|
||||
version: 3.0.0
|
||||
typescript:
|
||||
specifier: ^5.1.6
|
||||
version: 5.1.6
|
||||
|
@ -299,6 +302,12 @@ packages:
|
|||
ufo: 1.1.2
|
||||
dev: false
|
||||
|
||||
/prettier@3.0.0:
|
||||
resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/tunnel@0.0.6:
|
||||
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
|
||||
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 { configure } from "./stages/configure";
|
||||
import { push } from "./stages/push";
|
||||
|
||||
(async () => {
|
||||
const main = async () => {
|
||||
await install();
|
||||
await configure();
|
||||
await push();
|
||||
})().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
};
|
||||
|
||||
main();
|
||||
|
|
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 { getStorePaths } from "../utils";
|
||||
|
||||
export const configure = async () => {
|
||||
startGroup("Configure attic");
|
||||
const endpoint = getInput("endpoint");
|
||||
const token = getInput("token");
|
||||
core.startGroup("Configure attic");
|
||||
|
||||
await exec("attic", ["login", "--set-default", "ci", endpoint, token]);
|
||||
endGroup();
|
||||
try {
|
||||
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 { fetch } from "ofetch";
|
||||
|
||||
|
@ -7,23 +7,31 @@ import { tmpdir } from "node:os";
|
|||
import { join } from "node:path";
|
||||
|
||||
export const install = async () => {
|
||||
startGroup("Install attic");
|
||||
core.startGroup("Install attic");
|
||||
|
||||
core.info("Installing attic");
|
||||
const installScript = await fetch(
|
||||
"https://raw.githubusercontent.com/zhaofengli/attic/main/.github/install-attic-ci.sh"
|
||||
).then((r) => {
|
||||
if (!r.ok)
|
||||
throw new Error(
|
||||
`Failed to fetch install script: ${r.status} ${r.statusText}`
|
||||
);
|
||||
if (!r.ok) {
|
||||
core.setFailed(`Action failed with error: ${r.statusText}`);
|
||||
core.endGroup();
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
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 exec("bash", [installScriptPath]);
|
||||
await writeFile(installScriptPath, installScript);
|
||||
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 { getStorePaths } from "../utils";
|
||||
|
||||
export const push = async () => {
|
||||
const cache = getInput("cache");
|
||||
const paths = getMultilineInput("paths");
|
||||
core.startGroup("Push to Attic");
|
||||
|
||||
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