@voxelio/deploy 1.0.8 → 1.0.9

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/README.md CHANGED
@@ -1,26 +1,33 @@
1
- # @voxelio/deploy
2
- A CLI tool that automates deployment of Minecraft datapacks to Modrinth and CurseForge using GitHub Actions, The CLI will guide you through the setup (first time) or changeset creation (subsequent runs).
3
-
4
- ## Installation
5
- ```bash
6
- npm install -g @voxelio/deploy
7
- ```
8
-
9
- ## Usage
10
- Navigate to your datapack directory and run:
11
- ```bash
12
- npx voxset
13
- ```
14
- ---
15
- Here's a CI/CD setup for those interested in automatically deploying your datapacks to Modrinth and CurseForge. It also packages them as mods in .jar format using a library I wrote: `@voxelio/converter`.
16
- - `deploy.yaml` must be at the project root
17
- - `action.yml` as a GitHub Action
18
-
19
- You need to define in Settings > Secrets and Variables > Actions > Secrets > Repository secrets: `CURSEFORGE_TOKEN` and `MODRINTH_TOKEN`.
20
- - To get `CURSEFORGE_TOKEN`, go to https://legacy.curseforge.com/account/api-tokens and generate a token.
21
- - To get `MODRINTH_TOKEN`, go to https://modrinth.com/settings/pats with "Create Version" permission, which is sufficient.
22
-
23
- ## How to use
24
- Like changeset, it works similarly: when a `*.md` file is published in a commit in the `.changeset` folder, it triggers the deployment, automatically increments the version, and deletes files in the `.changeset` folder.
25
-
26
- The markdown file content will be used on Modrinth and CurseForge. You must specify Minecraft versions with `game_versions`, the release type `version_type` (beta or release), and which version number to increment `version_bump: patch|minor|major`.
1
+ # @voxelio/deploy
2
+ A CLI tool that automates deployment of Minecraft datapacks to Modrinth and CurseForge using GitHub Actions, The CLI will guide you through the setup (first time) or changeset creation (subsequent runs).
3
+
4
+ ## Installation
5
+ ```bash
6
+ npm install -g @voxelio/deploy
7
+ ```
8
+
9
+ ## Usage
10
+ Navigate to your datapack directory and run:
11
+ ```bash
12
+ npx voxset
13
+ ```
14
+ ---
15
+
16
+ ## First Time Setup
17
+ The CLI now guides you through creating a default configuration file for your project. It automatically generates:
18
+ - A `deploy.yml` configuration file at your project root
19
+ - GitHub Actions workflow files integrated into your repository
20
+
21
+ You need to define in Settings > Secrets and Variables > Actions > Secrets > Repository secrets: `CURSEFORGE_TOKEN` and `MODRINTH_TOKEN`.
22
+ - To get `CURSEFORGE_TOKEN`, go to https://legacy.curseforge.com/account/api-tokens and generate a token.
23
+ - To get `MODRINTH_TOKEN`, go to https://modrinth.com/settings/pats with "Create Version" permission, which is sufficient.
24
+
25
+ ## Subsequent Deployments
26
+ After the initial setup, the CLI simply prompts you to create a new version—**simple, fast, and efficient**. No configuration needed, just deploy.
27
+
28
+ ## Mod Packaging
29
+ The "package as mod" feature offered by Modrinth is handled by my `@voxelio/converter` library. Since there may be subtle differences between Modrinth's implementation and mine, please report any discrepancies you encounter so I can refine the converter.
30
+
31
+ ## How to use
32
+ Like changeset, it works similarly: when a `*.md` file is published in a commit in the `.changeset` folder, it triggers the deployment, automatically increments the version, and deletes files in the `.changeset` folder.
33
+ The markdown file content will be used on Modrinth and CurseForge. You must specify Minecraft versions with `game_versions`, the release type `version_type` (beta or release), and which version number to increment `version_bump: patch|minor|major`.
package/dist/cli.mjs ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import{a as e,c as t,i as n,n as r,o as i,r as a,s as o,t as s}from"./workflow-UmJaped2.mjs";import{writeFile as c}from"node:fs/promises";import{join as l}from"node:path";import{spawn as u}from"node:child_process";import{tmpdir as d}from"node:os";import{cancel as f,confirm as p,intro as m,isCancel as h,log as g,multiselect as _,note as v,outro as y,select as b,spinner as x,text as S}from"@clack/prompts";const C=[{version:`26.1.0`,javaVersion:`Java 25`},{version:`1.21.11`,javaVersion:`Java 17`},{version:`1.21.10`,javaVersion:`Java 17`},{version:`1.21.9`,javaVersion:`Java 17`},{version:`1.21.8`,javaVersion:`Java 17`},{version:`1.21.7`,javaVersion:`Java 17`},{version:`1.21.6`,javaVersion:`Java 17`},{version:`1.21.5`,javaVersion:`Java 17`},{version:`1.21.4`,javaVersion:`Java 17`},{version:`1.21.3`,javaVersion:`Java 17`},{version:`1.21.2`,javaVersion:`Java 17`},{version:`1.21.1`,javaVersion:`Java 17`},{version:`1.21`,javaVersion:`Java 17`},{version:`1.20.6`,javaVersion:`Java 17`},{version:`1.20.5`,javaVersion:`Java 17`},{version:`1.20.4`,javaVersion:`Java 17`},{version:`1.20.3`,javaVersion:`Java 17`},{version:`1.20.2`,javaVersion:`Java 17`},{version:`1.20.1`,javaVersion:`Java 17`},{version:`1.20`,javaVersion:`Java 17`}];function w(e){let t=new Set;for(let n of e){let e=C.find(e=>e.version===n);e&&t.add(e.javaVersion)}return Array.from(t).sort()}async function T(){await a()||(g.error(`The current directory is not a valid datapack. Check that there is a pack.mcmeta file with a 'description' field.`),process.exit(1));let e=await n(),t=await r();if(!e)await E();else if(t){m(`Voxelio Deploy`),v(`Ready to create a new changeset.
3
+
4
+ This will create a markdown file in the .changeset folder.
5
+ The deployment will be triggered on the next commit containing this file.`,`Create Changeset`);let e=await p({message:`Do you want to continue?`});(h(e)||!e)&&(f(`Operation cancelled`),process.exit(0)),await D(),y(`Changeset created successfully!`)}else{m(`Voxelio Deploy`);let e=x();e.start(`Creating workflow file...`),await s(),e.stop(`Workflow file created successfully!`),v(`Ready to create a new changeset.
6
+
7
+ This will create a markdown file in the .changeset folder.
8
+ The deployment will be triggered on the next commit containing this file.`,`Create Changeset`);let t=await p({message:`Do you want to continue?`});(h(t)||!t)&&(f(`Operation cancelled`),process.exit(0)),await D(),y(`Changeset created successfully!`)}}async function E(){m(`Voxelio Deploy - Configuration`),v(`Hello, welcome to the Voxelio Deploy configuration. We will prepare the environment together to easily deploy to Modrinth and CurseForge.
9
+
10
+ A few things to know:
11
+ - If you want to stop, you can press Escape or Ctrl+C
12
+ - You will need a Modrinth and/or CurseForge account`,`Welcome`);let t=await p({message:`Ready to start configuration?`});(h(t)||!t)&&(f(`Configuration cancelled`),process.exit(0)),v(`Don't forget to set environment variables in your GitHub project!
13
+
14
+ You need to define in Settings > Secrets and Variables > Actions > Secrets > Repository secrets:
15
+ - CURSEFORGE_TOKEN: https://legacy.curseforge.com/account/api-tokens
16
+ - MODRINTH_TOKEN: https://modrinth.com/settings/pats with "Create Version" permission`,`Environment Variables`);let n=await p({message:`All set, shall we continue?`});(h(n)||!n)&&(f(`Configuration cancelled`),process.exit(0)),v(`Do you want your datapack to be automatically converted to a mod?
17
+
18
+ - For Modrinth this will create 2 versions on the same project
19
+ - For CurseForge this will create 2 separate projects`,`Package as mod`);let r=await p({message:`Do you want package your datapack as a mod?`});h(r)&&(f(`Configuration cancelled`),process.exit(0));let i=[{value:`modrinth`,label:`Modrinth`}];r&&(i.push({value:`curseforge_datapack`,label:`CurseForge (Datapack)`}),i.push({value:`curseforge_mod`,label:`CurseForge (Mod)`})),r||i.push({value:`curseforge_datapack`,label:`CurseForge`});let a=await _({message:`Choose platforms`,options:i,required:!0});h(a)&&(f(`Configuration cancelled`),process.exit(0));let c=await S({message:`Project name (appears in CurseForge)`,placeholder:`My Datapack`,validate:e=>e.length===0?`Required`:void 0});h(c)&&(f(`Configuration cancelled`),process.exit(0));let l=`1.0.0`,u=await S({message:`Project Filename (For zipped datapack) - Preview: ${c}-${l}.zip`,placeholder:c,defaultValue:c});h(u)&&(f(`Configuration cancelled`),process.exit(0));let d=u;if(r){let e=await S({message:`Mod Filename (For JAR file) - Preview: ${u}-${l}.jar`,placeholder:u,defaultValue:u});h(e)&&(f(`Configuration cancelled`),process.exit(0)),d=e}let g=await S({message:`Initial version`,placeholder:`1.0.0`,defaultValue:`1.0.0`,validate:e=>e.length===0||/^\d+\.\d+\.\d+$/.test(e)?void 0:`Format: X.Y.Z`});h(g)&&(f(`Configuration cancelled`),process.exit(0));let b=e();b.project.name=c,b.project.filename=u,b.project.version=g;let C=a;if(C.includes(`modrinth`)){let e=await S({message:`Modrinth Project ID - https://modrinth.com/dashboard/projects`,placeholder:`AABBCCDD`,validate:e=>{if(e.length===0)return`Required`;if(e.length!==8)return`Must be exactly 8 characters`}});h(e)&&(f(`Configuration cancelled`),process.exit(0)),b.modrinth.enabled=!0,b.modrinth.project_id=e}if(C.includes(`curseforge_datapack`)){let e=await S({message:`CurseForge Datapack Project ID - Can be found on the Public Page e.g https://www.curseforge.com/minecraft/mc-mods/neoenchant`,placeholder:`12345678`,validate:e=>{if(e.length===0)return`Required`;if(!/^\d+$/.test(e))return`Must be a number`}});h(e)&&(f(`Configuration cancelled`),process.exit(0)),b.curseforge.datapack.enabled=!0,b.curseforge.datapack.project_id=Number.parseInt(e,10)}if(C.includes(`curseforge_mod`)){let e=await S({message:`CurseForge Mod Project ID`,placeholder:`12345678`,validate:e=>{if(e.length===0)return`Required`;if(!/^\d+$/.test(e))return`Must be a number`}});h(e)&&(f(`Configuration cancelled`),process.exit(0)),b.curseforge.mod.enabled=!0,b.curseforge.mod.project_id=Number.parseInt(e,10)}if(r){b.package_as_mod.enabled=!0,b.package_as_mod.filename=d;let e=await _({message:`Mod loaders`,options:[{value:`fabric`,label:`Fabric`},{value:`forge`,label:`Forge`},{value:`neoforge`,label:`NeoForge`},{value:`quilt`,label:`Quilt`}],required:!0});h(e)&&(f(`Configuration cancelled`),process.exit(0)),b.package_as_mod.loaders=e;let t=await S({message:`Mod ID`,placeholder:k(c),defaultValue:k(c)});h(t)&&(f(`Configuration cancelled`),process.exit(0)),b.package_as_mod.id=t}let w=await p({message:`Do you want to configure additional information (author, homepage, sources, issues)?`});if(h(w)&&(f(`Configuration cancelled`),process.exit(0)),w){let e=await S({message:`Authors (comma-separated)`,placeholder:`Author1, Author2`});!h(e)&&e&&e.trim()!==``&&(b.package_as_mod.authors=e.split(`,`).map(e=>e.trim()));let t=await S({message:`Homepage URL`,placeholder:`https://example.com`,validate:e=>{if(!(!e||e.trim()===``)){if(!e.startsWith(`https://`))return`Must start with https://`;try{return new URL(e).hostname.includes(`.`)?void 0:`Must be a valid domain (e.g., example.com)`}catch{return`Invalid URL format`}}}});!h(t)&&t&&t.trim()!==``&&(b.package_as_mod.homepage=t);let n=await S({message:`Sources URL`,placeholder:`https://github.com/user/repo`,validate:e=>{if(!(!e||e.trim()===``)){if(!e.startsWith(`https://`))return`Must start with https://`;try{return new URL(e).hostname.includes(`.`)?void 0:`Must be a valid domain (e.g., github.com)`}catch{return`Invalid URL format`}}}});!h(n)&&n&&n.trim()!==``&&(b.package_as_mod.sources=n);let r=await S({message:`Issues URL`,placeholder:`https://github.com/user/repo/issues`,validate:e=>{if(!(!e||e.trim()===``)){if(!e.startsWith(`https://`))return`Must start with https://`;try{return new URL(e).hostname.includes(`.`)?void 0:`Must be a valid domain (e.g., github.com)`}catch{return`Invalid URL format`}}}});!h(r)&&r&&r.trim()!==``&&(b.package_as_mod.issues=r)}let T=x();T.start(`Creating configuration...`),await o(b),await s(),T.stop(`Configuration created successfully!`),v(`The deploy.yaml file has been created at the root of your project.
20
+
21
+ You can edit this file to configure advanced settings such as:
22
+ - Excluding files from the build
23
+ - Java versions for CurseForge
24
+ - Environment settings (client/server)
25
+ - And more...`,`Configuration Complete`),y(`Thank you for your participation, we're done! 🎉`);let E=await p({message:`Do you want to create a changeset now?
26
+
27
+ (It will be deployed on the next commit)`});h(E)||!E||(m(`Create Changeset`),await D(),y(`Changeset created successfully!`))}async function D(){let e=await i(),n=await _({message:`Select Minecraft versions`,options:C.map(e=>({value:e.version,label:e.version})),required:!0});h(n)&&(f(`Operation cancelled`),process.exit(0));let r=n,a=w(r);e.curseforge.mod.enabled&&a.length>0&&(e.curseforge.mod.java_versions=a);let s=await b({message:`Version type`,options:[{value:`release`,label:`Release`},{value:`beta`,label:`Beta`},{value:`alpha`,label:`Alpha`}]});h(s)&&(f(`Operation cancelled`),process.exit(0));let c=await b({message:`Version bump`,options:[{value:`patch`,label:`Patch (0.0.X)`},{value:`minor`,label:`Minor (0.X.0)`},{value:`major`,label:`Major (X.0.0)`}]});h(c)&&(f(`Operation cancelled`),process.exit(0));let l=await S({message:`Changelog (press Enter to open editor)`,placeholder:`Added new features...`});if(h(l)&&(f(`Operation cancelled`),process.exit(0)),!l||l.trim()===``){v(`Opening editor...
28
+
29
+ ⚠️ IMPORTANT: Save your changes (Ctrl+S) before closing the editor!`,`Editor`);let e=await O();e===null&&(f(`Operation cancelled`),process.exit(0)),e.trim()===``&&(g.error(`Changelog cannot be empty. Please provide a changelog.`),process.exit(1)),l=e}let u=await t({game_versions:r,version_type:s,version_bump:c},l);e.curseforge.mod.enabled&&a.length>0&&await o(e),g.success(`Created ${u}`)}async function O(){let e=l(d(),`voxset-${Date.now()}.md`);await c(e,`# Write your changelog here
30
+ `,`utf-8`);let t=process.env.EDITOR||(process.platform===`win32`?`notepad`:`nano`);return new Promise(n=>{u(t,[e],{stdio:`inherit`}).on(`exit`,async t=>{if(t===0){let{readFile:t}=await import(`node:fs/promises`);n((await t(e,`utf-8`)).replace(/^# Write your changelog here\n/,``).trim())}else n(null)})})}function k(e){return e.toLowerCase().replace(/[^a-z0-9]+(.)/g,(e,t)=>t.toUpperCase()).replace(/^(.)/,e=>e.toLowerCase())}try{await T()}catch(e){console.error(`Error:`,e instanceof Error?e.message:e),process.exit(1)}export{};
@@ -1,23 +1,23 @@
1
- import * as arktype_internal_variants_string_ts0 from "arktype/internal/variants/string.ts";
2
- import * as arktype_internal_variants_object_ts0 from "arktype/internal/variants/object.ts";
1
+ import { Type } from "arktype";
3
2
 
4
3
  //#region src/types/schema.d.ts
5
- declare const VersionBump: arktype_internal_variants_string_ts0.StringType<"major" | "minor" | "patch", {}>;
4
+ declare const VersionBump: Type<"major" | "minor" | "patch">;
6
5
  type VersionBump = typeof VersionBump.infer;
7
- declare const VersionType: arktype_internal_variants_string_ts0.StringType<"release" | "beta" | "alpha", {}>;
6
+ declare const VersionType: Type<"release" | "beta" | "alpha">;
8
7
  type VersionType = typeof VersionType.infer;
9
- declare const GameVersion: arktype_internal_variants_string_ts0.StringType<string, {}>;
8
+ declare const GameVersion: Type<string>;
10
9
  type GameVersion = typeof GameVersion.infer;
11
- declare const Loader: arktype_internal_variants_string_ts0.StringType<"fabric" | "forge" | "neoforge" | "quilt", {}>;
10
+ declare const Loader: Type<"fabric" | "forge" | "neoforge" | "quilt">;
12
11
  type Loader = typeof Loader.infer;
13
- declare const ChangesetFrontmatter: arktype_internal_variants_object_ts0.ObjectType<{
12
+ interface ChangesetFrontmatterObj {
14
13
  game_versions: string[];
15
- version_type: "release" | "beta" | "alpha";
16
- version_bump: "major" | "minor" | "patch";
17
- loaders?: string[] | undefined;
18
- }, {}>;
14
+ version_type: VersionType;
15
+ version_bump: VersionBump;
16
+ loaders?: string[];
17
+ }
18
+ declare const ChangesetFrontmatter: Type<ChangesetFrontmatterObj>;
19
19
  type ChangesetFrontmatter = typeof ChangesetFrontmatter.infer;
20
- declare const DeployConfig: arktype_internal_variants_object_ts0.ObjectType<{
20
+ interface DeployConfigObj {
21
21
  project: {
22
22
  version: string;
23
23
  name: string;
@@ -26,34 +26,35 @@ declare const DeployConfig: arktype_internal_variants_object_ts0.ObjectType<{
26
26
  modrinth: {
27
27
  enabled: boolean;
28
28
  project_id: string;
29
- featured?: boolean | undefined;
29
+ featured?: boolean;
30
30
  };
31
31
  curseforge: {
32
32
  datapack: {
33
33
  enabled: boolean;
34
- project_id?: number | null | undefined;
34
+ project_id?: number | null;
35
35
  };
36
36
  mod: {
37
37
  enabled: boolean;
38
- project_id?: number | null | undefined;
39
- java_versions?: string[] | undefined;
40
- environments?: string[] | undefined;
38
+ project_id?: number | null;
39
+ java_versions?: string[];
40
+ environments?: string[];
41
41
  };
42
42
  };
43
43
  package_as_mod: {
44
44
  enabled: boolean;
45
45
  loaders: string[];
46
46
  id: string;
47
+ filename?: string;
47
48
  authors: string[];
48
- filename?: string | undefined;
49
- homepage?: string | undefined;
50
- issues?: string | undefined;
51
- sources?: string | undefined;
49
+ homepage?: string;
50
+ issues?: string;
51
+ sources?: string;
52
52
  };
53
53
  build?: {
54
- exclude?: string[] | undefined;
55
- } | undefined;
56
- }, {}>;
54
+ exclude?: string[];
55
+ };
56
+ }
57
+ declare const DeployConfig: Type<DeployConfigObj>;
57
58
  type DeployConfig = typeof DeployConfig.infer;
58
59
  //#endregion
59
60
  //#region src/utils/changeset.d.ts
@@ -79,5 +80,4 @@ declare function createMarkdownWithFrontmatter(data: ChangesetFrontmatter, conte
79
80
  declare function workflowExists(): Promise<boolean>;
80
81
  declare function createWorkflow(): Promise<void>;
81
82
  //#endregion
82
- export { type ChangesetFrontmatter, type DeployConfig, type GameVersion, type Loader, type VersionBump, type VersionType, configExists, createChangeset, createDefaultConfig, createMarkdownWithFrontmatter, createWorkflow, isValidDatapack, parseMarkdownFrontmatter, readConfig, workflowExists, writeConfig };
83
- //# sourceMappingURL=index.d.ts.map
83
+ export { type ChangesetFrontmatter, type DeployConfig, type GameVersion, type Loader, type VersionBump, type VersionType, configExists, createChangeset, createDefaultConfig, createMarkdownWithFrontmatter, createWorkflow, isValidDatapack, parseMarkdownFrontmatter, readConfig, workflowExists, writeConfig };
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{a as e,c as t,i as n,l as r,n as i,o as a,r as o,s,t as c,u as l}from"./workflow-UmJaped2.mjs";export{n as configExists,t as createChangeset,e as createDefaultConfig,r as createMarkdownWithFrontmatter,c as createWorkflow,o as isValidDatapack,l as parseMarkdownFrontmatter,a as readConfig,i as workflowExists,s as writeConfig};
@@ -1,2 +1 @@
1
- import{access as e,constants as t,copyFile as n,mkdir as r,readFile as i,writeFile as a}from"node:fs/promises";import{dirname as o,join as s,resolve as c}from"node:path";import{parse as l,stringify as u}from"yaml";import{type as d}from"arktype";import{fileURLToPath as f}from"node:url";function p(e){let t=e.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(!t)throw Error(`Invalid markdown frontmatter format`);let[,n,r]=t;return{data:l(n),content:r.trim()}}function m(e,t){return`---\n${u(e)}---\n\n${t}`}const h=`.changeset`;async function g(e,t){await r(h,{recursive:!0});let n=`${h}/${_()}`;return await a(n,m(e,t),`utf-8`),n}function _(){let e=[`happy`,`silly`,`brave`,`calm`,`bright`,`clever`,`fair`,`gentle`,`kind`,`lovely`],t=[`pandas`,`tigers`,`dragons`,`wolves`,`eagles`,`foxes`,`bears`,`lions`,`owls`,`hawks`],n=[`dance`,`jump`,`fly`,`swim`,`run`,`sing`,`play`,`rest`,`hunt`,`soar`];return`${e[Math.floor(Math.random()*e.length)]}-${t[Math.floor(Math.random()*t.length)]}-${n[Math.floor(Math.random()*n.length)]}.md`}const v=d(`'major'|'minor'|'patch'`),y=d(`'release'|'beta'|'alpha'`);d(`string`),d(`'fabric'|'forge'|'neoforge'|'quilt'`),d({game_versions:`string[]`,version_type:y,version_bump:v,"loaders?":`string[]`});const b=d({project:{version:`string`,name:`string`,filename:`string`},modrinth:{enabled:`boolean`,project_id:`string`,"featured?":`boolean`},curseforge:{datapack:{enabled:`boolean`,"project_id?":`number | null`},mod:{enabled:`boolean`,"project_id?":`number | null`,"java_versions?":`string[]`,"environments?":`string[]`}},package_as_mod:{enabled:`boolean`,loaders:`string[]`,id:`string`,"filename?":`string`,authors:`string[]`,"homepage?":`string`,"issues?":`string`,"sources?":`string`},"build?":{"exclude?":`string[]`}});async function x(){try{return await e(`deploy.yaml`,t.F_OK),!0}catch{return!1}}async function S(){let e=b(l(await i(`deploy.yaml`,`utf-8`)));if(e instanceof Error)throw Error(`Invalid deploy.yaml: ${e.message}`);return e}async function C(e){let t=b(e);if(t instanceof Error)throw Error(`Invalid config: ${t.message}`);await a(`deploy.yaml`,u(e),`utf-8`)}function w(){return{project:{version:`0.1.0`,name:``,filename:``},modrinth:{enabled:!1,project_id:``,featured:!1},curseforge:{datapack:{enabled:!1,project_id:null},mod:{enabled:!1,project_id:null,java_versions:[`Java 21`],environments:[`server`]}},package_as_mod:{enabled:!1,loaders:[`fabric`,`forge`,`neoforge`,`quilt`],id:``,authors:[]},build:{exclude:[`.git`,`.github`,`.changeset`,`.vscode`,`.cursor`,`node_modules`,`README.md`,`.gitignore`,`deploy.yaml`]}}}async function T(){try{await e(`pack.mcmeta`,t.F_OK);let n=await i(`pack.mcmeta`,`utf-8`);return!!JSON.parse(n).pack?.description}catch{return!1}}const E=`.github/workflows/voxset.yaml`;function D(){let e=o(f(import.meta.url));return s(e.includes(`src`)?c(e,`../..`):c(e,`..`),`examples/deploy.yml`)}async function O(){try{return await e(E,t.F_OK),!0}catch{return!1}}async function k(){await r(o(E),{recursive:!0}),await n(D(),E)}export{x as configExists,g as createChangeset,w as createDefaultConfig,m as createMarkdownWithFrontmatter,k as createWorkflow,T as isValidDatapack,p as parseMarkdownFrontmatter,S as readConfig,O as workflowExists,C as writeConfig};
2
- //# sourceMappingURL=workflow-BHPZ2hsY.js.map
1
+ import{access as e,constants as t,copyFile as n,mkdir as r,readFile as i,writeFile as a}from"node:fs/promises";import{parse as o,stringify as s}from"yaml";import{type as c}from"arktype";import{dirname as l,join as u,resolve as d}from"node:path";import{fileURLToPath as f}from"node:url";function p(e){let t=e.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(!t)throw Error(`Invalid markdown frontmatter format`);let[,n,r]=t;return{data:o(n),content:r.trim()}}function m(e,t){return`---\n${s(e)}---\n\n${t}`}const h=`.changeset`;async function g(e,t){await r(h,{recursive:!0});let n=`${h}/${_()}`;return await a(n,m(e,t),`utf-8`),n}function _(){let e=[`happy`,`silly`,`brave`,`calm`,`bright`,`clever`,`fair`,`gentle`,`kind`,`lovely`],t=[`pandas`,`tigers`,`dragons`,`wolves`,`eagles`,`foxes`,`bears`,`lions`,`owls`,`hawks`],n=[`dance`,`jump`,`fly`,`swim`,`run`,`sing`,`play`,`rest`,`hunt`,`soar`];return`${e[Math.floor(Math.random()*e.length)]}-${t[Math.floor(Math.random()*t.length)]}-${n[Math.floor(Math.random()*n.length)]}.md`}const v=c(`'major'|'minor'|'patch'`),y=c(`'release'|'beta'|'alpha'`);c(`string`),c(`'fabric'|'forge'|'neoforge'|'quilt'`),c({game_versions:`string[]`,version_type:y,version_bump:v,"loaders?":`string[]`});const b=c({project:{version:`string`,name:`string`,filename:`string`},modrinth:{enabled:`boolean`,project_id:`string`,"featured?":`boolean`},curseforge:{datapack:{enabled:`boolean`,"project_id?":`number | null`},mod:{enabled:`boolean`,"project_id?":`number | null`,"java_versions?":`string[]`,"environments?":`string[]`}},package_as_mod:{enabled:`boolean`,loaders:`string[]`,id:`string`,"filename?":`string`,authors:`string[]`,"homepage?":`string`,"issues?":`string`,"sources?":`string`},"build?":{"exclude?":`string[]`}});async function x(){try{return await e(`deploy.yaml`,t.F_OK),!0}catch{return!1}}async function S(){let e=b(o(await i(`deploy.yaml`,`utf-8`)));if(e instanceof Error)throw Error(`Invalid deploy.yaml: ${e.message}`);return e}async function C(e){let t=b(e);if(t instanceof Error)throw Error(`Invalid config: ${t.message}`);await a(`deploy.yaml`,s(e),`utf-8`)}function w(){return{project:{version:`0.1.0`,name:``,filename:``},modrinth:{enabled:!1,project_id:``,featured:!1},curseforge:{datapack:{enabled:!1,project_id:null},mod:{enabled:!1,project_id:null,java_versions:[`Java 17`],environments:[`server`]}},package_as_mod:{enabled:!1,loaders:[`fabric`,`forge`,`neoforge`,`quilt`],id:``,authors:[]},build:{exclude:[`.git`,`.github`,`.changeset`,`.vscode`,`.cursor`,`node_modules`,`README.md`,`.gitignore`,`deploy.yaml`]}}}async function T(){try{await e(`pack.mcmeta`,t.F_OK);let n=await i(`pack.mcmeta`,`utf-8`);return!!JSON.parse(n).pack?.description}catch{return!1}}const E=`.github/workflows/voxset.yaml`;function D(){let e=l(f(import.meta.url));return u(e.includes(`src`)?d(e,`../..`):d(e,`..`),`examples/deploy.yml`)}async function O(){try{return await e(E,t.F_OK),!0}catch{return!1}}async function k(){await r(l(E),{recursive:!0}),await n(D(),E)}export{w as a,g as c,x as i,m as l,O as n,S as o,T as r,C as s,k as t,p as u};
package/package.json CHANGED
@@ -1,45 +1,43 @@
1
- {
2
- "name": "@voxelio/deploy",
3
- "version": "1.0.8",
4
- "type": "module",
5
- "license": "MIT",
6
- "description": "CLI tool for deploying datapacks to Modrinth and CurseForge",
7
- "author": "Hardel",
8
- "scripts": {
9
- "build": "tsdown",
10
- "build:watch": "tsdown --watch",
11
- "lint": "tsc --noEmit",
12
- "biome:format": "biome format --write .",
13
- "biome:check": "biome check .",
14
- "biome:unsafefix": "biome check --write --unsafe .",
15
- "dev": "tsx src/cli.ts"
16
- },
17
- "bin": {
18
- "voxset": "dist/cli.js"
19
- },
20
- "exports": {
21
- ".": {
22
- "types": "./dist/index.d.ts",
23
- "default": "./dist/index.js"
24
- }
25
- },
26
- "files": [
27
- "dist",
28
- "examples"
29
- ],
30
- "engines": {
31
- "node": ">=22.0.0"
32
- },
33
- "devDependencies": {
34
- "@biomejs/biome": "^2.2.6",
35
- "@types/node": "^22.18.10",
36
- "tsdown": "^0.15.7",
37
- "tsx": "^4.20.6",
38
- "typescript": "^5.9.3"
39
- },
40
- "dependencies": {
41
- "@clack/prompts": "^0.11.0",
42
- "arktype": "^2.1.23",
43
- "yaml": "^2.8.1"
44
- }
1
+ {
2
+ "name": "@voxelio/deploy",
3
+ "version": "1.0.9",
4
+ "type": "module",
5
+ "description": "CLI tool for deploying datapacks to Modrinth and CurseForge",
6
+ "dependencies": {
7
+ "@clack/prompts": "^0.11.0",
8
+ "arktype": "^2.1.23",
9
+ "yaml": "^2.8.1"
10
+ },
11
+ "bin": {
12
+ "voxset": "dist/cli.js"
13
+ },
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "examples",
22
+ "dist/**/*.mjs",
23
+ "dist/**/*.d.mts",
24
+ "!dist/**/*.map"
25
+ ],
26
+ "author": {
27
+ "name": "Hardel",
28
+ "email": "teams.voxel@gmail.com",
29
+ "url": "https://voxel.hardel.io"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "scripts": {
35
+ "dev": "node src/cli.ts",
36
+ "build": "tsdown",
37
+ "build:watch": "tsdown --watch",
38
+ "check": "tsgo --noEmit",
39
+ "biome:format": "biome format --write .",
40
+ "biome:check": "biome check .",
41
+ "biome:unsafefix": "biome check --write --unsafe ."
42
+ }
45
43
  }
package/dist/cli.js DELETED
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env node
2
- import{configExists as e,createChangeset as t,createDefaultConfig as n,createWorkflow as r,isValidDatapack as i,workflowExists as a,writeConfig as o}from"./workflow-BHPZ2hsY.js";import{spawn as s}from"node:child_process";import{writeFile as c}from"node:fs/promises";import{tmpdir as l}from"node:os";import{join as u}from"node:path";import*as d from"@clack/prompts";const f=[`1.21.10`,`1.21.9`,`1.21.8`,`1.21.7`,`1.21.6`,`1.21.5`,`1.21.4`,`1.21.3`,`1.21.2`,`1.21.1`,`1.21`,`1.20.6`,`1.20.5`,`1.20.4`,`1.20.3`,`1.20.2`,`1.20.1`,`1.20`];async function p(){await i()||(d.log.error(`The current directory is not a valid datapack. Check that there is a pack.mcmeta file with a 'description' field.`),process.exit(1));let t=await e(),n=await a();if(!t)await m();else if(n){d.intro(`Voxelio Deploy`),d.note(`Ready to create a new changeset.
3
-
4
- This will create a markdown file in the .changeset folder.
5
- The deployment will be triggered on the next commit containing this file.`,`Create Changeset`);let e=await d.confirm({message:`Do you want to continue?`});(d.isCancel(e)||!e)&&(d.cancel(`Operation cancelled`),process.exit(0)),await h(),d.outro(`Changeset created successfully!`)}else{d.intro(`Voxelio Deploy`);let e=d.spinner();e.start(`Creating workflow file...`),await r(),e.stop(`Workflow file created successfully!`),d.note(`Ready to create a new changeset.
6
-
7
- This will create a markdown file in the .changeset folder.
8
- The deployment will be triggered on the next commit containing this file.`,`Create Changeset`);let t=await d.confirm({message:`Do you want to continue?`});(d.isCancel(t)||!t)&&(d.cancel(`Operation cancelled`),process.exit(0)),await h(),d.outro(`Changeset created successfully!`)}}async function m(){d.intro(`Voxelio Deploy - Configuration`),d.note(`Hello, welcome to the Voxelio Deploy configuration. We will prepare the environment together to easily deploy to Modrinth and CurseForge.
9
-
10
- A few things to know:
11
- - If you want to stop, you can press Escape or Ctrl+C
12
- - You will need a Modrinth and/or CurseForge account`,`Welcome`);let e=await d.confirm({message:`Ready to start configuration?`});(d.isCancel(e)||!e)&&(d.cancel(`Configuration cancelled`),process.exit(0)),d.note(`Don't forget to set environment variables in your GitHub project!
13
-
14
- You need to define in Settings > Secrets and Variables > Actions > Secrets > Repository secrets:
15
- - CURSEFORGE_TOKEN: https://legacy.curseforge.com/account/api-tokens
16
- - MODRINTH_TOKEN: https://modrinth.com/settings/pats with "Create Version" permission`,`Environment Variables`);let t=await d.confirm({message:`All set, shall we continue?`});(d.isCancel(t)||!t)&&(d.cancel(`Configuration cancelled`),process.exit(0)),d.note(`Do you want your datapack to be automatically converted to a mod?
17
-
18
- - For Modrinth this will create 2 versions on the same project
19
- - For CurseForge this will create 2 separate projects`,`Package as mod`);let i=await d.confirm({message:`Do you want package your datapack as a mod?`});d.isCancel(i)&&(d.cancel(`Configuration cancelled`),process.exit(0));let a=[{value:`modrinth`,label:`Modrinth`}];i&&(a.push({value:`curseforge_datapack`,label:`CurseForge (Datapack)`}),a.push({value:`curseforge_mod`,label:`CurseForge (Mod)`})),i||a.push({value:`curseforge_datapack`,label:`CurseForge`});let s=await d.multiselect({message:`Choose platforms`,options:a,required:!0});d.isCancel(s)&&(d.cancel(`Configuration cancelled`),process.exit(0));let c=await d.text({message:`Project name (appears in CurseForge)`,placeholder:`My Datapack`,validate:e=>e.length===0?`Required`:void 0});d.isCancel(c)&&(d.cancel(`Configuration cancelled`),process.exit(0));let l=`1.0.0`,u=await d.text({message:`Project Filename (For zipped datapack) - Preview: ${c}-${l}.zip`,placeholder:c,defaultValue:c});d.isCancel(u)&&(d.cancel(`Configuration cancelled`),process.exit(0));let f=u;if(i){let e=await d.text({message:`Mod Filename (For JAR file) - Preview: ${u}-${l}.jar`,placeholder:u,defaultValue:u});d.isCancel(e)&&(d.cancel(`Configuration cancelled`),process.exit(0)),f=e}let p=await d.text({message:`Initial version`,placeholder:`1.0.0`,defaultValue:`1.0.0`,validate:e=>e.length===0||/^\d+\.\d+\.\d+$/.test(e)?void 0:`Format: X.Y.Z`});d.isCancel(p)&&(d.cancel(`Configuration cancelled`),process.exit(0));let m=n();m.project.name=c,m.project.filename=u,m.project.version=p;let g=s;if(g.includes(`modrinth`)){let e=await d.text({message:`Modrinth Project ID - https://modrinth.com/dashboard/projects`,placeholder:`AABBCCDD`,validate:e=>{if(e.length===0)return`Required`;if(e.length!==8)return`Must be exactly 8 characters`}});d.isCancel(e)&&(d.cancel(`Configuration cancelled`),process.exit(0)),m.modrinth.enabled=!0,m.modrinth.project_id=e}if(g.includes(`curseforge_datapack`)){let e=await d.text({message:`CurseForge Datapack Project ID - Can be found on the Public Page e.g https://www.curseforge.com/minecraft/mc-mods/neoenchant`,placeholder:`12345678`,validate:e=>{if(e.length===0)return`Required`;if(!/^\d+$/.test(e))return`Must be a number`}});d.isCancel(e)&&(d.cancel(`Configuration cancelled`),process.exit(0)),m.curseforge.datapack.enabled=!0,m.curseforge.datapack.project_id=Number.parseInt(e,10)}if(g.includes(`curseforge_mod`)){let e=await d.text({message:`CurseForge Mod Project ID`,placeholder:`12345678`,validate:e=>{if(e.length===0)return`Required`;if(!/^\d+$/.test(e))return`Must be a number`}});d.isCancel(e)&&(d.cancel(`Configuration cancelled`),process.exit(0)),m.curseforge.mod.enabled=!0,m.curseforge.mod.project_id=Number.parseInt(e,10)}if(i){m.package_as_mod.enabled=!0,m.package_as_mod.filename=f;let e=await d.multiselect({message:`Mod loaders`,options:[{value:`fabric`,label:`Fabric`},{value:`forge`,label:`Forge`},{value:`neoforge`,label:`NeoForge`},{value:`quilt`,label:`Quilt`}],required:!0});d.isCancel(e)&&(d.cancel(`Configuration cancelled`),process.exit(0)),m.package_as_mod.loaders=e;let t=await d.text({message:`Mod ID`,placeholder:_(c),defaultValue:_(c)});d.isCancel(t)&&(d.cancel(`Configuration cancelled`),process.exit(0)),m.package_as_mod.id=t}let v=await d.confirm({message:`Do you want to configure additional information (author, homepage, sources, issues)?`});if(d.isCancel(v)&&(d.cancel(`Configuration cancelled`),process.exit(0)),v){let e=await d.text({message:`Authors (comma-separated)`,placeholder:`Author1, Author2`});!d.isCancel(e)&&e&&e.trim()!==``&&(m.package_as_mod.authors=e.split(`,`).map(e=>e.trim()));let t=await d.text({message:`Homepage URL`,placeholder:`https://example.com`,validate:e=>{if(!(!e||e.trim()===``)){if(!e.startsWith(`https://`))return`Must start with https://`;try{return new URL(e).hostname.includes(`.`)?void 0:`Must be a valid domain (e.g., example.com)`}catch{return`Invalid URL format`}}}});!d.isCancel(t)&&t&&t.trim()!==``&&(m.package_as_mod.homepage=t);let n=await d.text({message:`Sources URL`,placeholder:`https://github.com/user/repo`,validate:e=>{if(!(!e||e.trim()===``)){if(!e.startsWith(`https://`))return`Must start with https://`;try{return new URL(e).hostname.includes(`.`)?void 0:`Must be a valid domain (e.g., github.com)`}catch{return`Invalid URL format`}}}});!d.isCancel(n)&&n&&n.trim()!==``&&(m.package_as_mod.sources=n);let r=await d.text({message:`Issues URL`,placeholder:`https://github.com/user/repo/issues`,validate:e=>{if(!(!e||e.trim()===``)){if(!e.startsWith(`https://`))return`Must start with https://`;try{return new URL(e).hostname.includes(`.`)?void 0:`Must be a valid domain (e.g., github.com)`}catch{return`Invalid URL format`}}}});!d.isCancel(r)&&r&&r.trim()!==``&&(m.package_as_mod.issues=r)}let y=d.spinner();y.start(`Creating configuration...`),await o(m),await r(),y.stop(`Configuration created successfully!`),d.note(`The deploy.yaml file has been created at the root of your project.
20
-
21
- You can edit this file to configure advanced settings such as:
22
- - Excluding files from the build
23
- - Java versions for CurseForge
24
- - Environment settings (client/server)
25
- - And more...`,`Configuration Complete`),d.outro(`Thank you for your participation, we're done! 🎉`);let b=await d.confirm({message:`Do you want to create a changeset now?
26
-
27
- (It will be deployed on the next commit)`});d.isCancel(b)||!b||(d.intro(`Create Changeset`),await h(),d.outro(`Changeset created successfully!`))}async function h(){let e=await d.multiselect({message:`Select Minecraft versions`,options:f.map(e=>({value:e,label:e})),required:!0});d.isCancel(e)&&(d.cancel(`Operation cancelled`),process.exit(0));let n=await d.select({message:`Version type`,options:[{value:`release`,label:`Release`},{value:`beta`,label:`Beta`},{value:`alpha`,label:`Alpha`}]});d.isCancel(n)&&(d.cancel(`Operation cancelled`),process.exit(0));let r=await d.select({message:`Version bump`,options:[{value:`patch`,label:`Patch (0.0.X)`},{value:`minor`,label:`Minor (0.X.0)`},{value:`major`,label:`Major (X.0.0)`}]});d.isCancel(r)&&(d.cancel(`Operation cancelled`),process.exit(0));let i=await d.text({message:`Changelog (press Enter to open editor)`,placeholder:`Added new features...`});if(d.isCancel(i)&&(d.cancel(`Operation cancelled`),process.exit(0)),!i||i.trim()===``){d.note(`Opening editor...
28
-
29
- ⚠️ IMPORTANT: Save your changes (Ctrl+S) before closing the editor!`,`Editor`);let e=await g();e===null&&(d.cancel(`Operation cancelled`),process.exit(0)),e.trim()===``&&(d.log.error(`Changelog cannot be empty. Please provide a changelog.`),process.exit(1)),i=e}let a=await t({game_versions:e,version_type:n,version_bump:r},i);d.log.success(`Created ${a}`)}async function g(){let e=u(l(),`voxset-${Date.now()}.md`);await c(e,`# Write your changelog here
30
- `,`utf-8`);let t=process.env.EDITOR||(process.platform===`win32`?`notepad`:`nano`);return new Promise(n=>{s(t,[e],{stdio:`inherit`}).on(`exit`,async t=>{if(t===0){let{readFile:t}=await import(`node:fs/promises`);n((await t(e,`utf-8`)).replace(/^# Write your changelog here\n/,``).trim())}else n(null)})})}function _(e){return e.toLowerCase().replace(/[^a-z0-9]+(.)/g,(e,t)=>t.toUpperCase()).replace(/^(.)/,e=>e.toLowerCase())}try{await p()}catch(e){console.error(`Error:`,e instanceof Error?e.message:e),process.exit(1)}export{};
31
- //# sourceMappingURL=cli.js.map
package/dist/cli.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.js","names":["readFile"],"sources":["../src/commands/init.ts","../src/cli.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport * as clack from \"@clack/prompts\";\nimport type { ChangesetFrontmatter, VersionBump, VersionType } from \"@/types/schema\";\nimport { createChangeset } from \"@/utils/changeset\";\nimport { configExists, createDefaultConfig, writeConfig } from \"@/utils/config\";\nimport { isValidDatapack } from \"@/utils/datapack\";\nimport { createWorkflow, workflowExists } from \"@/utils/workflow\";\n\nconst MINECRAFT_VERSIONS = [\n \"1.21.10\",\n \"1.21.9\",\n \"1.21.8\",\n \"1.21.7\",\n \"1.21.6\",\n \"1.21.5\",\n \"1.21.4\",\n \"1.21.3\",\n \"1.21.2\",\n \"1.21.1\",\n \"1.21\",\n \"1.20.6\",\n \"1.20.5\",\n \"1.20.4\",\n \"1.20.3\",\n \"1.20.2\",\n \"1.20.1\",\n \"1.20\"\n];\n\nexport async function init(): Promise<void> {\n const isDatapack = await isValidDatapack();\n if (!isDatapack) {\n clack.log.error(\n \"The current directory is not a valid datapack. Check that there is a pack.mcmeta file with a 'description' field.\"\n );\n process.exit(1);\n }\n\n const hasConfig = await configExists();\n const hasWorkflow = await workflowExists();\n\n if (!hasConfig) {\n await runFullSetup();\n } else if (!hasWorkflow) {\n clack.intro(\"Voxelio Deploy\");\n\n const spinner = clack.spinner();\n spinner.start(\"Creating workflow file...\");\n await createWorkflow();\n spinner.stop(\"Workflow file created successfully!\");\n\n clack.note(\n \"Ready to create a new changeset.\\n\\nThis will create a markdown file in the .changeset folder.\\nThe deployment will be triggered on the next commit containing this file.\",\n \"Create Changeset\"\n );\n\n const wantsChangeset = await clack.confirm({\n message: \"Do you want to continue?\"\n });\n\n if (clack.isCancel(wantsChangeset) || !wantsChangeset) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n await createNewChangeset();\n clack.outro(\"Changeset created successfully!\");\n } else {\n clack.intro(\"Voxelio Deploy\");\n\n clack.note(\n \"Ready to create a new changeset.\\n\\nThis will create a markdown file in the .changeset folder.\\nThe deployment will be triggered on the next commit containing this file.\",\n \"Create Changeset\"\n );\n\n const wantsChangeset = await clack.confirm({\n message: \"Do you want to continue?\"\n });\n\n if (clack.isCancel(wantsChangeset) || !wantsChangeset) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n await createNewChangeset();\n clack.outro(\"Changeset created successfully!\");\n }\n}\n\nasync function runFullSetup(): Promise<void> {\n clack.intro(\"Voxelio Deploy - Configuration\");\n\n clack.note(\n \"Hello, welcome to the Voxelio Deploy configuration. We will prepare the environment together to easily deploy to Modrinth and CurseForge.\\n\\nA few things to know:\\n- If you want to stop, you can press Escape or Ctrl+C\\n- You will need a Modrinth and/or CurseForge account\",\n \"Welcome\"\n );\n\n const continueSetup = await clack.confirm({\n message: \"Ready to start configuration?\"\n });\n\n if (clack.isCancel(continueSetup) || !continueSetup) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n clack.note(\n 'Don\\'t forget to set environment variables in your GitHub project!\\n\\nYou need to define in Settings > Secrets and Variables > Actions > Secrets > Repository secrets:\\n- CURSEFORGE_TOKEN: https://legacy.curseforge.com/account/api-tokens\\n- MODRINTH_TOKEN: https://modrinth.com/settings/pats with \"Create Version\" permission',\n \"Environment Variables\"\n );\n\n const envVarConfirm = await clack.confirm({\n message: \"All set, shall we continue?\"\n });\n\n if (clack.isCancel(envVarConfirm) || !envVarConfirm) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n clack.note(\n \"Do you want your datapack to be automatically converted to a mod?\\n\\n- For Modrinth this will create 2 versions on the same project\\n- For CurseForge this will create 2 separate projects\",\n \"Package as mod\"\n );\n\n const packageAsMod = await clack.confirm({\n message: \"Do you want package your datapack as a mod?\"\n });\n\n if (clack.isCancel(packageAsMod)) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n const platformOptions = [{ value: \"modrinth\", label: \"Modrinth\" }];\n\n if (packageAsMod) {\n platformOptions.push({ value: \"curseforge_datapack\", label: \"CurseForge (Datapack)\" });\n platformOptions.push({ value: \"curseforge_mod\", label: \"CurseForge (Mod)\" });\n }\n\n if (!packageAsMod) {\n platformOptions.push({ value: \"curseforge_datapack\", label: \"CurseForge\" });\n }\n\n const platforms = await clack.multiselect({\n message: \"Choose platforms\",\n options: platformOptions,\n required: true\n });\n\n if (clack.isCancel(platforms)) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n const projectName = await clack.text({\n message: \"Project name (appears in CurseForge)\",\n placeholder: \"My Datapack\",\n validate: (value) => (value.length === 0 ? \"Required\" : undefined)\n });\n\n if (clack.isCancel(projectName)) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n const versionPreview = \"1.0.0\";\n\n const projectFilename = await clack.text({\n message: `Project Filename (For zipped datapack) - Preview: ${projectName}-${versionPreview}.zip`,\n placeholder: projectName as string,\n defaultValue: projectName as string\n });\n\n if (clack.isCancel(projectFilename)) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n let modFilename = projectFilename;\n if (packageAsMod) {\n const modFilenameInput = await clack.text({\n message: `Mod Filename (For JAR file) - Preview: ${projectFilename}-${versionPreview}.jar`,\n placeholder: projectFilename as string,\n defaultValue: projectFilename as string\n });\n\n if (clack.isCancel(modFilenameInput)) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n modFilename = modFilenameInput;\n }\n\n const version = await clack.text({\n message: \"Initial version\",\n placeholder: \"1.0.0\",\n defaultValue: \"1.0.0\",\n validate: (value) => (value.length === 0 || /^\\d+\\.\\d+\\.\\d+$/.test(value) ? undefined : \"Format: X.Y.Z\")\n });\n\n if (clack.isCancel(version)) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n const config = createDefaultConfig();\n config.project.name = projectName as string;\n config.project.filename = projectFilename as string;\n config.project.version = version as string;\n\n const platformList = platforms as string[];\n\n if (platformList.includes(\"modrinth\")) {\n const modrinthId = await clack.text({\n message: \"Modrinth Project ID - https://modrinth.com/dashboard/projects\",\n placeholder: \"AABBCCDD\",\n validate: (value) => {\n if (value.length === 0) return \"Required\";\n if (value.length !== 8) return \"Must be exactly 8 characters\";\n return undefined;\n }\n });\n\n if (clack.isCancel(modrinthId)) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n config.modrinth.enabled = true;\n config.modrinth.project_id = modrinthId as string;\n }\n\n if (platformList.includes(\"curseforge_datapack\")) {\n const curseforgeDatapackId = await clack.text({\n message:\n \"CurseForge Datapack Project ID - Can be found on the Public Page e.g https://www.curseforge.com/minecraft/mc-mods/neoenchant\",\n placeholder: \"12345678\",\n validate: (value) => {\n if (value.length === 0) return \"Required\";\n if (!/^\\d+$/.test(value)) return \"Must be a number\";\n return undefined;\n }\n });\n\n if (clack.isCancel(curseforgeDatapackId)) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n config.curseforge.datapack.enabled = true;\n config.curseforge.datapack.project_id = Number.parseInt(curseforgeDatapackId as string, 10);\n }\n\n if (platformList.includes(\"curseforge_mod\")) {\n const curseforgeModId = await clack.text({\n message: \"CurseForge Mod Project ID\",\n placeholder: \"12345678\",\n validate: (value) => {\n if (value.length === 0) return \"Required\";\n if (!/^\\d+$/.test(value)) return \"Must be a number\";\n return undefined;\n }\n });\n\n if (clack.isCancel(curseforgeModId)) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n config.curseforge.mod.enabled = true;\n config.curseforge.mod.project_id = Number.parseInt(curseforgeModId as string, 10);\n }\n\n if (packageAsMod) {\n config.package_as_mod.enabled = true;\n config.package_as_mod.filename = modFilename as string;\n\n const loaders = await clack.multiselect({\n message: \"Mod loaders\",\n options: [\n { value: \"fabric\", label: \"Fabric\" },\n { value: \"forge\", label: \"Forge\" },\n { value: \"neoforge\", label: \"NeoForge\" },\n { value: \"quilt\", label: \"Quilt\" }\n ],\n required: true\n });\n\n if (clack.isCancel(loaders)) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n config.package_as_mod.loaders = loaders as string[];\n\n const modId = await clack.text({\n message: \"Mod ID\",\n placeholder: toCamelCase(projectName as string),\n defaultValue: toCamelCase(projectName as string)\n });\n\n if (clack.isCancel(modId)) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n config.package_as_mod.id = modId as string;\n }\n\n const wantsExtra = await clack.confirm({\n message: \"Do you want to configure additional information (author, homepage, sources, issues)?\"\n });\n\n if (clack.isCancel(wantsExtra)) {\n clack.cancel(\"Configuration cancelled\");\n process.exit(0);\n }\n\n if (wantsExtra) {\n const authors = await clack.text({\n message: \"Authors (comma-separated)\",\n placeholder: \"Author1, Author2\"\n });\n\n if (!clack.isCancel(authors) && authors && (authors as string).trim() !== \"\") {\n config.package_as_mod.authors = (authors as string).split(\",\").map((a) => a.trim());\n }\n\n const homepage = await clack.text({\n message: \"Homepage URL\",\n placeholder: \"https://example.com\",\n validate: (value) => {\n if (!value || value.trim() === \"\") return undefined;\n if (!value.startsWith(\"https://\")) return \"Must start with https://\";\n try {\n const url = new URL(value);\n if (!url.hostname.includes(\".\")) return \"Must be a valid domain (e.g., example.com)\";\n return undefined;\n } catch {\n return \"Invalid URL format\";\n }\n }\n });\n\n if (!clack.isCancel(homepage) && homepage && (homepage as string).trim() !== \"\") {\n config.package_as_mod.homepage = homepage as string;\n }\n\n const sources = await clack.text({\n message: \"Sources URL\",\n placeholder: \"https://github.com/user/repo\",\n validate: (value) => {\n if (!value || value.trim() === \"\") return undefined;\n if (!value.startsWith(\"https://\")) return \"Must start with https://\";\n try {\n const url = new URL(value);\n if (!url.hostname.includes(\".\")) return \"Must be a valid domain (e.g., github.com)\";\n return undefined;\n } catch {\n return \"Invalid URL format\";\n }\n }\n });\n\n if (!clack.isCancel(sources) && sources && (sources as string).trim() !== \"\") {\n config.package_as_mod.sources = sources as string;\n }\n\n const issues = await clack.text({\n message: \"Issues URL\",\n placeholder: \"https://github.com/user/repo/issues\",\n validate: (value) => {\n if (!value || value.trim() === \"\") return undefined;\n if (!value.startsWith(\"https://\")) return \"Must start with https://\";\n try {\n const url = new URL(value);\n if (!url.hostname.includes(\".\")) return \"Must be a valid domain (e.g., github.com)\";\n return undefined;\n } catch {\n return \"Invalid URL format\";\n }\n }\n });\n\n if (!clack.isCancel(issues) && issues && (issues as string).trim() !== \"\") {\n config.package_as_mod.issues = issues as string;\n }\n }\n\n const spinner = clack.spinner();\n spinner.start(\"Creating configuration...\");\n\n await writeConfig(config);\n await createWorkflow();\n\n spinner.stop(\"Configuration created successfully!\");\n\n clack.note(\n \"The deploy.yaml file has been created at the root of your project.\\n\\nYou can edit this file to configure advanced settings such as:\\n- Excluding files from the build\\n- Java versions for CurseForge\\n- Environment settings (client/server)\\n- And more...\",\n \"Configuration Complete\"\n );\n\n clack.outro(\"Thank you for your participation, we're done! 🎉\");\n\n const wantsChangeset = await clack.confirm({\n message: \"Do you want to create a changeset now?\\n\\n (It will be deployed on the next commit)\"\n });\n\n if (clack.isCancel(wantsChangeset) || !wantsChangeset) {\n return;\n }\n\n clack.intro(\"Create Changeset\");\n await createNewChangeset();\n clack.outro(\"Changeset created successfully!\");\n}\n\nasync function createNewChangeset(): Promise<void> {\n const gameVersions = await clack.multiselect({\n message: \"Select Minecraft versions\",\n options: MINECRAFT_VERSIONS.map((version) => ({\n value: version,\n label: version\n })),\n required: true\n });\n\n if (clack.isCancel(gameVersions)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n const versionType = await clack.select({\n message: \"Version type\",\n options: [\n { value: \"release\", label: \"Release\" },\n { value: \"beta\", label: \"Beta\" },\n { value: \"alpha\", label: \"Alpha\" }\n ]\n });\n\n if (clack.isCancel(versionType)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n const versionBump = await clack.select({\n message: \"Version bump\",\n options: [\n { value: \"patch\", label: \"Patch (0.0.X)\" },\n { value: \"minor\", label: \"Minor (0.X.0)\" },\n { value: \"major\", label: \"Major (X.0.0)\" }\n ]\n });\n\n if (clack.isCancel(versionBump)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n let changelog = await clack.text({\n message: \"Changelog (press Enter to open editor)\",\n placeholder: \"Added new features...\"\n });\n\n if (clack.isCancel(changelog)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n if (!changelog || (changelog as string).trim() === \"\") {\n clack.note(\"Opening editor...\\n\\n⚠️ IMPORTANT: Save your changes (Ctrl+S) before closing the editor!\", \"Editor\");\n\n const editedChangelog = await openEditor();\n\n if (editedChangelog === null) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n if (editedChangelog.trim() === \"\") {\n clack.log.error(\"Changelog cannot be empty. Please provide a changelog.\");\n process.exit(1);\n }\n\n changelog = editedChangelog;\n }\n\n const frontmatter: ChangesetFrontmatter = {\n game_versions: gameVersions as string[],\n version_type: versionType as VersionType,\n version_bump: versionBump as VersionBump\n };\n\n const filepath = await createChangeset(frontmatter, changelog as string);\n clack.log.success(`Created ${filepath}`);\n}\n\nasync function openEditor(): Promise<string | null> {\n const tmpFile = join(tmpdir(), `voxset-${Date.now()}.md`);\n await writeFile(tmpFile, \"# Write your changelog here\\n\", \"utf-8\");\n\n const editor = process.env.EDITOR || (process.platform === \"win32\" ? \"notepad\" : \"nano\");\n\n return new Promise((resolve) => {\n const child = spawn(editor, [tmpFile], { stdio: \"inherit\" });\n\n child.on(\"exit\", async (code) => {\n if (code === 0) {\n const { readFile } = await import(\"node:fs/promises\");\n const content = await readFile(tmpFile, \"utf-8\");\n const cleaned = content.replace(/^# Write your changelog here\\n/, \"\").trim();\n resolve(cleaned);\n } else {\n resolve(null);\n }\n });\n });\n}\n\nfunction toCamelCase(str: string): string {\n return str\n .toLowerCase()\n .replace(/[^a-z0-9]+(.)/g, (_, char) => char.toUpperCase())\n .replace(/^(.)/, (char) => char.toLowerCase());\n}\n","#!/usr/bin/env node\nimport { init } from \"@/commands/init\";\n\ntry {\n await init();\n} catch (error) {\n console.error(\"Error:\", error instanceof Error ? error.message : error);\n process.exit(1);\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types/schema.ts","../src/utils/changeset.ts","../src/utils/config.ts","../src/utils/datapack.ts","../src/utils/frontmatter.ts","../src/utils/workflow.ts"],"sourcesContent":[],"mappings":";;;;cAEa,aAA6C,oCAAA,CAAlC;KACZ,WAAA,UAAqB,WAAA,CAAY;cAEhC,aAA8C,oCAAA,CAAnC;KACZ,WAAA,UAAqB,WAAA,CAAY;AAJhC,cAMA,WAN6C,EAMjB,oCAAA,CAAjB,UANA,CAAA,MAAA,EAAA,CAAA,CAAA,CAAA;AACZ,KAMA,WAAA,GANW,OAMU,WAAA,CAAY,KANK;AAErC,cAMA,MAN8C,EAMM,oCAAA,CAA9C,UANK,CAAA,QAAA,GAAA,OAAA,GAAA,UAAA,GAAA,OAAA,EAAA,CAAA,CAAA,CAAA;AACZ,KAMA,MAAA,GANW,OAMK,MAAA,CAAO,KANF;AAEpB,cAMA,oBAN4B,EAWvC,oCAAA,CAL+B,UANT,CAAA;EACZ,aAAA,EAAW,MAAA,EAAA;EAEV,YAAoD,EAAA,SAAA,GAAA,MAAA,GAAA,OAAA;EACrD,YAAM,EAAA,OAAU,GAAA,OAAO,GAAK,OAAA;EAE3B,OAAA,CAAA,EAAA,MAAA,EAAA,GAAA,SAKX;AACF,CAAA,EAAA,CAAY,CAAA,CAAA;AAEC,KAFD,oBAAA,GAsCV,OAtCwC,oBAAA,CAAqB,KAEtC;AAqCb,cArCC,YAqCqB,EADhC,oCAAA,CApCuB,UAqC2B,CAAA;;;;ICrD9B,QAAA,EAAA,MAAe;EAAA,CAAA;UAAO,EAAA;IAA0C,OAAA,EAAA,OAAA;IAAO,UAAA,EAAA,MAAA;;;;ICFvE,QAAA,EAAA;MASA,OAAU,EAAA,OAAA;MAAA,UAAA,CAAA,EAAA,MAAA,GAAA,IAAA,GAAA,SAAA;IAAY,CAAA;IAAR,GAAA,EAAA;MAAO,OAAA,EAAA,OAAA;MAWrB,UAAW,CAAA,EAAA,MAAA,GAAA,IAAA,GAAA,SAAA;MAAA,aAAA,CAAA,EAAA,MAAA,EAAA,GAAA,SAAA;MAAS,YAAA,CAAA,EAAA,MAAA,EAAA,GAAA,SAAA;IAAe,CAAA;EAAO,CAAA;EAUhD,cAAA,EAAA;;;;IC1BM,OAAA,EAAA,MAAe,EAAA;;;;ICLrB,OAAA,CAAA,EAAA,MAAA,GAAA,SAAwB;EAexB,CAAA;;;;ACLhB,CAAA,EAAA,CAAsB,CAAA,CAAA;AASA,KLqCV,YAAA,GKrCwB,OLqCF,YAAA,CAAa,KKrCA;;;iBJhBzB,eAAA,OAAsB,0CAA0C;;;iBCFhE,YAAA,CAAA,GAAgB;iBAShB,UAAA,CAAA,GAAc,QAAQ;iBAWtB,WAAA,SAAoB,eAAe;AFtB5C,iBEgCG,mBAAA,CAAA,CFhC0C,EEgCnB,YFhCmB;;;iBGMpC,eAAA,CAAA,GAAmB;;;iBCLzB,wBAAA;QAAmD;;AJDnE,CAAA;AACY,iBIeI,6BAAA,CJfkC,IAAA,EIeE,oBJfF,EAAA,OAAA,EAAA,MAAA,CAAA,EAAA,MAAA;;;iBKU5B,cAAA,CAAA,GAAkB;iBASlB,cAAA,CAAA,GAAkB"}
package/dist/index.js DELETED
@@ -1 +0,0 @@
1
- import{configExists as e,createChangeset as t,createDefaultConfig as n,createMarkdownWithFrontmatter as r,createWorkflow as i,isValidDatapack as a,parseMarkdownFrontmatter as o,readConfig as s,workflowExists as c,writeConfig as l}from"./workflow-BHPZ2hsY.js";export{e as configExists,t as createChangeset,n as createDefaultConfig,r as createMarkdownWithFrontmatter,i as createWorkflow,a as isValidDatapack,o as parseMarkdownFrontmatter,s as readConfig,c as workflowExists,l as writeConfig};
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflow-BHPZ2hsY.js","names":[],"sources":["../src/utils/frontmatter.ts","../src/utils/changeset.ts","../src/types/schema.ts","../src/utils/config.ts","../src/utils/datapack.ts","../src/utils/workflow.ts"],"sourcesContent":["import { parse, stringify } from \"yaml\";\nimport type { ChangesetFrontmatter } from \"@/types/schema\";\n\nexport function parseMarkdownFrontmatter(content: string): { data: ChangesetFrontmatter; content: string } {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/);\n if (!match) {\n throw new Error(\"Invalid markdown frontmatter format\");\n }\n\n const [, frontmatter, markdownContent] = match;\n const data = parse(frontmatter) as ChangesetFrontmatter;\n\n return {\n data,\n content: markdownContent.trim()\n };\n}\n\nexport function createMarkdownWithFrontmatter(data: ChangesetFrontmatter, content: string): string {\n const frontmatter = stringify(data);\n return `---\\n${frontmatter}---\\n\\n${content}`;\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport type { ChangesetFrontmatter } from \"@/types/schema\";\nimport { createMarkdownWithFrontmatter } from \"@/utils/frontmatter\";\n\nconst CHANGESET_DIR = \".changeset\";\n\nexport async function createChangeset(data: ChangesetFrontmatter, changelog: string): Promise<string> {\n await mkdir(CHANGESET_DIR, { recursive: true });\n\n const filename = generateChangesetFilename();\n const filepath = `${CHANGESET_DIR}/${filename}`;\n const content = createMarkdownWithFrontmatter(data, changelog);\n\n await writeFile(filepath, content, \"utf-8\");\n\n return filepath;\n}\n\nfunction generateChangesetFilename(): string {\n const adjectives = [\"happy\", \"silly\", \"brave\", \"calm\", \"bright\", \"clever\", \"fair\", \"gentle\", \"kind\", \"lovely\"];\n const nouns = [\"pandas\", \"tigers\", \"dragons\", \"wolves\", \"eagles\", \"foxes\", \"bears\", \"lions\", \"owls\", \"hawks\"];\n const verbs = [\"dance\", \"jump\", \"fly\", \"swim\", \"run\", \"sing\", \"play\", \"rest\", \"hunt\", \"soar\"];\n\n const adj = adjectives[Math.floor(Math.random() * adjectives.length)];\n const noun = nouns[Math.floor(Math.random() * nouns.length)];\n const verb = verbs[Math.floor(Math.random() * verbs.length)];\n\n return `${adj}-${noun}-${verb}.md`;\n}\n","import { type } from \"arktype\";\n\nexport const VersionBump = type(\"'major'|'minor'|'patch'\");\nexport type VersionBump = typeof VersionBump.infer;\n\nexport const VersionType = type(\"'release'|'beta'|'alpha'\");\nexport type VersionType = typeof VersionType.infer;\n\nexport const GameVersion = type(\"string\");\nexport type GameVersion = typeof GameVersion.infer;\n\nexport const Loader = type(\"'fabric'|'forge'|'neoforge'|'quilt'\");\nexport type Loader = typeof Loader.infer;\n\nexport const ChangesetFrontmatter = type({\n game_versions: \"string[]\",\n version_type: VersionType,\n version_bump: VersionBump,\n \"loaders?\": \"string[]\"\n});\nexport type ChangesetFrontmatter = typeof ChangesetFrontmatter.infer;\n\nexport const DeployConfig = type({\n project: {\n version: \"string\",\n name: \"string\",\n filename: \"string\"\n },\n modrinth: {\n enabled: \"boolean\",\n project_id: \"string\",\n \"featured?\": \"boolean\"\n },\n curseforge: {\n datapack: {\n enabled: \"boolean\",\n \"project_id?\": \"number | null\"\n },\n mod: {\n enabled: \"boolean\",\n \"project_id?\": \"number | null\",\n \"java_versions?\": \"string[]\",\n \"environments?\": \"string[]\"\n }\n },\n package_as_mod: {\n enabled: \"boolean\",\n loaders: \"string[]\",\n id: \"string\",\n \"filename?\": \"string\",\n authors: \"string[]\",\n \"homepage?\": \"string\",\n \"issues?\": \"string\",\n \"sources?\": \"string\"\n },\n \"build?\": {\n \"exclude?\": \"string[]\"\n }\n});\nexport type DeployConfig = typeof DeployConfig.infer;\n","import { access, constants, readFile, writeFile } from \"node:fs/promises\";\nimport { parse, stringify } from \"yaml\";\nimport { DeployConfig } from \"@/types/schema\";\n\nexport async function configExists(): Promise<boolean> {\n try {\n await access(\"deploy.yaml\", constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readConfig(): Promise<DeployConfig> {\n const content = await readFile(\"deploy.yaml\", \"utf-8\");\n const config = parse(content);\n const validation = DeployConfig(config);\n if (validation instanceof Error) {\n throw new Error(`Invalid deploy.yaml: ${validation.message}`);\n }\n\n return validation as DeployConfig;\n}\n\nexport async function writeConfig(config: DeployConfig): Promise<void> {\n const validation = DeployConfig(config);\n if (validation instanceof Error) {\n throw new Error(`Invalid config: ${validation.message}`);\n }\n\n const content = stringify(config);\n await writeFile(\"deploy.yaml\", content, \"utf-8\");\n}\n\nexport function createDefaultConfig(): DeployConfig {\n return {\n project: {\n version: \"0.1.0\",\n name: \"\",\n filename: \"\"\n },\n modrinth: {\n enabled: false,\n project_id: \"\",\n featured: false\n },\n curseforge: {\n datapack: {\n enabled: false,\n project_id: null\n },\n mod: {\n enabled: false,\n project_id: null,\n java_versions: [\"Java 21\"],\n environments: [\"server\"]\n }\n },\n package_as_mod: {\n enabled: false,\n loaders: [\"fabric\", \"forge\", \"neoforge\", \"quilt\"],\n id: \"\",\n authors: []\n },\n build: {\n exclude: [\".git\", \".github\", \".changeset\", \".vscode\", \".cursor\", \"node_modules\", \"README.md\", \".gitignore\", \"deploy.yaml\"]\n }\n };\n}\n","import { access, constants, readFile } from \"node:fs/promises\";\n\ninterface PackMcmeta {\n pack?: {\n description?: string;\n };\n}\n\nexport async function isValidDatapack(): Promise<boolean> {\n try {\n await access(\"pack.mcmeta\", constants.F_OK);\n const content = await readFile(\"pack.mcmeta\", \"utf-8\");\n const data = JSON.parse(content) as PackMcmeta;\n return !!data.pack?.description;\n } catch {\n return false;\n }\n}\n","import { access, constants, copyFile, mkdir } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nconst WORKFLOW_PATH = \".github/workflows/voxset.yaml\";\n\nfunction getExampleWorkflowPath(): string {\n const currentFile = fileURLToPath(import.meta.url);\n const currentDir = dirname(currentFile);\n const isSrcDir = currentDir.includes(\"src\");\n const packageRoot = isSrcDir ? resolve(currentDir, \"../..\") : resolve(currentDir, \"..\");\n return join(packageRoot, \"examples/deploy.yml\");\n}\n\nexport async function workflowExists(): Promise<boolean> {\n try {\n await access(WORKFLOW_PATH, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function createWorkflow(): Promise<void> {\n const dir = dirname(WORKFLOW_PATH);\n await mkdir(dir, { recursive: true });\n const examplePath = getExampleWorkflowPath();\n await copyFile(examplePath, WORKFLOW_PATH);\n}\n"],"mappings":"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"}
@@ -1,462 +0,0 @@
1
- name: Deploy
2
-
3
- on:
4
- push:
5
- paths:
6
- - '.changeset/*.md'
7
-
8
- jobs:
9
- deploy:
10
- runs-on: ubuntu-latest
11
- permissions:
12
- contents: write
13
-
14
- steps:
15
- - name: Checkout
16
- uses: actions/checkout@v4
17
- with:
18
- token: ${{ secrets.GITHUB_TOKEN }}
19
- fetch-depth: 0
20
-
21
- - name: Setup Node.js
22
- uses: actions/setup-node@v4
23
- with:
24
- node-version: '20'
25
-
26
- - name: Setup Python
27
- uses: actions/setup-python@v5
28
- with:
29
- python-version: '3.11'
30
-
31
- - name: Install dependencies
32
- run: pip install pyyaml
33
-
34
- - name: Detect changeset
35
- id: changeset
36
- run: |
37
- CHANGESET_FILE=$(find .changeset -name "*.md" -type f | head -n 1)
38
- if [ -z "$CHANGESET_FILE" ]; then
39
- echo "found=false" >> $GITHUB_OUTPUT
40
- exit 0
41
- fi
42
- echo "found=true" >> $GITHUB_OUTPUT
43
- echo "file=$CHANGESET_FILE" >> $GITHUB_OUTPUT
44
-
45
- - name: Parse changeset
46
- id: parse
47
- if: steps.changeset.outputs.found == 'true'
48
- run: |
49
- python - <<'EOF'
50
- import re, json, os, yaml
51
-
52
- with open("${{ steps.changeset.outputs.file }}", 'r') as f:
53
- content = f.read()
54
-
55
- match = re.match(r'^---\n(.*?)\n---\n(.*)$', content, re.DOTALL)
56
- if not match:
57
- exit(1)
58
-
59
- frontmatter = yaml.safe_load(match.group(1))
60
- changelog = match.group(2).strip()
61
-
62
- with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
63
- f.write(f"game_versions={json.dumps(frontmatter.get('game_versions', []))}\n")
64
- f.write(f"loaders={json.dumps(frontmatter.get('loaders', None)) if frontmatter.get('loaders') else 'null'}\n")
65
- f.write(f"version_type={frontmatter.get('version_type', 'release')}\n")
66
- f.write(f"version_bump={frontmatter.get('version_bump', 'patch')}\n")
67
- f.write(f"changelog<<EOF\n{changelog}\nEOF\n")
68
- EOF
69
-
70
- - name: Read config
71
- id: config
72
- if: steps.changeset.outputs.found == 'true'
73
- run: |
74
- python - <<'EOF'
75
- import yaml, json, os
76
-
77
- with open('deploy.yaml', 'r') as f:
78
- config = yaml.safe_load(f)
79
-
80
- modrinth = config.get('modrinth', {})
81
- curseforge = config.get('curseforge', {})
82
- cf_datapack = curseforge.get('datapack', {})
83
- cf_mod = curseforge.get('mod', {})
84
- package_as_mod = config.get('package_as_mod', {})
85
-
86
- with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
87
- f.write(f"current_version={config['project']['version']}\n")
88
- f.write(f"project_name={config['project']['name']}\n")
89
- f.write(f"project_filename={config['project']['filename']}\n")
90
- f.write(f"modrinth_enabled={str(modrinth.get('enabled', False)).lower()}\n")
91
- f.write(f"modrinth_project_id={modrinth.get('project_id', '')}\n")
92
- f.write(f"modrinth_featured={str(modrinth.get('featured', False)).lower()}\n")
93
- f.write(f"curseforge_datapack_enabled={str(cf_datapack.get('enabled', False)).lower()}\n")
94
- f.write(f"curseforge_datapack_id={cf_datapack.get('project_id', '')}\n")
95
- f.write(f"curseforge_mod_enabled={str(cf_mod.get('enabled', False)).lower()}\n")
96
- f.write(f"curseforge_mod_id={cf_mod.get('project_id', '')}\n")
97
- f.write(f"curseforge_java_versions={json.dumps(cf_mod.get('java_versions', []))}\n")
98
- f.write(f"curseforge_environments={json.dumps(cf_mod.get('environments', []))}\n")
99
- f.write(f"package_as_mod_enabled={str(package_as_mod.get('enabled', False)).lower()}\n")
100
- f.write(f"package_as_mod_loaders={json.dumps(package_as_mod.get('loaders', []))}\n")
101
- f.write(f"package_as_mod_id={package_as_mod.get('id', '')}\n")
102
- f.write(f"package_as_mod_filename={package_as_mod.get('filename', package_as_mod.get('id', ''))}\n")
103
- f.write(f"package_as_mod_authors={json.dumps(package_as_mod.get('authors', []))}\n")
104
- f.write(f"exclude_patterns={json.dumps(config.get('build', {}).get('exclude', []))}\n")
105
- EOF
106
-
107
- - name: Increment version
108
- id: version
109
- if: steps.changeset.outputs.found == 'true'
110
- run: |
111
- python - <<'EOF'
112
- import os
113
-
114
- bump = "${{ steps.parse.outputs.version_bump }}"
115
- current = "${{ steps.config.outputs.current_version }}"
116
- major, minor, patch = map(int, current.split('.'))
117
-
118
- if bump == "major":
119
- major += 1
120
- minor = 0
121
- patch = 0
122
- elif bump == "minor":
123
- minor += 1
124
- patch = 0
125
- elif bump == "patch":
126
- patch += 1
127
-
128
- new_version = f"{major}.{minor}.{patch}"
129
-
130
- with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
131
- f.write(f"version={new_version}\n")
132
- EOF
133
-
134
- - name: Resolve loaders
135
- id: loaders
136
- if: steps.changeset.outputs.found == 'true'
137
- run: |
138
- python - <<'EOF'
139
- import json, os
140
-
141
- changeset_loaders = '${{ steps.parse.outputs.loaders }}'
142
- default_loaders = '${{ steps.config.outputs.package_as_mod_loaders }}'
143
-
144
- if changeset_loaders != 'null':
145
- loaders = json.loads(changeset_loaders)
146
- else:
147
- loaders = json.loads(default_loaders)
148
-
149
- with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
150
- f.write(f"csv={','.join(loaders)}\n")
151
- f.write(f"json={json.dumps(loaders)}\n")
152
- EOF
153
-
154
- - name: Format game versions
155
- id: game_versions
156
- if: steps.changeset.outputs.found == 'true'
157
- run: |
158
- python - <<'EOF'
159
- import json, os
160
-
161
- game_versions = json.loads('${{ steps.parse.outputs.game_versions }}')
162
-
163
- with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
164
- f.write(f"csv={','.join(game_versions)}\n")
165
- f.write(f"json={json.dumps(game_versions)}\n")
166
- EOF
167
-
168
- - name: Create directories
169
- if: steps.changeset.outputs.found == 'true'
170
- run: mkdir -p dist build-temp
171
-
172
- - name: Copy files
173
- if: steps.changeset.outputs.found == 'true'
174
- run: |
175
- python - <<'EOF'
176
- import shutil, json
177
- from pathlib import Path
178
- from fnmatch import fnmatch
179
-
180
- exclude = json.loads('${{ steps.config.outputs.exclude_patterns }}') + ['build-temp', 'dist']
181
-
182
- def should_exclude(path):
183
- for pattern in exclude:
184
- if fnmatch(str(path.name), pattern) or any(fnmatch(str(p), pattern) for p in path.parents):
185
- return True
186
- return False
187
-
188
- src, dst = Path('.'), Path('build-temp')
189
- for item in src.rglob('*'):
190
- rel = item.relative_to(src)
191
- if not should_exclude(rel):
192
- dest = dst / rel
193
- if item.is_dir():
194
- dest.mkdir(parents=True, exist_ok=True)
195
- else:
196
- dest.parent.mkdir(parents=True, exist_ok=True)
197
- shutil.copy2(item, dest)
198
- EOF
199
-
200
- - name: Build datapack
201
- if: steps.changeset.outputs.found == 'true'
202
- run: |
203
- cd build-temp
204
- FILENAME="${{ steps.config.outputs.project_filename }}-${{ steps.version.outputs.version }}.zip"
205
- zip -r ../dist/${FILENAME} . -x "*.git*" "*.DS_Store"
206
- cd ..
207
- [ -f "dist/${FILENAME}" ] || exit 1
208
-
209
- - name: Package as mod
210
- if: steps.changeset.outputs.found == 'true' && steps.config.outputs.package_as_mod_enabled == 'true'
211
- run: |
212
- npm install @voxelio/converter @voxelio/breeze @voxelio/zip
213
-
214
- cat > convert.js <<'SCRIPT'
215
- import { convertDatapack, ModPlatforms } from "@voxelio/converter";
216
- import { readFile, writeFile } from "fs/promises";
217
-
218
- const args = JSON.parse(process.argv[2]);
219
- const buffer = await readFile(args.input);
220
- const file = new File([buffer], "datapack.zip");
221
- const platforms = args.loaders.map(l => ModPlatforms[l.toUpperCase()]).filter(Boolean);
222
-
223
- const response = await convertDatapack(file, platforms, {
224
- id: args.id,
225
- version: args.version,
226
- name: args.name,
227
- description: "",
228
- authors: args.authors
229
- });
230
-
231
- await writeFile(args.output, Buffer.from(await response.arrayBuffer()));
232
- SCRIPT
233
-
234
- MOD_NAME="${{ steps.config.outputs.project_name }}"
235
-
236
- node convert.js "$(jq -n \
237
- --arg input "dist/${{ steps.config.outputs.project_filename }}-${{ steps.version.outputs.version }}.zip" \
238
- --arg output "dist/${{ steps.config.outputs.package_as_mod_filename }}-${{ steps.version.outputs.version }}.jar" \
239
- --arg id "${{ steps.config.outputs.package_as_mod_id }}" \
240
- --arg version "${{ steps.version.outputs.version }}" \
241
- --arg name "$MOD_NAME" \
242
- --argjson authors '${{ steps.config.outputs.package_as_mod_authors }}' \
243
- --argjson loaders '${{ steps.loaders.outputs.json }}' \
244
- '{input:$input, output:$output, id:$id, version:$version, name:$name, authors:$authors, loaders:$loaders}')"
245
-
246
- - name: Fetch CurseForge game versions
247
- id: cf_versions
248
- if: steps.changeset.outputs.found == 'true' && (steps.config.outputs.curseforge_datapack_enabled == 'true' || steps.config.outputs.curseforge_mod_enabled == 'true')
249
- run: |
250
- curl -fsSL "https://minecraft.curseforge.com/api/game/version-types" \
251
- -H "Accept: application/json" \
252
- -H "X-Api-Token: ${{ secrets.CURSEFORGE_TOKEN }}" \
253
- -o cf_version_types.json
254
-
255
- curl -fsSL "https://minecraft.curseforge.com/api/game/versions" \
256
- -H "Accept: application/json" \
257
- -H "X-Api-Token: ${{ secrets.CURSEFORGE_TOKEN }}" \
258
- -o cf_versions.json
259
-
260
- python - <<'EOF'
261
- import json, os
262
-
263
- with open('cf_version_types.json','r') as f:
264
- version_types = json.load(f)
265
-
266
- with open('cf_versions.json','r') as f:
267
- versions = json.load(f)
268
-
269
- type_by_slug = {vt['slug']: vt['id'] for vt in version_types}
270
-
271
- minecraft_types = [
272
- tid for slug, tid in type_by_slug.items()
273
- if slug.startswith('minecraft-') and 'beta' not in slug.lower()
274
- ]
275
-
276
- modloader_type = type_by_slug.get('modloader')
277
- environment_type = type_by_slug.get('environment')
278
- java_type = type_by_slug.get('java')
279
-
280
- print(f"Minecraft types: {minecraft_types}")
281
- print(f"Modloader type: {modloader_type}")
282
- print(f"Environment type: {environment_type}")
283
- print(f"Java type: {java_type}")
284
-
285
- game_versions = json.loads('${{ steps.game_versions.outputs.json }}')
286
- loaders = json.loads('${{ steps.loaders.outputs.json }}')
287
- java_versions = json.loads('${{ steps.config.outputs.curseforge_java_versions }}')
288
- environments = json.loads('${{ steps.config.outputs.curseforge_environments }}')
289
-
290
- by_type = {}
291
- for v in versions:
292
- type_id = v.get('gameVersionTypeID')
293
- name = v.get('name')
294
- if type_id and name:
295
- if type_id not in by_type:
296
- by_type[type_id] = {}
297
- by_type[type_id][name] = v['id']
298
-
299
- ids = []
300
-
301
- for version in game_versions:
302
- found = False
303
- for type_id in minecraft_types:
304
- if type_id in by_type and version in by_type[type_id]:
305
- ids.append(by_type[type_id][version])
306
- print(f"✓ Minecraft {version} -> ID {by_type[type_id][version]} (type {type_id})")
307
- found = True
308
- break
309
- if not found:
310
- print(f"✗ Minecraft version NOT FOUND: {version}")
311
-
312
- if modloader_type:
313
- for loader in [l.capitalize() for l in loaders]:
314
- if modloader_type in by_type and loader in by_type[modloader_type]:
315
- ids.append(by_type[modloader_type][loader])
316
- print(f"✓ Loader {loader} -> ID {by_type[modloader_type][loader]}")
317
-
318
- if java_type:
319
- for java in java_versions:
320
- if java_type in by_type and java in by_type[java_type]:
321
- ids.append(by_type[java_type][java])
322
- print(f"✓ Java {java} -> ID {by_type[java_type][java]}")
323
-
324
- if environment_type:
325
- for env in [e.capitalize() for e in environments]:
326
- if environment_type in by_type and env in by_type[environment_type]:
327
- ids.append(by_type[environment_type][env])
328
- print(f"✓ Environment {env} -> ID {by_type[environment_type][env]}")
329
-
330
- ids = list(dict.fromkeys(ids))
331
- print(f"\n✅ Final IDs to send: {ids}")
332
-
333
- with open(os.environ['GITHUB_OUTPUT'], 'a') as out:
334
- out.write(f"ids={json.dumps(ids)}\n")
335
- EOF
336
-
337
- - name: Upload to Modrinth (Datapack)
338
- if: steps.changeset.outputs.found == 'true' && steps.config.outputs.modrinth_enabled == 'true'
339
- run: |
340
- FILENAME="${{ steps.config.outputs.project_filename }}-${{ steps.version.outputs.version }}.zip"
341
-
342
- DATA=$(jq -n \
343
- --arg pid "${{ steps.config.outputs.modrinth_project_id }}" \
344
- --arg name "v${{ steps.version.outputs.version }} (Datapack)" \
345
- --arg ver "${{ steps.version.outputs.version }}" \
346
- --arg type "${{ steps.parse.outputs.version_type }}" \
347
- --arg log "${{ steps.parse.outputs.changelog }}" \
348
- --argjson gv '${{ steps.game_versions.outputs.json }}' \
349
- --argjson loaders '["datapack"]' \
350
- --argjson featured ${{ steps.config.outputs.modrinth_featured || 'false' }} \
351
- '{name:$name, version_number:$ver, changelog:$log, game_versions:$gv, loaders:$loaders, project_id:$pid, version_type:$type, dependencies:[], featured:$featured, file_parts:["file"], primary_file:"file"}'
352
- )
353
-
354
- curl -sS -o resp.json -w "%{http_code}" \
355
- -X POST "https://api.modrinth.com/v2/version" \
356
- -H "Authorization: ${{ secrets.MODRINTH_TOKEN }}" \
357
- -H "User-Agent: ${{ github.repository }}" \
358
- -F "data=${DATA};type=application/json" \
359
- -F "file=@dist/${FILENAME};filename=${FILENAME}" | tee code.txt
360
-
361
- cat resp.json | jq .
362
- [ $(cat code.txt) -ge 200 ] && [ $(cat code.txt) -lt 300 ] || exit 1
363
-
364
- - name: Upload to Modrinth (Mod)
365
- if: steps.changeset.outputs.found == 'true' && steps.config.outputs.modrinth_enabled == 'true' && steps.config.outputs.package_as_mod_enabled == 'true'
366
- run: |
367
- FILENAME="${{ steps.config.outputs.package_as_mod_filename }}-${{ steps.version.outputs.version }}.jar"
368
- DATA=$(jq -n \
369
- --arg pid "${{ steps.config.outputs.modrinth_project_id }}" \
370
- --arg name "v${{ steps.version.outputs.version }} (Mod)" \
371
- --arg ver "${{ steps.version.outputs.version }}+mod" \
372
- --arg type "${{ steps.parse.outputs.version_type }}" \
373
- --arg log "${{ steps.parse.outputs.changelog }}" \
374
- --argjson gv '${{ steps.game_versions.outputs.json }}' \
375
- --argjson loaders '${{ steps.config.outputs.package_as_mod_loaders }}' \
376
- --argjson featured ${{ steps.config.outputs.modrinth_featured || 'false' }} \
377
- '{name:$name, version_number:$ver, changelog:$log, game_versions:$gv, loaders:$loaders, project_id:$pid, version_type:$type, dependencies:[], featured:$featured, file_parts:["file"], primary_file:"file"}'
378
- )
379
-
380
- curl -sS -o resp.json -w "%{http_code}" \
381
- -X POST "https://api.modrinth.com/v2/version" \
382
- -H "Authorization: ${{ secrets.MODRINTH_TOKEN }}" \
383
- -H "User-Agent: ${{ github.repository }}" \
384
- -F "data=${DATA};type=application/json" \
385
- -F "file=@dist/${FILENAME};filename=${FILENAME}" | tee code.txt
386
-
387
- cat resp.json | jq .
388
- [ $(cat code.txt) -ge 200 ] && [ $(cat code.txt) -lt 300 ] || exit 1
389
-
390
- - name: Upload to CurseForge (Datapack)
391
- if: steps.changeset.outputs.found == 'true' && steps.config.outputs.curseforge_datapack_enabled == 'true'
392
- run: |
393
- FILENAME="${{ steps.config.outputs.project_filename }}-${{ steps.version.outputs.version }}.zip"
394
-
395
- DATA=$(jq -n \
396
- --arg name "${{ steps.config.outputs.project_name }} - v${{ steps.version.outputs.version }}" \
397
- --arg log "${{ steps.parse.outputs.changelog }}" \
398
- --arg type "${{ steps.parse.outputs.version_type }}" \
399
- --argjson ids '${{ steps.cf_versions.outputs.game_ids }}' \
400
- '{displayName:$name, changelog:$log, changelogType:"markdown", releaseType:$type, gameVersions:$ids}')
401
-
402
- curl -sS -o resp.json -w "%{http_code}" \
403
- -X POST "https://minecraft.curseforge.com/api/projects/${{ steps.config.outputs.curseforge_datapack_id }}/upload-file" \
404
- -H "X-Api-Token: ${{ secrets.CURSEFORGE_TOKEN }}" \
405
- -H "User-Agent: ${{ github.repository }}" \
406
- -F "metadata=${DATA};type=application/json" \
407
- -F "file=@dist/${FILENAME}" | tee code.txt
408
-
409
- cat resp.json | jq .
410
- [ $(cat code.txt) -ge 200 ] && [ $(cat code.txt) -lt 300 ] || exit 1
411
-
412
- - name: Upload to CurseForge (Mod)
413
- if: steps.changeset.outputs.found == 'true' && steps.config.outputs.curseforge_mod_enabled == 'true'
414
- run: |
415
- FILENAME="${{ steps.config.outputs.package_as_mod_filename }}-${{ steps.version.outputs.version }}.jar"
416
-
417
- DATA=$(jq -n \
418
- --arg name "${{ steps.config.outputs.project_name }} - v${{ steps.version.outputs.version }}" \
419
- --arg log "${{ steps.parse.outputs.changelog }}" \
420
- --arg type "${{ steps.parse.outputs.version_type }}" \
421
- --argjson ids '${{ steps.cf_versions.outputs.ids }}' \
422
- '{displayName:$name, changelog:$log, changelogType:"markdown", releaseType:$type, gameVersions:$ids}')
423
-
424
- curl -sS -o resp.json -w "%{http_code}" \
425
- -X POST "https://minecraft.curseforge.com/api/projects/${{ steps.config.outputs.curseforge_mod_id }}/upload-file" \
426
- -H "X-Api-Token: ${{ secrets.CURSEFORGE_TOKEN }}" \
427
- -H "User-Agent: ${{ github.repository }}" \
428
- -F "metadata=${DATA};type=application/json" \
429
- -F "file=@dist/${FILENAME}" | tee code.txt
430
-
431
- cat resp.json | jq .
432
- [ $(cat code.txt) -ge 200 ] && [ $(cat code.txt) -lt 300 ] || exit 1
433
-
434
- - name: Update config
435
- if: steps.changeset.outputs.found == 'true'
436
- run: |
437
- python - <<'EOF'
438
- import re
439
-
440
- with open('deploy.yaml', 'r') as f:
441
- content = f.read()
442
-
443
- content = re.sub(
444
- r'(version:\s*["\']?)[0-9.]+(["\']?)',
445
- r'\g<1>${{ steps.version.outputs.version }}\g<2>',
446
- content
447
- )
448
-
449
- with open('deploy.yaml', 'w') as f:
450
- f.write(content)
451
- EOF
452
-
453
- - name: Commit
454
- if: steps.changeset.outputs.found == 'true'
455
- run: |
456
- rm ${{ steps.changeset.outputs.file }}
457
- rm -f .changeset/*.md
458
- git config user.name "github-actions[bot]"
459
- git config user.email "github-actions[bot]@users.noreply.github.com"
460
- git add deploy.yaml .changeset
461
- git commit -m "chore: release v${{ steps.version.outputs.version }}"
462
- git push
@@ -1,58 +0,0 @@
1
- project:
2
- version: "1.1.4" # Current project version, auto-incremented by the workflow
3
- name: NE+ Addons - Structure # Base display name version used in CurseForge (Not the filename) e.g "NE+ Addons - Structure - V1.1.0"
4
- filename: NeoEnchantAddon-Structure # Base filename for the built datapack zip. e.g "NeoEnchantAddon-Structure-1.1.0.zip"
5
-
6
- modrinth:
7
- enabled: true # Toggle Modrinth upload
8
- project_id: bHyNNNwZ # Modrinth project ID for API uploads format "AABBCCDD"
9
- featured: false # Whether to mark the version as featured on Modrinth (Prefer to keep it false)
10
-
11
- curseforge:
12
- datapack:
13
- enabled: false # Toggle CurseForge datapack upload
14
- project_id: null # CurseForge datapack project ID format "00000000"
15
-
16
- mod:
17
- enabled: true # Toggle CurseForge mod upload
18
- project_id: 1300883 # CurseForge mod project ID format "00000000"
19
- java_versions: # Java versions to tag in CurseForge metadata
20
- - "Java 21"
21
- - "Java 22"
22
- environments: # Environment tags for CurseForge (client/server)
23
- - server
24
-
25
- package_as_mod:
26
- enabled: true # Toggle datapack-to-mod conversion using @voxelio/converter, will upload a second version on Modrinth and for CurseForge it will use the curseforge.mod section.
27
- loaders: # Default mod loaders for conversion
28
- - fabric
29
- - forge
30
- - neoforge
31
- - quilt
32
- id: mr_nneoenchant_addonsstructure # Mod ID used in fabric.mod.json/mods.toml and other mod metadata
33
- filename: NeoEnchantAddon-Structure # Base filename for the built mod jar (If you want a different filename between zip and jar, you can change it here)
34
- authors: # Author list for mod metadata
35
- - Hardel
36
- - VoxelTeam
37
- homepage: https://modrinth.com/datapack/nneoenchant-addons-structure # Homepage URL in mod metadata
38
- issues: https://github.com/Hardel-DW/NeoEnchantAddons-Structure/issues # Issues URL in mod metadata
39
- sources: https://github.com/Hardel-DW/NeoEnchantAddons-Structure # Source URL in mod metadata
40
-
41
- build:
42
- exclude: # Files/patterns to exclude when copying to build the zip and jar, .changeset and deploy.yaml is mandatory else the workflow will fail.
43
- - .git
44
- - .github
45
- - .changeset
46
- - .vscode
47
- - .cursor
48
- - node_modules
49
- - README.md
50
- - .gitignore
51
- - deploy.yaml
52
- - package.json
53
- - package-lock.json
54
- - tsconfig.json
55
- - .editorconfig
56
- - '*.log'
57
- - '*.tmp'
58
- - .DS_Store
@@ -1,10 +0,0 @@
1
- ---
2
- game_versions:
3
- - "1.21.1"
4
- - "1.21"
5
- - "1.20.1"
6
- version_type: release
7
- version_bump: patch
8
- ---
9
-
10
- # Changelog Test with Github CI/CD
File without changes