Split per-type registration points into per-type files (#150)
Parallel package-type PRs conflict because every type edits the same shared lines (README tables, provider.go, server.go imports, the package type list, UI maps, the e2e caching table). A new type now only adds files.
- move per-type README sections into docs/<type>/{remote,local,virtual}.md
- split provider.go capability and metadata types into cap_*.go and metadata_*.go
- import providers via internal/provider/all, one file per type
- declare each package type in package_type_<type>.go, registered via init()
- split the e2e caching cases and UI colours/snippets into per-type files
Reviewed-on: #150
Co-authored-by: unkin-agent <unkin-agent@unkin.net>
Co-committed-by: unkin-agent <unkin-agent@unkin.net>
This commit was merged in pull request #150.
This commit is contained in:
@@ -1,37 +1,16 @@
|
||||
import { useState } from 'react';
|
||||
import './UsageInstructions.css';
|
||||
import { packageTypes } from '../packagetypes';
|
||||
import type { RepoClass, Snippet } from '../packagetypes/types';
|
||||
|
||||
// repoClass distinguishes the three ways a repository is consumed. remotes are
|
||||
// caching proxies, locals are real registries you also publish to, virtuals are
|
||||
// merged read-only indexes.
|
||||
type RepoClass = 'remote' | 'local' | 'virtual';
|
||||
|
||||
interface Snippet {
|
||||
title: string;
|
||||
language: string;
|
||||
code: string;
|
||||
note?: string;
|
||||
}
|
||||
|
||||
// baseURL resolves the externally reachable origin of this artifactapi instance.
|
||||
// The UI is served on the same origin as the API (client BASE is ''), so
|
||||
// window.location.origin is the address a host would actually curl/pull against
|
||||
// — no hardcoded hostname, works in prod and in `npm run dev` behind a proxy.
|
||||
// The UI is served on the same origin as the API, so this is the address clients use.
|
||||
function baseURL(): string {
|
||||
if (typeof window !== 'undefined' && window.location?.origin) {
|
||||
return window.location.origin.replace(/\/$/, '');
|
||||
}
|
||||
return 'https://artifactapi.k8s.syd1.au.unkin.net';
|
||||
return window.location.origin;
|
||||
}
|
||||
|
||||
// hostOnly is the bare host[:port] with no scheme, for docker/terraform source
|
||||
// addresses which are scheme-less.
|
||||
// hostOnly is host[:port] for scheme-less docker/terraform addresses.
|
||||
function hostOnly(): string {
|
||||
try {
|
||||
return new URL(baseURL()).host;
|
||||
} catch {
|
||||
return 'artifactapi.k8s.syd1.au.unkin.net';
|
||||
}
|
||||
return window.location.host;
|
||||
}
|
||||
|
||||
// remoteProxyBase is where a remote (or virtual) repo's proxied artifacts live.
|
||||
@@ -46,315 +25,8 @@ export function buildSnippets(packageType: string, repoClass: RepoClass, name: s
|
||||
const proxy = remoteProxyBase(repoClass, name);
|
||||
const isLocal = repoClass === 'local';
|
||||
|
||||
switch (packageType) {
|
||||
case 'rpm':
|
||||
return [
|
||||
{
|
||||
title: isLocal
|
||||
? 'Add the yum repo (real yum repo, repodata auto-regenerated)'
|
||||
: 'Add the yum repo (caching proxy)',
|
||||
language: 'bash',
|
||||
code: `sudo tee /etc/yum.repos.d/${name}.repo >/dev/null <<'EOF'
|
||||
[${name}]
|
||||
name=${name} (artifactapi)
|
||||
baseurl=${isLocal ? `${url}/api/v2/remotes/${name}/files/` : `${proxy}/`}
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
repo_gpgcheck=0
|
||||
EOF
|
||||
|
||||
sudo dnf install <package>`,
|
||||
note: isLocal
|
||||
? 'gpgcheck=0: artifactapi serves the repo unsigned. If you sign your RPMs, import your key and set gpgcheck=1.'
|
||||
: 'gpgcheck=0 trusts upstream over the proxy. To verify package signatures, import the upstream GPG key and set gpgcheck=1.',
|
||||
},
|
||||
...(isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Publish an RPM (repodata regenerates automatically)',
|
||||
language: 'bash',
|
||||
code: `curl -fsSL --upload-file ./my-package-1.0-1.el9.x86_64.rpm \\
|
||||
${url}/api/v2/remotes/${name}/files/my-package-1.0-1.el9.x86_64.rpm`,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
case 'pypi':
|
||||
return [
|
||||
{
|
||||
title: 'Install a package (one-off)',
|
||||
language: 'bash',
|
||||
code: `pip install --index-url ${proxy}/simple/ <package>`,
|
||||
},
|
||||
{
|
||||
title: 'Configure pip persistently',
|
||||
language: 'bash',
|
||||
code: `mkdir -p ~/.config/pip
|
||||
cat > ~/.config/pip/pip.conf <<'EOF'
|
||||
[global]
|
||||
index-url = ${proxy}/simple/
|
||||
EOF
|
||||
|
||||
pip install <package>`,
|
||||
},
|
||||
];
|
||||
|
||||
case 'npm':
|
||||
return [
|
||||
{
|
||||
title: 'Point npm at this registry',
|
||||
language: 'bash',
|
||||
code: `npm config set registry ${proxy}/
|
||||
npm install <package>`,
|
||||
},
|
||||
{
|
||||
title: 'Per-project (.npmrc)',
|
||||
language: 'bash',
|
||||
code: `echo 'registry=${proxy}/' >> .npmrc
|
||||
npm install`,
|
||||
},
|
||||
];
|
||||
|
||||
case 'docker':
|
||||
return [
|
||||
{
|
||||
title: 'Pull an image',
|
||||
language: 'bash',
|
||||
code: `docker pull ${host}/${name}/<image>:<tag>`,
|
||||
note: 'The first path segment after the host is the artifactapi repo name; the rest is the image name.',
|
||||
},
|
||||
...(isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Push an image (this is a real Registry V2)',
|
||||
language: 'bash',
|
||||
code: `docker tag myapp:latest ${host}/${name}/myapp:latest
|
||||
docker push ${host}/${name}/myapp:latest`,
|
||||
note: 'Works with docker, podman, skopeo and buildah. If the registry requires auth, run `docker login ' + host + '` first.',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
case 'terraform':
|
||||
return [
|
||||
{
|
||||
title: 'Use as a provider source (bare address, no mirror config)',
|
||||
language: 'hcl',
|
||||
code: `terraform {
|
||||
required_providers {
|
||||
${name} = {
|
||||
source = "${host}/${name}/<type>"
|
||||
version = ">= 0.1.0"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
note: 'The namespace segment is this repo name; <type> is the provider type. artifactapi signs SHA256SUMS server-side with its GPG key, so `terraform init` installs with no .terraformrc.',
|
||||
},
|
||||
...(isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Publish a provider build',
|
||||
language: 'bash',
|
||||
code: `curl -fsSL --upload-file terraform-provider-<type>_0.1.0_linux_amd64.zip \\
|
||||
${url}/api/v2/remotes/${name}/files/${name}/<type>/terraform-provider-<type>_0.1.0_linux_amd64.zip`,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
case 'helm':
|
||||
return [
|
||||
{
|
||||
title: isLocal ? 'Add the Helm repo (index.yaml auto-generated)' : 'Add the Helm repo',
|
||||
language: 'bash',
|
||||
code: `helm repo add ${name} ${isLocal ? `${url}/api/v1/local/${name}` : proxy}/
|
||||
helm repo update
|
||||
helm install <release> ${name}/<chart>`,
|
||||
},
|
||||
...(isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Publish a chart',
|
||||
language: 'bash',
|
||||
code: `helm package ./mychart
|
||||
curl -fsSL --upload-file mychart-0.1.0.tgz \\
|
||||
${url}/api/v2/remotes/${name}/files/mychart-0.1.0.tgz`,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
case 'alpine':
|
||||
return isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Add the apk repo (real apk repo, APKINDEX auto-generated)',
|
||||
language: 'bash',
|
||||
code: `echo '${url}/api/v1/local/${name}' | sudo tee -a /etc/apk/repositories
|
||||
sudo apk update --allow-untrusted
|
||||
sudo apk add --allow-untrusted <package>`,
|
||||
note: `Served unsigned (parity with the rpm repo's gpgcheck=0) — use --allow-untrusted, or install a signing key. apk fetches <arch>/APKINDEX.tar.gz under this base.`,
|
||||
},
|
||||
{
|
||||
title: 'Publish a .apk (index regenerates automatically)',
|
||||
language: 'bash',
|
||||
code: `curl -fsSL --upload-file ./mypkg-1.0-r0.apk \\
|
||||
${url}/api/v2/remotes/${name}/files/x86_64/mypkg-1.0-r0.apk`,
|
||||
note: 'Upload each package at <arch>/<name>-<version>.apk — apk reconstructs that exact path from the index (APKINDEX carries no filename), so a mismatched path will 404 on install.',
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
title: 'Add the APK repository',
|
||||
language: 'bash',
|
||||
code: `echo '${proxy}/' | sudo tee -a /etc/apk/repositories
|
||||
sudo apk update
|
||||
sudo apk add <package>`,
|
||||
note: 'If the index is unsigned over the proxy, add --allow-untrusted or install the signing key into /etc/apk/keys.',
|
||||
},
|
||||
];
|
||||
|
||||
case 'github_alpine':
|
||||
return [
|
||||
{
|
||||
title: 'Add the apk repo (metadata-only, from GitHub releases)',
|
||||
language: 'bash',
|
||||
code: `echo '${proxy}' | sudo tee -a /etc/apk/repositories
|
||||
sudo apk update --allow-untrusted
|
||||
sudo apk add --allow-untrusted <package>`,
|
||||
note: "The per-arch APKINDEX is synthesized from the configured GitHub repo's release .apk assets; package downloads are redirected to the backing releases remote. Served unsigned, so --allow-untrusted.",
|
||||
},
|
||||
];
|
||||
|
||||
case 'goproxy':
|
||||
return [
|
||||
{
|
||||
title: 'Point the Go module proxy here',
|
||||
language: 'bash',
|
||||
code: `export GOPROXY=${proxy}
|
||||
go mod download`,
|
||||
note: 'Append ,direct to fall back to VCS for modules this proxy does not cover.',
|
||||
},
|
||||
];
|
||||
|
||||
case 'cargo':
|
||||
return [
|
||||
{
|
||||
title: 'Point crates.io at this sparse registry',
|
||||
language: 'toml',
|
||||
code: `# ~/.cargo/config.toml
|
||||
[source.crates-io]
|
||||
replace-with = "${name}"
|
||||
|
||||
[registries.${name}]
|
||||
index = "sparse+${proxy}/"`,
|
||||
note: 'config.json is served by artifactapi so crate downloads also go through this remote.',
|
||||
},
|
||||
];
|
||||
|
||||
case 'nuget':
|
||||
return [
|
||||
{
|
||||
title: 'Use this feed instead of nuget.org',
|
||||
language: 'xml',
|
||||
code: `<!-- nuget.config -->
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="${name}" value="${proxy}/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
</configuration>`,
|
||||
note: 'Serve artifactapi over HTTPS: NuGet rejects the repository signatures resource over plain HTTP.',
|
||||
},
|
||||
];
|
||||
|
||||
case 'puppet':
|
||||
return [
|
||||
{
|
||||
title: isLocal ? 'Install a module from this forge' : 'Install a module from the Forge proxy',
|
||||
language: 'bash',
|
||||
code: `puppet module install <author>-<module> \\
|
||||
--module_repository ${isLocal ? `${url}/api/v1/local/${name}` : proxy}`,
|
||||
},
|
||||
...(isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Publish a module (versions are immutable)',
|
||||
language: 'bash',
|
||||
code: `puppet module build && curl -fsSL --upload-file pkg/<author>-<module>-<version>.tar.gz \\
|
||||
${url}/api/v2/remotes/${name}/files/<author>-<module>-<version>.tar.gz`,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
case 'deb':
|
||||
return isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Add the apt repo (real apt repo, flat — Packages/Release auto-generated)',
|
||||
language: 'bash',
|
||||
code: `echo 'deb [trusted=yes] ${url}/api/v1/local/${name}/ ./' | sudo tee /etc/apt/sources.list.d/${name}.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install <package>`,
|
||||
note: '[trusted=yes]: artifactapi serves the flat repo unsigned (matches the rpm repo\'s gpgcheck=0). The `./` is the flat-repo suite — apt fetches Packages/Release from the repo root.',
|
||||
},
|
||||
{
|
||||
title: 'Publish a .deb (index regenerates automatically)',
|
||||
language: 'bash',
|
||||
code: `curl -fsSL --upload-file ./my-package_1.0_amd64.deb \\
|
||||
${url}/api/v2/remotes/${name}/files/my-package_1.0_amd64.deb`,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
title: 'Add the apt repo (caching proxy)',
|
||||
language: 'bash',
|
||||
code: `echo 'deb ${proxy} <suite> <component>' | sudo tee /etc/apt/sources.list.d/${name}.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install <package>`,
|
||||
note: "Signatures are verified against the upstream mirror's real signed Release through the proxy (no [trusted=yes] needed). Example suite/component: bookworm main.",
|
||||
},
|
||||
];
|
||||
|
||||
case 'github_deb':
|
||||
return [
|
||||
{
|
||||
title: 'Add the apt repo (metadata-only, from GitHub releases)',
|
||||
language: 'bash',
|
||||
code: `echo 'deb [trusted=yes] ${proxy}/ ./' | sudo tee /etc/apt/sources.list.d/${name}.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install <package>`,
|
||||
note: "The apt index is synthesized from the configured GitHub repo's release .deb assets; package downloads are redirected to the backing releases remote. Served unsigned, so [trusted=yes].",
|
||||
},
|
||||
];
|
||||
|
||||
case 'generic':
|
||||
default:
|
||||
return [
|
||||
{
|
||||
title: 'Download a file',
|
||||
language: 'bash',
|
||||
code: `curl -fsSLO ${proxy}/<path>`,
|
||||
note:
|
||||
packageType === 'generic'
|
||||
? 'Generic repos are fetched as plain files at their upstream path.'
|
||||
: `No tailored client instructions for "${packageType}" yet — fetch artifacts directly by path.`,
|
||||
},
|
||||
...(isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Publish a file',
|
||||
language: 'bash',
|
||||
code: `curl -fsSL --upload-file ./myfile \\
|
||||
${url}/api/v2/remotes/${name}/files/<path>/myfile`,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
const def = packageTypes[packageType] ?? packageTypes.generic;
|
||||
return def.snippets({ packageType, name, url, host, proxy, isLocal });
|
||||
}
|
||||
|
||||
function CodeBox({ snippet }: { snippet: Snippet }) {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
color: 'green',
|
||||
snippets: ({ name, url, proxy, isLocal }) => {
|
||||
return isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Add the apk repo (real apk repo, APKINDEX auto-generated)',
|
||||
language: 'bash',
|
||||
code: `echo '${url}/api/v1/local/${name}' | sudo tee -a /etc/apk/repositories
|
||||
sudo apk update --allow-untrusted
|
||||
sudo apk add --allow-untrusted <package>`,
|
||||
note: `Served unsigned (parity with the rpm repo's gpgcheck=0) — use --allow-untrusted, or install a signing key. apk fetches <arch>/APKINDEX.tar.gz under this base.`,
|
||||
},
|
||||
{
|
||||
title: 'Publish a .apk (index regenerates automatically)',
|
||||
language: 'bash',
|
||||
code: `curl -fsSL --upload-file ./mypkg-1.0-r0.apk \\
|
||||
${url}/api/v2/remotes/${name}/files/x86_64/mypkg-1.0-r0.apk`,
|
||||
note: 'Upload each package at <arch>/<name>-<version>.apk — apk reconstructs that exact path from the index (APKINDEX carries no filename), so a mismatched path will 404 on install.',
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
title: 'Add the APK repository',
|
||||
language: 'bash',
|
||||
code: `echo '${proxy}/' | sudo tee -a /etc/apk/repositories
|
||||
sudo apk update
|
||||
sudo apk add <package>`,
|
||||
note: 'If the index is unsigned over the proxy, add --allow-untrusted or install the signing key into /etc/apk/keys.',
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
color: 'yellow',
|
||||
snippets: ({ name, proxy }) => {
|
||||
return [
|
||||
{
|
||||
title: 'Point crates.io at this sparse registry',
|
||||
language: 'toml',
|
||||
code: `# ~/.cargo/config.toml
|
||||
[source.crates-io]
|
||||
replace-with = "${name}"
|
||||
|
||||
[registries.${name}]
|
||||
index = "sparse+${proxy}/"`,
|
||||
note: 'config.json is served by artifactapi so crate downloads also go through this remote.',
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
snippets: ({ name, url, proxy, isLocal }) => {
|
||||
return isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Add the apt repo (real apt repo, flat — Packages/Release auto-generated)',
|
||||
language: 'bash',
|
||||
code: `echo 'deb [trusted=yes] ${url}/api/v1/local/${name}/ ./' | sudo tee /etc/apt/sources.list.d/${name}.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install <package>`,
|
||||
note: '[trusted=yes]: artifactapi serves the flat repo unsigned (matches the rpm repo\'s gpgcheck=0). The `./` is the flat-repo suite — apt fetches Packages/Release from the repo root.',
|
||||
},
|
||||
{
|
||||
title: 'Publish a .deb (index regenerates automatically)',
|
||||
language: 'bash',
|
||||
code: `curl -fsSL --upload-file ./my-package_1.0_amd64.deb \\
|
||||
${url}/api/v2/remotes/${name}/files/my-package_1.0_amd64.deb`,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
title: 'Add the apt repo (caching proxy)',
|
||||
language: 'bash',
|
||||
code: `echo 'deb ${proxy} <suite> <component>' | sudo tee /etc/apt/sources.list.d/${name}.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install <package>`,
|
||||
note: "Signatures are verified against the upstream mirror's real signed Release through the proxy (no [trusted=yes] needed). Example suite/component: bookworm main.",
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,28 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
color: 'blue',
|
||||
snippets: ({ name, host, isLocal }) => {
|
||||
return [
|
||||
{
|
||||
title: 'Pull an image',
|
||||
language: 'bash',
|
||||
code: `docker pull ${host}/${name}/<image>:<tag>`,
|
||||
note: 'The first path segment after the host is the artifactapi repo name; the rest is the image name.',
|
||||
},
|
||||
...(isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Push an image (this is a real Registry V2)',
|
||||
language: 'bash',
|
||||
code: `docker tag myapp:latest ${host}/${name}/myapp:latest
|
||||
docker push ${host}/${name}/myapp:latest`,
|
||||
note: 'Works with docker, podman, skopeo and buildah. If the registry requires auth, run `docker login ' + host + '` first.',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
color: 'default',
|
||||
snippets: ({ packageType, name, url, proxy, isLocal }) => {
|
||||
return [
|
||||
{
|
||||
title: 'Download a file',
|
||||
language: 'bash',
|
||||
code: `curl -fsSLO ${proxy}/<path>`,
|
||||
note:
|
||||
packageType === 'generic'
|
||||
? 'Generic repos are fetched as plain files at their upstream path.'
|
||||
: `No tailored client instructions for "${packageType}" yet — fetch artifacts directly by path.`,
|
||||
},
|
||||
...(isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Publish a file',
|
||||
language: 'bash',
|
||||
code: `curl -fsSL --upload-file ./myfile \\
|
||||
${url}/api/v2/remotes/${name}/files/<path>/myfile`,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
snippets: ({ proxy }) => {
|
||||
return [
|
||||
{
|
||||
title: 'Add the apk repo (metadata-only, from GitHub releases)',
|
||||
language: 'bash',
|
||||
code: `echo '${proxy}' | sudo tee -a /etc/apk/repositories
|
||||
sudo apk update --allow-untrusted
|
||||
sudo apk add --allow-untrusted <package>`,
|
||||
note: "The per-arch APKINDEX is synthesized from the configured GitHub repo's release .apk assets; package downloads are redirected to the backing releases remote. Served unsigned, so --allow-untrusted.",
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
snippets: ({ name, proxy }) => {
|
||||
return [
|
||||
{
|
||||
title: 'Add the apt repo (metadata-only, from GitHub releases)',
|
||||
language: 'bash',
|
||||
code: `echo 'deb [trusted=yes] ${proxy}/ ./' | sudo tee /etc/apt/sources.list.d/${name}.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install <package>`,
|
||||
note: "The apt index is synthesized from the configured GitHub repo's release .deb assets; package downloads are redirected to the backing releases remote. Served unsigned, so [trusted=yes].",
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
color: 'green',
|
||||
snippets: ({ proxy }) => {
|
||||
return [
|
||||
{
|
||||
title: 'Point the Go module proxy here',
|
||||
language: 'bash',
|
||||
code: `export GOPROXY=${proxy}
|
||||
go mod download`,
|
||||
note: 'Append ,direct to fall back to VCS for modules this proxy does not cover.',
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
color: 'green',
|
||||
snippets: ({ name, url, proxy, isLocal }) => {
|
||||
return [
|
||||
{
|
||||
title: isLocal ? 'Add the Helm repo (index.yaml auto-generated)' : 'Add the Helm repo',
|
||||
language: 'bash',
|
||||
code: `helm repo add ${name} ${isLocal ? `${url}/api/v1/local/${name}` : proxy}/
|
||||
helm repo update
|
||||
helm install <release> ${name}/<chart>`,
|
||||
},
|
||||
...(isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Publish a chart',
|
||||
language: 'bash',
|
||||
code: `helm package ./mychart
|
||||
curl -fsSL --upload-file mychart-0.1.0.tgz \\
|
||||
${url}/api/v2/remotes/${name}/files/mychart-0.1.0.tgz`,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
color: 'red',
|
||||
snippets: ({ proxy }) => {
|
||||
return [
|
||||
{
|
||||
title: 'Point npm at this registry',
|
||||
language: 'bash',
|
||||
code: `npm config set registry ${proxy}/
|
||||
npm install <package>`,
|
||||
},
|
||||
{
|
||||
title: 'Per-project (.npmrc)',
|
||||
language: 'bash',
|
||||
code: `echo 'registry=${proxy}/' >> .npmrc
|
||||
npm install`,
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
color: 'blue',
|
||||
snippets: ({ name, proxy }) => {
|
||||
return [
|
||||
{
|
||||
title: 'Use this feed instead of nuget.org',
|
||||
language: 'xml',
|
||||
code: `<!-- nuget.config -->
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="${name}" value="${proxy}/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
</configuration>`,
|
||||
note: 'Serve artifactapi over HTTPS: NuGet rejects the repository signatures resource over plain HTTP.',
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
color: 'yellow',
|
||||
snippets: ({ name, url, proxy, isLocal }) => {
|
||||
return [
|
||||
{
|
||||
title: isLocal ? 'Install a module from this forge' : 'Install a module from the Forge proxy',
|
||||
language: 'bash',
|
||||
code: `puppet module install <author>-<module> \\
|
||||
--module_repository ${isLocal ? `${url}/api/v1/local/${name}` : proxy}`,
|
||||
},
|
||||
...(isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Publish a module (versions are immutable)',
|
||||
language: 'bash',
|
||||
code: `puppet module build && curl -fsSL --upload-file pkg/<author>-<module>-<version>.tar.gz \\
|
||||
${url}/api/v2/remotes/${name}/files/<author>-<module>-<version>.tar.gz`,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
color: 'blue',
|
||||
snippets: ({ proxy }) => {
|
||||
return [
|
||||
{
|
||||
title: 'Install a package (one-off)',
|
||||
language: 'bash',
|
||||
code: `pip install --index-url ${proxy}/simple/ <package>`,
|
||||
},
|
||||
{
|
||||
title: 'Configure pip persistently',
|
||||
language: 'bash',
|
||||
code: `mkdir -p ~/.config/pip
|
||||
cat > ~/.config/pip/pip.conf <<'EOF'
|
||||
[global]
|
||||
index-url = ${proxy}/simple/
|
||||
EOF
|
||||
|
||||
pip install <package>`,
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
color: 'yellow',
|
||||
snippets: ({ name, url, proxy, isLocal }) => {
|
||||
return [
|
||||
{
|
||||
title: isLocal
|
||||
? 'Add the yum repo (real yum repo, repodata auto-regenerated)'
|
||||
: 'Add the yum repo (caching proxy)',
|
||||
language: 'bash',
|
||||
code: `sudo tee /etc/yum.repos.d/${name}.repo >/dev/null <<'EOF'
|
||||
[${name}]
|
||||
name=${name} (artifactapi)
|
||||
baseurl=${isLocal ? `${url}/api/v2/remotes/${name}/files/` : `${proxy}/`}
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
repo_gpgcheck=0
|
||||
EOF
|
||||
|
||||
sudo dnf install <package>`,
|
||||
note: isLocal
|
||||
? 'gpgcheck=0: artifactapi serves the repo unsigned. If you sign your RPMs, import your key and set gpgcheck=1.'
|
||||
: 'gpgcheck=0 trusts upstream over the proxy. To verify package signatures, import the upstream GPG key and set gpgcheck=1.',
|
||||
},
|
||||
...(isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Publish an RPM (repodata regenerates automatically)',
|
||||
language: 'bash',
|
||||
code: `curl -fsSL --upload-file ./my-package-1.0-1.el9.x86_64.rpm \\
|
||||
${url}/api/v2/remotes/${name}/files/my-package-1.0-1.el9.x86_64.rpm`,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { PackageTypeUI } from '../types';
|
||||
|
||||
const def: PackageTypeUI = {
|
||||
color: 'blue',
|
||||
snippets: ({ name, url, host, isLocal }) => {
|
||||
return [
|
||||
{
|
||||
title: 'Use as a provider source (bare address, no mirror config)',
|
||||
language: 'hcl',
|
||||
code: `terraform {
|
||||
required_providers {
|
||||
${name} = {
|
||||
source = "${host}/${name}/<type>"
|
||||
version = ">= 0.1.0"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
note: 'The namespace segment is this repo name; <type> is the provider type. artifactapi signs SHA256SUMS server-side with its GPG key, so `terraform init` installs with no .terraformrc.',
|
||||
},
|
||||
...(isLocal
|
||||
? [
|
||||
{
|
||||
title: 'Publish a provider build',
|
||||
language: 'bash',
|
||||
code: `curl -fsSL --upload-file terraform-provider-<type>_0.1.0_linux_amd64.zip \\
|
||||
${url}/api/v2/remotes/${name}/files/${name}/<type>/terraform-provider-<type>_0.1.0_linux_amd64.zip`,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default def;
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { BadgeColor, PackageTypeUI } from './types';
|
||||
|
||||
const defs = import.meta.glob<PackageTypeUI>('./defs/*.ts', { eager: true, import: 'default' });
|
||||
|
||||
export const packageTypes: Record<string, PackageTypeUI> = Object.fromEntries(
|
||||
Object.entries(defs).map(([path, def]) => [path.slice('./defs/'.length, -'.ts'.length), def]),
|
||||
);
|
||||
|
||||
export function typeColor(packageType: string): BadgeColor {
|
||||
return packageTypes[packageType]?.color || 'default';
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// repoClass distinguishes the three ways a repository is consumed. remotes are
|
||||
// caching proxies, locals are real registries you also publish to, virtuals are
|
||||
// merged read-only indexes.
|
||||
export type RepoClass = 'remote' | 'local' | 'virtual';
|
||||
|
||||
export interface Snippet {
|
||||
title: string;
|
||||
language: string;
|
||||
code: string;
|
||||
note?: string;
|
||||
}
|
||||
|
||||
export interface SnippetContext {
|
||||
packageType: string;
|
||||
name: string;
|
||||
url: string;
|
||||
host: string;
|
||||
proxy: string;
|
||||
isLocal: boolean;
|
||||
}
|
||||
|
||||
export type BadgeColor = 'blue' | 'green' | 'yellow' | 'red' | 'default';
|
||||
|
||||
export interface PackageTypeUI {
|
||||
color?: BadgeColor;
|
||||
snippets: (ctx: SnippetContext) => Snippet[];
|
||||
}
|
||||
+2
-16
@@ -4,23 +4,9 @@ import { api } from '../api/client';
|
||||
import type { Remote } from '../api/types';
|
||||
import { Badge } from '../components/Badge';
|
||||
import { DataTable } from '../components/DataTable';
|
||||
import { typeColor } from '../packagetypes';
|
||||
import './Remotes.css';
|
||||
|
||||
const typeColors: Record<string, 'blue' | 'green' | 'yellow' | 'red' | 'default'> = {
|
||||
docker: 'blue',
|
||||
helm: 'green',
|
||||
rpm: 'yellow',
|
||||
pypi: 'blue',
|
||||
npm: 'red',
|
||||
generic: 'default',
|
||||
alpine: 'green',
|
||||
puppet: 'yellow',
|
||||
terraform: 'blue',
|
||||
goproxy: 'green',
|
||||
cargo: 'yellow',
|
||||
nuget: 'blue',
|
||||
};
|
||||
|
||||
export function Locals() {
|
||||
const navigate = useNavigate();
|
||||
const [remotes, setRemotes] = useState<Remote[]>([]);
|
||||
@@ -66,7 +52,7 @@ export function Locals() {
|
||||
key: 'type',
|
||||
header: 'Type',
|
||||
render: (r: Remote) => (
|
||||
<Badge variant={typeColors[r.package_type] || 'default'}>
|
||||
<Badge variant={typeColor(r.package_type)}>
|
||||
{r.package_type}
|
||||
</Badge>
|
||||
),
|
||||
|
||||
@@ -4,23 +4,9 @@ import { api } from '../api/client';
|
||||
import type { Remote } from '../api/types';
|
||||
import { Badge } from '../components/Badge';
|
||||
import { DataTable } from '../components/DataTable';
|
||||
import { typeColor } from '../packagetypes';
|
||||
import './Remotes.css';
|
||||
|
||||
const typeColors: Record<string, 'blue' | 'green' | 'yellow' | 'red' | 'default'> = {
|
||||
docker: 'blue',
|
||||
helm: 'green',
|
||||
rpm: 'yellow',
|
||||
pypi: 'blue',
|
||||
npm: 'red',
|
||||
generic: 'default',
|
||||
alpine: 'green',
|
||||
puppet: 'yellow',
|
||||
terraform: 'blue',
|
||||
goproxy: 'green',
|
||||
cargo: 'yellow',
|
||||
nuget: 'blue',
|
||||
};
|
||||
|
||||
export function Remotes() {
|
||||
const navigate = useNavigate();
|
||||
const [remotes, setRemotes] = useState<Remote[]>([]);
|
||||
@@ -81,7 +67,7 @@ export function Remotes() {
|
||||
key: 'type',
|
||||
header: 'Type',
|
||||
render: (r: Remote) => (
|
||||
<Badge variant={typeColors[r.package_type] || 'default'}>
|
||||
<Badge variant={typeColor(r.package_type)}>
|
||||
{r.package_type}
|
||||
</Badge>
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user