ZIP教材は先に「すべて展開」します。下は01_教材を開く.htmlが入っているフォルダーです。別の場所へ移した場合だけ修正してください。初回導入用コードのパスに反映されます。
※ Windowsで展開した練習フォルダーと、実行するUbuntu側の保存先は別です。実際に動かすDifyはUbuntuの /home/ユーザー名/dify-training/dify-1.19.0 に置きます。
導入補助ファイルが行う処理を見る
Dockerの確認、公式ZIPの整合性確認、Ubuntuへの展開、設定の作成、部品の取得、起動と確認を順に行います。既存の導入先があれば停止し、既存Dockerのログイン設定は変更しません。
このZIP対応の改訂版は、ZIPの整合性・独立フォルダーへの展開・内部Python処理の構文を検査済みです。改訂版による別のDifyの新規起動は行っていません。サーバー起動の映像は、このPCで以前に実行した導入の記録です。
#!/usr/bin/env bash
# Dify 1.19.0: one-time, local learning installation for the current Ubuntu user.
# No Windows settings changes, administrator elevation, accounts, or API keys.
set -Eeuo pipefail
trap 'printf "\nSTOP: step failed at line %s. Keep this window open.\n" "$LINENO"' ERR
umask 077
printf '\n[1/5] Checking Ubuntu and Docker...\n'
test "$(id -un)" != root || { echo 'Run as your normal Ubuntu user.'; exit 1; }
command -v python3 >/dev/null
command -v docker >/dev/null || { echo 'Docker is not available. Check Docker Desktop WSL integration.'; exit 1; }
docker info --format 'Docker server: {{.ServerVersion}}'
docker compose version
python3 - <<'PY'
import re, subprocess
v=subprocess.check_output(['docker','compose','version','--short'],text=True).strip()
m=re.search(r'(\d+)\.(\d+)\.(\d+)',v)
if not m or tuple(map(int,m.groups())) < (2,35,0):
raise SystemExit('Docker Compose 2.35.0 or newer is required.')
PY
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ARCHIVE="$SCRIPT_DIR/dify-1.19.0.zip"
TARGET="$HOME/dify-training/dify-1.19.0"
export ARCHIVE TARGET
printf '\n[2/5] Verifying the official release and copying it into Ubuntu...\n'
python3 - <<'PY'
import hashlib, os, pathlib, zipfile, stat
archive=pathlib.Path(os.environ['ARCHIVE'])
target=pathlib.Path(os.environ['TARGET'])
if target.exists():
raise SystemExit('Installation folder already exists; stopped without overwriting it: '+str(target))
expected='a13a974657ced6c146b12459eb0ba9c0deea9b407d5c099fe1108355d53f5e40'
if hashlib.sha256(archive.read_bytes()).hexdigest()!=expected:
raise SystemExit('Archive verification failed.')
target.parent.mkdir(parents=True,exist_ok=True)
with zipfile.ZipFile(archive) as z:
for info in z.infolist():
name=info.filename
resolved=(target.parent/name).resolve()
if not name.startswith('dify-1.19.0/') or not resolved.is_relative_to(target.resolve()):
raise SystemExit('Unexpected archive path. Nothing extracted.')
if stat.S_ISLNK(info.external_attr >> 16):
link=z.read(info).decode('utf-8')
if not (resolved.parent/link).resolve().is_relative_to(target.resolve()):
raise SystemExit('Unexpected symbolic-link target. Nothing extracted.')
for info in z.infolist():
destination=target.parent/info.filename
if stat.S_ISLNK(info.external_attr >> 16):
destination.parent.mkdir(parents=True,exist_ok=True)
destination.symlink_to(z.read(info).decode('utf-8'))
else:
z.extract(info,target.parent)
mode=(info.external_attr >> 16) & 0o777
if mode:
destination.chmod(mode)
print('Saved to:',target)
PY
cd -- "$TARGET/docker"
printf '\n[3/5] Creating the local learning configuration...\n'
python3 - <<'PY'
from pathlib import Path
import secrets
p=Path('.env')
if p.exists(): raise SystemExit('Existing .env found; stopped without overwriting it.')
text=Path('.env.example').read_text()
key='DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN='
if text.count(key)!=1: raise SystemExit('Unexpected configuration template.')
text=text.replace(key,key+secrets.token_hex(32))
text+='\n# Local learning HTTP/HTTPS ports\nEXPOSE_NGINX_PORT=8080\nEXPOSE_NGINX_SSL_PORT=8443\n'
p.write_text(text)
p.chmod(0o600)
PY
# Restrict every published service port in this selected stack to this PC.
# Compose's !override tag replaces the original port list completely.
python3 - <<'PY'
from pathlib import Path
import json, subprocess
config=json.loads(subprocess.check_output(['docker','compose','-f','docker-compose.yaml','config','--format','json'],text=True))
lines=['services:']
for name, service in config['services'].items():
ports=service.get('ports',[])
if not ports: continue
lines += [' '+name+':',' ports: !override']
for port in ports:
if not isinstance(port,dict): raise SystemExit('Unexpected port format.')
port=dict(port)
port['host_ip']='127.0.0.1'
lines.append(' - '+json.dumps(port))
Path('docker-compose.override.yaml').write_text('\n'.join(lines)+'\n')
final=json.loads(subprocess.check_output(['docker','compose','config','--format','json'],text=True))
for service in final['services'].values():
for port in service.get('ports',[]):
if port.get('host_ip')!='127.0.0.1':
raise SystemExit('Port binding verification failed. Nothing was started.')
print('All published ports verified as local to this PC.')
PY
docker compose config --quiet
printf '\n[4/5] Downloading and starting Dify components. This can take several minutes...\n'
python3 -u - <<'PY'
import json,os,pathlib,subprocess,tempfile
config=json.loads(subprocess.check_output(['docker','compose','config','--format','json'],text=True))
images=set()
profiles={'weaviate','postgresql','collaboration'}
for service in config['services'].values():
selected=service.get('profiles',[])
if selected and not profiles.intersection(selected):
continue
for port in service.get('ports',[]):
if port.get('host_ip')!='127.0.0.1':
raise SystemExit('STOP: unexpected non-local port binding. Nothing was started.')
if service.get('image'): images.add(service['image'])
if not images: raise SystemExit('STOP: no images found in the existing configuration.')
print('Configuration verified. Existing Docker credentials will not be edited.',flush=True)
with tempfile.TemporaryDirectory(prefix='dify-public-pull-') as tmp:
pathlib.Path(tmp,'config.json').write_text(json.dumps({'auths':{}}))
env=os.environ.copy()
env.pop('DOCKER_CONTEXT',None)
for i,image in enumerate(sorted(images),1):
print(f'\nImage {i}/{len(images)}: {image}',flush=True)
result=subprocess.run(['docker','--config',tmp,'--host','unix:///var/run/docker.sock','image','pull',image],env=env)
if result.returncode:
raise SystemExit('STOP: image download failed. Keep the final error visible. Re-running this script can reuse completed downloads.')
print('\nAll public images downloaded.',flush=True)
PY
docker compose up -d --pull never --no-build
printf '\n[5/5] Checking containers...\n'
docker compose ps --all
printf '\nStartup command completed. The web application may need more time to become ready.\n'
printf 'Open: http://localhost:8080/install\n'
printf 'Folder: %s/docker\n' "$TARGET"
printf 'Keep this terminal open and report the last messages. Do not share passwords or .env.\n'
初回導入補助 / 再開用補助 / 入手先と検証範囲