Skip to content

Commit

Permalink
first
Browse files Browse the repository at this point in the history
  • Loading branch information
traviskuhl committed Jun 9, 2024
0 parents commit a28beab
Show file tree
Hide file tree
Showing 43 changed files with 2,198 additions and 0 deletions.
47 changes: 47 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Create and publish a Docker image

on:
push:
branches: ["release"]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
id: push
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Generate artifact attestation
uses: actions/attest-build-provenance@v1
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
tmp*/
.DS_Store
node_modules
*.log
*.local
.env
.cache
3 changes: 3 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["denoland.vscode-deno"]
}
20 changes: 20 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"deno.enablePaths": ["."],
"deno.lint": true,
"eslint.workingDirectories": [
{
"mode": "auto"
}
],
"[typescript]": {
"editor.defaultFormatter": "denoland.vscode-deno",
"editor.formatOnSave": true,
"editor.formatOnPaste": true
},
"[javascript]": {
"editor.defaultFormatter": "denoland.vscode-deno",
"editor.formatOnSave": true,
"editor.formatOnPaste": true
},
"editor.tabSize": 2
}
26 changes: 26 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
FROM amazonlinux:2


RUN yum -y update \
# systemd is not a hard requirement for Amazon ECS Anywhere, but the installation script currently only supports systemd to run.
# Amazon ECS Anywhere can be used without systemd, if you set up your nodes and register them into your ECS cluster **without** the installation script.
&& yum -y install systemd unzip tar xz curl \
&& yum clean all

RUN groupadd -g 3982 -o elwood_runner
RUN useradd -m -u 3982 -g 3982 -o -s /bin/bash elwood_runner

RUN mkdir -p /elwood/runner/bin \
/elwood/runner/workspace \
/elwood/runner/workspace-bin

RUN curl -fsSL https://deno.land/install.sh | DENO_DIR=/elwood/runner/deno-data DENO_INSTALL=/elwood/runner /bin/sh

RUN chown -R elwood_runner:elwood_runner /elwood/runner/workspace

ENV ELWOOD_RUNNER_ROOT /elwood/runner
ENV ELWOOD_RUNNER_WORKSPACE_DIR /elwood/runner/workspace
ENV ELWOOD_RUNNER_EXECUTION_UID 3982
ENV ELWOOD_RUNNER_EXECUTION_GID 3982

RUN tar --version
16 changes: 16 additions & 0 deletions actions/_core/args.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export function get(name: string, strict = true): string | undefined {
const envName = `ARG_${name.toUpperCase()}`;

if (strict && !Deno.env.get(envName)) {
throw new Error(`Missing required environment variable: ${envName}`);
}

if (
Deno.permissions.querySync({ name: "env", variable: envName })
.state !== "granted"
) {
return "";
}

return Deno.env.get(envName) as string;
}
25 changes: 25 additions & 0 deletions actions/_core/command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export async function execute(
bin: string,
options: Deno.CommandOptions = {},
): Promise<Deno.CommandOutput> {
const cmd = await create(bin, {
stdout: "inherit",
stderr: "inherit",
...options,
});

return await cmd.output();
}

export async function create(
bin: string,
options: Deno.CommandOptions = {},
): Promise<Deno.Command> {
return await Promise.resolve(
new Deno.Command(bin, {
stdout: "inherit",
stderr: "inherit",
...options,
}),
);
}
105 changes: 105 additions & 0 deletions actions/_core/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { toWritableStream } from "../deps.ts";
import { normalize } from "./path.ts";

export const native = globalThis.fetch;

// You should be using `request` not `fetch` for everything
// this makes that a bit harder to accidentally use `fetch
// deno-lint-ignore no-unused-vars -- intentional
const fetch = undefined;

export type RequestOptions = RequestInit & {
saveTo?: string;
asStream?: boolean;
};

export type Response<D = unknown> = {
data: D | null;
headers: Record<string, string>;
error: Error | undefined;
};

export async function request<T = unknown>(
url: string,
init: RequestOptions,
): Promise<Response<T>> {
const response = await native(url, init);
const headers = headersToObject(response.headers);

if (!response.ok) {
return {
data: null,
headers,
error: new Error(response.statusText),
};
}

if (!response.body) {
return {
data: null,
headers,
error: new Error("No body in response"),
};
}

let data: T | null = null;

if (init.asStream) {
return {
data: response.body as T,
headers,
error: undefined,
};
}

if (init.saveTo) {
const file = await Deno.open(await normalize(init.saveTo), {
write: true,
create: true,
});
const writableStream = toWritableStream(file);
await response.body.pipeTo(writableStream);

data = {
path: init.saveTo,
} as T;
} else if (
response.headers.get("Content-Type")?.includes("application/json")
) {
data = await response.json() as T;
} else {
data = await response.text() as T;
}

return {
data,
headers,
error: undefined,
};
}

function _methodProxy(method: RequestInit["method"]): typeof request {
return (url: string, options: Omit<RequestOptions, "method">) =>
request(url, { ...options, method });
}

export const get = _methodProxy("GET");
export const post = _methodProxy("POST");
export const put = _methodProxy("PUT");
export const patch = _methodProxy("PATCH");
export const del = _methodProxy("DELETE");

function headersToObject(headers: Headers): Record<string, string> {
const obj: Record<string, string> = {};

for (const [key, value] of headers.entries()) {
obj[key] = value;
}

return Array.from(headers.entries()).reduce((acc, [key, value]) => {
return {
...acc,
[key]: value,
};
}, {});
}
29 changes: 29 additions & 0 deletions actions/_core/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { type FilePathOrUrl, normalize } from "./path.ts";

export async function copy(src: FilePathOrUrl, dest: FilePathOrUrl) {
await Deno.copyFile(
await normalize(src),
await normalize(dest),
);
}

export async function mkdir(path: FilePathOrUrl, recursive = true) {
await Deno.mkdir(
await normalize(path),
{ recursive },
);
}

export async function rename(from: FilePathOrUrl, to: FilePathOrUrl) {
return await Deno.rename(
await normalize(from),
await normalize(to),
);
}

export async function remove(path: FilePathOrUrl, recursive = false) {
return await Deno.remove(
await normalize(path),
{ recursive },
);
}
82 changes: 82 additions & 0 deletions actions/_core/input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { normalize } from "./path.ts";

export function get(name: string, strict = true): string {
const inputEnvName = `INPUT_${name.toUpperCase()}`;

if (strict && !Deno.env.get(inputEnvName)) {
throw new Error(`Missing required environment variable: ${inputEnvName}`);
}

// if they don't strictly require the input, make sure we have permission
// to read it before trying to read it
if (
Deno.permissions.querySync({ name: "env", variable: inputEnvName })
.state !== "granted"
) {
return "";
}

return Deno.env.get(inputEnvName) as string;
}

export function getOptional<T = unknown>(
name: string,
fallback: T | undefined = undefined,
): string | T | undefined {
try {
const value = get(name, false);

return value === "" ? fallback : value;
} catch {
return fallback;
}
}

export function getJson<T = unknown>(
name: string,
strict = true,
): Record<string, T> | T[] {
const value = get(name, strict);

if (value && value.startsWith("json:")) {
return JSON.parse(value.substring(5));
}

throw new Error(`Input ${name} is not valid JSON: ${value}`);
}

export function getOptionalJson<T = unknown>(
name: string,
fallback: T | undefined = undefined,
): ReturnType<typeof getJson> | T | undefined {
try {
return getJson<T>(name, false);
} catch {
return fallback;
}
}

export function getBoolean(name: string, strict = true): boolean {
const value = get(name, strict);

if (!value) {
return false;
}

switch (value.toLowerCase()) {
case "1":
case "yes":
case "true":
return true;

default:
return false;
}
}

export async function getNormalizedPath(
name: string,
strict = true,
): Promise<string> {
return await normalize(get(name, strict));
}
Loading

0 comments on commit a28beab

Please sign in to comment.