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

nikelborm / fetch-github-folder / 12817020873

16 Jan 2025 08:25PM UTC coverage: 56.484% (-0.3%) from 56.785%
12817020873

push

github

nikelborm
quick save: Thu Jan 16 23:25:50 MSK 2025

52 of 62 branches covered (83.87%)

Branch coverage included in aggregate %.

18 of 33 new or added lines in 4 files covered. (54.55%)

5 existing lines in 2 files now uncovered.

340 of 632 relevant lines covered (53.8%)

2.39 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

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

42
const GitHubStringHandleSchema = NonEmptyString.pipe(
×
43
  filter(s => validateGitHubHandle(s) || badGitHubHandleMessage),
×
44
);
×
45

46
const withGitHubHandleConfigValidation = validateConfig({
×
47
  message: badGitHubHandleMessage,
×
48
  validation: validateGitHubHandle,
×
49
});
×
50

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

77
export const RepoNameConfig = pipe(
×
78
  nonEmptyStringConfig('GITHUB_REPO_NAME'),
×
79
  withGitHubHandleConfigValidation,
×
80
  withConfigDescription(repoNameDescription),
×
81
);
×
82

83
export const RepoOwnerConfig = pipe(
×
84
  nonEmptyStringConfig('GITHUB_REPO_OWNER'),
×
85
  withGitHubHandleConfigValidation,
×
86
  withConfigDescription(repoOwnerDescription),
×
87
);
×
88

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

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

101
export const GitRefConfig = pipe(
×
102
  nonEmptyStringConfig('GIT_REF'),
×
103
  withConfigDefault('HEAD'),
×
104
  withConfigDescription(gitRefDescription),
×
105
);
×
106

107
const RepoConfig = allConfig([RepoNameConfig, RepoOwnerConfig]);
×
108

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

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

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

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

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

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

165
const destinationPathCLIOption = pipe(
×
166
  text('destinationPath'),
×
167
  withOptionDescription(destinationPathDescription),
×
168
  withFallbackConfig(DestinationPathConfig),
×
169
);
×
170

171
const gitRefCLIOption = pipe(
×
172
  text('gitRef'),
×
173
  withOptionDescription(gitRefDescription),
×
174
  withFallbackConfig(GitRefConfig),
×
175
);
×
176

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

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

202
pipe(
×
203
  process.argv,
×
204
  cli,
×
205
  provide(NodeFileSystemLayer),
×
206
  provide(NodePathLayer),
×
207
  provide(NodeTerminalLayer),
×
208
  provideService(
×
209
    OctokitTag,
×
210
    new OctokitClient({
×
211
      // auth: getEnvVarOrFail('GITHUB_ACCESS_TOKEN'),
212
    }),
×
213
  ),
×
214
  runMain,
×
215
);
×
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