Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .envrc.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use flake
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/.envrc
/node_modules/
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,30 @@
# OpenMS CI Tools

Scripts used in the OpenMS CI/CD system.

## Actions

To build the actions:

```sh
npm ci
npm run build
```

### Cache via rsync

Push/pull a directory via rsync.

To use, put something like this in your workflow YAML:

```yml
- uses: OpenMS/ci-tools/actions/rsync-cache@XXXX
with:
name: vcpkg-cache
path: ${{ github.workspace }}/.vcpkg-cache
key: ${{ runner.os }}-${{ runner.arch }}-${{ matrix.compiler }}
ssh_key: ${{ secrets.ARCHIVE_RRSYNC_SSH }}
ssh_host: ${{ secrets.ARCHIVE_RRSYNC_HOST }}
ssh_port: ${{ secrets.ARCHIVE_RRSYNC_PORT }}
ssh_user: ${{ secrets.ARCHIVE_RRSYNC_USER }}
```
29 changes: 29 additions & 0 deletions actions/rsync-cache/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: "Cache via rsync"
description: "Cache a directory to a private server using rsync"
inputs:
name:
description: "A name to use for the sub-directory on the remote server"
required: true
path:
description: "An absolute path to a directory that should be cached"
required: true
key:
description: "A cache key, which is essentially a sub-directory name on the remote server"
required: true
ssh_key:
description: "The contents of an SSH private key"
required: true
ssh_host:
description: "The host name of the remote server"
required: true
ssh_port:
description: "The port to use for SSH"
required: false
ssh_user:
description: "The remote SSH user"
required: true
runs:
using: 'node24'
main: dist/restore.js
post: dist/commit.js
post-if: "success()"
13 changes: 13 additions & 0 deletions actions/rsync-cache/src/commit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Rsync from "./rsync.js";
import * as core from "@actions/core";

async function main() {
try {
const rsync = new Rsync();
await rsync.push();
} catch (error) {
core.setFailed(error.message);
}
}

main();
13 changes: 13 additions & 0 deletions actions/rsync-cache/src/restore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Rsync from "./rsync.js";
import * as core from "@actions/core";

async function main() {
try {
const rsync = new Rsync();
await rsync.pull();
} catch (error) {
core.setFailed(error.message);
}
}

main();
114 changes: 114 additions & 0 deletions actions/rsync-cache/src/rsync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import * as core from "@actions/core";
import * as github from "@actions/github";
import * as exec from "@actions/exec";
import * as io from "@actions/io";

export default class Rsync {
constructor() {
// Cache settings:
this.name = core.getInput("name", {required: true});
this.path = core.getInput("path", {required: true});
this.key = core.getInput("key", {required: true});

// SSH settings:
this.ssh_key_dir = os.homedir() + "/.ssh"
this.ssh_key_file = this.ssh_key_dir + "/private.key";

this.ssh_key = core.getInput("ssh_key", {required: true});
this.ssh_host = core.getInput("ssh_host", {required: true});
this.ssh_port = core.getInput("ssh_port", {required: false});
this.ssh_user = core.getInput("ssh_user", {required: true});
this.ssh_dir = `${this.ssh_user}@${this.ssh_host}:${this.name}/${this.key}/`;

this._validate();
this._set_vars();
}

// Ensure all our settings look good.
_validate() {
if (!this.ssh_port) {
this.ssh_port = "22"
}

if (!path.isAbsolute(this.path)) {
throw new Error(`path to cache must be absolute: ${this.path}`)
}
}

// Prepare some variables.
_set_vars() {
// The SSH command line.
const ssh_command = [
"ssh",
"-i", this.ssh_key_file,
"-p", this.ssh_port,
"-o", "StrictHostKeyChecking=accept-new",
].join(" ")

// The base rsync command:
this.command = "rsync"
this.args = [
"-e", ssh_command,
"--progress",
"--human-readable",
"--archive",
"--verbose",
"--compress",

// We need this so we can create sub-directories, one for each
// cache key (OS, architecture, compiler, etc.)
"--mkpath",
];
}

// Run rsync with extra arguments and return its exit code.
async run(additional_arguments=[]) {
await io.mkdirP(this.path);
await io.mkdirP(this.ssh_key_dir);
await fs.writeFile(this.ssh_key_file, this.ssh_key, 'utf8');
await fs.chmod(this.ssh_key_file, 0o600);

// Run rsync.
const options = {ignoreReturnCode: true};
const exit_code = await exec.exec(this.command,
[...this.args, ...additional_arguments], options);

// Remove the key file.
await fs.unlink(this.ssh_key_file);

// Let callers decide what to do with the exit code.
return exit_code;
}

// Pull the cache from the remote server.
async pull() {
const rsync_args = [
this.ssh_dir, // FROM
this.path, // TO
];

const exit_code = await this.run(rsync_args);

if (exit_code != 0) {
core.notice(`Cache ${this.name}: error fetching remote cache`);
}
}

// Push the cache to the remote server.
async push() {
const rsync_args = [
"--delete-before",
this.path, // FROM
this.ssh_dir, // TO
];

const exit_code = await this.run(rsync_args);

if (exit_code != 0) {
core.notice(`Cache ${this.name}: error pushing to remote cache`);
}
}
}
61 changes: 61 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
};

outputs =
inputs:
inputs.flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"x86_64-linux"
"aarch64-darwin"
"x86_64-darwin"
];

perSystem =
{ pkgs, ... }:
{
devShells.default = pkgs.mkShell {
nativeBuildInputs = with pkgs; [
nodejs_24
shellcheck
shfmt
];
};
};
};
}
Loading