Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
5
Help us understand the problem. What is going on with this article?
@MizoTake

Unityの.alfファイルから自動で.ulfをダウンロードしたい!

More than 1 year has passed since last update.

CIのActivateとかでライセンスを自動でActivateさせたい!

CIでUnityを扱う時はJenkinsとかであれば問題ないのですが、CircleCIやGitHub Actionsを使用するときにDockerでのライセンス認証では.ulfファイルというのが必要になってきます。

現在.ulfファイルをコマンドラインから生成することはできません。生成するにはブラウザ経由の一択です。
それを今回Puppeteerというnode.jsのツールを使って自動化してみました。

※今回の認証フローはPersonalEdition固定になります。

Puppetterとは、Webブラウザでの操作をソースコードから行えるものになります。
詳しくはこちら
Puppeteer

今回のリポジトリはこちら
MizoTake/unity-license-activate

実装

npm経由でPuppeteerを入れて以下のjsで実装しました。

今回はライセンスの認証が必要になるので https://license.unity3d.com/manual のページで操作を行います。
手元にあるalfファイルから最終的にulfファイルをダウンロードする操作になります。

叩くコマンドは以下になります
node activate.js $email $password $alf_file_path

以下が今回のScriptの全容ですが細かく分けてどうなっているのか下で記述します。

activate.js
const puppeteer = require('puppeteer')
const fs = require('fs')

;(async () => {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()

  const downloadPath = process.cwd()
  const client = await page.target().createCDPSession()
  await client.send('Page.setDownloadBehavior', {
    behavior: 'allow',
    downloadPath: downloadPath
  })

  await page.goto('https://license.unity3d.com/manual')

  await page.waitForNavigation({
    timeout: 60000,
    waitUntil: 'domcontentloaded'
  })

  const email = `${process.argv[2]}`
  await page.type('input[type=email]', email)

  const password = `${process.argv[3]}`
  await page.type('input[type=password]', password)
  await page.click('input[name="commit"]')

  await page.waitForNavigation({
    timeout: 60000,
    waitUntil: 'domcontentloaded'
  })

  const input = await page.$('input[name="licenseFile"]')

  const alfPath = `${process.argv[4]}`
  await input.uploadFile(alfPath)

  await page.click('input[name="commit"]')

  await page.waitForNavigation({
    timeout: 60000,
    waitUntil: 'domcontentloaded'
  })

  const selectedTypePersonal = 'input[id="type_personal"][value="personal"]'
  await page.evaluate(
    s => document.querySelector(s).click(),
    selectedTypePersonal
  )

  const selectedPersonalCapacity =
    'input[id="option3"][name="personal_capacity"]'
  await page.evaluate(
    s => document.querySelector(s).click(),
    selectedPersonalCapacity
  )

  await page.click('input[class="btn mb10"]')

  await page.waitForNavigation()

  await page.click('input[name="commit"]')

  let _ = await (async () => {
    let ulf
    do {
      for (const file of fs.readdirSync(downloadPath)) {
        ulf |= file.endsWith('.ulf')
      }
      await sleep(1000)
    } while (!ulf)
  })()

  function sleep(milliSeconds) {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, milliSeconds)
    })
  }

  await browser.close()
})()

画面ごとの処理

1.png

  // ライセンス認証を行うページに行く
  await page.goto('https://license.unity3d.com/manual')

  await page.waitForNavigation({
    timeout: 60000,
    waitUntil: 'domcontentloaded'
  })

  // ライセンス認証を行うページに飛ばしたがリダイレクトでUnityのログインページに飛んでいる

  //コマンド引数からメールアドレスとパスワードをとってくる
  const email = `${process.argv[2]}`
  await page.type('input[type=email]', email)

  const password = `${process.argv[3]}`
  await page.type('input[type=password]', password)

  // Sign inのボタンを押す
  await page.click('input[name="commit"]')

2.png


  const input = await page.$('input[name="licenseFile"]')

  // コマンドライン引数で指定したpathからfileを添付
  const alfPath = `${process.argv[4]}`
  await input.uploadFile(alfPath)

  // Nextボタンを押す
  await page.click('input[name="commit"]')

3.png

これでファイル添付ができてることがわかります。ファイル添付までできるのすげぇ…Puppeteer…

4.png

  // Personal Editionの選択
  const selectedTypePersonal = 'input[id="type_personal"][value="personal"]'
  await page.evaluate(
    s => document.querySelector(s).click(),
    selectedTypePersonal
  )

  // Personal Edition選択後に出てくるのOptionを選択
  const selectedPersonalCapacity =
    'input[id="option3"][name="personal_capacity"]'
  await page.evaluate(
    s => document.querySelector(s).click(),
    selectedPersonalCapacity
  )
  
 // Nextボタンを押す
  await page.click('input[class="btn mb10"]')

この画面の実行後はこうなっています。
5.png

6.png

  // Download license fileボタンを押す
  await page.click('input[name="commit"]')

  // ダウンロードが始まるので手元に.ulfファイルができるまで待つ
  let _ = await (async () => {
    let ulf
    do {
      for (const file of fs.readdirSync(downloadPath)) {
        ulf |= file.endsWith('.ulf')
      }
      await sleep(1000)
    } while (!ulf)
  })()

  function sleep(milliSeconds) {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, milliSeconds)
    })
  }

以上のような流れになっています。

さいごに

Puppeteer便利!!!!

  await page.screenshot( {
    path: "./example.png"
  });

でScreenShotを撮りつつ実装してました。

ブラウザでしか行えない操作もこれがあればできるので色々と捗るんじゃないかなと思っています。

追記 (2019/12/11)

今回のScriptでは二段階認証を設定していると一度認証されているPCからでのみうまくいきます。
その辺をクリアしようと検証はしてますがまだうまくいかないので注意しつつ使用してみてください。

5
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
MizoTake
趣味でチマチマ何か作ってます
unity-game-dev-guild
趣味・仕事問わずUnityでゲームを作っている開発者のみで構成されるオンラインコミュニティです。Unityでゲームを開発・運用するにあたって必要なあらゆる知見を共有することを目的とします。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
5
Help us understand the problem. What is going on with this article?