-
Notifications
You must be signed in to change notification settings - Fork 168
Deploy option to enable continuous deployment #1745
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
adaa867
fa5e762
6e614ca
a7efd4c
1077838
44b1e5f
b6c2a3f
bb790ef
93203ca
8128173
56e4d14
6f7c852
9e234a2
c9effe0
8c75f52
75cbf50
e80786b
ea676b1
06b52f7
a7ba250
5830afc
048fd88
7c719ae
a6ac45e
8407f88
476ebec
bc60124
14ce19b
27128bb
3a5de29
82f7b0d
105f0ed
e511cc7
bb9c311
ff60b80
f1cd74d
e188a39
fae07e1
41f9682
dd50d70
3f0bbb8
fede926
172bf50
87f707d
159d708
1a5c9f9
4a8e4e7
a23c35c
36e2153
3e5e669
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -204,7 +204,9 @@ class Deployer { | |
} | ||
|
||
private async cloudBuild(deployTarget: DeployTargetInfo) { | ||
if (deployTarget.create) return false; // TODO | ||
if (deployTarget.create) { | ||
throw Error("Incorrect deployTarget state"); | ||
} | ||
const {deployPollInterval: pollInterval = DEPLOY_POLL_INTERVAL_MS} = this.deployOptions; | ||
await this.apiClient.postProjectBuild(deployTarget.project.id); | ||
const spinner = this.effects.clack.spinner(); | ||
|
@@ -225,20 +227,25 @@ class Deployer { | |
deployTarget.workspace.login | ||
}/${deployTarget.project.slug}/deploys/${latestCreatedDeployId}` | ||
); | ||
return latestCreatedDeployId; | ||
// latestCreatedDeployId is initially null for a new project, but once | ||
// it changes to a string it can never change back; since we know it has | ||
// changed, we assert here that it’s not null | ||
return latestCreatedDeployId!; | ||
} | ||
await new Promise((resolve) => setTimeout(resolve, pollInterval)); | ||
} | ||
} | ||
|
||
private async maybeLinkGitHub(deployTarget: DeployTargetInfo): Promise<boolean> { | ||
if (!this.effects.isTty || deployTarget.create) return false; | ||
if (deployTarget.create) { | ||
throw Error("Incorrect deployTarget state"); | ||
tophtucker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
if (!this.effects.isTty) return false; | ||
tophtucker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (deployTarget.environment.build_environment_id && deployTarget.environment.source) { | ||
// can do cloud build | ||
return true; | ||
} else { | ||
// TODO Where should it look for .git? only supports projects at root rn… | ||
// const isGit = existsSync(this.deployOptions.config.root + "/.git"); | ||
// We only support cloud builds from the root directory so this ignores this.deployOptions.config.root | ||
const isGit = existsSync(".git"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. todo: log cwd when running yarn deploy. you can run yarn deploy from a child directory like src, but i think it still runs in the context of the root directory, in which case this is correct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tried logging cwd out of curiosity and indeed, as one would hope/expect, yarn deploy runs in the root directory no matter where you call it from. |
||
if (isGit) { | ||
const remotes = (await promisify(exec)("git remote -v", {cwd: this.deployOptions.config.root})).stdout | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note that in loader.ts we make our promises "by hand" (and with spawn instead of exec), but in create.ts we already use promisify, so… seems fine! |
||
|
@@ -264,7 +271,9 @@ class Deployer { | |
} else { | ||
// repo not auth’ed; link to auth page and poll for auth | ||
this.effects.clack.log.info( | ||
`Authorize Observable to access the ${bold(repoName)} repository: ${link("https://github.com/apps/observable-data-apps-dev/installations/select_target")}` | ||
`Authorize Observable to access the ${bold(repoName)} repository: ${link( | ||
"https://github.com/apps/observable-data-apps-dev/installations/select_target" | ||
)}` | ||
); | ||
const spinner = this.effects.clack.spinner(); | ||
spinner.start("Waiting for repository to be authorized"); | ||
|
@@ -292,23 +301,19 @@ class Deployer { | |
} | ||
} | ||
} else { | ||
throw new CliError("No GitHub remote"); // TODO better error | ||
this.effects.clack.log.error("No GitHub remote found"); | ||
} | ||
} else { | ||
throw new CliError("Not at root of a git repository"); // TODO better error | ||
this.effects.clack.log.error("Not at root of a git repository"); | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
private async startNewDeploy(): Promise<GetDeployResponse> { | ||
const deployConfig = await this.getUpdatedDeployConfig(); | ||
const deployTarget = await this.getDeployTarget(deployConfig); | ||
const deployConfig2 = await this.getUpdatedDeployConfig(); // TODO inelegant… move cd prompt to getUpdatedDeployConfig? | ||
let deployId; | ||
if (deployConfig2.continuousDeployment) { | ||
// TODO move maybeLinkGitHub so that continuous deployment is only enabled if it succeeds | ||
await this.maybeLinkGitHub(deployTarget); | ||
const {deployConfig, deployTarget} = await this.getDeployTarget(await this.getUpdatedDeployConfig()); | ||
let deployId: string | null; | ||
if (deployConfig.continuousDeployment) { | ||
deployId = await this.cloudBuild(deployTarget); | ||
} else { | ||
const buildFilePaths = await this.getBuildFilePaths(); | ||
|
@@ -360,8 +365,6 @@ class Deployer { | |
); | ||
} | ||
|
||
// TODO validate continuousDeployment | ||
|
||
if (deployConfig.projectId && (!deployConfig.projectSlug || !deployConfig.workspaceLogin)) { | ||
const spinner = this.effects.clack.spinner(); | ||
this.effects.clack.log.warn("The `projectSlug` or `workspaceLogin` is missing from your deploy.json."); | ||
|
@@ -394,7 +397,9 @@ class Deployer { | |
} | ||
|
||
// Get the deploy target, prompting the user as needed. | ||
private async getDeployTarget(deployConfig: DeployConfig): Promise<DeployTargetInfo> { | ||
private async getDeployTarget( | ||
deployConfig: DeployConfig | ||
): Promise<{deployTarget: DeployTargetInfo; deployConfig: DeployConfig}> { | ||
let deployTarget: DeployTargetInfo; | ||
if (deployConfig.workspaceLogin && deployConfig.projectSlug) { | ||
try { | ||
|
@@ -481,11 +486,11 @@ class Deployer { | |
workspaceId: deployTarget.workspace.id, | ||
accessLevel: deployTarget.accessLevel | ||
}); | ||
// TODO(toph): initial env config | ||
deployTarget = { | ||
create: false, | ||
workspace: deployTarget.workspace, | ||
project, | ||
// TODO: In the future we may have a default environment | ||
environment: { | ||
automatic_builds_enabled: null, | ||
build_environment_id: null, | ||
|
@@ -515,33 +520,40 @@ class Deployer { | |
} | ||
} | ||
|
||
let continuousDeployment = deployConfig.continuousDeployment; | ||
let {continuousDeployment} = deployConfig; | ||
if (continuousDeployment === null) { | ||
continuousDeployment = !!(await this.effects.clack.confirm({ | ||
const enable = await this.effects.clack.confirm({ | ||
message: wrapAnsi( | ||
`Do you want to enable continuous deployment? ${faint( | ||
"This builds in the cloud instead of on this machine and redeploys whenever you push to this repository." | ||
"This builds in the cloud and redeploys whenever you push to this repository." | ||
)}`, | ||
this.effects.outputColumns | ||
), | ||
active: "Yes", | ||
inactive: "No" | ||
})); | ||
active: "Yes, enable and build in cloud", | ||
inactive: "No, build locally" | ||
}); | ||
if (this.effects.clack.isCancel(enable)) throw new CliError("User canceled deploy", {print: false, exitCode: 0}); | ||
continuousDeployment = enable; | ||
} | ||
|
||
// Disables continuous deployment if there’s no env/source & we can’t link GitHub | ||
if (continuousDeployment) continuousDeployment = await this.maybeLinkGitHub(deployTarget); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fil says: if you enable continuous deployment, it should stay on, and if we can't connect to github for whatever reason (you're not in a repo, or no git remote, or no link in our database), the deploy should just fail. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done, and it feels like a much better way to think about it |
||
|
||
const newDeployConfig = { | ||
projectId: deployTarget.project.id, | ||
projectSlug: deployTarget.project.slug, | ||
workspaceLogin: deployTarget.workspace.login, | ||
continuousDeployment | ||
}; | ||
|
||
await this.effects.setDeployConfig( | ||
this.deployOptions.config.root, | ||
this.deployOptions.deployConfigPath, | ||
{ | ||
projectId: deployTarget.project.id, | ||
projectSlug: deployTarget.project.slug, | ||
workspaceLogin: deployTarget.workspace.login, | ||
continuousDeployment | ||
}, | ||
newDeployConfig, | ||
this.effects | ||
); | ||
|
||
return deployTarget; | ||
return {deployConfig: newDeployConfig, deployTarget}; | ||
} | ||
|
||
// Create the new deploy on the server. | ||
|
Uh oh!
There was an error while loading. Please reload this page.