Skip to content

Commit

Permalink
CLI npm packaging
Browse files Browse the repository at this point in the history
Close #104
  • Loading branch information
dahlia committed Jul 18, 2024
1 parent 8cf6d86 commit 9531e42
Show file tree
Hide file tree
Showing 13 changed files with 383 additions and 13 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ jobs:
working-directory: ${{ github.workspace }}/src/npm/
- run: deno task pack
working-directory: ${{ github.workspace }}/cli/
- run: 'deno task npm "$(jq -r .version deno.json)"'
working-directory: ${{ github.workspace }}/cli/
- id: extract-changelog
uses: dahlia/submark@5a5ff0a58382fb812616a5801402f5aef00f90ce
with:
Expand Down Expand Up @@ -175,6 +177,14 @@ jobs:
env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
working-directory: ${{ github.workspace }}/src/npm/
- if: github.event_name == 'push' && github.ref_type == 'tag'
run: |
set -ex
npm config set //registry.npmjs.org/:_authToken "$NPM_AUTH_TOKEN"
npm publish --provenance --access public fedify-cli-*.tgz
env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
working-directory: ${{ github.workspace }}/cli/

publish-examples-blog:
if: github.event_name == 'push'
Expand Down
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
{
"deno.enable": true,
"deno.enablePaths": [
"cli",
"src",
"examples/blog",
"examples/hono-sample"
],
"deno.unstable": true,
"files.eol": "\n",
"files.insertFinalNewline": true,
Expand Down
1 change: 1 addition & 0 deletions cli/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
fedify-cli-*.tar.xz
fedify-cli-*.tgz
fedify-cli-*.zip
33 changes: 28 additions & 5 deletions cli/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
<!-- deno-fmt-ignore-file -->

`fedify`: the CLI toolchain for debugging fediverse apps
========================================================
`fedify`: the CLI toolchain for Fedify and debugging ActivityPub
================================================================

[![JSR][JSR badge]][JSR]
[![npm][npm badge]][npm]
[![GitHub Releases][GitHub Releases badge]][GitHub Releases]

The `fedify` is a CLI toolchain for debugging ActivityPub-enabled federated
server apps. Although it is primarily designed for developers who use [Fedify],
it can be used with any ActivityPub-enabled server.
The `fedify` is a CLI toolchain for Fedify and debugging ActivityPub-enabled
federated server apps. Although it is primarily designed for developers who use
[Fedify], it can be used with any ActivityPub-enabled server.

[JSR]: https://jsr.io/@fedify/cli
[JSR badge]: https://jsr.io/badges/@fedify/cli
[npm]: https://www.npmjs.com/package/@fedify/cli
[npm badge]: https://img.shields.io/npm/v/@fedify/cli?logo=npm
[GitHub Releases]: https://github.com/dahlia/fedify/releases
[GitHub Releases badge]: https://img.shields.io/github/v/release/dahlia/fedify?sort=semver&logo=github
[Fedify]: https://fedify.dev/
Expand All @@ -20,6 +23,26 @@ it can be used with any ActivityPub-enabled server.
Installation
------------

### Using npm

If you have npm installed, you can install `fedify` by running the following
command:

~~~~ sh
npm install -g @fedify/cli
~~~~

### Using Bun

If you have [Bun] installed, you can install `fedify` by running the following
command:

~~~~ sh [Bun]
bun install -g @fedify/cli
~~~~

[Bun]: https://bun.sh/

### Using Deno

If you have [Deno] installed, you can install `fedify` by running the following
Expand Down
5 changes: 4 additions & 1 deletion cli/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
"exclude": [
".vscode",
"fedify-cli-*.tar.xz",
"fedify-cli-*.tgz",
"fedify-cli-*.zip"
],
"unstable": [
"fs",
"kv",
"process",
"temporal"
],
"tasks": {
Expand All @@ -26,6 +28,7 @@
"run": "deno task codegen && deno run --allow-all mod.ts",
"publish": "deno task codegen && deno task generate-import-map --release && deno publish",
"publish-dry-run": "deno task codegen && deno publish --dry-run --allow-dirty",
"pack": "deno task codegen && deno run -A scripts/pack.ts"
"pack": "deno task codegen && deno run -A scripts/pack.ts",
"npm": "deno run --allow-read --allow-write --allow-run --allow-env npm/pack.ts"
}
}
1 change: 0 additions & 1 deletion cli/import_map.g.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"@deno/dnt": "jsr:@deno/dnt@^0.41.2",
"@fedify/fedify": "../src/./mod.ts",
"@fedify/fedify/federation": "../src/./federation/mod.ts",
"@fedify/fedify/httpsig": "./httpsig/mod.ts",
"@fedify/fedify/nodeinfo": "../src/./nodeinfo/mod.ts",
"@fedify/fedify/runtime": "../src/./runtime/mod.ts",
"@fedify/fedify/vocab": "../src/./vocab/mod.ts",
Expand Down
2 changes: 2 additions & 0 deletions cli/npm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bin/
*.tgz
177 changes: 177 additions & 0 deletions cli/npm/install.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { execFile } from "node:child_process";
import { createWriteStream } from "node:fs";
import {
access,
chmod,
constants,
copyFile,
mkdir,
mkdtemp,
readFile,
realpath,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { Readable } from "node:stream";
import { fileURLToPath } from "node:url";

const platforms = {
darwin: {
arm64: "macos-aarch64.tar.xz",
x64: "macos-x86_64.tar.xz",
},
linux: {
arm64: "linux-aarch64.tar.xz",
x64: "linux-x86_64.tar.xz",
},
win32: {
x64: "windows-x86_64.zip",
},
};

export async function main(version) {
const filename = fileURLToPath(import.meta.url);
const dirName = dirname(filename);
const packageJson = await readFile(join(dirName, "package.json"), {
encoding: "utf8",
});
const pkg = JSON.parse(packageJson);
const binDir = join(dirName, "bin");
await mkdir(binDir, { recursive: true });
await install(version ?? pkg.version, binDir);
}

async function install(version, targetDir) {
const downloadUrl = getDownloadUrl(version);
const downloadPath = await download(downloadUrl);
let extractPath;
if (downloadPath.endsWith(".zip")) {
extractPath = await extractZip(downloadPath);
} else {
extractPath = await extractTar(downloadPath);
}
const exePath = join(extractPath, "fedify.exe");
if (await isFile(exePath)) {
const targetPath = join(targetDir, "fedify.exe");
await copyFile(exePath, targetPath);
return targetPath;
}
const binPath = join(extractPath, "fedify");
if (await isFile(binPath)) {
const targetPath = join(targetDir, "fedify");
await copyFile(binPath, targetPath);
await chmod(targetPath, 0o755);
return targetPath;
}
throw new Error("Executable not found in the archive");
}

function getDownloadUrl(version) {
const platform = platforms[process.platform];
if (!platform) {
console.error("Unsupported platform:", process.platform);
return;
}
const suffix = platform[process.arch];
if (!suffix) {
console.error("Unsupported architecture:", process.arch);
return;
}
const filename = `fedify-cli-${version}-${suffix}`;
const url =
`https://github.com/dahlia/fedify/releases/download/${version}/${filename}`;
return url;
}

async function download(url) {
const response = await fetch(url, { redirect: "follow" });
if (!response.ok) {
console.error("Download failed:", response.statusText);
return;
}
const tmpDir = await mkdtemp(join(tmpdir(), `fedify-`));
const filename = url.substring(url.lastIndexOf("/") + 1);
const downloadPath = join(tmpDir, filename);
const fileStream = createWriteStream(downloadPath);
const readable = Readable.fromWeb(response.body);
await new Promise((resolve, reject) => {
readable.pipe(fileStream);
readable.on("error", reject);
fileStream.on("finish", resolve);
});
return downloadPath;
}

async function extractZip(path) {
const dir = await mkdtemp(join(tmpdir(), "fedify-"));
await new Promise((resolve, reject) => {
execFile("powershell", [
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-Command",
`Import-Module Microsoft.PowerShell.Archive;\
Expand-Archive -LiteralPath '${path}' -DestinationPath '${dir}'`,
], (error, _, stderr) => {
if (error) {
console.error("Extraction failed:", error);
reject(error);
return;
}
if (stderr) console.warn(stderr);
resolve();
});
});
return dir;
}

async function extractTar(path) {
const switches = {
".tar": "",
".tar.gz": "z",
".tgz": "z",
".tar.bz2": "j",
".tbz2": "j",
".tar.xz": "J",
".txz": "J",
};
let switch_ = "";
for (const ext in switches) {
if (path.endsWith(ext)) {
switch_ = switches[ext];
break;
}
}
path = await realpath(path);
const dir = await mkdtemp(join(tmpdir(), "fedify-"));
await execTar(`xvf${switch_}`, dir, path);
return dir;
}

function execTar(switch_, dir, path) {
return new Promise((resolve, reject) => {
execFile("tar", [switch_, path], { cwd: dir }, (error, _, stderr) => {
if (error) {
console.error("Extraction failed:", error);
reject(error);
return;
}
if (stderr) console.warn(stderr);
resolve();
});
});
}

export async function isFile(path) {
try {
await access(path, constants.R_OK);
return true;
} catch (error) {
if (error.code === "ENOENT") return false;
throw error;
}
}

if (fileURLToPath(import.meta.url) === process.argv[1]) {
await main(process.argv[2]);
}
48 changes: 48 additions & 0 deletions cli/npm/pack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { which } from "jsr:@david/[email protected]";
import { dirname, join } from "jsr:@std/[email protected]";
import denoJson from "../deno.json" with { type: "json" };
import metadataTemplate from "./package.json" with { type: "json" };

async function main() {
const metadata = {
...metadataTemplate,
name: denoJson.name,
version: Deno.args.length < 1 ? denoJson.version : Deno.args[0],
private: false,
};
const tempDir = await Deno.makeTempDir();
console.debug("Working directory:", tempDir);
await Deno.writeTextFile(
join(tempDir, "package.json"),
JSON.stringify(metadata),
);
await Deno.copyFile(
join(import.meta.dirname!, "install.mjs"),
join(tempDir, "install.mjs"),
);
await Deno.copyFile(
join(import.meta.dirname!, "run.mjs"),
join(tempDir, "run.mjs"),
);
await Deno.copyFile(
join(dirname(import.meta.dirname!), "README.md"),
join(tempDir, "README.md"),
);
const command = new Deno.Command(await which("npm") ?? "npm", {
args: ["pack"],
stdin: "inherit",
stdout: "inherit",
stderr: "inherit",
cwd: tempDir,
});
const result = await command.output();
if (!result.success) Deno.exit(result.code);
for await (const entry of Deno.readDir(tempDir)) {
if (entry.isFile && entry.name.endsWith(".tgz")) {
await Deno.copyFile(join(tempDir, entry.name), entry.name);
console.log(entry.name);
}
}
}

if (import.meta.main) await main();
Loading

0 comments on commit 9531e42

Please sign in to comment.