reposets 0.4.1 → 0.4.2

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/index.js CHANGED
@@ -1,2 +1,19 @@
1
- export { BypassActorSchema, CONFIG_FILENAME, CREDENTIALS_FILENAME, CleanupSchema, CleanupScopeSchema, ConfigFilesLive, ConfigSchema, CredentialProfileSchema, CredentialResolver, CredentialResolverLive, CredentialsSchema, GitHubApiError, GitHubClient, GitHubClientLive, GitHubClientTest, GroupSchema, LogLevelSchema, OnePasswordClient, OnePasswordClientLive, OnePasswordClientTest, OnePasswordError, ReposetsConfigFile, ReposetsCredentialsFile, ResolveError, ResolveSectionSchema, ResolvedRefSchema, RulesetSchema, SecretGroupSchema, SyncEngine, SyncEngineLive, SyncError, SyncLogger, SyncLoggerLive, VariableGroupSchema, buildRulesetPayload, encryptSecret, makeConfigFilesLive, validateConfigRefs } from "./500.js";
2
- export { AppDirs, ConfigError as XdgConfigError, ConfigFile } from "xdg-effect";
1
+ import { GitHubApiError, OnePasswordError, ResolveError, SyncError } from "./errors.js";
2
+ import { encryptSecret } from "./lib/crypto.js";
3
+ import { CleanupSchema, CleanupScopeSchema, SecretGroupSchema, VariableGroupSchema } from "./schemas/common.js";
4
+ import { BypassActorSchema, ResolvedRefSchema, RulesetSchema, buildRulesetPayload } from "./schemas/ruleset.js";
5
+ import { ConfigSchema, GroupSchema, LogLevelSchema } from "./schemas/config.js";
6
+ import { CredentialProfileSchema, CredentialsSchema, ResolveSectionSchema } from "./schemas/credentials.js";
7
+ import { CONFIG_FILENAME, CREDENTIALS_FILENAME, ConfigFilesLive, ReposetsConfigFile, ReposetsCredentialsFile, makeConfigFilesLive, validateConfigRefs } from "./services/ConfigFiles.js";
8
+ import { OnePasswordClient, OnePasswordClientLive, OnePasswordClientTest } from "./services/OnePasswordClient.js";
9
+ import { CredentialResolver, CredentialResolverLive } from "./services/CredentialResolver.js";
10
+ import { GitHubClient, GitHubClientLive, GitHubClientTest } from "./services/GitHubClient.js";
11
+ import { SyncLogger, SyncLoggerLive } from "./services/SyncLogger.js";
12
+ import { SyncEngine, SyncEngineLive } from "./services/SyncEngine.js";
13
+ import { AppDirs, ConfigError as XdgConfigError, ConfigFile } from "xdg-effect";
14
+
15
+ //#region src/index.ts
16
+ /* v8 ignore stop */
17
+
18
+ //#endregion
19
+ export { AppDirs, BypassActorSchema, CONFIG_FILENAME, CREDENTIALS_FILENAME, CleanupSchema, CleanupScopeSchema, ConfigFile, ConfigFilesLive, ConfigSchema, CredentialProfileSchema, CredentialResolver, CredentialResolverLive, CredentialsSchema, GitHubApiError, GitHubClient, GitHubClientLive, GitHubClientTest, GroupSchema, LogLevelSchema, OnePasswordClient, OnePasswordClientLive, OnePasswordClientTest, OnePasswordError, ReposetsConfigFile, ReposetsCredentialsFile, ResolveError, ResolveSectionSchema, ResolvedRefSchema, RulesetSchema, SecretGroupSchema, SyncEngine, SyncEngineLive, SyncError, SyncLogger, SyncLoggerLive, VariableGroupSchema, XdgConfigError, buildRulesetPayload, encryptSecret, makeConfigFilesLive, validateConfigRefs };
package/lib/crypto.js ADDED
@@ -0,0 +1,27 @@
1
+ import { blake2b } from "blakejs";
2
+ import nacl from "tweetnacl";
3
+
4
+ //#region src/lib/crypto.ts
5
+ /**
6
+ * Encrypt a secret using libsodium's sealed box algorithm.
7
+ * Implementation based on tweetsodium using tweetnacl + blakejs.
8
+ *
9
+ * The sealed box format is: ephemeral_public_key (32 bytes) || ciphertext
10
+ */
11
+ function encryptSecret(publicKey, secretValue) {
12
+ const messageBytes = Buffer.from(secretValue);
13
+ const publicKeyBytes = Buffer.from(publicKey, "base64");
14
+ const ephemeralKeyPair = nacl.box.keyPair();
15
+ const nonceInput = new Uint8Array(64);
16
+ nonceInput.set(ephemeralKeyPair.publicKey);
17
+ nonceInput.set(publicKeyBytes, 32);
18
+ const nonce = blake2b(nonceInput, void 0, 24);
19
+ const ciphertext = nacl.box(messageBytes, nonce, publicKeyBytes, ephemeralKeyPair.secretKey);
20
+ const sealed = new Uint8Array(ephemeralKeyPair.publicKey.length + ciphertext.length);
21
+ sealed.set(ephemeralKeyPair.publicKey);
22
+ sealed.set(ciphertext, ephemeralKeyPair.publicKey.length);
23
+ return Buffer.from(sealed).toString("base64");
24
+ }
25
+
26
+ //#endregion
27
+ export { encryptSecret };
package/package.json CHANGED
@@ -1,75 +1,63 @@
1
1
  {
2
- "name": "reposets",
3
- "version": "0.4.1",
4
- "private": false,
5
- "description": "CLI tool to sync GitHub repo settings, secrets and rulesets across personal repositories",
6
- "keywords": [
7
- "github",
8
- "sync",
9
- "cli",
10
- "secrets",
11
- "rulesets",
12
- "variables",
13
- "environments",
14
- "toml",
15
- "effect",
16
- "xdg",
17
- "repository",
18
- "settings",
19
- "automation",
20
- "devops"
21
- ],
22
- "homepage": "https://github.com/spencerbeggs/reposets#readme",
23
- "bugs": {
24
- "url": "https://github.com/spencerbeggs/reposets/issues"
25
- },
26
- "repository": {
27
- "type": "git",
28
- "url": "git+https://github.com/spencerbeggs/reposets.git",
29
- "directory": "package"
30
- },
31
- "license": "MIT",
32
- "author": {
33
- "name": "C. Spencer Beggs",
34
- "email": "spencer@beggs.codes",
35
- "url": "https://spencerbeg.gs"
36
- },
37
- "type": "module",
38
- "exports": {
39
- ".": {
40
- "types": "./index.d.ts",
41
- "import": "./index.js"
42
- }
43
- },
44
- "bin": {
45
- "reposets": "bin/reposets.js"
46
- },
47
- "dependencies": {
48
- "@1password/sdk": "^0.4.0",
49
- "@effect/cli": "^0.75.1",
50
- "@effect/platform": "^0.96.1",
51
- "@effect/platform-node": "^0.106.0",
52
- "@octokit/rest": "^22.0.1",
53
- "blakejs": "^1.2.1",
54
- "effect": "^3.21.2",
55
- "smol-toml": "^1.6.1",
56
- "tweetnacl": "^1.0.3",
57
- "xdg-effect": "^1.0.1"
58
- },
59
- "engines": {
60
- "node": ">=20.0.0"
61
- },
62
- "files": [
63
- "!reposets.api.json",
64
- "!tsconfig.json",
65
- "!tsdoc.json",
66
- "500.js",
67
- "LICENSE",
68
- "README.md",
69
- "bin/reposets.js",
70
- "index.d.ts",
71
- "index.js",
72
- "package.json",
73
- "tsdoc-metadata.json"
74
- ]
2
+ "name": "reposets",
3
+ "version": "0.4.2",
4
+ "private": false,
5
+ "description": "CLI tool to sync GitHub repo settings, secrets and rulesets across personal repositories",
6
+ "keywords": [
7
+ "github",
8
+ "sync",
9
+ "cli",
10
+ "secrets",
11
+ "rulesets",
12
+ "variables",
13
+ "environments",
14
+ "toml",
15
+ "effect",
16
+ "xdg",
17
+ "repository",
18
+ "settings",
19
+ "automation",
20
+ "devops"
21
+ ],
22
+ "homepage": "https://github.com/spencerbeggs/reposets#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/spencerbeggs/reposets/issues"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/spencerbeggs/reposets.git",
29
+ "directory": "package"
30
+ },
31
+ "license": "MIT",
32
+ "author": {
33
+ "name": "C. Spencer Beggs",
34
+ "email": "spencer@beggs.codes",
35
+ "url": "https://spencerbeg.gs"
36
+ },
37
+ "type": "module",
38
+ "exports": {
39
+ ".": {
40
+ "types": "./index.d.ts",
41
+ "import": "./index.js"
42
+ },
43
+ "./package.json": "./package.json"
44
+ },
45
+ "bin": {
46
+ "reposets": "bin/reposets.js"
47
+ },
48
+ "dependencies": {
49
+ "@1password/sdk": "^0.4.0",
50
+ "@effect/cli": "^0.75.2",
51
+ "@effect/platform": "^0.96.1",
52
+ "@effect/platform-node": "^0.107.0",
53
+ "@octokit/rest": "^22.0.1",
54
+ "blakejs": "^1.2.1",
55
+ "effect": "^3.21.3",
56
+ "smol-toml": ">=1.6.1",
57
+ "tweetnacl": "^1.0.3",
58
+ "xdg-effect": "^1.0.3"
59
+ },
60
+ "engines": {
61
+ "node": ">=24.11.0"
62
+ }
75
63
  }
@@ -0,0 +1,130 @@
1
+ import { Jsonifiable, taplo, tombi } from "xdg-effect";
2
+ import { Schema } from "effect";
3
+
4
+ //#region src/schemas/common.ts
5
+ const ResourceFileKind = Schema.Struct({ file: Schema.Record({
6
+ key: Schema.String,
7
+ value: Schema.String
8
+ }).annotations({
9
+ title: "File entries",
10
+ description: "Named entries with file path values, resolved relative to config directory",
11
+ jsonSchema: tombi({ additionalKeyLabel: "name" })
12
+ }) });
13
+ const ResourceValueKind = Schema.Struct({ value: Schema.Record({
14
+ key: Schema.String,
15
+ value: Schema.Union(Schema.String, Schema.Record({
16
+ key: Schema.String,
17
+ value: Jsonifiable
18
+ }))
19
+ }).annotations({
20
+ title: "Value entries",
21
+ description: "Named entries with inline values. Strings used as-is, objects JSON-stringified.",
22
+ jsonSchema: tombi({ additionalKeyLabel: "name" })
23
+ }) });
24
+ const ResourceResolvedKind = Schema.Struct({ resolved: Schema.Record({
25
+ key: Schema.String,
26
+ value: Schema.String
27
+ }).annotations({
28
+ title: "Resolved entries",
29
+ description: "Named entries mapped to credential labels. Values come from the active credential profile.",
30
+ jsonSchema: tombi({ additionalKeyLabel: "name" })
31
+ }) });
32
+ const SecretGroupSchema = Schema.Union(ResourceFileKind, ResourceValueKind, ResourceResolvedKind).annotations({
33
+ identifier: "SecretGroup",
34
+ title: "Secret group",
35
+ description: "A group of secrets. Must be exactly one kind: file, value, or resolved.",
36
+ jsonSchema: taplo({ links: { key: "https://github.com/spencerbeggs/reposets/blob/main/docs/secrets-and-variables.md" } })
37
+ });
38
+ const VariableGroupSchema = Schema.Union(ResourceFileKind, ResourceValueKind, ResourceResolvedKind).annotations({
39
+ identifier: "VariableGroup",
40
+ title: "Variable group",
41
+ description: "A group of variables. Must be exactly one kind: file, value, or resolved.",
42
+ jsonSchema: taplo({ links: { key: "https://github.com/spencerbeggs/reposets/blob/main/docs/secrets-and-variables.md" } })
43
+ });
44
+ const CleanupScopeSchema = Schema.Union(Schema.Boolean, Schema.Struct({ preserve: Schema.Array(Schema.String).annotations({
45
+ title: "Preserve list",
46
+ description: "Resource names that should never be deleted during cleanup",
47
+ examples: [["LEGACY_TOKEN", "DEPLOY_KEY"]]
48
+ }) })).annotations({
49
+ identifier: "CleanupScope",
50
+ title: "Cleanup scope",
51
+ description: "Controls cleanup for a single resource scope. false disables cleanup, true enables full cleanup, or specify names to preserve."
52
+ });
53
+ const CleanupSecretsSchema = Schema.Struct({
54
+ actions: Schema.optionalWith(CleanupScopeSchema, { default: () => false }).annotations({
55
+ title: "Clean up Actions secrets",
56
+ description: "Delete Actions secrets not declared in any referenced secret group",
57
+ default: false
58
+ }),
59
+ dependabot: Schema.optionalWith(CleanupScopeSchema, { default: () => false }).annotations({
60
+ title: "Clean up Dependabot secrets",
61
+ description: "Delete Dependabot secrets not declared in any referenced secret group",
62
+ default: false
63
+ }),
64
+ codespaces: Schema.optionalWith(CleanupScopeSchema, { default: () => false }).annotations({
65
+ title: "Clean up Codespaces secrets",
66
+ description: "Delete Codespaces secrets not declared in any referenced secret group",
67
+ default: false
68
+ }),
69
+ environments: Schema.optionalWith(CleanupScopeSchema, { default: () => false }).annotations({
70
+ title: "Clean up environment secrets",
71
+ description: "Delete environment secrets not declared in any referenced secret group",
72
+ default: false
73
+ })
74
+ }).annotations({
75
+ identifier: "CleanupSecrets",
76
+ title: "Secrets cleanup configuration",
77
+ description: "Controls deletion of secrets by scope (Actions, Dependabot, Codespaces, environments)."
78
+ });
79
+ const CleanupVariablesSchema = Schema.Struct({
80
+ actions: Schema.optionalWith(CleanupScopeSchema, { default: () => false }).annotations({
81
+ title: "Clean up Actions variables",
82
+ description: "Delete Actions variables not declared in any referenced variable group",
83
+ default: false
84
+ }),
85
+ environments: Schema.optionalWith(CleanupScopeSchema, { default: () => false }).annotations({
86
+ title: "Clean up environment variables",
87
+ description: "Delete environment variables not declared in any referenced variable group",
88
+ default: false
89
+ })
90
+ }).annotations({
91
+ identifier: "CleanupVariables",
92
+ title: "Variables cleanup configuration",
93
+ description: "Controls deletion of variables by scope (Actions, environments)."
94
+ });
95
+ const CleanupSchema = Schema.Struct({
96
+ secrets: Schema.optionalWith(CleanupSecretsSchema, { default: () => ({
97
+ actions: false,
98
+ dependabot: false,
99
+ codespaces: false,
100
+ environments: false
101
+ }) }).annotations({
102
+ title: "Secrets cleanup",
103
+ description: "Controls cleanup of secrets by scope"
104
+ }),
105
+ variables: Schema.optionalWith(CleanupVariablesSchema, { default: () => ({
106
+ actions: false,
107
+ environments: false
108
+ }) }).annotations({
109
+ title: "Variables cleanup",
110
+ description: "Controls cleanup of variables by scope"
111
+ }),
112
+ rulesets: Schema.optionalWith(CleanupScopeSchema, { default: () => false }).annotations({
113
+ title: "Clean up rulesets",
114
+ description: "Delete repository rulesets not declared in any referenced ruleset group",
115
+ default: false
116
+ }),
117
+ environments: Schema.optionalWith(CleanupScopeSchema, { default: () => false }).annotations({
118
+ title: "Clean up environments",
119
+ description: "Delete repository environments not declared in config",
120
+ default: false
121
+ })
122
+ }).annotations({
123
+ identifier: "Cleanup",
124
+ title: "Cleanup configuration",
125
+ description: "Controls deletion of resources not declared in config. All disabled by default.",
126
+ jsonSchema: taplo({ links: { key: "https://github.com/spencerbeggs/reposets/blob/main/docs/cleanup.md" } })
127
+ });
128
+
129
+ //#endregion
130
+ export { CleanupSchema, CleanupScopeSchema, SecretGroupSchema, VariableGroupSchema };