Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/gorgeous-dolls-jump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ryanatkn/orc': minor
---

support `pulls` for public repos
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GITHUB_TOKEN_SECRET=
1 change: 1 addition & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npx gro sync # TODO hack to init env
- run: npx gro check --workspace
- run: npx gro build
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,29 @@
I maintain a lot of git repos between
[Felt](https://github.com/feltjs/felt), [Fuz](https://github.com/fuz-dev/fuz),
[Gro](https://github.com/grogarden/gro), and [others](https://github.com/ryanatkn).
Orc is a tool to help me orchestrate this complexity.
Orc is a tool for helping me orchestrate this complexity.
It's first user project is [Spiderspace](https://github.com/spiderspace/spiderspace).

The goal is to make a generic tool that works for your projects too,
but for now I'm hardcoding all values in
[`$lib/orc.config.ts`](src/lib/orc.config.ts).
If you want to try it yourself, you can fork the repo and change the config manually,
and eventually I'll stabilize the APIs and publish a reusable library.

## Usage

- see [`.env.example`](/.env.example) and add your own `.env` with `GITHUB_TOKEN_SECRET`,
whose value is a [GitHub token](https://github.com/settings/tokens)
(currently optional because it's only used to read public repos)

Getting started as a dev? Start with [Gro](https://github.com/grogarden/gro)
and the [Fuz template](https://github.com/fuz-dev/fuz_template)

TODO

- figure out better automation than manually running `gro packages`
- show the rate limit info
- automate `.env` (gro sync?)

## License [🐦](https://wikipedia.org/wiki/Free_and_open-source_software)

Expand Down
105 changes: 93 additions & 12 deletions package-lock.json

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

11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@
"devDependencies": {
"@changesets/changelog-git": "^0.1.14",
"@feltjs/eslint-config": "^0.4.1",
"@fuz.dev/fuz": "^0.75.2",
"@fuz.dev/fuz": "^0.75.3",
"@fuz.dev/fuz_contextmenu": "^0.4.2",
"@fuz.dev/fuz_dialog": "^0.3.2",
"@fuz.dev/fuz_library": "^0.14.2",
"@grogarden/gro": "^0.96.2",
"@fuz.dev/fuz_library": "^0.15.2",
"@grogarden/gro": "^0.96.3",
"@grogarden/util": "^0.15.2",
"@octokit/request": "^8.1.4",
"@sveltejs/adapter-static": "^2.0.3",
"@sveltejs/kit": "^1.27.1",
"@sveltejs/package": "^2.2.2",
Expand Down Expand Up @@ -86,6 +87,10 @@
"svelte": "./dist/RepoTable.svelte",
"default": "./dist/RepoTable.svelte",
"types": "./dist/RepoTable.svelte.d.ts"
},
"./sync.task.js": {
"default": "./dist/sync.task.js",
"types": "./dist/sync.task.d.ts"
}
}
}
27 changes: 26 additions & 1 deletion src/lib/RepoTable.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
import {format_host} from '@fuz.dev/fuz_library/package_meta.js';
import {page} from '$app/stores';
import {base} from '$app/paths';
import {strip_end} from '@grogarden/util/string.js';

import type {FetchedPackageMeta} from '$lib/fetch_packages.js';

export let pkgs: FetchedPackageMeta[];
export let deps = ['@fuz.dev/fuz', '@fuz.dev/fuz_library', '@grogarden/gro', '@grogarden/util'];
export let deps = ['@fuz.dev/fuz', '@fuz.dev/fuz_library', '@grogarden/gro']; // TODO add felt

// TODO fade out the `version` column if all deps are upgraded to the latest

Expand Down Expand Up @@ -38,6 +39,14 @@

const format_version = (version: string | null): string =>
version === null ? '' : version.replace(/^(\^|>=)\s*/u, '');

const lookup_pulls = (pkgs: FetchedPackageMeta[] | null, pkg: FetchedPackageMeta) => {
const found = pkgs?.find((p) => p.url === pkg.url);
if (!found?.package_json) return null;
const {pulls} = found;
console.log(`pulls`, pulls);
return pulls;
};
</script>

<table>
Expand All @@ -46,6 +55,7 @@
<th>homepage</th>
<th>repo</th>
<th>npm</th>
<th>pull requests</th>
<th>version</th>
{#each deps as dep (dep)}
<th>{dep}</th>
Expand Down Expand Up @@ -87,6 +97,21 @@
</div>
{/if}
</td>
<td>
{#if package_json && pkg.repo_url}
{@const pulls = lookup_pulls(pkgs, pkg)}
<!-- TODO show something like `and N more` with a link to a dialog list -->
<div class="row">
{#if pulls}
{#each pulls as pull (pull)}
<a href="{strip_end(pkg.repo_url, '/')}/pull/{pull.number}" class="chip"
>#{pull.number}</a
>
{/each}
{/if}
</div>
{/if}
</td>
<td>
{#if package_json && package_json.version !== '0.0.1'}
<a href={pkg.changelog_url}>{format_version(package_json.version)}</a>
Expand Down
74 changes: 57 additions & 17 deletions src/lib/fetch_packages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,81 @@ import type {Url} from '@grogarden/gro/paths.js';
import {strip_end} from '@grogarden/util/string.js';
import type {Logger} from '@grogarden/util/log.js';
import {wait} from '@grogarden/util/async.js';
import type {PackageMeta} from '@fuz.dev/fuz_library/package_meta.js';
import {parse_package_meta, type PackageMeta} from '@fuz.dev/fuz_library/package_meta.js';
import {request} from '@octokit/request';
import {GITHUB_TOKEN_SECRET} from '$env/static/private';

// TODO rethink with `Package`
// TODO rethink with `Package` and `FetchedPackage2`
export interface FetchedPackage {
url: Url;
package_json: PackageJson | null; // TODO forward error
pulls: GithubIssue[];
}

// TODO rethink with `PackageMeta`
export type FetchedPackageMeta = PackageMeta | {url: Url; package_json: null};
type GithubIssue = any; // TODO

// TODO obviously bad names
export interface FetchedPackage2 extends PackageMeta {
pulls: GithubIssue[] | null;
}

export interface UnfetchablePackage {
url: Url;
package_json: null;
pulls: null;
}

// TODO rethink these names
export type FetchedPackageMeta = FetchedPackage2 | UnfetchablePackage;

/* eslint-disable no-await-in-loop */

export const fetch_packages = async (
urls: Url[],
log?: Logger,
delay = 50,
token = GITHUB_TOKEN_SECRET,
): Promise<FetchedPackage[]> => {
console.log(`urls`, urls);
const packages: FetchedPackage[] = [];
for (const url of urls) {
const package_json_url = strip_end(url, '/') + '/.well-known/package.json'; // TODO helper
log?.info('fetching', package_json_url);
try {
const res = await fetch(package_json_url, {
headers: {'content-type': 'application/json', accept: 'application/json'},
});
const json = await res.json();
const package_json = PackageJson.parse(json); // TODO maybe not?
packages.push({url, package_json});
} catch (err) {
packages.push({url, package_json: null}); // TODO better error
}
// TODO delay?
const package_json = await load_package_json(url, log);
if (!package_json) throw Error('failed to load package_json: ' + url);
await wait(delay);
const pkg = parse_package_meta(url, package_json);
if (!pkg) throw Error('failed to parse package_json: ' + url);
const pulls = await fetch_github_issues(url, pkg, log, token);
if (!pulls) throw Error('failed to fetch issues: ' + url);
await wait(delay);
packages.push({url, package_json, pulls});
}
return packages;
};

const fetch_github_issues = async (url: string, pkg: PackageMeta, log?: Logger, token?: string) => {
log?.info(`[fetch_github_issues] url`, url);
if (!pkg.owner_name) return null;
const res = await request('GET /repos/{owner}/{repo}/pulls', {
headers: {authorization: 'token ' + token},
owner: pkg.owner_name,
repo: pkg.repo_name,
sort: 'updated',
});
log?.info(`res`, res);
return res.data;
};

const load_package_json = async (url: string, log?: Logger): Promise<PackageJson | null> => {
const package_json_url = strip_end(url, '/') + '/.well-known/package.json'; // TODO helper
log?.info('fetching', package_json_url);
try {
const res = await fetch(package_json_url, {
headers: {'content-type': 'application/json', accept: 'application/json'},
});
const json = await res.json();
const package_json = PackageJson.parse(json); // TODO maybe not?
return package_json;
} catch (err) {
return null; // TODO better error
}
};
Loading