2023-02-10 flutter pub發(fā)布package流程

pub源代碼:
https://github.com/dart-lang/pub

pub publish 核心代碼:

OAuth2認(rèn)證

   try {
      final officialPubServers = {
        'https://pub.dev',
        // [validateAndNormalizeHostedUrl] normalizes https://pub.dartlang.org
        // to https://pub.dev, so we don't need to do allow that here.

        // Pub uses oauth2 credentials only for authenticating official pub
        // servers for security purposes (to not expose pub.dev access token to
        // 3rd party servers).
        // For testing publish command we're using mock servers hosted on
        // localhost address which is not a known pub server address. So we
        // explicitly have to define mock servers as official server to test
        // publish command with oauth2 credentials.
        if (runningFromTest &&
            Platform.environment.containsKey('_PUB_TEST_DEFAULT_HOSTED_URL'))
          Platform.environment['_PUB_TEST_DEFAULT_HOSTED_URL'],
      };

      // Using OAuth2 authentication client for the official pub servers
      final isOfficialServer = officialPubServers.contains(host.toString());
      if (isOfficialServer && !cache.tokenStore.hasCredential(host)) {
        // Using OAuth2 authentication client for the official pub servers, when
        // we don't have an explicit token from [TokenStore] to use instead.
        //
        // This allows us to use `dart pub token add` to inject a token for use
        // with the official servers.
        await oauth2.withClient(cache, (client) {
          return _publishUsingClient(packageBytes, client);
        });
      } else {
        // For third party servers using bearer authentication client
        await withAuthenticatedClient(cache, host, (client) {
          return _publishUsingClient(packageBytes, client);
        });
      }
    } on PubHttpResponseException catch (error) {
      var url = error.response.request!.url;
      if (Uri.parse(url.origin) == Uri.parse(host.origin)) {
        handleJsonError(error.response);
      } else {
        rethrow;
      }
    }

上述實(shí)現(xiàn)流程如下:

定義了一個(gè) officialPubServers 變量,表示所有官方的 Pub 倉(cāng)庫(kù)地址。

使用 OAuth2 認(rèn)證客戶(hù)端連接官方的 Pub 倉(cāng)庫(kù)旦部,如果不存在 OAuth2 認(rèn)證信息則通過(guò)命令行工具添加笆檀。

如果不是官方的 Pub 倉(cāng)庫(kù)則使用 bearer 認(rèn)證客戶(hù)端連接已卷。

調(diào)用 _publishUsingClient 函數(shù)來(lái)發(fā)布包孽鸡。

如果發(fā)布失敗犀忱,并且失敗的請(qǐng)求的地址是 Pub 倉(cāng)庫(kù)地址钞它,則通過(guò) handleJsonError 函數(shù)處理錯(cuò)誤拜银。如果不是 Pub 倉(cāng)庫(kù)地址,則重新拋出異常遭垛。

發(fā)布包

 try {
      await log.progress('Uploading', () async {
        /// 1. Initiate upload
        final parametersResponse =
            await retryForHttp('initiating upload', () async {
          final request =
              http.Request('GET', host.resolve('api/packages/versions/new'));
          request.attachPubApiHeaders();
          request.attachMetadataHeaders();
          return await client.fetch(request);
        });
        final parameters = parseJsonResponse(parametersResponse);

        /// 2. Upload package
        var url = _expectField(parameters, 'url', parametersResponse);
        if (url is! String) invalidServerResponse(parametersResponse);
        cloudStorageUrl = Uri.parse(url);
        final uploadResponse =
            await retryForHttp('uploading package', () async {
          // TODO(nweiz): Cloud Storage can provide an XML-formatted error. We
          // should report that error and exit.
          var request = http.MultipartRequest('POST', cloudStorageUrl!);

          var fields = _expectField(parameters, 'fields', parametersResponse);
          if (fields is! Map) invalidServerResponse(parametersResponse);
          fields.forEach((key, value) {
            if (value is! String) invalidServerResponse(parametersResponse);
            request.fields[key] = value;
          });

          request.followRedirects = false;
          request.files.add(
            http.MultipartFile.fromBytes(
              'file',
              packageBytes,
              filename: 'package.tar.gz',
            ),
          );
          return await client.fetch(request);
        });

        /// 3. Finalize publish
        var location = uploadResponse.headers['location'];
        if (location == null) throw PubHttpResponseException(uploadResponse);
        final finalizeResponse =
            await retryForHttp('finalizing publish', () async {
          final request = http.Request('GET', Uri.parse(location));
          request.attachPubApiHeaders();
          request.attachMetadataHeaders();
          return await client.fetch(request);
        });
        handleJsonSuccess(finalizeResponse);
      });
    } on AuthenticationException catch (error) {
      var msg = '';
      if (error.statusCode == 401) {
        msg += '$host package repository requested authentication!\n'
            'You can provide credentials using:\n'
            '    $topLevelProgram pub token add $host\n';
      }
      if (error.statusCode == 403) {
        msg += 'Insufficient permissions to the resource at the $host '
            'package repository.\nYou can modify credentials using:\n'
            '    $topLevelProgram pub token add $host\n';
      }
      if (error.serverMessage != null) {
        msg += '\n${error.serverMessage!}\n';
      }
      dataError(msg + log.red('Authentication failed!'));
    }

上述代碼流程:

  • Initiate upload:發(fā)送一個(gè) GET 請(qǐng)求到 api/packages/versions/new 這個(gè)接口尼桶,以獲取上傳包的一些參數(shù)。請(qǐng)求會(huì)帶有 pubApiHeaders 和 metadataHeaders 兩個(gè)請(qǐng)求頭锯仪,具體信息根據(jù)代碼未知泵督;

  • Upload package:使用獲取的參數(shù),通過(guò)發(fā)送一個(gè)帶有多個(gè)部分的 POST 請(qǐng)求庶喜,將包文件上傳到云存儲(chǔ)服務(wù)小腊。在請(qǐng)求中,會(huì)有一些字段久窟,具體內(nèi)容在 fields 字段中秩冈,一般用于描述文件的一些元數(shù)據(jù);

  • Finalize publish:根據(jù)第二步的響應(yīng)結(jié)果中的 location 字段斥扛,發(fā)送一個(gè) GET 請(qǐng)求以確認(rèn)發(fā)布完成入问。請(qǐng)求同樣帶有 pubApiHeaders 和 metadataHeaders 兩個(gè)請(qǐng)求頭。

上面的代碼中的 retryForHttp 是一個(gè)重試機(jī)制稀颁,在某些情況下芬失,請(qǐng)求失敗時(shí)會(huì)重試多次,以獲得成功的請(qǐng)求匾灶。

最后棱烂,handleJsonSuccess 方法被調(diào)用,該方法可以判斷最終的響應(yīng)結(jié)果是否是有效的 JSON 格式阶女,如果不是颊糜,會(huì)拋出異常。

第二步請(qǐng)求地址:
https://storage.googleapis.com

返回第三步請(qǐng)求地址:

https://pub.dev/api/packages/versions/newUploadFinish?
upload_id=1c23e23c-c08d-4c08-bde4-b40bc58fe3b8
&bucket=dartlang-pub-incoming-packages
&key=tmp/1c23e23c-c08d-4c08-bde4-b40bc58fe3b8&etag=1846afb2360418d23d6c17db2bf90c6d

如果賬號(hào)權(quán)限不一致 則在第三步返回:
xxx@gmail.com has insufficient permissions to upload new versions to existing package http.

如果是版本已經(jīng)存在第三步返回:
Version 0.13.27 of package httptest already exists.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末秃踩,一起剝皮案震驚了整個(gè)濱河市芭析,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吞瞪,老刑警劉巖馁启,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡惯疙,警方通過(guò)查閱死者的電腦和手機(jī)翠勉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)霉颠,“玉大人对碌,你說(shuō)我怎么就攤上這事≥镔耍” “怎么了朽们?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)诉位。 經(jīng)常有香客問(wèn)我骑脱,道長(zhǎng),這世上最難降的妖魔是什么苍糠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任叁丧,我火速辦了婚禮,結(jié)果婚禮上岳瞭,老公的妹妹穿的比我還像新娘拥娄。我一直安慰自己,他們只是感情好瞳筏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布稚瘾。 她就那樣靜靜地躺著,像睡著了一般姚炕。 火紅的嫁衣襯著肌膚如雪孟抗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天钻心,我揣著相機(jī)與錄音,去河邊找鬼铅协。 笑死捷沸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的狐史。 我是一名探鬼主播痒给,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼骏全!你這毒婦竟也來(lái)了苍柏?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤姜贡,失蹤者是張志新(化名)和其女友劉穎试吁,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡熄捍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年烛恤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片余耽。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缚柏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出碟贾,到底是詐尸還是另有隱情币喧,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布袱耽,位于F島的核電站杀餐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏扛邑。R本人自食惡果不足惜怜浅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蔬崩。 院中可真熱鬧恶座,春花似錦、人聲如沸沥阳。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)桐罕。三九已至脉让,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間功炮,已是汗流浹背溅潜。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留薪伏,地道東北人滚澜。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像嫁怀,于是被迫代替她去往敵國(guó)和親设捐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容