Skip to content

Commit

Permalink
[eas-cli] Add workflow:run (#2669)
Browse files Browse the repository at this point in the history
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

We want to allow users to start workflows manually.

# How

I added a `createRunMutation` to GraphQL and used it here. We upload project archive, `eas.json` and use to start a new workflow run.

# Test Plan

```
Workflows are in beta and subject to breaking changes.

Compressing project files and uploading to EAS. Learn more: https://expo.fyi/eas-build-archive
✔ Uploaded eas.json to EAS 
✔ Uploaded project archive to EAS 1s

✔ Workflow started successfully. https://staging.expo.dev/accounts/sjchmiela/projects/staging-app/workflows/019301f1-b1b4-700d-9e3b-82dc97d76a32
```

Co-authored-by: Szymon Dziedzic <[email protected]>
  • Loading branch information
sjchmiela and szdziedzic authored Nov 7, 2024
1 parent d47b80a commit b81c60e
Show file tree
Hide file tree
Showing 10 changed files with 2,834 additions and 247 deletions.
2,357 changes: 2,158 additions & 199 deletions packages/eas-cli/graphql.schema.json

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions packages/eas-cli/src/build/utils/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,16 @@ export function getUpdateGroupUrl(
getExpoWebsiteBaseUrl()
).toString();
}

export function getWorkflowRunUrl(
accountName: string,
projectName: string,
workflowRunId: string
): string {
return new URL(
`/accounts/${encodeURIComponent(accountName)}/projects/${encodeURIComponent(
projectName
)}/workflows/${workflowRunId}`,
getExpoWebsiteBaseUrl()
).toString();
}
115 changes: 115 additions & 0 deletions packages/eas-cli/src/commands/workflow/run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import fs from 'node:fs';
import * as path from 'node:path';

import { getWorkflowRunUrl } from '../../build/utils/url';
import EasCommand from '../../commandUtils/EasCommand';
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
import { WorkflowProjectSourceType } from '../../graphql/generated';
import { WorkflowRunMutation } from '../../graphql/mutations/WorkflowRunMutation';
import Log, { link } from '../../log';
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
import { uploadAccountScopedEasJsonAsync } from '../../project/uploadAccountScopedEasJsonAsync';
import { uploadAccountScopedProjectSourceAsync } from '../../project/uploadAccountScopedProjectSourceAsync';

export default class WorkflowRun extends EasCommand {
static override description = 'Run an EAS workflow';

// TODO(@sjchmiela): Keep command hidden until workflows are live
static override hidden = true;
static override state = 'beta';

static override args = [{ name: 'file', description: 'Path to the workflow file to run' }];

static override flags = {
...EASNonInteractiveFlag,
};

static override contextDefinition = {
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Vcs,
...this.ContextOptions.LoggedIn,
};

async runAsync(): Promise<void> {
const { flags, args } = await this.parse(WorkflowRun);

Log.warn('Workflows are in beta and subject to breaking changes.');

const {
getDynamicPrivateProjectConfigAsync,
loggedIn: { graphqlClient },
vcsClient,
projectDir,
} = await this.getContextAsync(WorkflowRun, {
nonInteractive: flags['non-interactive'],
withServerSideEnvironment: null,
});

let yamlConfig: string;
try {
yamlConfig = await fs.promises.readFile(path.join(projectDir, args.file), 'utf8');
} catch (err) {
Log.error('Failed to read workflow file.');

throw err;
}

const {
projectId,
exp: { slug: projectName },
} = await getDynamicPrivateProjectConfigAsync();
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);

let projectArchiveBucketKey: string;
let easJsonBucketKey: string;

try {
({ projectArchiveBucketKey } = await uploadAccountScopedProjectSourceAsync({
graphqlClient,
vcsClient,
accountId: account.id,
}));
({ easJsonBucketKey } = await uploadAccountScopedEasJsonAsync({
graphqlClient,
accountId: account.id,
projectDir,
}));
} catch (err) {
Log.error('Failed to upload project sources.');

throw err;
}

try {
const { id: workflowRunId } = await WorkflowRunMutation.createWorkflowRunAsync(
graphqlClient,
{
appId: projectId,
workflowRevisionInput: {
fileName: path.basename(args.file),
yamlConfig,
},
workflowRunInput: {
projectSource: {
type: WorkflowProjectSourceType.Gcs,
projectArchiveBucketKey,
easJsonBucketKey,
},
},
}
);

Log.newLine();
Log.succeed(
`Workflow started successfully. ${link(
getWorkflowRunUrl(account.name, projectName, workflowRunId)
)}`
);
} catch (err) {
Log.error('Failed to start the workflow with the API.');

throw err;
}
}
}
Loading

0 comments on commit b81c60e

Please sign in to comment.