eoas 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +1 -0
- package/.eslintrc.js +73 -0
- package/.prettierrc +9 -0
- package/README.md +17 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +6 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +7 -0
- package/package.json +92 -0
- package/src/commands/generate-certs.ts +95 -0
- package/src/commands/init.ts +117 -0
- package/src/commands/publish.ts +277 -0
- package/src/index.d.ts +7 -0
- package/src/lib/assets.ts +118 -0
- package/src/lib/auth.ts +67 -0
- package/src/lib/expoConfig.ts +265 -0
- package/src/lib/log.ts +122 -0
- package/src/lib/ora.ts +113 -0
- package/src/lib/package.ts +6 -0
- package/src/lib/prompts.ts +97 -0
- package/src/lib/repo.ts +62 -0
- package/src/lib/runtimeVersion.ts +177 -0
- package/src/lib/utils.ts +3 -0
- package/src/lib/vcs/README.md +1 -0
- package/src/lib/vcs/clients/git.ts +390 -0
- package/src/lib/vcs/clients/gitNoCommit.ts +45 -0
- package/src/lib/vcs/clients/noVcs.ts +23 -0
- package/src/lib/vcs/git.ts +68 -0
- package/src/lib/vcs/index.ts +25 -0
- package/src/lib/vcs/local.ts +88 -0
- package/src/lib/vcs/vcs.ts +90 -0
- package/src/lib/workflow.ts +47 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import spawnAsync from '@expo/spawn-async';
|
|
2
|
+
|
|
3
|
+
export async function isGitInstalledAsync(): Promise<boolean> {
|
|
4
|
+
try {
|
|
5
|
+
await spawnAsync('git', ['--help']);
|
|
6
|
+
} catch (error: any) {
|
|
7
|
+
if (error.code === 'ENOENT') {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
throw error;
|
|
11
|
+
}
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function doesGitRepoExistAsync(cwd: string | undefined): Promise<boolean> {
|
|
16
|
+
try {
|
|
17
|
+
await spawnAsync('git', ['rev-parse', '--git-dir'], {
|
|
18
|
+
cwd,
|
|
19
|
+
});
|
|
20
|
+
return true;
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface GitStatusOptions {
|
|
27
|
+
showUntracked: boolean;
|
|
28
|
+
cwd: string | undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function gitStatusAsync({ showUntracked, cwd }: GitStatusOptions): Promise<string> {
|
|
32
|
+
return (
|
|
33
|
+
await spawnAsync('git', ['status', '-s', showUntracked ? '-uall' : '-uno'], {
|
|
34
|
+
cwd,
|
|
35
|
+
})
|
|
36
|
+
).stdout;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function getGitDiffOutputAsync(cwd: string | undefined): Promise<string> {
|
|
40
|
+
return (
|
|
41
|
+
await spawnAsync('git', ['--no-pager', 'diff'], {
|
|
42
|
+
cwd,
|
|
43
|
+
})
|
|
44
|
+
).stdout;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function gitDiffAsync({
|
|
48
|
+
withPager = false,
|
|
49
|
+
cwd,
|
|
50
|
+
}: {
|
|
51
|
+
withPager?: boolean;
|
|
52
|
+
cwd: string | undefined;
|
|
53
|
+
}): Promise<void> {
|
|
54
|
+
const options = withPager ? [] : ['--no-pager'];
|
|
55
|
+
try {
|
|
56
|
+
await spawnAsync('git', [...options, 'diff'], {
|
|
57
|
+
stdio: ['ignore', 'inherit', 'inherit'],
|
|
58
|
+
cwd,
|
|
59
|
+
});
|
|
60
|
+
} catch (error: any) {
|
|
61
|
+
if (typeof error.message === 'string' && error.message.includes('SIGPIPE')) {
|
|
62
|
+
// This error is thrown when the user exits the pager with `q`.
|
|
63
|
+
// do nothing
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
import GitClient from './clients/git';
|
|
4
|
+
import GitNoCommitClient from './clients/gitNoCommit';
|
|
5
|
+
import NoVcsClient from './clients/noVcs';
|
|
6
|
+
import { Client } from './vcs';
|
|
7
|
+
|
|
8
|
+
const NO_VCS_WARNING = `Using EAS CLI without version control system is not recommended, use this mode only if you know what you are doing.`;
|
|
9
|
+
|
|
10
|
+
export function resolveVcsClient(requireCommit: boolean = false): Client {
|
|
11
|
+
if (process.env.EAS_NO_VCS) {
|
|
12
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
13
|
+
// This log might be printed before cli arguments are evaluated,
|
|
14
|
+
// so it needs to go to stderr in case command is run in JSON
|
|
15
|
+
// only mode.
|
|
16
|
+
// eslint-disable-next-line no-console
|
|
17
|
+
console.error(chalk.yellow(NO_VCS_WARNING));
|
|
18
|
+
}
|
|
19
|
+
return new NoVcsClient();
|
|
20
|
+
}
|
|
21
|
+
if (requireCommit) {
|
|
22
|
+
return new GitClient();
|
|
23
|
+
}
|
|
24
|
+
return new GitNoCommitClient();
|
|
25
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import fg from 'fast-glob';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import createIgnore, { Ignore as SingleFileIgnore } from 'ignore';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
const EASIGNORE_FILENAME = '.easignore';
|
|
7
|
+
const GITIGNORE_FILENAME = '.gitignore';
|
|
8
|
+
|
|
9
|
+
const DEFAULT_IGNORE = `
|
|
10
|
+
.git
|
|
11
|
+
node_modules
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
export function getRootPath(): string {
|
|
15
|
+
const rootPath = process.env.EAS_PROJECT_ROOT ?? process.cwd();
|
|
16
|
+
if (!path.isAbsolute(rootPath)) {
|
|
17
|
+
return path.resolve(process.cwd(), rootPath);
|
|
18
|
+
}
|
|
19
|
+
return rootPath;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Ignore wraps the 'ignore' package to support multiple .gitignore files
|
|
24
|
+
* in subdirectories.
|
|
25
|
+
*
|
|
26
|
+
* Inconsistencies with git behavior:
|
|
27
|
+
* - if parent .gitignore has ignore rule and child has exception to that rule,
|
|
28
|
+
* file will still be ignored,
|
|
29
|
+
* - node_modules is always ignored,
|
|
30
|
+
* - if .easignore exists, .gitignore files are not used.
|
|
31
|
+
*/
|
|
32
|
+
export class Ignore {
|
|
33
|
+
private ignoreMapping: (readonly [string, SingleFileIgnore])[] = [];
|
|
34
|
+
|
|
35
|
+
constructor(private readonly rootDir: string) {}
|
|
36
|
+
|
|
37
|
+
public async initIgnoreAsync(): Promise<void> {
|
|
38
|
+
const easIgnorePath = path.join(this.rootDir, EASIGNORE_FILENAME);
|
|
39
|
+
if (await fs.pathExists(easIgnorePath)) {
|
|
40
|
+
this.ignoreMapping = [
|
|
41
|
+
['', createIgnore().add(DEFAULT_IGNORE)],
|
|
42
|
+
['', createIgnore().add(await fs.readFile(easIgnorePath, 'utf-8'))],
|
|
43
|
+
];
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const ignoreFilePaths = (
|
|
47
|
+
await fg(`**/${GITIGNORE_FILENAME}`, {
|
|
48
|
+
cwd: this.rootDir,
|
|
49
|
+
ignore: ['node_modules'],
|
|
50
|
+
followSymbolicLinks: false,
|
|
51
|
+
})
|
|
52
|
+
)
|
|
53
|
+
// ensure that parent dir is before child directories
|
|
54
|
+
.sort((a, b) => a.length - b.length && a.localeCompare(b));
|
|
55
|
+
|
|
56
|
+
const ignoreMapping = await Promise.all(
|
|
57
|
+
ignoreFilePaths.map(async filePath => {
|
|
58
|
+
return [
|
|
59
|
+
filePath.slice(0, filePath.length - GITIGNORE_FILENAME.length),
|
|
60
|
+
createIgnore().add(await fs.readFile(path.join(this.rootDir, filePath), 'utf-8')),
|
|
61
|
+
] as const;
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
this.ignoreMapping = [['', createIgnore().add(DEFAULT_IGNORE)], ...ignoreMapping];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public ignores(relativePath: string): boolean {
|
|
68
|
+
for (const [prefix, ignore] of this.ignoreMapping) {
|
|
69
|
+
if (relativePath.startsWith(prefix) && ignore.ignores(relativePath.slice(prefix.length))) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function makeShallowCopyAsync(src: string, dst: string): Promise<void> {
|
|
78
|
+
const ignore = new Ignore(src);
|
|
79
|
+
await ignore.initIgnoreAsync();
|
|
80
|
+
await fs.copy(src, dst, {
|
|
81
|
+
filter: (srcFilePath: string) => {
|
|
82
|
+
if (srcFilePath === src) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return !ignore.ignores(path.relative(src, srcFilePath));
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export abstract class Client {
|
|
2
|
+
// makeShallowCopyAsync should copy current project (result of getRootPathAsync()) to the specified
|
|
3
|
+
// destination, folder created this way will be uploaded "as is", so implementation should skip
|
|
4
|
+
// anything that is not committed to the repository. Most optimal solution is to create shallow clone
|
|
5
|
+
// using tooling provided by specific VCS, that respects all ignore rules
|
|
6
|
+
public abstract makeShallowCopyAsync(destinationPath: string): Promise<void>;
|
|
7
|
+
|
|
8
|
+
// Find root of the repository.
|
|
9
|
+
//
|
|
10
|
+
// On windows path might look different depending on implementation
|
|
11
|
+
// - git based clients will return "C:/path/to/repo"
|
|
12
|
+
// - non-git clients will return "C:\path\to\repo"
|
|
13
|
+
public abstract getRootPathAsync(): Promise<string>;
|
|
14
|
+
|
|
15
|
+
// (optional) ensureRepoExistsAsync should verify whether repository exists and tooling is installed
|
|
16
|
+
// it's not required for minimal support, but lack of validation might cause the failure at a later stage.
|
|
17
|
+
public async ensureRepoExistsAsync(): Promise<void> {}
|
|
18
|
+
|
|
19
|
+
// (optional) checks whether commit is necessary before calling makeShallowCopyAsync
|
|
20
|
+
//
|
|
21
|
+
// If it's not implemented method `makeShallowCopyAsync` needs to be able to include uncommitted changes
|
|
22
|
+
// when creating copy
|
|
23
|
+
public async isCommitRequiredAsync(): Promise<boolean> {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// (optional) hasUncommittedChangesAsync should check whether there are changes in local repository
|
|
28
|
+
public async hasUncommittedChangesAsync(): Promise<boolean | undefined> {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// (optional) commitAsync commits changes
|
|
33
|
+
//
|
|
34
|
+
// - Should be implemented if hasUncommittedChangesAsync is implemented
|
|
35
|
+
// - If it's not implemented method `makeShallowCopyAsync` needs to be able to include uncommitted changes
|
|
36
|
+
// in project copy
|
|
37
|
+
public async commitAsync(_arg: {
|
|
38
|
+
commitMessage: string;
|
|
39
|
+
commitAllFiles?: boolean;
|
|
40
|
+
nonInteractive: boolean;
|
|
41
|
+
}): Promise<void> {
|
|
42
|
+
// it should not be called unless hasUncommittedChangesAsync is implemented
|
|
43
|
+
throw new Error('commitAsync is not implemented');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// (optional) mark file as tracked, if this method is called on file, the next call to
|
|
47
|
+
// `commitAsync({ commitAllFiles: false })` should commit that file
|
|
48
|
+
public async trackFileAsync(_file: string): Promise<void> {}
|
|
49
|
+
|
|
50
|
+
// (optional) print diff of the changes that will be commited in the next call to
|
|
51
|
+
// `commitAsync({ commitAllFiles: false })`
|
|
52
|
+
public async showDiffAsync(): Promise<void> {}
|
|
53
|
+
|
|
54
|
+
/** (optional) print list of changed files */
|
|
55
|
+
public async showChangedFilesAsync(): Promise<void> {}
|
|
56
|
+
|
|
57
|
+
// (optional) returns hash of the last commit
|
|
58
|
+
// used for metadata - implementation can be safely skipped
|
|
59
|
+
public async getCommitHashAsync(): Promise<string | undefined> {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// (optional) returns name of the current branch
|
|
64
|
+
// used for EAS Update - implementation can be safely skipped
|
|
65
|
+
public async getBranchNameAsync(): Promise<string | null> {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// (optional) returns message of the last commit
|
|
70
|
+
// used for EAS Update - implementation can be safely skipped
|
|
71
|
+
public async getLastCommitMessageAsync(): Promise<string | null> {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// (optional) checks if the file is ignored, an implementation should ensure
|
|
76
|
+
// that if file exists and `isFileIgnoredAsync` returns true, then that file
|
|
77
|
+
// should not be included in the project tarball.
|
|
78
|
+
//
|
|
79
|
+
// @param filePath has to be a relative normalized path pointing to a file
|
|
80
|
+
// located under the root of the repository
|
|
81
|
+
public async isFileIgnoredAsync(_filePath: string): Promise<boolean> {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Whether this VCS client can get the last commit message.
|
|
87
|
+
* Used for EAS Update - implementation can be false for noVcs client.
|
|
88
|
+
*/
|
|
89
|
+
public abstract canGetLastCommitMessage(): boolean;
|
|
90
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { AndroidConfig, IOSConfig } from '@expo/config-plugins';
|
|
2
|
+
import { Platform, Workflow } from '@expo/eas-build-job';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
import { Client } from './vcs/vcs';
|
|
7
|
+
|
|
8
|
+
export async function resolveWorkflowAsync(
|
|
9
|
+
projectDir: string,
|
|
10
|
+
platform: Platform,
|
|
11
|
+
vcsClient: Client
|
|
12
|
+
): Promise<Workflow> {
|
|
13
|
+
let platformWorkflowMarkers: string[];
|
|
14
|
+
try {
|
|
15
|
+
platformWorkflowMarkers =
|
|
16
|
+
platform === Platform.ANDROID
|
|
17
|
+
? [
|
|
18
|
+
path.join(projectDir, 'android/app/build.gradle'),
|
|
19
|
+
await AndroidConfig.Paths.getAndroidManifestAsync(projectDir),
|
|
20
|
+
]
|
|
21
|
+
: [IOSConfig.Paths.getPBXProjectPath(projectDir)];
|
|
22
|
+
} catch {
|
|
23
|
+
return Workflow.MANAGED;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const vcsRootPath = path.normalize(await vcsClient.getRootPathAsync());
|
|
27
|
+
for (const marker of platformWorkflowMarkers) {
|
|
28
|
+
if (
|
|
29
|
+
(await fs.pathExists(marker)) &&
|
|
30
|
+
!(await vcsClient.isFileIgnoredAsync(path.relative(vcsRootPath, marker)))
|
|
31
|
+
) {
|
|
32
|
+
return Workflow.GENERIC;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return Workflow.MANAGED;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function resolveWorkflowPerPlatformAsync(
|
|
39
|
+
projectDir: string,
|
|
40
|
+
vcsClient: Client
|
|
41
|
+
): Promise<Record<Platform, Workflow>> {
|
|
42
|
+
const [android, ios] = await Promise.all([
|
|
43
|
+
resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient),
|
|
44
|
+
resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient),
|
|
45
|
+
]);
|
|
46
|
+
return { android, ios };
|
|
47
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "@tsconfig/node18/tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"importHelpers": true,
|
|
6
|
+
"module": "commonjs",
|
|
7
|
+
"moduleResolution": "node",
|
|
8
|
+
"noFallthroughCasesInSwitch": true,
|
|
9
|
+
"noImplicitOverride": true,
|
|
10
|
+
"noImplicitReturns": true,
|
|
11
|
+
"noUnusedLocals": true,
|
|
12
|
+
"noUnusedParameters": true,
|
|
13
|
+
"outDir": "dist",
|
|
14
|
+
"rootDir": "src"
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*"]
|
|
17
|
+
}
|