Skip to content

Commit

Permalink
Merge pull request #10 from fluentci-io/feat/flu-13-cli-publish
Browse files Browse the repository at this point in the history
feat: implement `publish` subcommand
  • Loading branch information
tsirysndr authored Dec 23, 2023
2 parents a41d70f + c1a4a44 commit c025fba
Show file tree
Hide file tree
Showing 10 changed files with 537 additions and 72 deletions.
256 changes: 222 additions & 34 deletions deno.lock

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,16 @@ export { load } from "https://deno.land/std@0.205.0/dotenv/mod.ts";
export { Secret } from "https://deno.land/x/cliffy@v1.0.0-rc.3/prompt/secret.ts";
import dir from "https://deno.land/x/dir@1.5.2/mod.ts";
export { dir };
export { walkSync } from "https://deno.land/std@0.208.0/fs/walk.ts";
export type { WalkEntry } from "https://deno.land/std@0.208.0/fs/walk.ts";
export {
BlobReader,
BlobWriter,
TextReader,
TextWriter,
ZipReader,
ZipWriter,
} from "https://deno.land/x/zipjs@v2.7.32/index.js";
import introspect from "https://cdn.jsdelivr.net/gh/fluentci-io/daggerverse@main/deno-sdk/sdk/src/mod/introspect.ts";
export { introspect };
export { wait } from "https://deno.land/x/wait@0.1.13/mod.ts";
5 changes: 5 additions & 0 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import cache from "./src/cmd/cache.ts";
import doctor from "./src/cmd/doctor.ts";
import showEnvs from "./src/cmd/env.ts";
import login from "./src/cmd/login.ts";
import publish from "./src/cmd/publish.ts";
import { brightGreen } from "./deps.ts";

export async function main() {
Expand Down Expand Up @@ -193,6 +194,10 @@ export async function main() {
.action(async function () {
await login();
})
.command("publish", "Publish a pipeline to FluentCI Registry")
.action(async function () {
await publish();
})
.parse(Deno.args);
}

Expand Down
2 changes: 1 addition & 1 deletion src/cmd/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async function docs(

const result = await fetch(`${BASE_URL}/pipeline/${pipeline}`);
const data = await result.json();
if (!data.github_url) {
if (!data.github_url && !data.version) {
console.log(
`Pipeline template ${green('"')}${green(pipeline)}${green(
'"'
Expand Down
120 changes: 85 additions & 35 deletions src/cmd/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,47 +20,27 @@ async function init(
_name?: string
) {
template = template || "base";
if (!template.endsWith("_pipeline")) {
template = template + "_pipeline";
}

const result = await fetch(`${BASE_URL}/pipeline/${template}`);
const data = await result.json();
if (data.github_url) {
const archiveUrl =
data.version && data.version.startsWith("v")
? `${data.github_url.replace(
"https://github.com",
"https://codeload.github.com"
)}/zip/refs/tags/${data.version}`
: `${data.github_url.replace(
"https://github.com",
"https://api.github.com/repos"
)}/zipball/${data.version || data.default_branch}`;

await download(archiveUrl, template);

const repoName = data.github_url.split("/").pop();

let outputDir = `${repoName}-${data.version.replace("v", "")}`;

if (data.directory) {
outputDir += `/${data.directory}`;
outputDir = `${data.owner}-${outputDir}`;
}
let result = await fetch(`${BASE_URL}/pipeline/${template}`);
let data = await result.json();

await copyDir(outputDir, standalone ? "." : ".fluentci");

if (data.directory) {
outputDir = outputDir.split("/")[0];
if (data.version) {
if (
await downloadTemplateFromRegistry(template, data.version, standalone)
) {
return;
}
}

await Deno.remove(outputDir, { recursive: true });
if (!template.endsWith("_pipeline")) {
template += "_pipeline";
}

if (!standalone) {
await setupDevbox();
}
result = await fetch(`${BASE_URL}/pipeline/${template}`);
data = await result.json();

if (data.github_url) {
await downloadTemplateFromGithub(data, template, standalone);
return;
}

Expand Down Expand Up @@ -141,4 +121,74 @@ function exists(file: string): boolean {
}
}

async function downloadTemplateFromGithub(
data: {
github_url: string;
version: string;
default_branch: string;
directory?: string;
owner: string;
},
template: string,
standalone?: boolean
) {
const archiveUrl =
data.version && data.version.startsWith("v")
? `${data.github_url.replace(
"https://github.com",
"https://codeload.github.com"
)}/zip/refs/tags/${data.version}`
: `${data.github_url.replace(
"https://github.com",
"https://api.github.com/repos"
)}/zipball/${data.version || data.default_branch}`;

await download(archiveUrl, template);

const repoName = data.github_url.split("/").pop();

let outputDir = `${repoName}-${data.version.replace("v", "")}`;

if (data.directory) {
outputDir += `/${data.directory}`;
outputDir = `${data.owner}-${outputDir}`;
}

await copyDir(outputDir, standalone ? "." : ".fluentci");

if (data.directory) {
outputDir = outputDir.split("/")[0];
}

await Deno.remove(outputDir, { recursive: true });

if (!standalone) {
await setupDevbox();
}
}

async function downloadTemplateFromRegistry(
template: string,
version: string,
standalone?: boolean
) {
const status = await fetch(
`https://pkg.fluentci.io/${template}@${version}`
).then((res) => res.status);
if (status === 200) {
await download(`https://pkg.fluentci.io/${template}@${version}`, template);

const outputDir = `${template}/${version}`;
await copyDir(outputDir, standalone ? "." : ".fluentci");

await Deno.remove(template, { recursive: true });

if (!standalone) {
await setupDevbox();
}
return true;
}
return false;
}

export default init;
3 changes: 2 additions & 1 deletion src/cmd/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ async function listJobs(pipeline = ".") {

const result = await fetch(`${BASE_URL}/pipeline/${pipeline}`);
const data = await result.json();
if (!data.github_url) {

if (!data.github_url && !data.version) {
console.log(
`Pipeline template ${green('"')}${green(pipeline)}${green(
'"'
Expand Down
122 changes: 122 additions & 0 deletions src/cmd/publish.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {
walkSync,
ZipWriter,
BlobWriter,
brightGreen,
wait,
} from "../../deps.ts";
import { isLogged, getAccessToken } from "../utils.ts";
import { validatePackage, validateConfigFiles } from "../validate.ts";
import "https://deno.land/x/xhr@0.1.0/mod.ts";

const REGISTRY_URL = "https://whole-badger-28.deno.dev";

const publish = async () => {
if (!(await isLogged())) {
console.log(
`FLUENTCI_ACCESS_TOKEN is not set, Please login first with ${brightGreen(
"`fluentci login`"
)}`
);
Deno.exit(1);
}

const accessToken = getAccessToken();

const entries = walkSync(".", {
skip: parseIgnoredFiles(),
});
const paths = [];

for await (const entry of entries) {
paths.push({
path: entry.path,
isFile: entry.isFile,
});
}

if (paths.length === 0) {
console.error("No files found in the current directory");
Deno.exit(1);
}

if (paths.length > 1000) {
console.error(
`A FluentCI package can contain a maximum of ${brightGreen(
"1000 files"
)}, found ${paths.length}`
);
Deno.exit(1);
}

if (!validatePackage(paths.map((x) => x.path))) {
console.error(
`A valid FluentCI package must contain ${brightGreen(
"mod.ts"
)} and ${brightGreen("dagger.json")}`
);
Deno.exit(1);
}

validateConfigFiles();

const blobWriter = new BlobWriter("application/zip");
const zipWriter = new ZipWriter(blobWriter);

for await (const { path, isFile } of paths) {
console.log(path);
if (isFile) {
const file = await Deno.open(path);
await zipWriter.add(path, file);
}
}

await zipWriter.close();

const spinner = wait("Publishing package...").start();

const blob = await blobWriter.getData();
const xhr = new XMLHttpRequest();
xhr.open("POST", REGISTRY_URL);
xhr.setRequestHeader("Content-Type", "application/zip");
xhr.setRequestHeader("Authorization", `Bearer ${accessToken}`);

xhr.onload = function () {
if (xhr.status != 200) {
spinner.fail(`Failed to publish package, ${xhr.responseText}`);
} else {
spinner.succeed(brightGreen(" Package published successfully"));
}
};

xhr.send(blob);
};

const parseIgnoredFiles = () => {
let ignoredFilesArray: RegExp[] = [
new RegExp(".git"),
new RegExp(".fluentci"),
];
try {
// verify if .fluentciignore exists
if (Deno.statSync(".fluentciignore").isFile) {
const ignoredFiles = Deno.readTextFileSync(".fluentciignore");
ignoredFilesArray = ignoredFilesArray.concat(
ignoredFiles.split("\n").map((file) => new RegExp(file))
);
}
} catch (_e) {
// do nothing
}

try {
const ignoredFiles = Deno.readTextFileSync(".gitignore");
return ignoredFilesArray.concat(
ignoredFiles.split("\n").map((file: string) => new RegExp(file))
);
} catch (_e) {
return ignoredFilesArray;
}
};

export default publish;
2 changes: 1 addition & 1 deletion src/cmd/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ async function run(

const result = await fetch(`${BASE_URL}/pipeline/${pipeline}`);
const data = await result.json();
if (!data.github_url) {
if (!data.github_url && !data.version) {
console.log(
`Pipeline template ${green('"')}${green(pipeline)}${green(
'"'
Expand Down
36 changes: 36 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { dir } from "../deps.ts";

export async function isLogged(): Promise<boolean> {
if (Deno.env.get("FLUENTCI_ACCESS_TOKEN")) {
const accessToken = Deno.env.get("FLUENTCI_ACCESS_TOKEN");
const status = await fetch(
`https://api.fluentci.io/validate?token=${accessToken}`
).then((res) => res.status);
return status === 200;
}
try {
const accessToken = Deno.readTextFileSync(
`${Deno.env.get("HOME")}/.fluentci/access-token`
);
const status = await fetch(
`https://api.fluentci.io/validate?token=${accessToken}`
).then((res) => res.status);
return status === 200;
} catch (_a) {
return false;
}
}

export function getAccessToken(): string | undefined {
if (Deno.env.get("FLUENTCI_ACCESS_TOKEN")) {
return Deno.env.get("FLUENTCI_ACCESS_TOKEN");
}
try {
const accessToken = Deno.readTextFileSync(
`${dir("home")}/.fluentci/access-token`
);
return accessToken;
} catch (_) {
return undefined;
}
}
Loading

0 comments on commit c025fba

Please sign in to comment.