Compare commits

...

16 commits
0.3.0 ... main

Author SHA1 Message Date
rzmk
f0ac63a368 docs: add ckanaction to Next Steps in README and ignore README for CI
Some checks failed
Verify ckan-devstaller runs CKAN-only install successfully / Run ckan-devstaller with a CKAN-only install (push) Has been cancelled
2025-10-31 14:28:22 -04:00
rzmk
6c173fb779 ci: ignore docs path for running workflow 2025-10-30 09:29:00 -04:00
rzmk
e5d9324c7a feat(docs): add mention of the new --skip-run flag 2025-10-30 09:23:38 -04:00
rzmk
8489e50eb9 chore: move running ckan text to within conditional 2025-10-30 09:14:49 -04:00
rzmk
475bcc4932 feat: update ckan-devstaller to v0.3.1 2025-10-30 09:10:01 -04:00
rzmk
0e263202a1 fix(docs): remove export for WhyInteractive 2025-10-21 18:18:15 -04:00
rzmk
82200d5f94 feat(docs): add interactive Why section on home page 2025-10-21 18:16:48 -04:00
rzmk
d2e2c70dca feat: add Privacy Policy 2025-10-21 17:15:54 -04:00
rzmk
518ef00432 chore: fix typo 2025-10-21 16:21:47 -04:00
rzmk
93fcb8a2c5 ui(docs): add steps component and WIP WSL reference page 2025-10-15 13:24:11 -04:00
rzmk
a0cb19978b ui(docs): improve mobile view 2025-10-15 13:04:26 -04:00
rzmk
a24d49042b docs: link to CKAN in README 2025-10-14 08:57:06 -04:00
rzmk
6af03d8c0b fix: resource_formats.json file path and use rust-ini instead of ckan config-tool 2025-10-14 08:49:14 -04:00
rzmk
cb0802a4b2 fix: refactor extension installs into separate steps 2025-10-14 07:55:44 -04:00
rzmk
1f2b68f4d2 fix(docs): icon-xs issue and quick-start href 2025-10-14 06:39:59 -04:00
rzmk
451184a914 feat(docs): add /docs/changelog and changelog for v0.3.0 2025-10-14 06:34:25 -04:00
25 changed files with 941 additions and 278 deletions

View file

@ -0,0 +1,26 @@
name: Verify ckan-devstaller runs CKAN-only install successfully
on:
push:
branches:
- main
paths-ignore:
- 'docs/**'
- 'README.md'
workflow_dispatch:
jobs:
ckanonlyinstall:
name: Run ckan-devstaller with a CKAN-only install
runs-on: ubuntu-22.04
permissions:
# For the git-auto-commit-action
contents: write
steps:
- uses: actions/checkout@v4
- name: Install Rust stable toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo deps and target folder
uses: Swatinem/rust-cache@v2
with:
workspaces: ". -> target"
- name: Run ckan-devstaller
run: cargo run --release --verbose -- --ckan-version 2.11.4 --skip-interactive --skip-run

View file

@ -3,7 +3,7 @@
<a href="https://ckan-devstaller.dathere.com"><img width="1165" height="668" alt="{1329F0BA-A29F-4BF8-BB6B-E3BA84FDAFCC}" src="https://github.com/user-attachments/assets/8f0cc4ef-d90d-4715-ba21-9083dff0c3ff" /></a> <a href="https://ckan-devstaller.dathere.com"><img width="1165" height="668" alt="{1329F0BA-A29F-4BF8-BB6B-E3BA84FDAFCC}" src="https://github.com/user-attachments/assets/8f0cc4ef-d90d-4715-ba21-9083dff0c3ff" /></a>
`ckan-devstaller` attempts to install a CKAN instance using [ckan-compose](https://github.com/tino097/ckan-compose) for development usage in a new Ubuntu 22.04 instance. `ckan-devstaller` attempts to install a [CKAN](https://ckan.org) instance using [ckan-compose](https://github.com/tino097/ckan-compose/tree/ckan-devstaller) for development usage in a new Ubuntu 22.04 instance.
You may find `ckan-devstaller` useful for: You may find `ckan-devstaller` useful for:
@ -29,6 +29,7 @@ You may find the following guides useful while developing with CKAN:
- [Customize your config file](https://docs.ckan.org/en/latest/extensions/index.html) - [Customize your config file](https://docs.ckan.org/en/latest/extensions/index.html)
- [Create test data](https://docs.ckan.org/en/latest/maintaining/getting-started.html#creating-test-data) - [Create test data](https://docs.ckan.org/en/latest/maintaining/getting-started.html#creating-test-data)
- [Visit ckan.org](https://ckan.org) - [Visit ckan.org](https://ckan.org)
- [Interact with your CKAN API at ckanaction.dathere.com](https://ckanaction.dathere.com)
## `ckan-devstaller` demos ## `ckan-devstaller` demos

View file

@ -1,6 +1,7 @@
/** biome-ignore-all lint/suspicious/noArrayIndexKey: Would need to look into this trivial issue */ /** biome-ignore-all lint/suspicious/noArrayIndexKey: Would need to look into this trivial issue */
"use client"; "use client";
import { CodeBlock } from "fumadocs-ui/components/codeblock";
import defaultMdxComponents from "fumadocs-ui/mdx"; import defaultMdxComponents from "fumadocs-ui/mdx";
import { cn } from "fumadocs-ui/utils/cn"; import { cn } from "fumadocs-ui/utils/cn";
import { import {
@ -8,11 +9,14 @@ import {
GitMergeIcon, GitMergeIcon,
HomeIcon, HomeIcon,
SailboatIcon, SailboatIcon,
TerminalIcon,
Trash2Icon,
ZapIcon, ZapIcon,
} from "lucide-react"; } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { useState } from "react"; import { type HTMLProps, type ReactNode, useState } from "react";
import { Pre } from "@/components/codeblock";
import { buttonVariants } from "@/components/ui/button"; import { buttonVariants } from "@/components/ui/button";
import CkanDevstallerDemo from "./ckan-devstaller-demo.gif"; import CkanDevstallerDemo from "./ckan-devstaller-demo.gif";
@ -37,8 +41,34 @@ export default function HomePage() {
> >
<div className="relative mb-4"> <div className="relative mb-4">
<Hero /> <Hero />
<Why />
</div> </div>
</div> </div>
<hr className="mt-12 mb-4" />
<footer className="flex flex-col bg-brand-secondary pb-12 text-brand-secondary-foreground rounded-2xl">
<p className="mb-1 text-xl font-semibold">ckan-devstaller</p>
<p className="text-xs">
Provided by{" "}
<a
href="https://dathere.com"
target="_blank"
className="font-medium text-blue-400"
rel="noopener"
>
datHere
</a>
.{" "}
<a
href="https://dathere.com/privacy-policy/"
target="_blank"
className="font-medium text-blue-400"
rel="noopener"
>
Privacy Policy
</a>
.
</p>
</footer>
</main> </main>
</> </>
); );
@ -88,7 +118,7 @@ function Hero() {
</p> </p>
<div className="inline-flex items-center gap-3 max-md:mx-auto mb-8"> <div className="inline-flex items-center gap-3 max-md:mx-auto mb-8">
<Link <Link
href="/docs" href="/docs/builder"
className={cn( className={cn(
buttonVariants({ size: "lg", className: "rounded-full" }), buttonVariants({ size: "lg", className: "rounded-full" }),
)} )}
@ -109,11 +139,7 @@ function Hero() {
</Link> </Link>
</div> </div>
<Cards> <Cards>
<Card <Card icon={<ZapIcon />} href="/docs/builder" title="Quick start">
icon={<ZapIcon />}
href="/docs/quick-start"
title="Quick start"
>
Get started with ckan-devstaller and install CKAN within minutes Get started with ckan-devstaller and install CKAN within minutes
</Card> </Card>
<Card icon={<BlocksIcon />} href="/docs/builder" title="Builder"> <Card icon={<BlocksIcon />} href="/docs/builder" title="Builder">
@ -124,8 +150,7 @@ function Hero() {
href="/docs/reference/installation-architecture" href="/docs/reference/installation-architecture"
title="Installation architecture" title="Installation architecture"
> >
Learn about where files are installed after running Learn about where files are installed after running ckan-devstaller
ckan-devstaller
</Card> </Card>
<Card <Card
icon={<GitMergeIcon />} icon={<GitMergeIcon />}
@ -150,7 +175,7 @@ function PreviewImages() {
]; ];
return ( return (
<div className="p-8 min-w-[800px] overflow-hidden xl:-mx-12 dark:[mask-image:linear-gradient(to_top,transparent,white_40px)]"> <div className="p-8 min-w-[600px] md:min-w-[800px] overflow-hidden xl:-mx-12 dark:[mask-image:linear-gradient(to_top,transparent,white_40px)]">
<div className="absolute flex flex-row left-1/2 -translate-1/2 bottom-4 z-2 p-1 rounded-full bg-fd-card border shadow-xl dark:shadow-fd-background"> <div className="absolute flex flex-row left-1/2 -translate-1/2 bottom-4 z-2 p-1 rounded-full bg-fd-card border shadow-xl dark:shadow-fd-background">
{/* <div {/* <div
role="none" role="none"
@ -176,7 +201,7 @@ function PreviewImages() {
alt="preview" alt="preview"
priority priority
className={cn( className={cn(
"rounded-xl w-full select-none duration-1000 animate-in fade-in -mb-60 slide-in-from-bottom-12 lg:-mb-0", "rounded-xl w-full select-none duration-1000 animate-in fade-in md:-mb-60 slide-in-from-bottom-12 lg:-mb-0",
active !== i && "hidden", active !== i && "hidden",
)} )}
/> />
@ -184,3 +209,207 @@ function PreviewImages() {
</div> </div>
); );
} }
function Why() {
return (
<div className="relative overflow-hidden border-x border-t p-2">
<WhyInteractive
codeblockInstall={
<CodeBlock lang="bash">
<Pre className="text-wrap pl-4">./ckan-devstaller</Pre>
</CodeBlock>
}
codeblockUninstall={
<CodeBlock lang="bash">
<Pre className="text-wrap pl-4">./ckan-devstaller uninstall</Pre>
</CodeBlock>
}
/>
</div>
);
}
function WhyInteractive(props: {
codeblockInstall: ReactNode;
codeblockUninstall: ReactNode;
}) {
const [active, setActive] = useState(0);
const items = [
[
<ZapIcon className="w-4 h-4 inline-block" key={0} />,
"Install CKAN within minutes",
],
[
<BlocksIcon className="w-4 h-4 inline-block" key={1} />,
"Customize your installation",
],
[
<TerminalIcon className="w-4 h-4 inline-block" key={2} />,
"Designed for developers",
],
[
<Trash2Icon className="w-4 h-4 inline-block" key={3} />,
"Uninstall with ease",
],
];
return (
<div
id="why-interactive"
className="flex flex-col-reverse gap-3 md:flex-row md:min-h-[200px]"
>
<div className="flex flex-col">
{items.map((item, i) => (
<button
key={item[1] as string}
ref={(element) => {
if (!element || i !== active) return;
}}
type="button"
className={cn(
"transition-colors text-nowrap border border-transparent rounded-lg px-3 py-2.5 text-start text-sm text-fd-muted-foreground font-medium",
i === active
? "text-fd-primary bg-fd-primary/10 border-fd-primary/10"
: "hover:text-fd-accent-foreground/80 cursor-pointer",
)}
onClick={() => {
setActive(i);
}}
>
{item[0]} {item[1]}
</button>
))}
</div>
<style>
{`
@keyframes why-interactive-x {
from {
width: 0px;
}
to {
width: 100%;
}
}`}
</style>
<div className="flex-1 p-4 border border-fd-primary/10 bg-fd-card/40 rounded-lg shadow-lg">
{active === 0 ? (
<WhyPanel>
<h3>
<ZapIcon className="w-4 h-4 inline-block mr-1 mb-1" />
Install CKAN within minutes.
</h3>
<p>
One of the primary goals of ckan-devstaller is to ease
installation of CKAN for development. Built with Rust for speed
and streamlining installation with{" "}
<a href="https://github.com/tino097/ckan-compose/tree/ckan-devstaller">
ckan-compose
</a>
, ckan-devstaller improves installation speeds{" "}
<strong>from hours/days to just minutes</strong> depending on your
download speed.
</p>
<div className="flex gap-2">
<Link href="/docs/builder" className={cn(buttonVariants(), "not-prose")}>
Get started
</Link>
</div>
</WhyPanel>
) : null}
{active === 1 ? (
<WhyPanel>
<h3>
<BlocksIcon className="w-4 h-4 inline-block mr-1 mb-1" />
Customize your installation with the Builder.
</h3>
<p>
Try out the interactive web GUI for customizing your CKAN
installation. You can select:
</p>
<ul>
<li>Presets</li>
<li>CKAN version</li>
<li>Extensions</li>
<li>Features</li>
</ul>
<p>
Then you can copy the provided ckan-devstaller command to run your
selected configuration.
</p>
<div className="mt-4 flex flex-row items-center gap-1.5 not-prose">
<Link href="/docs/builder" className={cn(buttonVariants())}>
Try out the Builder
</Link>
</div>
</WhyPanel>
) : null}
{active === 2 ? (
<WhyPanel>
<h3>
<TerminalIcon className="w-4 h-4 inline-block mr-1 mb-1" />
Designed for developers.
</h3>
<p>
We've kept development use cases in mind while developing
ckan-devstaller, such as:
</p>
<ul>
<li>Trying out a new version of CKAN</li>
<li>Developing CKAN extensions and themes</li>
</ul>
<div className="flex gap-2">
<Link
href="/docs/reference/installation-architecture"
className={cn(buttonVariants(), "not-prose")}
>
View the installation architecture
</Link>
<Link
href="https://github.com/dathere/ckan-devstaller"
className={cn(buttonVariants({ variant: "ghost" }))}
>
Source code
</Link>
</div>
</WhyPanel>
) : null}
{active === 3 ? (
<WhyPanel>
<h3>
<Trash2Icon className="w-4 h-4 inline-block mr-1 mb-1" />
Uninstall CKAN with ease.
</h3>
<p>
After you've installed CKAN with ckan-devstaller, you can
uninstall CKAN with ease. This allows for quickly re-installing
CKAN for a different use case.
</p>
{props.codeblockUninstall}
<Link
href="/docs/tutorials/uninstall-ckan"
className={cn(buttonVariants(), "not-prose")}
>
Learn more about uninstalling
</Link>
</WhyPanel>
) : null}
</div>
</div>
);
}
function WhyPanel(props: HTMLProps<HTMLDivElement>) {
return (
<div
{...props}
className={cn(
"duration-700 animate-in fade-in text-sm prose",
props.className,
)}
>
{props.children}
</div>
);
}

View file

@ -6,6 +6,8 @@
"dependencies": { "dependencies": {
"@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tabs": "^1.1.13",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
@ -227,6 +229,8 @@
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="],
"@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w=="], "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w=="],
"@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="],
@ -245,6 +249,8 @@
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="],
"@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="],
"@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],

View file

@ -11,26 +11,26 @@ export default function CKANVersionBuilderSection({ config, setConfig }: any) {
<Cards> <Cards>
<Card <Card
icon={<SailboatIcon />} icon={<SailboatIcon />}
title="2.11.3" title="2.11.4"
className={ className={
config.ckanVersion === "2.11.3" config.ckanVersion === "2.11.4"
? selectedCardClasses ? selectedCardClasses
: "cursor-pointer" : "cursor-pointer"
} }
onClick={() => { onClick={() => {
setConfig({ ...config, ckanVersion: "2.11.3" }); setConfig({ ...config, ckanVersion: "2.11.4" });
}} }}
></Card> ></Card>
<Card <Card
icon={<SailboatIcon />} icon={<SailboatIcon />}
title="2.10.8" title="2.10.9"
className={ className={
config.ckanVersion === "2.10.8" config.ckanVersion === "2.10.9"
? selectedCardClasses ? selectedCardClasses
: "cursor-pointer" : "cursor-pointer"
} }
onClick={() => { onClick={() => {
setConfig({ ...config, ckanVersion: "2.10.8" }); setConfig({ ...config, ckanVersion: "2.10.9" });
}} }}
></Card> ></Card>
</Cards> </Cards>

View file

@ -49,7 +49,7 @@ export default function PresetsBuilderSection({
setConfig({ setConfig({
...config, ...config,
preset: "dathere-default", preset: "dathere-default",
ckanVersion: "2.11.3", ckanVersion: "2.11.4",
extensions: ["ckanext-scheming", "DataStore", "DataPusher+"], extensions: ["ckanext-scheming", "DataStore", "DataPusher+"],
features: ["enable-ssh"], features: ["enable-ssh"],
}); });

View file

@ -1,18 +1,13 @@
"use client"; "use client";
import { CodeBlock, Pre } from "fumadocs-ui/components/codeblock"; import { CodeBlock, Pre } from "fumadocs-ui/components/codeblock";
import defaultMdxComponents from "fumadocs-ui/mdx"; import { Label } from "@/components/ui/label"
import { import { Switch } from "@/components/ui/switch"
BarChartBigIcon,
BlocksIcon,
SailboatIcon,
TerminalSquareIcon,
} from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import PresetsBuilderSection from "./builder-sections/presets"; import PresetsBuilderSection from "@/components/builder-sections/presets";
import CKANVersionBuilderSection from "./builder-sections/ckan-version"; import CKANVersionBuilderSection from "@/components/builder-sections/ckan-version";
import CKANExtensionsBuilderSection from "./builder-sections/ckan-extensions"; import CKANExtensionsBuilderSection from "@/components/builder-sections/ckan-extensions";
import FeaturesBuilderSection from "./builder-sections/features"; import FeaturesBuilderSection from "@/components/builder-sections/features";
export type Config = { export type Config = {
preset: string | undefined; preset: string | undefined;
@ -25,11 +20,28 @@ export const selectedCardClasses =
"bg-blue-100 dark:bg-blue-950 border-blue-300 dark:border-blue-900 border-2"; "bg-blue-100 dark:bg-blue-950 border-blue-300 dark:border-blue-900 border-2";
export default function Builder() { export default function Builder() {
const { Card, Cards } = defaultMdxComponents; const [downloadScript, setDownloadScript] = useState(true);
const downloadScriptString = `#!/usr/bin/env bash
# Update/Upgrade system dependencies
sudo apt update -y
sudo apt upgrade -y
# Install curl
sudo apt install curl -y
# Change to the home directory
cd ~/
# Download the ckan-devstaller binary file
wget https://github.com/dathere/ckan-devstaller/releases/download/0.3.1/ckan-devstaller
# Add execute permission to ckan-devstaller binary file
sudo chmod +x ./ckan-devstaller\n\n# Run ckan-devstaller script\n`;
const [command, setCommand] = useState("./ckan-devstaller"); const [command, setCommand] = useState("./ckan-devstaller");
const [config, setConfig] = useState<Config>({ const [config, setConfig] = useState<Config>({
preset: "ckan-only", preset: "ckan-only",
ckanVersion: "2.11.3", ckanVersion: "2.11.4",
extensions: [], extensions: [],
features: [], features: [],
}); });
@ -52,12 +64,16 @@ ${ckanVersionString}${extensionsString ? extensionsString : ""}${featuresString
}, [config]); }, [config]);
return ( return (
<div className="grid grid-cols-3 gap-4"> <div className="md:grid md:grid-cols-3 md:gap-4">
<div className="col-span-1 border-r-2 pr-4"> <div className="md:col-span-1 md:border-r-2 md:pr-4">
<div className="sticky top-8"> <div className="sticky top-8">
<h2>ckan-devstaller command</h2> <h2>ckan-devstaller command</h2>
<div className="flex items-center space-x-2">
<Switch defaultChecked={downloadScript} onCheckedChange={() => setDownloadScript(!downloadScript)} id="download-script" />
<Label htmlFor="download-script">Include download script?</Label>
</div>
<CodeBlock title="Installation command"> <CodeBlock title="Installation command">
<Pre className="text-wrap pl-4 max-w-[21rem]">{command}</Pre> <Pre className="text-wrap pl-4 max-w-[21rem]">{downloadScript ? downloadScriptString : ""}{command}</Pre>
</CodeBlock> </CodeBlock>
<h2>Selected configuration</h2> <h2>Selected configuration</h2>
<div> <div>
@ -87,7 +103,7 @@ ${ckanVersionString}${extensionsString ? extensionsString : ""}${featuresString
</div> </div>
</div> </div>
</div> </div>
<div className="col-span-2"> <div className="md:col-span-2">
<h2>Configuration options</h2> <h2>Configuration options</h2>
<PresetsBuilderSection config={config} setConfig={setConfig} /> <PresetsBuilderSection config={config} setConfig={setConfig} />
<CKANVersionBuilderSection config={config} setConfig={setConfig} /> <CKANVersionBuilderSection config={config} setConfig={setConfig} />

View file

@ -0,0 +1,9 @@
import type { ReactNode } from 'react';
export function Steps({ children }: { children: ReactNode }) {
return <div className="fd-steps">{children}</div>;
}
export function Step({ children }: { children: ReactNode }) {
return <div className="fd-step">{children}</div>;
}

View file

@ -20,6 +20,7 @@ const buttonVariants = cva(
sm: "h-9 px-3", sm: "h-9 px-3",
lg: "h-11 px-6", lg: "h-11 px-6",
xs: "px-2 py-1.5 text-xs", xs: "px-2 py-1.5 text-xs",
"icon-xs": "p-1 [&_svg]:size-4"
}, },
}, },
defaultVariants: { defaultVariants: {

View file

@ -0,0 +1,26 @@
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName
export { Label }

View file

@ -0,0 +1,29 @@
"use client"
import * as React from "react"
import * as SwitchPrimitives from "@radix-ui/react-switch"
import { cn } from "@/lib/utils"
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-green-500 data-[state=unchecked]:bg-gray-500 dark:data-[state=checked]:bg-blue-500 dark:data-[state=unchecked]:bg-gray-600",
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-white dark:bg-blue-800 shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName
export { Switch }

View file

@ -4,6 +4,19 @@ description: Customize your CKAN installation before running ckan-devstaller.
icon: Blocks icon: Blocks
--- ---
ckan-devstaller attempts to install a CKAN instance from source along with [ckan-compose](https://github.com/tino097/ckan-compose/tree/ckan-devstaller) and other optional features, intended for development use in a new Ubuntu 22.04 instance.
<Callout title="Please run ckan-devstaller in a new Ubuntu 22.04 instance only" type="error">Make sure `ckan-devstaller` is run in a **new** Ubuntu 22.04 instance. Do NOT run `ckan-devstaller` in an existing instance that is important for your usage.</Callout>
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
<Accordions type="single">
<Accordion title="Note for VirtualBox users">If you are using Ubuntu 22.04 on VirtualBox, you may need to add your user to the sudoers file before running the ckan-devstaller install script. Open a terminal in your virtual machine (VM), run `su -` and log in as the root user with the password you used to set up the VM, then type: <br /><br />`sudo adduser <username> sudo`<br /><br /> where `<username>` is your username then restart your VM and run the ckan-devstaller installer script.</Accordion>
<Accordion title="Note for ARM64 users">Currently `ckan-devstaller` supports `x86_64` architecture. `ARM64` support is planned.</Accordion>
</Accordions>
---
import Builder from "@/components/builder"; import Builder from "@/components/builder";
<Builder /> <Builder />

View file

@ -0,0 +1,37 @@
---
title: Changelog for ckan-devstaller v0.3.0 (2025-10-14)
---
Since v0.2.1 of ckan-devstaller, there have been many new features and changes now available in v0.3.0.
## New web app: ckan-devstaller.dathere.com
We've released a new web app [ckan-devstaller.dathere.com](https://ckan-devstaller.dathere.com) as the primary documentation site for ckan-devstaller.
## Builder page
There is now an interactive web GUI, the [Builder](/docs/builder), for users to customize their CKAN installation before copying the (now updated) ckan-devstaller command and running it on their terminal. This helps resolve issue [#6](https://github.com/dathere/ckan-devstaller/issues/11).
## Updated Quick Start page
The [Quick Start](/docs) page now includes three options for suggested installation methods:
1. Use the interactive [Builder](/docs/builder).
2. Install the "CKAN-only" preset with a script which installs the latest stable version of CKAN and ckan-compose (with optional non-interactive script).
3. Install the "datHere default" preset with a script which installs the latest stable version of CKAN and ckan-compose (with optional non-interactive script) along with the DataStore, ckanext-scheming, and DataPusher+ extensions and also installs the `openssh-server` package.
## Installation architecture page
There is now an [Installation Architecture](/docs/reference/installation-architecture) page in the Reference section of the web app that provides a visual representation of where `ckan-devstaller` installs relevant files/folders.
## Uninstall CKAN page
There is now an [Uninstall CKAN](/docs/tutorials/uninstall-ckan) page in the Tutorials section of the web app that helps users understand how to uninstall their newly installed CKAN installation. This includes the option to either use the new `ckan-devstaller uninstall` subcommand or run the script directly.
## README update
The README on the [ckan-devstaller GitHub repository](https://github.com/dathere/ckan-devstaller) has been updated to have a more user-friendly focus for users and developers that may be new to CKAN thanks to the suggestions by [@drw](https://github.com/drw) in issues [#10](https://github.com/dathere/ckan-devstaller/issues/10) and [#11](https://github.com/dathere/ckan-devstaller/issues/11).
## Changelog section
We've added a Changelog section to the web app to denote new changes to `ckan-devstaller` for each release.

View file

@ -0,0 +1,37 @@
---
title: Changelog for ckan-devstaller v0.3.1 (2025-10-30)
---
## Updated CKAN default versions to latest stable versions
We have updated ckan-devstaller to suggest the recent releases of CKAN [2.11.4](https://docs.ckan.org/en/2.11/changelog.html#v-2-11-4-2025-10-29) and [2.10.9](https://docs.ckan.org/en/2.10/changelog.html#v-2-10-9-2025-10-29).
## Added the "Developing with WSL" page in the Reference section
Developers using Windows may benefit from the new [Developing with WSL](/docs/reference/developing-with-wsl) page in the Reference section to try their builds of ckan-devstaller and verify things work on a new install of Ubuntu 22.04.
Alternatively if not trying to reset an entire WSL environment, developers can look into [`ckan-devstaller uninstall`](/docs/tutorials/uninstall-ckan).
## Added install script switch to Builder page
There is now a switch on the Builder page that is enabled by default for including the installation script for ckan-devstaller before running it. This is necessary for users that don't have ckan-devstaller installed yet and are using the Builder over the Quick Start scripts.
## Default start with the Builder page
We've changed "Get Started" links (and similar links) to the Builder page instead of the Quick Start page to users can quickly access the Builder again and get started using ckan-devstaller.
## Update ckan-compose links to use ckan-devstaller branch
We've updated ckan-compose links to use [the ckan-devstaller branch](https://github.com/tino097/ckan-compose/tree/ckan-devstaller).
## Update VirtualBox notice with better formatting
To ensure users using VirtualBox see the full command for adding a user to the sudoers file, we've improved the formatting of the note.
## GitHub Action to verify CKAN install runs
We've added a GitHub Action to run on push to the main branch to verify that a CKAN-only install runs. This doesn't include verbose testing but rather ensuring that the ckan-devstaller finishes without errors.
## New --skip-run flag
We've added a `--skip-run` flag to skip running CKAN at the end of installation.

View file

@ -4,22 +4,29 @@ description: Get started with ckan-devstaller and install CKAN within minutes.
icon: Zap icon: Zap
--- ---
ckan-devstaller attempts to install a CKAN instance from source along with [ckan-compose](https://github.com/tino097/ckan-compose) and other optional features, intended for development use in a new Ubuntu 22.04 instance. ckan-devstaller attempts to install a CKAN instance from source along with [ckan-compose](https://github.com/tino097/ckan-compose/tree/ckan-devstaller) and other optional features, intended for development use in a new Ubuntu 22.04 instance.
<Callout title="Please run ckan-devstaller in a new Ubuntu 22.04 instance only" type="error">Make sure `ckan-devstaller` is run in a **new** Ubuntu 22.04 instance. Do NOT run `ckan-devstaller` in an existing instance that is important for your usage.</Callout> <Callout title="Please run ckan-devstaller in a new Ubuntu 22.04 instance only" type="error">Make sure `ckan-devstaller` is run in a **new** Ubuntu 22.04 instance. Do NOT run `ckan-devstaller` in an existing instance that is important for your usage.</Callout>
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion'; import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
<Accordions type="single"> <Accordions type="single">
<Accordion title="Note for VirtualBox users">If you are using Ubuntu 22.04 on VirtualBox, you may need to add your user to the sudoers file before running the ckan-devstaller install script. Open a terminal in your virtual machine (VM), run `su -` and log in as the root user with the password you used to set up the VM, then type `sudo adduser <username> sudo` where `<username>` is your username then restart your VM and run the ckan-devstaller installer script.</Accordion> <Accordion title="Note for VirtualBox users">If you are using Ubuntu 22.04 on VirtualBox, you may need to add your user to the sudoers file before running the ckan-devstaller install script. Open a terminal in your virtual machine (VM), run `su -` and log in as the root user with the password you used to set up the VM, then type: <br /><br />`sudo adduser <username> sudo`<br /><br /> where `<username>` is your username then restart your VM and run the ckan-devstaller installer script.</Accordion>
<Accordion title="Note for ARM64 users">Currently `ckan-devstaller` supports `x86_64` architecture. `ARM64` support is planned.</Accordion> <Accordion title="Note for ARM64 users">Currently `ckan-devstaller` supports `x86_64` architecture. `ARM64` support is planned.</Accordion>
</Accordions> </Accordions>
--- ---
You have several options to choose from for installation. Here are a few: ## Install CKAN using ckan-devstaller
## [1/3] Customize your CKAN installation with the Builder (Recommended) You have several options to choose from for installation. Here are a few you may choose one from:
import { Step, Steps } from 'fumadocs-ui/components/steps';
<Steps>
<Step>
### Customize your CKAN installation with the Builder (Recommended)
<Card <Card
icon={<BlocksIcon />} icon={<BlocksIcon />}
@ -29,25 +36,33 @@ You have several options to choose from for installation. Here are a few:
Click here to customize your CKAN installation with an interactive web GUI Click here to customize your CKAN installation with an interactive web GUI
</Card> </Card>
## [2/3] Install the "CKAN-only" preset </Step>
By running the following script, ckan-devstaller will be downloaded and the default configuration for installing CKAN 2.11.3 with ckan-compose will be selected. You can then customize your configuration interactively in your terminal after running this script. <Step>
### Install the "CKAN-only" preset
By running the following script, ckan-devstaller will be downloaded and the default configuration for installing CKAN with ckan-compose will be selected. You can then customize your configuration interactively in your terminal after running this script.
```bash ```bash
wget -O - https://github.com/dathere/ckan-devstaller/releases/download/0.3.0/install.bash | bash wget -O - https://github.com/dathere/ckan-devstaller/releases/download/0.3.1/install.bash | bash
``` ```
If you'd rather skip the interactivity and go straight to installation, then run the following script instead: If you'd rather skip the interactivity and go straight to installation, then run the following script instead:
```bash ```bash
wget -O - https://github.com/dathere/ckan-devstaller/releases/download/0.3.0/install.bash | bash -s skip-interactive wget -O - https://github.com/dathere/ckan-devstaller/releases/download/0.3.1/install.bash | bash -s skip-interactive
``` ```
## [3/3] Install the "datHere Default" preset </Step>
<Step>
### Install the "datHere Default" preset
The following script will download ckan-devstaller and select the following configuration: The following script will download ckan-devstaller and select the following configuration:
- CKAN 2.11.3 - CKAN latest stable version
- [ckan-compose](https://github.com/tino097/ckan-compose/tree/ckan-devstaller) - [ckan-compose](https://github.com/tino097/ckan-compose/tree/ckan-devstaller)
- [DataStore extension](https://docs.ckan.org/en/2.11/maintaining/datastore.html) - [DataStore extension](https://docs.ckan.org/en/2.11/maintaining/datastore.html)
- [ckanext-scheming extension](https://github.com/ckan/ckanext-scheming) - [ckanext-scheming extension](https://github.com/ckan/ckanext-scheming)
@ -58,15 +73,18 @@ The following script will download ckan-devstaller and select the following conf
You can then customize your configuration interactively in your terminal after running this script. You can then customize your configuration interactively in your terminal after running this script.
```bash ```bash
wget -O - https://github.com/dathere/ckan-devstaller/releases/download/0.3.0/install.bash | bash -s dathere-default wget -O - https://github.com/dathere/ckan-devstaller/releases/download/0.3.1/install.bash | bash -s dathere-default
``` ```
If you'd rather skip the interactivity and go straight to installation, then run the following script instead: If you'd rather skip the interactivity and go straight to installation, then run the following script instead:
```bash ```bash
wget -O - https://github.com/dathere/ckan-devstaller/releases/download/0.3.0/install.bash | bash -s dathere-default skip-interactive wget -O - https://github.com/dathere/ckan-devstaller/releases/download/0.3.1/install.bash | bash -s dathere-default skip-interactive
``` ```
</Step>
</Steps>
## Learn more ## Learn more
import { BlocksIcon, HomeIcon, GitMergeIcon, Trash2Icon } from 'lucide-react'; import { BlocksIcon, HomeIcon, GitMergeIcon, Trash2Icon } from 'lucide-react';

View file

@ -5,6 +5,9 @@
"builder", "builder",
"---Further reading---", "---Further reading---",
"tutorials", "tutorials",
"reference" "reference",
"changelog",
"--- ---",
"[Privacy Policy](https://dathere.com/privacy-policy/)"
] ]
} }

View file

@ -0,0 +1,131 @@
---
title: Developing with WSL
description: Tips on how to develop ckan-devstaller on a Windows machine by leveraging Windows Subsystem for Linux.
---
When developing ckan-devstaller on Windows, using Windows Subsystem for Linux (WSL) can be advantageous to demo an Ubuntu 22.04 environment without having to set up a virtual machine.
import { Step, Steps } from 'fumadocs-ui/components/steps';
import { File, Folder, Files } from 'fumadocs-ui/components/files';
<Steps>
<Step>
### Install the Ubuntu-22.04 distribution
You'll need to have the Ubuntu-22.04 distribution installed by running the following command:
```bash
wsl --install Ubuntu-22.04 --version 2
```
</Step>
<Step>
### Export a base image
</Step>
Here's the expected set up where we create a WSL folder:
```files
/c/Users/rzmk/WSL
├── images
| ├── ubuntu-22-04-snapshot.tar
├── instances
| ├── cdr.vdhx
```
First we'll generate the `images/ubuntu-22-04-snapshot.tar` file:
```bash
wsl --export Ubuntu-22.04 ./images/ubuntu-22-04-snapshot.tar
```
<Step>
### Generate a VDHX file for our new instance
Next we'll generate the `instances/cdr.vdhx` file, so we can run the following:
```bash
wsl --import cdr ./instances ./images/ubuntu-22-04-snapshot.tar
```
</Step>
<Step>
### Access your new instance as root
Now try to access your new Ubuntu 22.04 instance `cdr` as the `root` user by running:
```bash
wsl -d cdr
```
</Step>
<Step>
### Set yourself as the admin user on the instance and log in as the admin user
Once logged in as `root`, you'll want to edit the `/etc/wsl.conf` file and modify it so you can login as an admin user instead of `root`. We can use the `nano` editor to modify the file:
```bash
nano /etc/wsl.conf
```
Modify the file by adding a `[user]` section with the value `default=rzmk` (where `rzmk` is your username). The file should look similar to this:
```ini
[boot]
systemd=true
[user]
default=rzmk
```
Now leave your instance:
```bash
exit
```
Then terminate the instance:
```bash
wsl --terminate cdr
```
Run the instance again and you should be logged in as your admin user by default now:
```bash
wsl -d cdr
```
</Step>
<Step>
### Install ckan-devstaller
Great, now you can go to the home directory and run one of the [quick start](/docs) scripts:
```bash
cd ~/
```
</Step>
</Steps>
## Removing your instance
When you want to remove your instance (e.g. so that you can start a brand new instance) then run the following to unregister it:
```bash
wsl --unregister cdr
```
Then you can follow the steps again from step 3 to generate and try out a new instance.

View file

@ -27,7 +27,7 @@ The configuration file for CKAN is installed at `/etc/ckan/default/ckan.ini`:
## ckan-compose ## ckan-compose
We install certain first-time install files and `ckan-compose` as a directory in the user's home (`~`) directory. For example for the user `adam`: We install certain first-time install files and [`ckan-compose`](https://github.com/tino097/ckan-compose/tree/ckan-devstaller) as a directory in the user's home (`~`) directory. For example for the user `adam`:
```files ```files
/home/adam /home/adam

View file

@ -0,0 +1,6 @@
{
"pages": [
"installation-architecture",
"developing-with-wsl"
]
}

View file

@ -13,6 +13,8 @@
"dependencies": { "dependencies": {
"@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tabs": "^1.1.13",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",

View file

@ -11,7 +11,7 @@ sudo apt install curl -y
cd ~/ cd ~/
# Download the ckan-devstaller binary file # Download the ckan-devstaller binary file
curl -LO https://github.com/dathere/ckan-devstaller/releases/download/0.3.0/ckan-devstaller curl -LO https://github.com/dathere/ckan-devstaller/releases/download/0.3.1/ckan-devstaller
# Add execute permission to ckan-devstaller binary file # Add execute permission to ckan-devstaller binary file
sudo chmod +x ./ckan-devstaller sudo chmod +x ./ckan-devstaller
@ -22,9 +22,9 @@ skip_interactive=$2
if [ $preset == "dathere-default" ]; then if [ $preset == "dathere-default" ]; then
if [ $skip_interactive == "skip-interactive" ]; then if [ $skip_interactive == "skip-interactive" ]; then
./ckan-devstaller --ckan-version 2.11.3 --extensions ckanext-scheming DataStore DataPusher+ --features enable-ssh --skip-interactive ./ckan-devstaller --ckan-version 2.11.4 --extensions ckanext-scheming DataStore DataPusher+ --features enable-ssh --skip-interactive
else else
./ckan-devstaller --ckan-version 2.11.3 --extensions ckanext-scheming DataStore DataPusher+ --features enable-ssh ./ckan-devstaller --ckan-version 2.11.4 --extensions ckanext-scheming DataStore DataPusher+ --features enable-ssh
fi fi
else else
if [ $preset == "skip-interactive" ]; then if [ $preset == "skip-interactive" ]; then

View file

@ -5,7 +5,9 @@ mod styles;
use crate::{ use crate::{
questions::{question_ckan_version, question_ssh, question_sysadmin}, questions::{question_ckan_version, question_ssh, question_sysadmin},
steps::{ steps::{
step_install_ahoy, step_install_and_run_ckan_compose, step_install_curl, step_install_ahoy, step_install_and_run_ckan_compose,
step_install_ckanext_scheming_extension, step_install_curl,
step_install_datapusher_plus_extension, step_install_datastore_extension,
step_install_docker, step_install_openssh, step_package_updates, step_install_docker, step_install_openssh, step_package_updates,
}, },
styles::{important_text, step_text, success_text}, styles::{important_text, step_text, success_text},
@ -14,7 +16,6 @@ use anyhow::Result;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use human_panic::{metadata, setup_panic}; use human_panic::{metadata, setup_panic};
use inquire::Confirm; use inquire::Confirm;
use serde_json::json;
use std::{path::PathBuf, str::FromStr}; use std::{path::PathBuf, str::FromStr};
use xshell::cmd; use xshell::cmd;
use xshell_venv::{Shell, VirtualEnv}; use xshell_venv::{Shell, VirtualEnv};
@ -26,6 +27,9 @@ struct Args {
/// Skip interactive steps /// Skip interactive steps
#[arg(short, long)] #[arg(short, long)]
skip_interactive: bool, skip_interactive: bool,
/// Skip running CKAN at the end of installation
#[arg(short, long)]
skip_run: bool,
#[arg(short, long)] #[arg(short, long)]
/// CKAN version to install defined by semantic versioning from official releases from https://github.com/ckan/ckan /// CKAN version to install defined by semantic versioning from official releases from https://github.com/ckan/ckan
ckan_version: Option<String>, ckan_version: Option<String>,
@ -109,7 +113,7 @@ rm -rf README ckan-compose ahoy dpp_default_config.ini get-docker.sh permissions
ckan_version: if args.ckan_version.is_some() { ckan_version: if args.ckan_version.is_some() {
args.ckan_version.unwrap() args.ckan_version.unwrap()
} else { } else {
"2.11.3".to_string() "2.11.4".to_string()
}, },
sysadmin: default_sysadmin.clone(), sysadmin: default_sysadmin.clone(),
extension_datastore: args extension_datastore: args
@ -133,7 +137,7 @@ rm -rf README ckan-compose ahoy dpp_default_config.ini get-docker.sh permissions
if config.ssh { if config.ssh {
default_config_text.push_str("\n- Install openssh-server to enable SSH access"); default_config_text.push_str("\n- Install openssh-server to enable SSH access");
} }
default_config_text.push_str("\n- Install ckan-compose (https://github.com/tino097/ckan-compose) which sets up the CKAN backend (PostgreSQL, SOLR, Redis)"); default_config_text.push_str("\n- Install ckan-compose (https://github.com/tino097/ckan-compose/tree/ckan-devstaller) which sets up the CKAN backend (PostgreSQL, SOLR, Redis)");
default_config_text.push_str(format!("\n- Install CKAN v{}", config.ckan_version).as_str()); default_config_text.push_str(format!("\n- Install CKAN v{}", config.ckan_version).as_str());
if config.extension_datastore { if config.extension_datastore {
default_config_text.push_str("\n- Install the DataStore extension"); default_config_text.push_str("\n- Install the DataStore extension");
@ -263,207 +267,26 @@ rm -rf README ckan-compose ahoy dpp_default_config.ini get-docker.sh permissions
success_text(format!("6. Installed CKAN {}.", config.ckan_version).as_str()) success_text(format!("6. Installed CKAN {}.", config.ckan_version).as_str())
); );
// Install extensions
if config.extension_datastore {
step_install_datastore_extension("7.".to_string(), &sh, username.clone())?;
}
if config.extension_ckanext_scheming {
step_install_ckanext_scheming_extension("8.".to_string(), &sh, username.clone())?;
}
if config.extension_datapusher_plus { if config.extension_datapusher_plus {
println!( step_install_datapusher_plus_extension(
"\n{} Enabling DataStore plugin, adding config URLs in /etc/ckan/default/ckan.ini and updating permissions...", "9.".to_string(),
step_text("7."), &sh,
); sysadmin_username,
let mut conf = ini::Ini::load_from_file("/etc/ckan/default/ckan.ini")?; username.clone(),
let app_main_section = conf.section_mut(Some("app:main")).unwrap();
let mut ckan_plugins = app_main_section.get("ckan.plugins").unwrap().to_string();
ckan_plugins.push_str(" datastore");
app_main_section.insert("ckan.plugins", ckan_plugins);
app_main_section.insert(
"ckan.datastore.write_url",
"postgresql://ckan_default:pass@localhost/datastore_default",
);
app_main_section.insert(
"ckan.datastore.read_url",
"postgresql://datastore_default:pass@localhost/datastore_default",
);
app_main_section.insert("ckan.datastore.sqlsearch.enabled", "true");
conf.write_to_file("/etc/ckan/default/ckan.ini")?;
let postgres_container_id = cmd!(
sh,
"sudo docker ps -aqf name=^ckan-devstaller-project-postgres$"
)
.read()?;
let set_permissions_output = cmd!(
sh,
"ckan -c /etc/ckan/default/ckan.ini datastore set-permissions"
)
.read()?;
std::fs::write("permissions.sql", set_permissions_output)?;
loop {
std::thread::sleep(std::time::Duration::from_secs(2));
if std::fs::exists("permissions.sql")? {
break;
}
}
sh.change_dir(format!("/home/{username}"));
cmd!(
sh,
"sudo docker cp permissions.sql {postgres_container_id}:/permissions.sql"
)
.run()?;
cmd!(sh, "sudo docker exec {postgres_container_id} psql -U ckan_default --set ON_ERROR_STOP=1 -f permissions.sql").run()?;
println!(
"{}",
success_text(
"7. Enabled DataStore plugin, set DataStore URLs in /etc/ckan/default/ckan.ini, and updated permissions."
)
);
println!(
"{}",
step_text("\n{} Installing ckanext-scheming and DataPusher+ extensions..."),
);
cmd!(
sh,
"pip install -e git+https://github.com/ckan/ckanext-scheming.git#egg=ckanext-scheming"
)
.run()?;
let mut conf = ini::Ini::load_from_file("/etc/ckan/default/ckan.ini")?;
let app_main_section = conf.section_mut(Some("app:main")).unwrap();
let mut ckan_plugins = app_main_section.get("ckan.plugins").unwrap().to_string();
ckan_plugins.push_str(" scheming_datasets");
cmd!(
sh,
"ckan config-tool /etc/ckan/default/ckan.ini -s app:main ckan.plugins={ckan_plugins}"
)
.run()?;
cmd!(sh, "ckan config-tool /etc/ckan/default/ckan.ini -s app:main scheming.presets=ckanext.scheming:presets.json").run()?;
cmd!(sh, "ckan config-tool /etc/ckan/default/ckan.ini -s app:main scheming.dataset_fallback=false").run()?;
// app_main_section.insert("ckan.plugins", ckan_plugins);
// app_main_section.insert("scheming.presets", "ckanext.scheming:presets.json");
// app_main_section.insert("scheming.dataset_fallback", "false");
// conf.write_to_file("/etc/ckan/default/ckan.ini")?;
// Install DataPusher+
cmd!(sh, "sudo apt install python3-virtualenv python3-dev python3-pip python3-wheel build-essential libxslt1-dev libxml2-dev zlib1g-dev git libffi-dev libpq-dev uchardet -y").run()?;
sh.change_dir("/usr/lib/ckan/default/src");
cmd!(sh, "pip install -e git+https://github.com/dathere/datapusher-plus.git@main#egg=datapusher-plus").run()?;
sh.change_dir("/usr/lib/ckan/default/src/datapusher-plus");
cmd!(sh, "pip install -r requirements.txt").run()?;
sh.change_dir(format!("/home/{username}"));
cmd!(sh, "wget https://github.com/dathere/qsv/releases/download/4.0.0/qsv-4.0.0-x86_64-unknown-linux-gnu.zip").run()?;
cmd!(sh, "sudo apt install unzip -y").run()?;
cmd!(sh, "unzip qsv-4.0.0-x86_64-unknown-linux-gnu.zip").run()?;
cmd!(sh, "sudo rm -rf qsv-4.0.0-x86_64-unknown-linux-gnu.zip").run()?;
cmd!(sh, "sudo mv ./qsvdp_glibc-2.31 /usr/local/bin/qsvdp").run()?;
let mut conf = ini::Ini::load_from_file("/etc/ckan/default/ckan.ini")?;
let app_main_section = conf.section_mut(Some("app:main")).unwrap();
let mut ckan_plugins = app_main_section.get("ckan.plugins").unwrap().to_string();
ckan_plugins.push_str(" datapusher_plus");
cmd!(
sh,
"ckan config-tool /etc/ckan/default/ckan.ini -s app:main ckan.plugins={ckan_plugins}"
)
.run()?;
cmd!(sh, "ckan config-tool /etc/ckan/default/ckan.ini -s app:main scheming.dataset_schemas=ckanext.datapusher_plus:dataset-druf.yaml").run()?;
// app_main_section.insert("ckan.plugins", ckan_plugins);
// app_main_section.insert(
// "scheming.dataset_schemas",
// "ckanext.datapusher_plus:dataset-druf.yaml",
// );
// conf.write_to_file("/etc/ckan/default/ckan.ini")?;
let dpp_default_config = r#"
ckanext.datapusher_plus.use_proxy = false
ckanext.datapusher_plus.download_proxy =
ckanext.datapusher_plus.ssl_verify = false
# supports INFO, DEBUG, TRACE - use DEBUG or TRACE when debugging scheming Formulas
ckanext.datapusher_plus.upload_log_level = INFO
ckanext.datapusher_plus.formats = csv tsv tab ssv xls xlsx xlsxb xlsm ods geojson shp qgis zip
ckanext.datapusher_plus.pii_screening = false
ckanext.datapusher_plus.pii_found_abort = false
ckanext.datapusher_plus.pii_regex_resource_id_or_alias =
ckanext.datapusher_plus.pii_show_candidates = false
ckanext.datapusher_plus.pii_quick_screen = false
ckanext.datapusher_plus.qsv_bin = /usr/local/bin/qsvdp
ckanext.datapusher_plus.preview_rows = 100
ckanext.datapusher_plus.download_timeout = 300
ckanext.datapusher_plus.max_content_length = 1256000000000
ckanext.datapusher_plus.chunk_size = 16384
ckanext.datapusher_plus.default_excel_sheet = 0
ckanext.datapusher_plus.sort_and_dupe_check = true
ckanext.datapusher_plus.dedup = false
ckanext.datapusher_plus.unsafe_prefix = unsafe_
ckanext.datapusher_plus.reserved_colnames = _id
ckanext.datapusher_plus.prefer_dmy = false
ckanext.datapusher_plus.ignore_file_hash = true
ckanext.datapusher_plus.auto_index_threshold = 3
ckanext.datapusher_plus.auto_index_dates = true
ckanext.datapusher_plus.auto_unique_index = true
ckanext.datapusher_plus.summary_stats_options =
ckanext.datapusher_plus.add_summary_stats_resource = false
ckanext.datapusher_plus.summary_stats_with_preview = false
ckanext.datapusher_plus.qsv_stats_string_max_length = 32767
ckanext.datapusher_plus.qsv_dates_whitelist = date,time,due,open,close,created
ckanext.datapusher_plus.qsv_freq_limit = 10
ckanext.datapusher_plus.auto_alias = true
ckanext.datapusher_plus.auto_alias_unique = false
ckanext.datapusher_plus.copy_readbuffer_size = 1048576
ckanext.datapusher_plus.type_mapping = {"String": "text", "Integer": "numeric","Float": "numeric","DateTime": "timestamp","Date": "date","NULL": "text"}
ckanext.datapusher_plus.auto_spatial_simplication = true
ckanext.datapusher_plus.spatial_simplication_relative_tolerance = 0.1
ckanext.datapusher_plus.latitude_fields = latitude,lat
ckanext.datapusher_plus.longitude_fields = longitude,long,lon
ckanext.datapusher_plus.jinja2_bytecode_cache_dir = /tmp/jinja2_butecode_cache
ckanext.datapusher_plus.auto_unzip_one_file = true
ckanext.datapusher_plus.api_token = <CKAN service account token for CKAN user with sysadmin privileges>
ckanext.datapusher_plus.describeGPT_api_key = <Token for OpenAI API compatible service>
ckanext.datapusher_plus.file_bin = /usr/bin/file
ckanext.datapusher_plus.enable_druf = false
ckanext.datapusher_plus.enable_form_redirect = true
"#;
std::fs::write("dpp_default_config.ini", dpp_default_config)?;
cmd!(
sh,
"ckan config-tool /etc/ckan/default/ckan.ini -f dpp_default_config.ini"
)
.run()?;
let resource_formats_str = std::fs::read_to_string(
"/usr/lib/ckan/default/src/ckan/config/resource_formats.json",
)?; )?;
let mut resource_formats_val: serde_json::Value =
serde_json::from_str(&resource_formats_str)?;
let all_resource_formats = resource_formats_val
.get_mut(0)
.unwrap()
.as_array_mut()
.unwrap();
all_resource_formats.push(json!([
"TAB",
"Tab Separated Values File",
"text/tab-separated-values",
[]
]));
std::fs::write(
"/usr/lib/ckan/default/src/ckan/config/resource_formats.json",
serde_json::to_string(&resource_formats_val)?,
)?;
cmd!(sh, "sudo locale-gen en_US.UTF-8").run()?;
cmd!(sh, "sudo update-locale").run()?;
let token_command_output = cmd!(
sh,
"ckan -c /etc/ckan/default/ckan.ini user token add {sysadmin_username} dpplus"
)
.read()?;
let tail_output = cmd!(sh, "tail -n 1").stdin(token_command_output).read()?;
let dpp_api_token = cmd!(sh, "tr -d '\t'").stdin(tail_output).read()?;
cmd!(sh, "ckan config-tool /etc/ckan/default/ckan.ini ckanext.datapusher_plus.api_token={dpp_api_token}").env("LC_ALL", "en_US.UTF-8").run()?;
cmd!(
sh,
"ckan -c /etc/ckan/default/ckan.ini db upgrade -p datapusher_plus"
)
.run()?;
println!(
"{}",
success_text("8. Installed ckanext-scheming and DataPusher+ extensions.")
);
} }
if !args.skip_run {
println!("\n{}", success_text("Running CKAN instance...")); println!("\n{}", success_text("Running CKAN instance..."));
cmd!(sh, "ckan -c /etc/ckan/default/ckan.ini run").run()?; cmd!(sh, "ckan -c /etc/ckan/default/ckan.ini run").run()?;
}
} else { } else {
println!("Cancelling installation."); println!("Cancelling installation.");
} }

View file

@ -16,7 +16,7 @@ pub fn question_ssh() -> Result<bool> {
} }
pub fn question_ckan_version() -> Result<String> { pub fn question_ckan_version() -> Result<String> {
let ckan_version_options: Vec<&str> = vec!["2.11.3", "2.10.8", "Other"]; let ckan_version_options: Vec<&str> = vec!["2.11.4", "2.10.9", "Other"];
let answer_ckan_version = Select::new( let answer_ckan_version = Select::new(
"What CKAN version would you like to install? (optional)", "What CKAN version would you like to install? (optional)",
ckan_version_options, ckan_version_options,
@ -26,7 +26,7 @@ pub fn question_ckan_version() -> Result<String> {
if answer_ckan_version == "Other" { if answer_ckan_version == "Other" {
Ok( Ok(
Text::new("What CKAN version would you like to install? (optional)") Text::new("What CKAN version would you like to install? (optional)")
.with_default("2.11.3") .with_default("2.11.4")
.prompt()?, .prompt()?,
) )
} else { } else {

View file

@ -1,5 +1,6 @@
use crate::styles::{highlighted_text, important_text, step_text, success_text}; use crate::styles::{highlighted_text, important_text, step_text, success_text};
use anyhow::Result; use anyhow::Result;
use serde_json::json;
use xshell::{Shell, cmd}; use xshell::{Shell, cmd};
pub fn step_intro() { pub fn step_intro() {
@ -10,7 +11,7 @@ pub fn step_intro() {
); );
println!( println!(
"This installer should assist in setting up {} from a source installation along with ckan-compose. If you have any issues, please report them at https://support.dathere.com or https://github.com/dathere/ckan-devstaller/issues.", "This installer should assist in setting up {} from a source installation along with ckan-compose. If you have any issues, please report them at https://support.dathere.com or https://github.com/dathere/ckan-devstaller/issues.",
highlighted_text("CKAN 2.11.3") highlighted_text("CKAN 2.11.4")
); );
println!( println!(
"\nYou may also learn more about ckan-devstaller at https://ckan-devstaller.dathere.com." "\nYou may also learn more about ckan-devstaller at https://ckan-devstaller.dathere.com."
@ -135,3 +136,252 @@ POSTGRES_PASSWORD=pass";
); );
Ok(()) Ok(())
} }
pub fn step_install_datastore_extension(
step_prefix: String,
sh: &Shell,
username: String,
) -> Result<()> {
println!(
"\n{} Enabling DataStore plugin, adding config URLs in /etc/ckan/default/ckan.ini and updating permissions...",
step_text(step_prefix.as_str()),
);
let mut conf = ini::Ini::load_from_file("/etc/ckan/default/ckan.ini")?;
let app_main_section = conf.section_mut(Some("app:main")).unwrap();
let mut ckan_plugins = app_main_section.get("ckan.plugins").unwrap().to_string();
ckan_plugins.push_str(" datastore");
app_main_section.insert("ckan.plugins", ckan_plugins);
app_main_section.insert(
"ckan.datastore.write_url",
"postgresql://ckan_default:pass@localhost/datastore_default",
);
app_main_section.insert(
"ckan.datastore.read_url",
"postgresql://datastore_default:pass@localhost/datastore_default",
);
app_main_section.insert("ckan.datastore.sqlsearch.enabled", "true");
conf.write_to_file("/etc/ckan/default/ckan.ini")?;
let postgres_container_id = cmd!(
sh,
"sudo docker ps -aqf name=^ckan-devstaller-project-postgres$"
)
.read()?;
let set_permissions_output = cmd!(
sh,
"ckan -c /etc/ckan/default/ckan.ini datastore set-permissions"
)
.read()?;
std::fs::write("permissions.sql", set_permissions_output)?;
loop {
std::thread::sleep(std::time::Duration::from_secs(2));
if std::fs::exists("permissions.sql")? {
break;
}
}
sh.change_dir(format!("/home/{username}"));
cmd!(
sh,
"sudo docker cp permissions.sql {postgres_container_id}:/permissions.sql"
)
.run()?;
cmd!(sh, "sudo docker exec {postgres_container_id} psql -U ckan_default --set ON_ERROR_STOP=1 -f permissions.sql").run()?;
println!(
"{}",
success_text(
format!("{step_prefix} Enabled DataStore plugin, set DataStore URLs in /etc/ckan/default/ckan.ini, and updated permissions.").as_str()
)
);
Ok(())
}
pub fn step_install_ckanext_scheming_extension(
step_prefix: String,
sh: &Shell,
username: String,
) -> Result<()> {
println!(
"{}",
step_text("\n{} Installing the ckanext-scheming extension..."),
);
cmd!(
sh,
"pip install -e git+https://github.com/ckan/ckanext-scheming.git#egg=ckanext-scheming"
)
.run()?;
let mut conf = ini::Ini::load_from_file("/etc/ckan/default/ckan.ini")?;
let app_main_section = conf.section_mut(Some("app:main")).unwrap();
let mut ckan_plugins = app_main_section.get("ckan.plugins").unwrap().to_string();
ckan_plugins.push_str(" scheming_datasets");
cmd!(
sh,
"ckan config-tool /etc/ckan/default/ckan.ini -s app:main ckan.plugins={ckan_plugins}"
)
.run()?;
cmd!(sh, "ckan config-tool /etc/ckan/default/ckan.ini -s app:main scheming.presets=ckanext.scheming:presets.json").run()?;
cmd!(
sh,
"ckan config-tool /etc/ckan/default/ckan.ini -s app:main scheming.dataset_fallback=false"
)
.run()?;
// app_main_section.insert("ckan.plugins", ckan_plugins);
// app_main_section.insert("scheming.presets", "ckanext.scheming:presets.json");
// app_main_section.insert("scheming.dataset_fallback", "false");
// conf.write_to_file("/etc/ckan/default/ckan.ini")?;
Ok(())
}
pub fn step_install_datapusher_plus_extension(
step_prefix: String,
sh: &Shell,
sysadmin_username: String,
username: String,
) -> Result<()> {
// Install DataPusher+
println!(
"{}",
step_text(format!("\n{step_prefix} Installing DataPusher+ extension...").as_str())
);
cmd!(sh, "sudo apt install python3-virtualenv python3-dev python3-pip python3-wheel build-essential libxslt1-dev libxml2-dev zlib1g-dev git libffi-dev libpq-dev uchardet -y").run()?;
sh.change_dir("/usr/lib/ckan/default/src");
cmd!(
sh,
"pip install -e git+https://github.com/dathere/datapusher-plus.git@main#egg=datapusher-plus"
)
.run()?;
sh.change_dir("/usr/lib/ckan/default/src/datapusher-plus");
cmd!(sh, "pip install -r requirements.txt").run()?;
sh.change_dir(format!("/home/{username}"));
cmd!(sh, "wget https://github.com/dathere/qsv/releases/download/4.0.0/qsv-4.0.0-x86_64-unknown-linux-gnu.zip").run()?;
cmd!(sh, "sudo apt install unzip -y").run()?;
cmd!(sh, "unzip qsv-4.0.0-x86_64-unknown-linux-gnu.zip").run()?;
cmd!(sh, "sudo rm -rf qsv-4.0.0-x86_64-unknown-linux-gnu.zip").run()?;
cmd!(sh, "sudo mv ./qsvdp_glibc-2.31 /usr/local/bin/qsvdp").run()?;
let mut conf = ini::Ini::load_from_file("/etc/ckan/default/ckan.ini")?;
let app_main_section = conf.section_mut(Some("app:main")).unwrap();
let mut ckan_plugins = app_main_section.get("ckan.plugins").unwrap().to_string();
ckan_plugins.push_str(" datapusher_plus");
app_main_section.insert("ckan.plugins", ckan_plugins);
app_main_section.insert(
"scheming.dataset_schemas",
"ckanext.datapusher_plus:dataset-druf.yaml",
);
app_main_section.insert("ckanext.datapusher_plus.use_proxy", "false");
app_main_section.insert("ckanext.datapusher_plus.download_proxy", "");
app_main_section.insert("ckanext.datapusher_plus.ssl_verify", "false");
app_main_section.insert("ckanext.datapusher_plus.upload_log_level", "INFO");
app_main_section.insert(
"ckanext.datapusher_plus.formats",
"csv tsv tab ssv xls xlsx xlsxb xlsm ods geojson shp qgis zip",
);
app_main_section.insert("ckanext.datapusher_plus.pii_screening", "false");
app_main_section.insert("ckanext.datapusher_plus.pii_found_abort", "false");
app_main_section.insert("ckanext.datapusher_plus.pii_regex_resource_id_or_alias", "");
app_main_section.insert("ckanext.datapusher_plus.pii_show_candidates", "false");
app_main_section.insert("ckanext.datapusher_plus.pii_quick_screen", "false");
app_main_section.insert("ckanext.datapusher_plus.qsv_bin", "/usr/local/bin/qsvdp");
app_main_section.insert("ckanext.datapusher_plus.preview_rows", "100");
app_main_section.insert("ckanext.datapusher_plus.download_timeout", "300");
app_main_section.insert(
"ckanext.datapusher_plus.max_content_length",
"1256000000000",
);
app_main_section.insert("ckanext.datapusher_plus.chunk_size", "16384");
app_main_section.insert("ckanext.datapusher_plus.default_excel_sheet", "0");
app_main_section.insert("ckanext.datapusher_plus.sort_and_dupe_check", "true");
app_main_section.insert("ckanext.datapusher_plus.dedup", "false");
app_main_section.insert("ckanext.datapusher_plus.unsafe_prefix", "unsafe_");
app_main_section.insert("ckanext.datapusher_plus.reserved_colnames", "_id");
app_main_section.insert("ckanext.datapusher_plus.prefer_dmy", "false");
app_main_section.insert("ckanext.datapusher_plus.ignore_file_hash", "true");
app_main_section.insert("ckanext.datapusher_plus.auto_index_threshold", "3");
app_main_section.insert("ckanext.datapusher_plus.auto_index_dates", "true");
app_main_section.insert("ckanext.datapusher_plus.auto_unique_index", "true");
app_main_section.insert("ckanext.datapusher_plus.summary_stats_options", "");
app_main_section.insert(
"ckanext.datapusher_plus.add_summary_stats_resource",
"false",
);
app_main_section.insert(
"ckanext.datapusher_plus.summary_stats_with_preview",
"false",
);
app_main_section.insert(
"ckanext.datapusher_plus.qsv_stats_string_max_length",
"32767",
);
app_main_section.insert(
"ckanext.datapusher_plus.qsv_dates_whitelist",
"date,time,due,open,close,created",
);
app_main_section.insert("ckanext.datapusher_plus.qsv_freq_limit", "10");
app_main_section.insert("ckanext.datapusher_plus.auto_alias", "true");
app_main_section.insert("ckanext.datapusher_plus.auto_alias_unique", "false");
app_main_section.insert("ckanext.datapusher_plus.copy_readbuffer_size", "1048576");
app_main_section.insert("ckanext.datapusher_plus.type_mapping", r#"{"String": "text", "Integer": "numeric","Float": "numeric","DateTime": "timestamp","Date": "date","NULL": "text"}"#);
app_main_section.insert("ckanext.datapusher_plus.auto_spatial_simplication", "true");
app_main_section.insert(
"ckanext.datapusher_plus.spatial_simplication_relative_tolerance",
"0.1",
);
app_main_section.insert("ckanext.datapusher_plus.latitude_fields", "latitude,lat");
app_main_section.insert(
"ckanext.datapusher_plus.longitude_fields",
"longitude,long,lon",
);
app_main_section.insert(
"ckanext.datapusher_plus.jinja2_bytecode_cache_dir",
"/tmp/jinja2_butecode_cache",
);
app_main_section.insert("ckanext.datapusher_plus.auto_unzip_one_file", "true");
app_main_section.insert(
"ckanext.datapusher_plus.api_token",
"<CKAN service account token for CKAN user with sysadmin privileges>",
);
app_main_section.insert(
"ckanext.datapusher_plus.describeGPT_api_key",
"<Token for OpenAI API compatible service>",
);
app_main_section.insert("ckanext.datapusher_plus.file_bin", "/usr/bin/file");
app_main_section.insert("ckanext.datapusher_plus.enable_druf", "false");
app_main_section.insert("ckanext.datapusher_plus.enable_form_redirect", "true");
conf.write_to_file("/etc/ckan/default/ckan.ini")?;
let resource_formats_str = std::fs::read_to_string(
"/usr/lib/ckan/default/src/ckan/ckan/config/resource_formats.json",
)?;
let mut resource_formats_val: serde_json::Value = serde_json::from_str(&resource_formats_str)?;
let all_resource_formats = resource_formats_val
.get_mut(0)
.unwrap()
.as_array_mut()
.unwrap();
all_resource_formats.push(json!([
"TAB",
"Tab Separated Values File",
"text/tab-separated-values",
[]
]));
std::fs::write(
"/usr/lib/ckan/default/src/ckan/ckan/config/resource_formats.json",
serde_json::to_string(&resource_formats_val)?,
)?;
cmd!(sh, "sudo locale-gen en_US.UTF-8").run()?;
cmd!(sh, "sudo update-locale").run()?;
let token_command_output = cmd!(
sh,
"ckan -c /etc/ckan/default/ckan.ini user token add {sysadmin_username} dpplus"
)
.read()?;
let tail_output = cmd!(sh, "tail -n 1").stdin(token_command_output).read()?;
let dpp_api_token = cmd!(sh, "tr -d '\t'").stdin(tail_output).read()?;
cmd!(sh, "ckan config-tool /etc/ckan/default/ckan.ini ckanext.datapusher_plus.api_token={dpp_api_token}").env("LC_ALL", "en_US.UTF-8").run()?;
cmd!(
sh,
"ckan -c /etc/ckan/default/ckan.ini db upgrade -p datapusher_plus"
)
.run()?;
println!(
"{}",
success_text(format!("{step_prefix} Installed DataPusher+ extension.").as_str())
);
Ok(())
}