Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic cloud deploy #562

Merged
merged 6 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,8 @@ jobs:
projectName: cli
directory: dist
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
# TODO: This doesn't include the examples. How can we fix that?
- name: Deploy to Observable Cloud
run: yarn deploy -m "$(git log -1 --pretty=%s)"
env:
OBSERVABLE_TOKEN: ${{ secrets.OBSERVABLE_API_TOKEN }}
14 changes: 11 additions & 3 deletions bin/observable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,19 @@ try {
}
case "deploy": {
const {
values: {config, root}
values: {config, root, message}
} = helpArgs(command, {
options: {...CONFIG_OPTION}
options: {
...CONFIG_OPTION,
message: {
type: "string",
short: "m"
}
}
});
await import("../src/deploy.js").then(async (deploy) => deploy.deploy({config: await readConfig(config, root)}));
await import("../src/deploy.js").then(async (deploy) =>
deploy.deploy({config: await readConfig(config, root), message})
);
break;
}
case "preview": {
Expand Down
3 changes: 3 additions & 0 deletions docs/.observablehq/deploy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"projectId": "497663d656fa5557"
}
10 changes: 7 additions & 3 deletions observablehq.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default {
{name: "Files", path: "/javascript/files"},
{name: "Promises", path: "/javascript/promises"},
{name: "Generators", path: "/javascript/generators"},
{name: "Mutables", path: "/javascript/mutables"},
{name: "Mutables", path: "/javascript/mutables"}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prettier did this. I can revert it if you want, but I'm inclined to keep it formatted.

]
},
{
Expand Down Expand Up @@ -88,10 +88,14 @@ export default {
{name: "TeX", path: "/lib/tex"},
{name: "TopoJSON", path: "/lib/topojson"},
{name: "Vega-Lite", path: "/lib/vega-lite"},
{name: "ZIP", path: "/lib/zip"},
{name: "ZIP", path: "/lib/zip"}
]
},
{name: "Contributing", path: "/contributing"}
],
footer: `© ${new Date().getUTCFullYear()} Observable, Inc.`
footer: `© ${new Date().getUTCFullYear()} Observable, Inc.`,
deploy: {
workspace: "@observablehq",
project: "cli"
}
};
5 changes: 3 additions & 2 deletions src/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {blue, bold, hangingIndentLog, magenta, yellow} from "./tty.js";

export interface DeployOptions {
config: Config;
message: string | undefined;
}

export interface DeployEffects extends ConfigEffects {
Expand All @@ -46,7 +47,7 @@ const defaultEffects: DeployEffects = {
};

/** Deploy a project to ObservableHQ */
export async function deploy({config}: DeployOptions, effects = defaultEffects): Promise<void> {
export async function deploy({config, message}: DeployOptions, effects = defaultEffects): Promise<void> {
Telemetry.record({event: "deploy", step: "start"});
const {logger} = effects;
const apiKey = await effects.getObservableApiKey(effects);
Expand Down Expand Up @@ -166,7 +167,7 @@ export async function deploy({config}: DeployOptions, effects = defaultEffects):
await effects.setDeployConfig(config.root, {projectId});

// Create the new deploy on the server
const message = await promptUserForInput(effects.input, effects.output, "Deploy message: ");
if (message === undefined) message = await promptUserForInput(effects.input, effects.output, "Deploy message: ");
const deployId = await apiClient.postDeploy({projectId, message});

// Build the project
Expand Down
58 changes: 41 additions & 17 deletions test/deploy-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ const TEST_CONFIG = await normalizeConfig({
title: "Mock BI",
deploy: {workspace: "mock-user-ws", project: "bi"}
});
const TEST_OPTIONS = {config: TEST_CONFIG, message: undefined};

// TODO These tests need mockJsDelivr, too!
describe("deploy", () => {
Expand All @@ -145,7 +146,30 @@ describe("deploy", () => {
.start();

const effects = new MockDeployEffects({deployConfig}).addIoResponse(/^Deploy message: /, "fix some bugs");
await deploy({config: TEST_CONFIG}, effects);
await deploy(TEST_OPTIONS, effects);

effects.close();
});

it("does not prompt for a message when one is supplied on the command line", async () => {
const projectId = "project123";
const deployConfig = {projectId};
const deployId = "deploy456";
const message = "this is test deploy";
getCurentObservableApi()
.handleGetProject({
workspaceLogin: TEST_CONFIG.deploy!.workspace,
projectSlug: TEST_CONFIG.deploy!.project,
projectId
})
.handlePostDeploy({projectId, deployId})
.handlePostDeployFile({deployId, repeat: EXTRA_FILES.length + 1})
.handlePostDeployUploaded({deployId})
.start();

// no io response for message
const effects = new MockDeployEffects({deployConfig});
await deploy({...TEST_OPTIONS, message}, effects);

effects.close();
});
Expand All @@ -172,7 +196,7 @@ describe("deploy", () => {
.addIoResponse(/Do you want to create it now\?/, "y")
.addIoResponse(/^Deploy message: /, "fix some bugs");

await deploy({config: TEST_CONFIG}, effects);
await deploy(TEST_OPTIONS, effects);

effects.close();
});
Expand All @@ -195,7 +219,7 @@ describe("deploy", () => {
);

try {
await deploy({config: TEST_CONFIG}, effects);
await deploy(TEST_OPTIONS, effects);
assert.fail("expected error");
} catch (error) {
CliError.assert(error, {message: "User cancelled deploy.", print: false, exitCode: 0});
Expand All @@ -219,7 +243,7 @@ describe("deploy", () => {
const effects = new MockDeployEffects({deployConfig, isTty: false});

try {
await deploy({config: TEST_CONFIG}, effects);
await deploy(TEST_OPTIONS, effects);
assert.fail("expected error");
} catch (error) {
CliError.assert(error, {message: "Cancelling deploy due to non-existent project."});
Expand Down Expand Up @@ -247,7 +271,7 @@ describe("deploy", () => {
const effects = new MockDeployEffects({deployConfig, isTty: true});

try {
await deploy({config}, effects);
await deploy({...TEST_OPTIONS, config}, effects);
assert.fail("expected error");
} catch (err) {
CliError.assert(err, {message: /You haven't configured a project title/});
Expand Down Expand Up @@ -280,7 +304,7 @@ describe("deploy", () => {
);

try {
await deploy({config}, effects);
await deploy({...TEST_OPTIONS, config}, effects);
assert.fail("expected error");
} catch (err) {
CliError.assert(err, {message: /Workspace super-ws-123 not found/});
Expand All @@ -297,7 +321,7 @@ describe("deploy", () => {
const effects = new MockDeployEffects({isTty: true});

try {
await deploy({config}, effects);
await deploy({...TEST_OPTIONS, config}, effects);
assert.fail("expected error");
} catch (err) {
CliError.assert(err, {message: /"ACME Inc.".*isn't valid.*"acme-inc"/});
Expand All @@ -314,7 +338,7 @@ describe("deploy", () => {
const effects = new MockDeployEffects({isTty: true});

try {
await deploy({config}, effects);
await deploy({...TEST_OPTIONS, config}, effects);
assert.fail("expected error");
} catch (err) {
CliError.assert(err, {message: /"Business Intelligence".*isn't valid.*"business-intelligence"/});
Expand All @@ -327,7 +351,7 @@ describe("deploy", () => {
const effects = new MockDeployEffects({apiKey: null});

try {
await deploy({config: TEST_CONFIG}, effects);
await deploy(TEST_OPTIONS, effects);
assert.fail("expected error");
} catch (err) {
if (!(err instanceof Error)) throw err;
Expand All @@ -347,7 +371,7 @@ describe("deploy", () => {
const effects = new MockDeployEffects({apiKey: invalidApiKey});

try {
await deploy({config: TEST_CONFIG}, effects);
await deploy(TEST_OPTIONS, effects);
assert.fail("Should have thrown");
} catch (error) {
assert.ok(isHttpError(error));
Expand All @@ -372,7 +396,7 @@ describe("deploy", () => {
);

try {
await deploy({config: TEST_CONFIG}, effects);
await deploy(TEST_OPTIONS, effects);
fail("Should have thrown an error");
} catch (error) {
assert.ok(isHttpError(error));
Expand Down Expand Up @@ -400,7 +424,7 @@ describe("deploy", () => {
);

try {
await deploy({config: TEST_CONFIG}, effects);
await deploy(TEST_OPTIONS, effects);
fail("Should have thrown an error");
} catch (error) {
assert.ok(isHttpError(error));
Expand Down Expand Up @@ -429,7 +453,7 @@ describe("deploy", () => {
);

try {
await deploy({config: TEST_CONFIG}, effects);
await deploy(TEST_OPTIONS, effects);
fail("Should have thrown an error");
} catch (error) {
assert.ok(isHttpError(error));
Expand All @@ -443,7 +467,7 @@ describe("deploy", () => {
const config = {...TEST_CONFIG, deploy: null};
const effects = new MockDeployEffects();
try {
await deploy({config}, effects);
await deploy({...TEST_OPTIONS, config}, effects);
assert.fail("expected error");
} catch (err) {
CliError.assert(err, {message: /You haven't configured a project to deploy to/});
Expand All @@ -467,7 +491,7 @@ describe("deploy", () => {
const effects = new MockDeployEffects({deployConfig: {projectId: "oldProjectId"}, isTty: true})
.addIoResponse(/Do you want to deploy anyway\?/, "y")
.addIoResponse(/^Deploy message: /, "deploying to re-created project");
await deploy({config: TEST_CONFIG}, effects);
await deploy(TEST_OPTIONS, effects);
effects.logger.assertAtLeastLogs([/This project was last deployed/]);
effects.close();
});
Expand All @@ -486,7 +510,7 @@ describe("deploy", () => {
"n"
);
try {
await deploy({config: TEST_CONFIG}, effects);
await deploy(TEST_OPTIONS, effects);
assert.fail("expected error");
} catch (error) {
CliError.assert(error, {message: "User cancelled deploy", print: false, exitCode: 0});
Expand All @@ -506,7 +530,7 @@ describe("deploy", () => {
.start();
const effects = new MockDeployEffects({deployConfig: {projectId: "oldProjectId"}, isTty: false, debug: true});
try {
await deploy({config: TEST_CONFIG}, effects);
await deploy(TEST_OPTIONS, effects);
assert.fail("expected error");
} catch (error) {
CliError.assert(error, {message: "Cancelling deploy due to misconfiguration."});
Expand Down