Skip to content

Commit

Permalink
Merge pull request #9064 from quarto-dev/feature/publish-hugging-face
Browse files Browse the repository at this point in the history
Feature/publish hugging face
  • Loading branch information
cscheid authored Mar 14, 2024
2 parents b9e30ae + e175d49 commit 751baef
Show file tree
Hide file tree
Showing 8 changed files with 344 additions and 110 deletions.
42 changes: 25 additions & 17 deletions src/command/publish/cmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ export const publishCommand =
"Publish document (prompt for provider)",
"quarto publish document.qmd",
)
.example(
"Publish project to Hugging Face Spaces",
"quarto publish huggingface",
)
.example(
"Publish project to Netlify",
"quarto publish netlify",
Expand Down Expand Up @@ -163,7 +167,7 @@ async function publishAction(
await initYamlIntelligence();

// coalesce options
const publishOptions = await createPublishOptions(options, path);
const publishOptions = await createPublishOptions(options, provider, path);

// helper to publish (w/ account confirmation)
const doPublish = async (
Expand Down Expand Up @@ -302,6 +306,7 @@ async function publish(

async function createPublishOptions(
options: PublishCommandOptions,
provider?: PublishProvider,
path?: string,
): Promise<PublishOptions> {
const nbContext = notebookContext();
Expand All @@ -315,27 +320,30 @@ async function createPublishOptions(
// determine publish input
let input: ProjectContext | string | undefined;

if (provider && provider.resolveProjectPath) {
const resolvedPath = provider.resolveProjectPath(path);
try {
if (Deno.statSync(resolvedPath).isDirectory) {
path = resolvedPath;
}
} catch (_e) {
// ignore
}
}

// check for directory (either website or single-file project)
const project = (await projectContext(path, nbContext)) ||
singleFileProjectContext(path, nbContext);
if (Deno.statSync(path).isDirectory) {
if (project) {
if (projectIsWebsite(project)) {
input = project;
} else if (
projectIsManuscript(project) && project.files.input.length > 0
) {
input = project;
} else if (project.files.input.length === 1) {
input = project.files.input[0];
}
if (projectIsWebsite(project)) {
input = project;
} else if (
projectIsManuscript(project) && project.files.input.length > 0
) {
input = project;
} else if (project.files.input.length === 1) {
input = project.files.input[0];
} else {
const inputFiles = await projectInputFiles(project);
if (inputFiles.files.length === 1) {
input = inputFiles.files[0];
}
}
if (!input) {
throw new Error(
`The specified path (${path}) is not a website, manuscript or book project so cannot be published.`,
);
Expand Down
36 changes: 36 additions & 0 deletions src/core/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,42 @@

import { which } from "./path.ts";
import { execProcess } from "./process.ts";
import SemVer from "semver/mod.ts";

export async function gitCmds(dir: string, cmds: Array<string[]>) {
for (const cmd of cmds) {
if (
!(await execProcess({
cmd: ["git", ...cmd],
cwd: dir,
})).success
) {
throw new Error();
}
}
}

export async function gitVersion(): Promise<SemVer> {
const result = await execProcess(
{
cmd: ["git", "--version"],
stdout: "piped",
},
);
if (!result.success) {
throw new Error(
"Unable to determine git version. Please check that git is installed and available on your PATH.",
);
}
const match = result.stdout?.match(/git version (\d+\.\d+\.\d+)/);
if (match) {
return new SemVer(match[1]);
} else {
throw new Error(
`Unable to determine git version from string ${result.stdout}`,
);
}
}

export async function lsFiles(
cwd?: string,
Expand Down
11 changes: 11 additions & 0 deletions src/publish/common/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* errors.ts
*
* Copyright (C) 2020-2024 Posit Software, PBC
*/

export const throwUnableToPublish = (reason: string, provider: string) => {
throw new Error(
`Unable to publish to ${provider} (${reason})`,
);
};
66 changes: 66 additions & 0 deletions src/publish/common/git.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* git.ts
*
* Copyright (C) 2020-2024 Posit Software, PBC
*/

import { websiteBaseurl } from "../../project/types/website/website-config.ts";
import { gitHubContext } from "../../core/github.ts";
import { ProjectContext } from "../../project/types.ts";
import { dirname } from "../../deno_ral/path.ts";
import { AccountToken, AccountTokenType } from "../provider-types.ts";
import { PublishOptions } from "../types.ts";
import { GitHubContext } from "../../core/github-types.ts";
import { throwUnableToPublish } from "./errors.ts";

export async function gitHubContextForPublish(input: string | ProjectContext) {
// Create the base context
const dir = typeof input === "string" ? dirname(input) : input.dir;
const context = await gitHubContext(dir);

// always prefer configured website URL
if (typeof input !== "string") {
const configSiteUrl = websiteBaseurl(input?.config);
if (configSiteUrl) {
context.siteUrl = configSiteUrl;
}
}
return context;
}

export function anonymousAccount(): AccountToken {
return {
type: AccountTokenType.Anonymous,
name: "anonymous",
server: null,
token: "anonymous",
};
}

export function verifyContext(
ghContext: GitHubContext,
provider: string,
) {
if (!ghContext.git) {
throwUnableToPublish(
"git does not appear to be installed on this system",
provider,
);
}

// validate we are in a git repo
if (!ghContext.repo) {
throwUnableToPublish(
"the target directory is not a git repository",
provider,
);
}

// validate that we have an origin
if (!ghContext.originUrl) {
throwUnableToPublish(
"the git repository does not have a remote origin",
provider,
);
}
}
91 changes: 7 additions & 84 deletions src/publish/gh-pages/gh-pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { execProcess } from "../../core/process.ts";
import { ProjectContext } from "../../project/types.ts";
import {
AccountToken,
AccountTokenType,
PublishFiles,
PublishProvider,
} from "../provider-types.ts";
Expand All @@ -27,10 +26,13 @@ import { sleep } from "../../core/wait.ts";
import { joinUrl } from "../../core/url.ts";
import { completeMessage, withSpinner } from "../../core/console.ts";
import { renderForPublish } from "../common/publish.ts";
import { websiteBaseurl } from "../../project/types/website/website-config.ts";
import { RenderFlags } from "../../command/render/types.ts";
import SemVer from "semver/mod.ts";
import { gitHubContext } from "../../core/github.ts";
import { gitCmds, gitVersion } from "../../core/git.ts";
import {
anonymousAccount,
gitHubContextForPublish,
verifyContext,
} from "../common/git.ts";

export const kGhpages = "gh-pages";
const kGhpagesDescription = "GitHub Pages";
Expand All @@ -50,35 +52,13 @@ export const ghpagesProvider: PublishProvider = {
isNotFound,
};

function anonymousAccount(): AccountToken {
return {
type: AccountTokenType.Anonymous,
name: "anonymous",
server: null,
token: "anonymous",
};
}

function accountTokens() {
return Promise.resolve([anonymousAccount()]);
}

async function authorizeToken(options: PublishOptions) {
const ghContext = await gitHubContextForPublish(options.input);

if (!ghContext.git) {
throwUnableToPublish("git does not appear to be installed on this system");
}

// validate we are in a git repo
if (!ghContext.repo) {
throwUnableToPublish("the target directory is not a git repository");
}

// validate that we have an origin
if (!ghContext.originUrl) {
throwUnableToPublish("the git repository does not have a remote origin");
}
verifyContext(ghContext, "GitHub Pages");

// good to go!
return Promise.resolve(anonymousAccount());
Expand Down Expand Up @@ -106,28 +86,6 @@ function resolveTarget(
return Promise.resolve(target);
}

async function gitVersion(): Promise<SemVer> {
const result = await execProcess(
{
cmd: ["git", "--version"],
stdout: "piped",
},
);
if (!result.success) {
throw new Error(
"Unable to determine git version. Please check that git is installed and available on your PATH.",
);
}
const match = result.stdout?.match(/git version (\d+\.\d+\.\d+)/);
if (match) {
return new SemVer(match[1]);
} else {
throw new Error(
`Unable to determine git version from string ${result.stdout}`,
);
}
}

async function publish(
_account: AccountToken,
type: "document" | "site",
Expand Down Expand Up @@ -405,38 +363,3 @@ async function gitCreateGhPages(dir: string) {
["push", "origin", `HEAD:gh-pages`],
]);
}

async function gitCmds(dir: string, cmds: Array<string[]>) {
for (const cmd of cmds) {
if (
!(await execProcess({
cmd: ["git", ...cmd],
cwd: dir,
})).success
) {
throw new Error();
}
}
}

// validate we have git
const throwUnableToPublish = (reason: string) => {
throw new Error(
`Unable to publish to GitHub Pages (${reason})`,
);
};

async function gitHubContextForPublish(input: string | ProjectContext) {
// Create the base context
const dir = typeof input === "string" ? dirname(input) : input.dir;
const context = await gitHubContext(dir);

// always prefer configured website URL
if (typeof input !== "string") {
const configSiteUrl = websiteBaseurl(input?.config);
if (configSiteUrl) {
context.siteUrl = configSiteUrl;
}
}
return context;
}
Loading

0 comments on commit 751baef

Please sign in to comment.