@tutorialkit-rb/cli 0.1.7 → 0.1.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/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import "yargs-parser";
14
14
  // package.json
15
15
  var package_default = {
16
16
  name: "@tutorialkit-rb/cli",
17
- version: "0.1.7",
17
+ version: "0.1.9",
18
18
  description: "Interactive tutorials powered by WebContainer API",
19
19
  author: "StackBlitz Inc.",
20
20
  type: "module",
@@ -675,30 +675,32 @@ import path4 from "node:path";
675
675
  import * as prompts3 from "@clack/prompts";
676
676
  import chalk4 from "chalk";
677
677
 
678
- // src/commands/create/hosting-config/_headers.txt?raw
679
- var headers_default = "/*\n Cross-Origin-Embedder-Policy: require-corp\n Cross-Origin-Opener-Policy: same-origin\n";
678
+ // src/commands/create/hosting-config/Dockerfile.txt?raw
679
+ var Dockerfile_default = `# Minimal nginx image to serve pre-built static files
680
+ # Build happens in GitHub Actions (with WASM caching), not here
680
681
 
681
- // src/commands/create/hosting-config/netlify_toml.txt?raw
682
- var netlify_toml_default = '[[headers]]\n for = "/*"\n [headers.values]\n Cross-Origin-Embedder-Policy = "require-corp"\n Cross-Origin-Opener-Policy = "same-origin"\n';
682
+ FROM nginx:alpine
683
683
 
684
- // src/commands/create/hosting-config/vercel.json?raw
685
- var vercel_default = {
686
- headers: [
687
- {
688
- source: "/(.*)",
689
- headers: [
690
- {
691
- key: "Cross-Origin-Embedder-Policy",
692
- value: "require-corp"
693
- },
694
- {
695
- key: "Cross-Origin-Opener-Policy",
696
- value: "same-origin"
697
- }
698
- ]
699
- }
700
- ]
701
- };
684
+ # Copy pre-built site from GitHub Actions
685
+ COPY dist /usr/share/nginx/html
686
+
687
+ # Add required headers for WebContainers (SharedArrayBuffer support)
688
+ RUN echo 'add_header Cross-Origin-Embedder-Policy "require-corp";' > /etc/nginx/conf.d/custom-headers.conf \\
689
+ && echo 'add_header Cross-Origin-Opener-Policy "same-origin";' >> /etc/nginx/conf.d/custom-headers.conf \\
690
+ && echo 'add_header Cross-Origin-Resource-Policy "cross-origin";' >> /etc/nginx/conf.d/custom-headers.conf
691
+
692
+ EXPOSE 80
693
+ CMD ["nginx", "-g", "daemon off;"]
694
+ `;
695
+
696
+ // src/commands/create/hosting-config/deploy_flyio_yml.txt?raw
697
+ var deploy_flyio_yml_default = "name: Build and Deploy\n\non:\n push:\n branches: [main]\n workflow_dispatch:\n\nconcurrency:\n group: deploy\n cancel-in-progress: false\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n\n - uses: actions-rust-lang/setup-rust-toolchain@v1\n with:\n toolchain: 1.74.0\n\n - name: Install wasi-vfs\n env:\n WASI_VFS_VERSION: 0.5.4\n run: |\n curl -LO \"https://github.com/kateinoigakukun/wasi-vfs/releases/download/v${WASI_VFS_VERSION}/wasi-vfs-cli-x86_64-unknown-linux-gnu.zip\"\n unzip wasi-vfs-cli-x86_64-unknown-linux-gnu.zip\n mv wasi-vfs /usr/local/bin/wasi-vfs\n\n - uses: ruby/setup-ruby@v1\n env:\n BUNDLE_GEMFILE: ruby-wasm/Gemfile\n BUNDLE_PATH: ruby-wasm/vendor\n with:\n ruby-version: 3.3\n bundler-cache: true\n\n - uses: actions/setup-node@v4\n with:\n node-version: 20\n cache: npm\n\n - name: Cache ruby.wasm build artifacts\n uses: actions/cache@v4\n with:\n path: |\n ruby-wasm/build\n ruby-wasm/rubies\n key: ${{ runner.os }}-ruby-wasm-${{ hashFiles('ruby-wasm/Gemfile.lock') }}\n restore-keys: |\n ${{ runner.os }}-ruby-wasm-\n\n - name: Build ruby.wasm\n run: npm run build:wasm\n\n - name: Build tutorial\n run: |\n npm install\n npm run build\n\n - name: Setup flyctl\n if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'\n uses: superfly/flyctl-actions/setup-flyctl@master\n\n - name: Deploy to Fly.io\n if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'\n run: flyctl deploy --local-only\n env:\n FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}\n";
698
+
699
+ // src/commands/create/hosting-config/fly_toml.txt?raw
700
+ var fly_toml_default = "# Fly.io configuration\n# Update 'app' to your Fly.io app name after running: fly apps create <name>\napp = 'my-tutorial'\nprimary_region = 'sjc'\n\n[build]\n\n[http_service]\n internal_port = 80\n force_https = true\n auto_stop_machines = 'stop'\n auto_start_machines = true\n min_machines_running = 0\n processes = ['app']\n\n[[vm]]\n size = 'shared-cpu-1x'\n";
701
+
702
+ // src/commands/create/hosting-config/netlify_toml.txt?raw
703
+ var netlify_toml_default = '[build]\n publish = "./dist"\n command = "# no build needed \u2014 CI handles the build"\n\n[[headers]]\n for = "/*"\n [headers.values]\n Cross-Origin-Embedder-Policy = "require-corp"\n Cross-Origin-Opener-Policy = "same-origin"\n';
702
704
 
703
705
  // src/commands/create/generate-hosting-config.ts
704
706
  async function generateHostingConfig(dest, flags) {
@@ -707,9 +709,11 @@ async function generateHostingConfig(dest, flags) {
707
709
  provider = await prompts3.select({
708
710
  message: "Select hosting providers for automatic configuration:",
709
711
  options: [
710
- { value: "Vercel", label: "Vercel" },
711
712
  { value: "Netlify", label: "Netlify" },
712
- { value: "Cloudflare", label: "Cloudflare" },
713
+ { value: "Fly.io", label: "Fly.io" },
714
+ // TODO: re-enable when full deployment pipelines are added
715
+ // { value: 'Vercel', label: 'Vercel' },
716
+ // { value: 'Cloudflare', label: 'Cloudflare' },
713
717
  { value: "skip", label: "Skip hosting configuration" }
714
718
  ],
715
719
  initialValue: DEFAULT_VALUES.provider
@@ -731,20 +735,20 @@ async function generateHostingConfig(dest, flags) {
731
735
  }
732
736
  let config;
733
737
  let filename;
738
+ let extraFiles;
734
739
  switch (provider.toLowerCase()) {
735
- case "vercel": {
736
- config = typeof vercel_default === "string" ? vercel_default : JSON.stringify(vercel_default, null, 2);
737
- filename = "vercel.json";
738
- break;
739
- }
740
740
  case "netlify": {
741
741
  config = netlify_toml_default;
742
742
  filename = "netlify.toml";
743
743
  break;
744
744
  }
745
- case "cloudflare": {
746
- config = headers_default;
747
- filename = "_headers";
745
+ case "fly.io": {
746
+ config = fly_toml_default;
747
+ filename = "fly.toml";
748
+ extraFiles = [
749
+ { filename: "Dockerfile", content: Dockerfile_default },
750
+ { filename: path4.join(".github", "workflows", "deploy.yml"), content: deploy_flyio_yml_default }
751
+ ];
748
752
  break;
749
753
  }
750
754
  }
@@ -756,7 +760,16 @@ async function generateHostingConfig(dest, flags) {
756
760
  task: async () => {
757
761
  const filepath = path4.join(resolvedDest, filename);
758
762
  fs3.writeFileSync(filepath, config);
759
- return `Added ${filepath}`;
763
+ const added = [filepath];
764
+ if (extraFiles) {
765
+ for (const extra of extraFiles) {
766
+ const extraPath = path4.join(resolvedDest, extra.filename);
767
+ fs3.mkdirSync(path4.dirname(extraPath), { recursive: true });
768
+ fs3.writeFileSync(extraPath, extra.content);
769
+ added.push(extraPath);
770
+ }
771
+ }
772
+ return added.map((f) => `Added ${f}`).join("\n");
760
773
  }
761
774
  });
762
775
  }
@@ -1122,10 +1135,6 @@ function updatePackageJson(dest, projectName, flags, provider) {
1122
1135
  pkgJson.name = projectName;
1123
1136
  updateWorkspaceVersions(pkgJson.dependencies, TUTORIALKIT_VERSION);
1124
1137
  updateWorkspaceVersions(pkgJson.devDependencies, TUTORIALKIT_VERSION);
1125
- if (provider.toLowerCase() === "cloudflare") {
1126
- pkgJson.scripts = pkgJson.scripts || {};
1127
- pkgJson.scripts.postbuild = "cp _headers ./dist/";
1128
- }
1129
1138
  fs7.writeFileSync(pkgPath, JSON.stringify(pkgJson, null, 2));
1130
1139
  try {
1131
1140
  const pkgLockPath = path8.resolve(dest, "package-lock.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tutorialkit-rb/cli",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Interactive tutorials powered by WebContainer API",
5
5
  "author": "StackBlitz Inc.",
6
6
  "type": "module",
@@ -7,7 +7,7 @@ set -e
7
7
  #
8
8
  # This is the fast path (~30s) compared to the monolithic `bin/build-wasm`
9
9
  # (~5-20min). Requires a pre-built base binary (from bin/build-ruby-base
10
- # or the @tutorialkit-rb/ruby-3.3 npm package).
10
+ # or downloaded from GitHub Releases).
11
11
  #
12
12
  # Usage: ./bin/build-pack
13
13
 
@@ -38,12 +38,34 @@ if [ -f "$RUBY_WASM_DIR/dist/ruby-base.wasm" ]; then
38
38
  echo " Found: local build (dist/ruby-base.wasm)"
39
39
  fi
40
40
 
41
- # 2. npm package fallback
41
+ # 2. GitHub Releases fallback
42
42
  if [ -z "$BASE_WASM" ]; then
43
- NPM_BASE="$RUBY_WASM_DIR/node_modules/@tutorialkit-rb/ruby-3.3/dist/ruby-base.wasm"
44
- if [ -f "$NPM_BASE" ]; then
45
- BASE_WASM="$NPM_BASE"
46
- echo " Found: @tutorialkit-rb/ruby-3.3 npm package"
43
+ echo " Not found locally, downloading from GitHub Releases..."
44
+ DOWNLOAD_DIR="$RUBY_WASM_DIR/dist"
45
+ mkdir -p "$DOWNLOAD_DIR"
46
+ DOWNLOAD_PATH="$DOWNLOAD_DIR/ruby-base.wasm"
47
+
48
+ REPO="Bakaface/tutorialkit.rb"
49
+
50
+ if command -v gh &> /dev/null; then
51
+ TAG=$(gh release list --repo "$REPO" --json tagName --jq '[.[] | select(.tagName | startswith("ruby-base-"))][0].tagName' 2>/dev/null)
52
+
53
+ if [ -n "$TAG" ]; then
54
+ echo " Release: $TAG"
55
+ gh release download "$TAG" --repo "$REPO" --pattern "ruby-base.wasm" --dir "$DOWNLOAD_DIR" --clobber 2>/dev/null
56
+
57
+ # Also download rbconfig if not present locally
58
+ RBCONFIG_DIR="$RUBY_WASM_DIR/rbconfig/wasm32-wasi"
59
+ if [ ! -f "$RBCONFIG_DIR/rbconfig.rb" ]; then
60
+ mkdir -p "$RBCONFIG_DIR"
61
+ gh release download "$TAG" --repo "$REPO" --pattern "rbconfig.rb" --dir "$RBCONFIG_DIR" --clobber 2>/dev/null
62
+ fi
63
+ fi
64
+ fi
65
+
66
+ if [ -f "$DOWNLOAD_PATH" ]; then
67
+ BASE_WASM="$DOWNLOAD_PATH"
68
+ echo " Downloaded from GitHub Releases"
47
69
  fi
48
70
  fi
49
71
 
@@ -51,7 +73,7 @@ if [ -z "$BASE_WASM" ]; then
51
73
  echo
52
74
  echo "Error: No base WASM binary found."
53
75
  echo "Either run 'bin/build-ruby-base' to build one,"
54
- echo "or install @tutorialkit-rb/ruby-3.3 npm package."
76
+ echo "or ensure a ruby-base release exists on GitHub."
55
77
  exit 1
56
78
  fi
57
79
 
@@ -22,8 +22,8 @@
22
22
  "@codemirror/lang-yaml": "^6.1.2",
23
23
  "@codemirror/legacy-modes": "^6.5.1",
24
24
  "@nanostores/react": "0.7.2",
25
- "@tutorialkit-rb/react": "0.1.7",
26
- "@tutorialkit-rb/runtime": "0.1.7",
25
+ "@tutorialkit-rb/react": "0.1.9",
26
+ "@tutorialkit-rb/runtime": "0.1.9",
27
27
  "nanostores": "^0.10.3",
28
28
  "react": "^18.3.1",
29
29
  "react-dom": "^18.3.1"
@@ -31,9 +31,9 @@
31
31
  "devDependencies": {
32
32
  "@astrojs/check": "^0.7.0",
33
33
  "@astrojs/react": "^3.6.0",
34
- "@tutorialkit-rb/astro": "0.1.7",
35
- "@tutorialkit-rb/theme": "0.1.7",
36
- "@tutorialkit-rb/types": "0.1.7",
34
+ "@tutorialkit-rb/astro": "0.1.9",
35
+ "@tutorialkit-rb/theme": "0.1.9",
36
+ "@tutorialkit-rb/types": "0.1.9",
37
37
  "@types/mdast": "^4.0.4",
38
38
  "@types/node": "^20.14.6",
39
39
  "@types/react": "^18.3.3",
@@ -78,28 +78,18 @@ def find_rbconfig
78
78
  shipped = File.join(SCRIPT_DIR, "rbconfig", "wasm32-wasi", "rbconfig.rb")
79
79
  return shipped if File.exist?(shipped)
80
80
 
81
- # 2. From @tutorialkit-rb/ruby-3.3 npm package
82
- npm_pkg = File.join(SCRIPT_DIR, "node_modules", "@tutorialkit-rb", "ruby-3.3",
83
- "rbconfig", "wasm32-wasi", "rbconfig.rb")
84
- return npm_pkg if File.exist?(npm_pkg)
85
-
86
- # 3. Fallback: generated by a previous base binary build
81
+ # 2. Fallback: generated by a previous base binary build
87
82
  Dir.glob(File.join(SCRIPT_DIR, "rubies", "*/usr/local/lib/ruby/*/wasm32-wasi/rbconfig.rb")).first
88
83
  end
89
84
 
90
85
  def find_base_wasm
91
- # 1. Locally-built custom binary (from bin/build-ruby-base)
86
+ # Locally-built or downloaded binary (from bin/build-ruby-base or GitHub Releases)
92
87
  local = File.join(SCRIPT_DIR, "dist", "ruby-base.wasm")
93
88
  return local if File.exist?(local)
94
89
 
95
- # 2. From @tutorialkit-rb/ruby-3.3 npm package
96
- npm_pkg = File.join(SCRIPT_DIR, "node_modules", "@tutorialkit-rb", "ruby-3.3",
97
- "dist", "ruby-base.wasm")
98
- return npm_pkg if File.exist?(npm_pkg)
99
-
100
90
  $stderr.puts "Error: No base WASM binary found."
101
91
  $stderr.puts "Either run 'bin/build-ruby-base' to build a custom binary,"
102
- $stderr.puts "or install @tutorialkit-rb/ruby-3.3 npm package."
92
+ $stderr.puts "or run 'bin/build-pack' which downloads from GitHub Releases."
103
93
  exit 1
104
94
  end
105
95
 
@@ -111,12 +101,7 @@ def find_usr_dir
111
101
  return path if Dir.exist?(path)
112
102
  end
113
103
 
114
- # 2. From @tutorialkit-rb/ruby-3.3 npm package
115
- npm_pkg = File.join(SCRIPT_DIR, "node_modules", "@tutorialkit-rb", "ruby-3.3",
116
- "rubies", "usr")
117
- return npm_pkg if Dir.exist?(npm_pkg)
118
-
119
- # 3. Fallback: glob for the rubies/ directory left by rbwasm
104
+ # 2. Fallback: glob for the rubies/ directory left by rbwasm
120
105
  candidates = Dir.glob(File.join(SCRIPT_DIR, "rubies", "ruby-3.3-wasm32-unknown-wasip1-full-*", "usr"))
121
106
  return candidates.first if candidates.any?
122
107
 
@@ -133,7 +118,7 @@ rbconfig_path = find_rbconfig
133
118
  unless rbconfig_path
134
119
  $stderr.puts "Error: Could not find rbconfig.rb for cross-compilation"
135
120
  $stderr.puts "Expected at: rbconfig/wasm32-wasi/rbconfig.rb"
136
- $stderr.puts "Or install @tutorialkit-rb/ruby-3.3 npm package."
121
+ $stderr.puts "Or run 'bin/build-ruby-base' to generate one."
137
122
  exit 1
138
123
  end
139
124
  puts "rbconfig: #{rbconfig_path}"
@@ -149,7 +134,7 @@ if usr_dir
149
134
  else
150
135
  $stderr.puts "Warning: Could not find usr/ directory for /usr mapping."
151
136
  $stderr.puts " The packed binary may be missing Ruby stdlib at /usr."
152
- $stderr.puts " Run 'bin/build-ruby-base' or install @tutorialkit-rb/ruby-3.3."
137
+ $stderr.puts " Run 'bin/build-ruby-base' to generate it."
153
138
  end
154
139
  puts
155
140
 
@@ -1,6 +1,3 @@
1
1
  {
2
- "private": true,
3
- "optionalDependencies": {
4
- "@tutorialkit-rb/ruby-3.3": "^3.3.0"
5
- }
2
+ "private": true
6
3
  }