• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nikelborm / fetch-github-folder / 12811968662

16 Jan 2025 03:18PM UTC coverage: 52.778% (-3.1%) from 55.882%
12811968662

push

github

nikelborm
quick save: Thu Jan 16 18:18:48 MSK 2025

51 of 64 branches covered (79.69%)

Branch coverage included in aggregate %.

1 of 82 new or added lines in 2 files covered. (1.22%)

7 existing lines in 1 file now uncovered.

329 of 656 relevant lines covered (50.15%)

2.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/fetch-github-folder.ts
1
#!/usr/bin/env node
×
2

3
import { make, run } from '@effect/cli/Command';
4
import {
5
  text,
6
  withFallbackConfig,
7
  withDescription as withOptionDescription,
8
  withSchema,
9
} from '@effect/cli/Options';
10
import { layer as NodeFileSystemLayer } from '@effect/platform-node/NodeFileSystem';
11
import { layer as NodePathLayer } from '@effect/platform-node/NodePath';
12
import { runMain } from '@effect/platform-node/NodeRuntime';
13
import { layer as NodeTerminalLayer } from '@effect/platform-node/NodeTerminal';
14
import { Path } from '@effect/platform/Path';
15
import { Octokit as OctokitClient } from '@octokit/core';
16
import {
17
  all as allConfig,
18
  nonEmptyString as nonEmptyStringConfig,
19
  validate as validateConfig,
20
  withDefault as withConfigDefault,
21
  withDescription as withConfigDescription,
22
} from 'effect/Config';
23
import { flatMap, provide, provideService } from 'effect/Effect';
24
import { pipe } from 'effect/Function';
25
import { Type, fail, succeed } from 'effect/ParseResult';
26
import { NonEmptyString, filter, transformOrFail } from 'effect/Schema';
27
import { outdent } from 'outdent';
28
import {
29
  OctokitTag,
30
  createSingleTargetConfigContext,
31
  downloadEntityFromRepo,
32
} from './src/index.js';
33

34
// Those values updated automatically. If you edit names of constants or
35
// move them to a different file, update ./scripts/build.sh
36
const PACKAGE_VERSION = '0.1.6';
×
37
const PACKAGE_NAME = 'fetch-github-folder';
×
38

39
// TODO: add support for effect CLI, to properly render what env vars can be used as fallback and also what default values are.
40

NEW
41
const validateGitHubHandle = (s: string) => !!s.match(/^[a-z0-9.\-_]+$/gi);
×
NEW
42
const badGitHubHandleMessage = `GitHub handle should have only ASCII letters, digits, and the characters ".", "-", and "_"`;
×
43

NEW
44
const GitHubStringHandleSchema = NonEmptyString.pipe(
×
NEW
45
  filter(s => validateGitHubHandle(s) || badGitHubHandleMessage),
×
UNCOV
46
);
×
47

NEW
48
const withGitHubHandleConfigValidation = validateConfig({
×
NEW
49
  message: badGitHubHandleMessage,
×
NEW
50
  validation: validateGitHubHandle,
×
NEW
51
});
×
52

NEW
53
const pathToEntityInRepoDescription = 'Path to file or directory in repo';
×
NEW
54
const repoOwnerDescription = outdent`
×
55
  This is a username (login handle) of a person owning repo you
56
  are trying to download from. For example, if the repository's URL is
57
  \`https://github.com/apache/superset\`, the owner is \`apache\`
58
`;
NEW
59
const repoNameDescription = outdent`
×
60
  This is the name handle of the repository you are trying to download
61
  from. For example, if the repository's URL is
62
  \`https://github.com/apache/superset\`, the name is \`superset\`
63
`;
NEW
64
const destinationPathDescription = outdent`
×
65
  Local path of the downloaded file or directory. If
66
  "pathToEntityInRepo" points to a file, then last element of the
67
  destination path will be new file name. If "pathToEntityInRepo" points
68
  to a directory then all files and directories inside directory at
69
  "pathToEntityInRepo" will be put into a directory with name equal last
70
  element of destination path. If the directory doesn't exist, it will
71
  be automatically created.
72
`;
NEW
73
const gitRefDescription = outdent`
×
74
  This is the commit's SHA hash, branch name, tag name, or any other ref
75
  you want to download from. If you don't specify it, the default branch
76
  in the repository will be used.
77
`;
78

NEW
79
export const RepoNameConfig = pipe(
×
NEW
80
  nonEmptyStringConfig('GITHUB_REPO_NAME'),
×
NEW
81
  withGitHubHandleConfigValidation,
×
NEW
82
  withConfigDescription(repoNameDescription),
×
UNCOV
83
);
×
84

NEW
85
export const RepoOwnerConfig = pipe(
×
NEW
86
  nonEmptyStringConfig('GITHUB_REPO_OWNER'),
×
NEW
87
  withGitHubHandleConfigValidation,
×
NEW
88
  withConfigDescription(repoOwnerDescription),
×
UNCOV
89
);
×
90

NEW
91
export const DestinationPathConfig = pipe(
×
NEW
92
  nonEmptyStringConfig('DESTINATION_PATH'),
×
NEW
93
  withConfigDefault('./destination'),
×
NEW
94
  withConfigDescription(destinationPathDescription),
×
NEW
95
);
×
96

NEW
97
export const PathToEntityInRepoConfig = pipe(
×
NEW
98
  nonEmptyStringConfig('PATH_TO_ENTITY_IN_REPO'),
×
NEW
99
  withConfigDefault('.'),
×
NEW
100
  withConfigDescription(pathToEntityInRepoDescription),
×
NEW
101
);
×
102

NEW
103
export const GitRefConfig = pipe(
×
NEW
104
  nonEmptyStringConfig('GIT_REF'),
×
NEW
105
  withConfigDefault('HEAD'),
×
NEW
106
  withConfigDescription(gitRefDescription),
×
UNCOV
107
);
×
108

NEW
109
const RepoConfig = allConfig([RepoNameConfig, RepoOwnerConfig]);
×
110

NEW
111
export const SingleDownloadTargetConfig = allConfig([
×
NEW
112
  RepoConfig,
×
NEW
113
  PathToEntityInRepoConfig,
×
NEW
114
  DestinationPathConfig,
×
NEW
115
  GitRefConfig,
×
NEW
116
]);
×
117

NEW
118
export const CleanRepoEntityPathString = transformOrFail(
×
119
  NonEmptyString,
×
120
  NonEmptyString,
×
121
  {
×
122
    strict: true,
×
123
    decode: (dirtyPathToEntityInRepo, _, ast) =>
×
124
      flatMap(Path, path => {
×
125
        // dot can be there only when that's all there is. path.join(...)
126
        // removes all './', so '.' will never be just left by themself. If it's
127
        // there, it's very intentional and no other elements in the path exist.
NEW
128
        const cleanPathToEntityInRepo = path
×
NEW
129
          .join(dirtyPathToEntityInRepo)
×
NEW
130
          .replaceAll(/[/]*$/g, '');
×
131

132
        if (cleanPathToEntityInRepo.startsWith('..'))
×
133
          return fail(
×
134
            new Type(
×
135
              ast,
×
136
              dirtyPathToEntityInRepo,
×
137
              "Can\'t request contents that lie higher than the root of the repo",
×
138
            ),
×
139
          );
×
140
        return succeed(cleanPathToEntityInRepo);
×
141
      }),
×
142
    encode: succeed,
×
143
  },
×
144
);
×
145

NEW
146
const pathToEntityInRepoCLIOption = pipe(
×
NEW
147
  text('pathToEntityInRepo'),
×
NEW
148
  withOptionDescription(pathToEntityInRepoDescription),
×
NEW
149
  withFallbackConfig(PathToEntityInRepoConfig),
×
NEW
150
  withSchema(CleanRepoEntityPathString),
×
NEW
151
);
×
152

NEW
153
const repoOwnerCLIOption = pipe(
×
NEW
154
  text('repoOwner'),
×
NEW
155
  withOptionDescription(repoOwnerDescription),
×
NEW
156
  withFallbackConfig(RepoOwnerConfig),
×
NEW
157
  withSchema(GitHubStringHandleSchema),
×
NEW
158
);
×
159

NEW
160
const repoNameCLIOption = pipe(
×
NEW
161
  text('repoName'),
×
NEW
162
  withOptionDescription(repoNameDescription),
×
NEW
163
  withFallbackConfig(RepoNameConfig),
×
NEW
164
  withSchema(GitHubStringHandleSchema),
×
NEW
165
);
×
166

NEW
167
const destinationPathCLIOption = pipe(
×
168
  text('destinationPath'),
×
NEW
169
  withOptionDescription(destinationPathDescription),
×
NEW
170
  withFallbackConfig(DestinationPathConfig),
×
UNCOV
171
);
×
172

NEW
173
const gitRefCLIOption = pipe(
×
174
  text('gitRef'),
×
NEW
175
  withOptionDescription(gitRefDescription),
×
NEW
176
  withFallbackConfig(GitRefConfig),
×
UNCOV
177
);
×
178

179
const appCommand = make(
×
180
  'fetch-github-folder',
×
181
  {
×
182
    repo: {
×
NEW
183
      owner: repoOwnerCLIOption,
×
NEW
184
      name: repoNameCLIOption,
×
185
    },
×
NEW
186
    pathToEntityInRepo: pathToEntityInRepoCLIOption,
×
NEW
187
    localPathAtWhichEntityFromRepoWillBeAvailable:
×
NEW
188
      destinationPathCLIOption,
×
NEW
189
    gitRef: gitRefCLIOption,
×
190
  },
×
NEW
191
  singleDownloadTargetConfig =>
×
NEW
192
    provide(
×
NEW
193
      downloadEntityFromRepo,
×
NEW
194
      createSingleTargetConfigContext(singleDownloadTargetConfig),
×
NEW
195
    ),
×
UNCOV
196
);
×
197

198
const cli = run(appCommand, {
×
199
  // those values will be filled automatically from package.json
200
  name: PACKAGE_NAME,
×
201
  version: PACKAGE_VERSION,
×
202
});
×
203

204
pipe(
×
205
  process.argv,
×
206
  cli,
×
207
  provide(NodeFileSystemLayer),
×
208
  provide(NodePathLayer),
×
209
  provide(NodeTerminalLayer),
×
210
  provideService(
×
211
    OctokitTag,
×
212
    new OctokitClient({
×
213
      // auth: getEnvVarOrFail('GITHUB_ACCESS_TOKEN'),
214
    }),
×
215
  ),
×
216
  runMain,
×
217
);
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc