mirror of
https://git.openapi.site/https://github.com/desirecore/config-center.git
synced 2026-10-21 06:44:14 +08:00
feat: 增加配置中心纯运行数据发布流程 (#122)
This commit is contained in:
205
.github/runtime-publication/LICENSE.DesireCore
vendored
Normal file
205
.github/runtime-publication/LICENSE.DesireCore
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
DesireCore License
|
||||
|
||||
Copyright (c) 2026 Yunnan Solar Corona Technology Co., Ltd.
|
||||
|
||||
This project uses a tiered licensing model based on version numbers (vx.y).
|
||||
Let x denote the latest major version and y denote the latest minor version.
|
||||
|
||||
IMPORTANT NOTICE ON HISTORICAL VERSIONS:
|
||||
|
||||
Versions v9 and earlier were developed and released as proprietary
|
||||
commercial software by Yunnan Solar Corona Technology Co., Ltd. They are
|
||||
NOT open source and are NOT covered by any open-source license, including
|
||||
the MIT License. All rights to those versions are reserved. No source code,
|
||||
binaries, or other materials from v9 or earlier are licensed under the terms
|
||||
of this document.
|
||||
|
||||
The open-source licensing model described below applies ONLY to v10 and later.
|
||||
|
||||
The applicable license depends on the version:
|
||||
|
||||
Part A — v(x-1) and earlier open-source versions → MIT License
|
||||
Part B — vx, previous minor releases → Competition-Exclusion License (commercial use allowed)
|
||||
Part C — vx latest minor and dev branches → Non-Commercial & Competition-Exclusion License
|
||||
|
||||
When a new minor version is released, the previous minor version transitions
|
||||
from Part C to Part B. When a new major version is released, the previous
|
||||
major version transitions from Part B to Part A (MIT License).
|
||||
|
||||
Example timeline:
|
||||
v10.0 released → v10.0 is Part C
|
||||
v10.1 released → v10.0 becomes Part B, v10.1 is Part C
|
||||
v11.0 released → all v10.x become Part A (MIT), v11.0 is Part C
|
||||
|
||||
Note: This MIT transition (Part A) will first take effect when v11 is
|
||||
released, making v10.x the first versions to receive the MIT License.
|
||||
Versions v9 and earlier remain proprietary and are never relicensed.
|
||||
|
||||
================================================================================
|
||||
PART A — MIT License
|
||||
Applies to: v(x-1) and all earlier open-source versions
|
||||
(First applicable when v11 is released, covering v10.x)
|
||||
================================================================================
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
PART B — Competition-Exclusion License (Commercial Use Allowed)
|
||||
Applies to: vx previous minor releases (vx.0 – vx.(y-1))
|
||||
================================================================================
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
1. DEFINITIONS
|
||||
"Software" refers to the DesireCore releases covered by this Part.
|
||||
|
||||
"Competing Product" means any software product or service that provides
|
||||
substantially similar functionality to DesireCore, including but not limited
|
||||
to delegation-based interactive agent operating systems, AI companion desktop
|
||||
applications, or any product that directly competes with DesireCore in the
|
||||
same market segment.
|
||||
|
||||
2. PERMITTED USE
|
||||
You are permitted to use, copy, modify, and distribute this Software for
|
||||
any purpose, including commercial purposes, provided that you comply with
|
||||
all the terms and conditions of this License.
|
||||
|
||||
3. COMPETITION EXCLUSION
|
||||
You may NOT use this Software, in whole or in part, to develop, build,
|
||||
operate, market, or contribute to any Competing Product, whether for
|
||||
commercial or non-commercial purposes. This restriction applies to:
|
||||
- Direct use of the Software's source code or binaries
|
||||
- Incorporating substantial portions of the Software into a Competing Product
|
||||
- Using the Software's architecture, design, or implementation as a basis
|
||||
for a Competing Product
|
||||
- Providing the Software or modifications thereof as part of a Competing
|
||||
Product's offering
|
||||
|
||||
4. ATTRIBUTION
|
||||
You must include a copy of this License and the above copyright notice in
|
||||
any copies or substantial portions of the Software. You must retain all
|
||||
copyright, patent, trademark, and attribution notices in the Software.
|
||||
|
||||
5. MODIFICATIONS
|
||||
If you modify this Software, you must clearly indicate that modifications
|
||||
have been made and retain all original copyright notices. Modified versions
|
||||
must also be licensed under this same Part (Part B).
|
||||
|
||||
6. NO WARRANTY
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
YUNNAN SOLAR CORONA TECHNOLOGY CO., LTD. BE LIABLE FOR ANY CLAIM, DAMAGES,
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
7. TERMINATION
|
||||
Your rights under this License are automatically terminated if you violate
|
||||
any of its terms. Upon termination, you must cease all use and destroy all
|
||||
copies of the Software in your possession.
|
||||
|
||||
================================================================================
|
||||
PART C — Non-Commercial & Competition-Exclusion License
|
||||
Applies to: vx latest minor and all development branches
|
||||
================================================================================
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
1. DEFINITIONS
|
||||
"Software" refers to the latest minor release of the current major version
|
||||
of DesireCore, as well as all code on development branches (main/dev) and
|
||||
any pre-release, alpha, beta, or release-candidate versions.
|
||||
|
||||
"Competing Product" has the same meaning as defined in Part B.
|
||||
|
||||
2. PERMITTED USE
|
||||
You are permitted to use, copy, modify, and distribute this Software for
|
||||
non-commercial purposes only, including educational and research purposes,
|
||||
provided that you comply with all the terms and conditions of this License.
|
||||
|
||||
3. COMMERCIAL USE RESTRICTION
|
||||
You may NOT use, sell, rent, lease, transfer, assign, or otherwise dispose
|
||||
of this Software for any commercial purpose, or in any way that generates
|
||||
revenue, profit, or business advantage, without prior written consent from
|
||||
Yunnan Solar Corona Technology Co., Ltd.
|
||||
|
||||
4. COMPETITION EXCLUSION
|
||||
You may NOT use this Software, in whole or in part, to develop, build,
|
||||
operate, market, or contribute to any Competing Product, whether for
|
||||
commercial or non-commercial purposes. This restriction applies to:
|
||||
- Direct use of the Software's source code or binaries
|
||||
- Incorporating substantial portions of the Software into a Competing Product
|
||||
- Using the Software's architecture, design, or implementation as a basis
|
||||
for a Competing Product
|
||||
- Providing the Software or modifications thereof as part of a Competing
|
||||
Product's offering
|
||||
|
||||
5. ATTRIBUTION
|
||||
You must include a copy of this License and the above copyright notice in
|
||||
any copies or substantial portions of the Software. You must retain all
|
||||
copyright, patent, trademark, and attribution notices in the Software.
|
||||
|
||||
6. MODIFICATIONS
|
||||
If you modify this Software, you must clearly indicate that modifications
|
||||
have been made and retain all original copyright notices. Modified versions
|
||||
must also be licensed under this same Part (Part C).
|
||||
|
||||
7. DISTRIBUTION
|
||||
You may distribute the Software and modifications thereof, provided that:
|
||||
- You include this License with every copy or substantial portion
|
||||
- You retain all copyright and attribution notices
|
||||
- Distribution is for non-commercial purposes only
|
||||
- The recipient agrees to all terms of this License
|
||||
|
||||
8. NO WARRANTY
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
YUNNAN SOLAR CORONA TECHNOLOGY CO., LTD. BE LIABLE FOR ANY CLAIM, DAMAGES,
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
9. TERMINATION
|
||||
Your rights under this License are automatically terminated if you violate
|
||||
any of its terms. Upon termination, you must cease all use and destroy all
|
||||
copies of the Software in your possession.
|
||||
|
||||
10. GOVERNING LAW
|
||||
This License is governed by the laws of the People's Republic of China.
|
||||
|
||||
================================================================================
|
||||
COMMERCIAL LICENSING
|
||||
================================================================================
|
||||
|
||||
This tiered licensing model is designed to strike a balance between commercial
|
||||
investment and open-source contribution. We believe in sharing our work with
|
||||
the community while sustaining the resources needed to build and improve
|
||||
DesireCore.
|
||||
|
||||
For commercial use of the latest version (Part C), please contact us to obtain
|
||||
a commercial license:
|
||||
|
||||
Email: ceo@solar-corona.com
|
||||
Website: https://www.solar-corona.com
|
||||
|
||||
We are happy to discuss licensing arrangements that work for your use case.
|
||||
13
.github/runtime-publication/README.md
vendored
Normal file
13
.github/runtime-publication/README.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# 纯运行配置发布工具
|
||||
|
||||
作者 `main` 保留完整资料、生成器与测试。客户端消费独立 `runtime` 历史,不从作者树过滤ZIP后复用原Git历史。
|
||||
|
||||
`publisher.cjs` 是经审阅的自包含 Node 24 工具,不安装依赖、不读取私有应用仓库、不执行本仓的来源脚本。生成源提交、精确输入清单和字节摘要在 `publisher.metadata.json`;第一方与第三方许可保留在同目录,不重新许可。生成源维护于 DesireCore 主仓 `scripts/sync/config-center-runtime-standalone-entry.ts`,不是独立手改的一份规则。
|
||||
|
||||
工作流仅在同仓 main 的精确 `Schema Validation` 运行与必需 validator/test 步骤成功后派生。只读取已通过的固定来源SHA;工具来自工作流自身的可信提交,不从PR或运行产物下载。仅发布job取得本仓 `contents: write`,不使用或复制用户PAT。
|
||||
|
||||
运行字节未变不创建runtime提交,来源/时间不进入数据摘要。运行数据变化必须提升原 `presetDataVersion`,否则失败。发布器只重建准入blob,唯一父提交来自已核验的runtime,不导入main对象;同一数据发布线用于随包种子和后续更新。
|
||||
|
||||
本地手工执行默认dry-run;`--apply`只写指定的独立bare目录,仍不会联网推送。完整开发校验是调用前置,不因这个工具成功而自动成立。工作流普通快进push只发送runtime一个ref,不写main、不force;仓库管理应保护runtime不被回退或删除,不能把普通push当成防管理员外部回退的绝对租约。
|
||||
|
||||
更新工具必须从准确生成源重建,审查第一方闭包、锁定依赖及许可、只允许Node内建external、拒绝SourceMap/私有路径/无关应用模块,再连同metadata和许可提交PR。运行文件白名单、信封与完整性算法只在生成源维护,禁止直接编辑bundle修补门禁。
|
||||
129
.github/runtime-publication/THIRD_PARTY_NOTICES.txt
vendored
Normal file
129
.github/runtime-publication/THIRD_PARTY_NOTICES.txt
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
第三方发布工具依赖许可(由实际闭包派生,不改变原许可)
|
||||
|
||||
===== ajv@8.20.0 (MIT) =====
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2021 Evgeny Poberezkin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
|
||||
===== ajv-formats@3.0.1 (MIT) =====
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Evgeny Poberezkin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
===== fast-deep-equal@3.1.3 (MIT) =====
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Evgeny Poberezkin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
===== fast-uri@3.1.8 (BSD-3-Clause) =====
|
||||
Copyright (c) 2011-2021, Gary Court until https://github.com/garycourt/uri-js/commit/a1acf730b4bba3f1097c9f52e7d9d3aba8cdcaae
|
||||
Copyright (c) 2021-present The Fastify team <https://github.com/fastify/fastify#team>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The names of any contributors may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
* * *
|
||||
|
||||
The complete list of contributors can be found at:
|
||||
- https://github.com/garycourt/uri-js/graphs/contributors
|
||||
|
||||
===== json-schema-traverse@1.0.0 (MIT) =====
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Evgeny Poberezkin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
8915
.github/runtime-publication/publisher.cjs
vendored
Normal file
8915
.github/runtime-publication/publisher.cjs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
331
.github/runtime-publication/publisher.metadata.json
vendored
Normal file
331
.github/runtime-publication/publisher.metadata.json
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"profile": "config-center-runtime-publisher-node-cjs-v1",
|
||||
"sourceCommit": "ef8dae564f553bddb6d04f2f4708154c7565d7bf",
|
||||
"sha256": "32cefa568a04801c184f3b3c237cf84c6781aa54f032c665080d828afb984f96",
|
||||
"inputs": [
|
||||
{
|
||||
"path": "node_modules/ajv-formats/dist/formats.js",
|
||||
"sha256": "b61693a26869970a92cc1fe832c931ccd3afe8b6d66802b19c48306349e84880"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv-formats/dist/index.js",
|
||||
"sha256": "3f3014150293846086d11058bb8bf43e669e354a98b37adcf10281454d5e753b"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv-formats/dist/limit.js",
|
||||
"sha256": "ffc374031a55c3f6e39dc3fe69e663f8251b9d9d7ddb8f513a6807c985c18bb8"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/ajv.js",
|
||||
"sha256": "edaed56fc546f5551b0d6dc2ff2a9cc25c2d6c83318a086eaa8c3ad0b2479297"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/codegen/code.js",
|
||||
"sha256": "1287153722f1fc3c5342647de7ae1d7b6fb68bdb3378a0776a937d273fc97e0d"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/codegen/index.js",
|
||||
"sha256": "f03b2b72597ef86878d1d4030580c2d0964046efad4867f3d05aa91065ba034c"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/codegen/scope.js",
|
||||
"sha256": "d2a0a97af2faa2cad88702314fe421e235ab17f11e0d62883d58d8d4dc734286"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/errors.js",
|
||||
"sha256": "53990f8f2cc05922bba410ae68231d3f869de4f8343277ede5b4834fa564c6c2"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/index.js",
|
||||
"sha256": "05b34da22814f3787bfa3ac68dad59c9047f56e55a607e0a20719ebf5afe69ac"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/names.js",
|
||||
"sha256": "3cab37a2240b996b9bed92fe7a041d91689cfe3ad2e9ae4c0ab99e4146408493"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/ref_error.js",
|
||||
"sha256": "81c98fdf34c3037c91bfcc02277ec32156c71e34048f36cfe4e084416e916cb7"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/resolve.js",
|
||||
"sha256": "df75af0e0cf03fa1c6452e8dc61019f5aec645a8a5f6f494dc4e13cb5502d2d9"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/rules.js",
|
||||
"sha256": "ec1d86500f497aa8e20f9f0c1695951159d9a909669d00a261ce1bb22b3cf0a9"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/util.js",
|
||||
"sha256": "fa911f5631f29d4d6420adfe086cb693b94b08de2719475c78c54bcb82f831a5"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/validate/applicability.js",
|
||||
"sha256": "50c5cba5894a7fb9874b3432355b46ec71b73c9368dafb1323ded8567cca8fc0"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/validate/boolSchema.js",
|
||||
"sha256": "c08abdbdbd3d8b1dcc0552a20fbc7d837ed44f4ca6abb2e464985590ac94377f"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/validate/dataType.js",
|
||||
"sha256": "697bd7005361ec7066a23c18830a10b0ef28aec1a33138b4e3098bf78c4fae52"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/validate/defaults.js",
|
||||
"sha256": "05721971df94faa57b09c8cfd60c2eef81b6d14910a7dea5f539d693a029c428"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/validate/index.js",
|
||||
"sha256": "cb2103e5f890e04592c10d24b45857dad91155bae9f9460138a2459bdaaea2cd"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/validate/keyword.js",
|
||||
"sha256": "ab8f070c75c25eee9be5c837f364748909b58f5db66149a2beccbd2f66afeb76"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/compile/validate/subschema.js",
|
||||
"sha256": "ca783d6052af91e65ebb59a5b48b47fb459a2241d37ca6a042f767d4ce451bdc"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/core.js",
|
||||
"sha256": "51be29c7ed7288622f755c0f1bebd3da4a9907d77aa2c33b0f2a0a5b6b339603"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/refs/data.json",
|
||||
"sha256": "d24ab8a97c18b3f5afdde7d7024a0e0ac1df0614a16a41cb88f81379edcc2db3"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/refs/json-schema-draft-07.json",
|
||||
"sha256": "f7e8b13cad4fecff9771f3626fef33e20e59027b90938a28fd9d2f6c17cd0773"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/runtime/equal.js",
|
||||
"sha256": "c94467da09ee94e5d194483a7ab821b9bb3bf1c04397270f2ab53dea9de59274"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/runtime/ucs2length.js",
|
||||
"sha256": "1df012dd3d23fced07255a7204798004509687cb1f431ae0ab976db436bbd890"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/runtime/uri.js",
|
||||
"sha256": "d1d61baf97ed87d7310df847a78ed73d19c8cc003207bf5aaec977500bdb1578"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/runtime/validation_error.js",
|
||||
"sha256": "11bf5b4affc8114e5e7cdf65b93006c2d997ab60ada1152c22e136f9b828b815"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/additionalItems.js",
|
||||
"sha256": "7beb0fbda5e349907067c8398bb8cc3260a6267b6e5dff9793a5ab2171934099"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js",
|
||||
"sha256": "e237cb180cd2e3f2e676feb714d7dcc6a42116640f9db2ad830cd20d9dfc540a"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/allOf.js",
|
||||
"sha256": "98f46a6434ebe5f851b406480e18b776dd7f1bcc275e28c16a13bfef3cc067c7"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/anyOf.js",
|
||||
"sha256": "8d7368d4aebad0514b713191b934993b4f0bf570e1bab93b07dc69712db2cec5"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/contains.js",
|
||||
"sha256": "56165dc93cd8b0b4611db50a97f1e7d7f9af6c5ffe95a7a5b054d6eeaa7f6a8c"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/dependencies.js",
|
||||
"sha256": "c1a83bf484b6fde4d268935f079ddba5e74a2f589dfc6d15ca2e20ba4d06ee5b"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/if.js",
|
||||
"sha256": "42374ee5dc681433579cc689b0c4ae5343f5bf78d4d6a561722b9277c5335ec4"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/index.js",
|
||||
"sha256": "5a61978f6d81bbabfa8b9ccb71d7de816ed6dd1fd7de5c72657439a76c2a7cb3"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/items.js",
|
||||
"sha256": "fa87f74adc3aac1f1b5e3e7dddf73294e6850e1dd49ed131fca4352e9645c54e"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/items2020.js",
|
||||
"sha256": "c449ef64aec16263c7935293ed450cfc2da3ba66f6df5f99c3198c05f169838f"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/not.js",
|
||||
"sha256": "ab2f2758d4ea97ac8763fe5540dd0f4d877859676fe5d6c750a18deca387f718"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/oneOf.js",
|
||||
"sha256": "e2aeb6c22d49fd5f348bedcf7f5c4d7b31f5a05dd7ff1a0c531374c6968792a2"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/patternProperties.js",
|
||||
"sha256": "0d43da49a82452c439ba990de7372ad5cedb663717918a5f6dae07025abff655"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/prefixItems.js",
|
||||
"sha256": "16264f7b3233a6dcb98b48700643efbd3ebc4d11e27b3eacffc83ccb5f44fb23"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/properties.js",
|
||||
"sha256": "c896873ae7251093a2c8880545b69b6e8cb55c44817a53ec830a49818d74104f"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/propertyNames.js",
|
||||
"sha256": "b3411cfeaff8efd5e6de12971bb5dc339e64b61fbe62d3b950794c077315c2e7"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/applicator/thenElse.js",
|
||||
"sha256": "9bc42eb8699d57188a7ec2a710d3f38052f92bd78c5d4d0442cf98cf2ebbe22d"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/code.js",
|
||||
"sha256": "9212ddf8bd0a4c3b1c4b44cdadd141183b2b36f561b91d13df53c83911e3bf63"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/core/id.js",
|
||||
"sha256": "49c8eff694ce4371450c1f9986e934fcbf4bd143311caef5332113f29175bc11"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/core/index.js",
|
||||
"sha256": "f420e1011b95e27b54c58323a7ed4ffbd0a88cdb4496f771bb6782c2aa22d23e"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/core/ref.js",
|
||||
"sha256": "1422c1454b3621d890066042048f37746fe1cf213088307099816199889bbfd1"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/discriminator/index.js",
|
||||
"sha256": "b58b58025d2dbe5a6967376faec9295b860a338664c47037e9a1035c1e8fb88e"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/discriminator/types.js",
|
||||
"sha256": "2365341d1f30dc72cece7703e908e47aacb8f904b9f66ec4019fe889ae1e57fa"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/draft7.js",
|
||||
"sha256": "8c62022c6c3aa302c3ea6f6709a697fc9eb6385d243fa7c2156dbe9f02228170"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/format/format.js",
|
||||
"sha256": "6fae0d5124fd821b1a9959492f7d6d7b528bee8830d49a7a4759fc9a9ab81d5f"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/format/index.js",
|
||||
"sha256": "d5dad5f95c9c5f48f8ca956cdbb927aafc7b56baabfca8c0b0403d92c5ebeb67"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/metadata.js",
|
||||
"sha256": "45c178744a7ec57e20c85ceeaea8d9f36b5663636ab7e495ea2c3c1bf4a58609"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/validation/const.js",
|
||||
"sha256": "ace994e9ab97a70899a9484794794f4be5ee03bfb3cf32242d3f091d77f0329a"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/validation/enum.js",
|
||||
"sha256": "61062b318a40eb52fc5a8283e7f4781db98349841744961ef4fe04a4722ebf22"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/validation/index.js",
|
||||
"sha256": "4a1839832de5f10ff423c1c0c6cb232b5b6e1b34980a28862cf56b2763136de4"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/validation/limitItems.js",
|
||||
"sha256": "49f0427d29b38aef2fa8aead26e68bda12deb9a2da22f7a7804ba0fed44e1fec"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/validation/limitLength.js",
|
||||
"sha256": "379c28317faaf7784680b6049c3562692d3df6d65cce33f48e6c6c723beeeec2"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/validation/limitNumber.js",
|
||||
"sha256": "3dd4bd41dbfc76c522078ff54b6479683485c4e97faa04cfcfaf2d8cb0fb8e65"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/validation/limitProperties.js",
|
||||
"sha256": "8523a3f7b36c34882ca32b0ec71a2157d7930907cb86b778a97c1514fa6258ac"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/validation/multipleOf.js",
|
||||
"sha256": "df128c2a6ca74ae57c65fbfe3aea8a101447a3ae8c41fd53cfc5693fa23cc8bd"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/validation/pattern.js",
|
||||
"sha256": "86e84206bfc6a66ef18bf1fe43fbc56ebe7f7f4d57841197b6d044c529e0c195"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/validation/required.js",
|
||||
"sha256": "b953bc18d94274bd73374dd5b4282d7a21159cec8c0e7a357e1eaf3f326b9054"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/ajv/dist/vocabularies/validation/uniqueItems.js",
|
||||
"sha256": "666d1a2aa25c0dfe3cf41312d0f9d40e9f99eb2c19be7469134ad89005aadd52"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/fast-deep-equal/index.js",
|
||||
"sha256": "eb469e206280321a3878f2335ec98aa2104a155079d8ed83a23029098dccd215"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/fast-uri/index.js",
|
||||
"sha256": "62876e5094b43ba2ba90374613dab4576fd337063dad20b4b07cc5e3a7e2303b"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/fast-uri/lib/schemes.js",
|
||||
"sha256": "c2c61d3fd0bc63a587100928a399e75be8c420d210c439d75f7346da4aa4f3c3"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/fast-uri/lib/utils.js",
|
||||
"sha256": "610a6cfa7b1c9d5689443b3a3d14af4c24afed51ef5852495ae1489ae455a9f4"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/json-schema-traverse/index.js",
|
||||
"sha256": "024191b747afac01b28ff11d744e9f82720bbad5f716163ba5c962eb62aa2350"
|
||||
},
|
||||
{
|
||||
"path": "packages/schemas/src/agent-service/compute-availability.ts",
|
||||
"sha256": "bd1e2b1a940a043f179a634bc863f9aeb4cea8ec7ecb56393c9d0ec69d944ef9"
|
||||
},
|
||||
{
|
||||
"path": "packages/schemas/src/agent-service/compute.ts",
|
||||
"sha256": "ed8975dc6df3b408e971d4512e674a26db4bbd3d4e38356dd7898485c084296f"
|
||||
},
|
||||
{
|
||||
"path": "packages/schemas/src/agent-service/config-center-runtime.ts",
|
||||
"sha256": "92cc8f8deb8cdc8bafb2f5d8fb71ce8026479a8dbf657bf179a864eb03745085"
|
||||
},
|
||||
{
|
||||
"path": "packages/schemas/src/validation.ts",
|
||||
"sha256": "79476b931fabdc7049e72f21f1687a2e1f2e667483b9956baf86d57c1ee600ee"
|
||||
},
|
||||
{
|
||||
"path": "packages/shared/src/config-center-runtime.ts",
|
||||
"sha256": "09f1306171aecfed557f295bc18be598308d28629ded7789ab9c127b229b97a6"
|
||||
},
|
||||
{
|
||||
"path": "scripts/sync/config-center-runtime-cli.ts",
|
||||
"sha256": "8318706b15458db54798636fea35c6d3d03570817bb1354b4d405105fce9847d"
|
||||
},
|
||||
{
|
||||
"path": "scripts/sync/config-center-runtime-publisher.ts",
|
||||
"sha256": "9ea3292bc97b8eca5a6629136d356efbe4a4a5874e2614357a22a5ea5809a982"
|
||||
},
|
||||
{
|
||||
"path": "scripts/sync/config-center-runtime-standalone-entry.ts",
|
||||
"sha256": "4b0b16c2cb844cd74c971575c67f758164c5e4444518c62f9c5394c14d15780f"
|
||||
}
|
||||
],
|
||||
"externalImports": [
|
||||
"node:child_process",
|
||||
"node:crypto",
|
||||
"node:fs",
|
||||
"node:os",
|
||||
"node:path"
|
||||
]
|
||||
}
|
||||
253
.github/workflows/publish-runtime.yml
vendored
Normal file
253
.github/workflows/publish-runtime.yml
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
# 上游部署模板:经审阅复制到 config-center 的 .github/workflows/publish-runtime.yml。
|
||||
# 需先提交独立受审工具及 metadata;本模板不是本地执行入口,也不代表已经持续上线。
|
||||
name: Publish Runtime Data
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [Schema Validation]
|
||||
types: [completed]
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: config-center-runtime-publication
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
if: >-
|
||||
github.repository == 'desirecore/config-center' &&
|
||||
github.event.workflow_run.conclusion == 'success' &&
|
||||
github.event.workflow_run.event == 'push' &&
|
||||
github.event.workflow_run.head_branch == 'main' &&
|
||||
github.event.workflow_run.head_repository.id == github.event.repository.id &&
|
||||
github.event.workflow_run.head_repository.full_name == github.repository
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
outputs:
|
||||
eligible: ${{ steps.gate.outputs.eligible }}
|
||||
source_sha: ${{ steps.gate.outputs.source_sha }}
|
||||
steps:
|
||||
- name: 固定 Node 工具链
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
|
||||
with:
|
||||
node-version: '24'
|
||||
- name: 复核同仓库精确校验运行与必需 job
|
||||
id: gate
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
shell: bash
|
||||
run: |
|
||||
node <<'NODE'
|
||||
const fs = require('node:fs')
|
||||
;(async () => {
|
||||
const event = JSON.parse(fs.readFileSync(process.env.GITHUB_EVENT_PATH, 'utf8'))
|
||||
const expected = 'desirecore/config-center', runEvent = event.workflow_run
|
||||
const sha = runEvent?.head_sha
|
||||
if (process.env.GITHUB_REPOSITORY !== expected || event.repository?.full_name !== expected
|
||||
|| runEvent?.head_repository?.id !== event.repository?.id
|
||||
|| runEvent?.head_repository?.full_name !== expected || runEvent.event !== 'push'
|
||||
|| runEvent.head_branch !== 'main' || runEvent.conclusion !== 'success'
|
||||
|| !/^[a-f0-9]{40}$/.test(sha ?? '') || !Number.isSafeInteger(runEvent.id)
|
||||
|| !Number.isSafeInteger(runEvent.run_attempt) || runEvent.run_attempt < 1) throw new Error('guard')
|
||||
const request = async path => {
|
||||
const response = await fetch(`https://api.github.com/repos/${expected}${path}`, {
|
||||
headers: { accept: 'application/vnd.github+json', authorization: `Bearer ${process.env.GH_TOKEN}`,
|
||||
'x-github-api-version': '2022-11-28' }, signal: AbortSignal.timeout(20000),
|
||||
})
|
||||
if (!response.ok) throw new Error('api')
|
||||
return response.json()
|
||||
}
|
||||
const run = await request(`/actions/runs/${runEvent.id}`)
|
||||
if (run.id !== runEvent.id || run.run_attempt !== runEvent.run_attempt || run.head_sha !== sha
|
||||
|| run.repository?.id !== event.repository.id || run.head_repository?.id !== event.repository.id
|
||||
|| run.event !== 'push' || run.head_branch !== 'main' || run.status !== 'completed'
|
||||
|| run.conclusion !== 'success' || run.name !== 'Schema Validation'
|
||||
|| !Number.isSafeInteger(run.workflow_id) || run.workflow_id !== runEvent.workflow_id) throw new Error('run')
|
||||
const workflow = await request(`/actions/workflows/${run.workflow_id}`)
|
||||
if (workflow.id !== run.workflow_id || workflow.path !== '.github/workflows/validate.yml'
|
||||
|| workflow.name !== 'Schema Validation') throw new Error('workflow')
|
||||
const jobs = await request(`/actions/runs/${run.id}/attempts/${run.run_attempt}/jobs?per_page=100`)
|
||||
if (!Array.isArray(jobs.jobs) || !Number.isSafeInteger(jobs.total_count)
|
||||
|| jobs.jobs.length !== jobs.total_count) throw new Error('jobs')
|
||||
const required = jobs.jobs.filter(job => job.name === 'Validate against frozen schema')
|
||||
if (required.length !== 1 || required[0].run_id !== run.id || required[0].head_sha !== sha
|
||||
|| required[0].run_attempt !== run.run_attempt || required[0].status !== 'completed'
|
||||
|| required[0].conclusion !== 'success') throw new Error('required_job')
|
||||
for (const name of ['Run validator', 'Run unit tests']) {
|
||||
const steps = required[0].steps?.filter(step => step.name === name)
|
||||
if (steps?.length !== 1 || steps[0].status !== 'completed' || steps[0].conclusion !== 'success') throw new Error('required_step')
|
||||
}
|
||||
const main = await request('/git/ref/heads/main')
|
||||
const eligible = main.object?.sha === sha
|
||||
fs.appendFileSync(process.env.GITHUB_OUTPUT, `eligible=${eligible}\nsource_sha=${sha}\n`)
|
||||
console.log(eligible ? '精确 main 校验已通过;允许纯运行派生。' : 'main 已前进;跳过旧校验运行,等待新 main 校验。')
|
||||
})().catch(() => { console.error('config_center_runtime_ci_verification_failed'); process.exitCode = 1 })
|
||||
NODE
|
||||
|
||||
publish:
|
||||
needs: verify
|
||||
if: needs.verify.outputs.eligible == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: 读取可信发布工具(不是触发运行的 artifact)
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
ref: ${{ github.workflow_sha }}
|
||||
path: runtime-tool
|
||||
persist-credentials: false
|
||||
fetch-depth: 1
|
||||
sparse-checkout: .github/runtime-publication
|
||||
- name: 读取已验证的精确来源快照
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
ref: ${{ needs.verify.outputs.source_sha }}
|
||||
path: runtime-source
|
||||
persist-credentials: false
|
||||
fetch-depth: 1
|
||||
submodules: false
|
||||
- name: 固定 Node 工具链
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
|
||||
with:
|
||||
node-version: '24'
|
||||
- name: 审核工具摘要并派生独立本地 runtime
|
||||
env:
|
||||
SOURCE_SHA: ${{ needs.verify.outputs.source_sha }}
|
||||
shell: bash
|
||||
run: |
|
||||
node <<'NODE'
|
||||
const fs = require('node:fs'), path = require('node:path'), os = require('node:os')
|
||||
const crypto = require('node:crypto'), cp = require('node:child_process')
|
||||
function main() {
|
||||
const sha1 = /^[a-f0-9]{40}$/, sha256 = /^[a-f0-9]{64}$/
|
||||
if (!sha1.test(process.env.SOURCE_SHA ?? '')) throw new Error('source')
|
||||
const toolRoot = path.join(process.env.GITHUB_WORKSPACE, 'runtime-tool', '.github', 'runtime-publication')
|
||||
const tool = path.join(toolRoot, 'publisher.cjs'), metadataFile = path.join(toolRoot, 'publisher.metadata.json')
|
||||
for (const file of [tool, metadataFile]) {
|
||||
const info = fs.lstatSync(file)
|
||||
if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1 || info.size < 1 || info.size > 8 * 1024 * 1024) throw new Error('tool')
|
||||
}
|
||||
const metadata = JSON.parse(fs.readFileSync(metadataFile, 'utf8'))
|
||||
const builtin = new Set(require('node:module').builtinModules.map(name => name.replace(/^node:/, '')))
|
||||
if (metadata.formatVersion !== 1 || metadata.profile !== 'config-center-runtime-publisher-node-cjs-v1'
|
||||
|| !sha1.test(metadata.sourceCommit ?? '') || !sha256.test(metadata.sha256 ?? '')
|
||||
|| !Array.isArray(metadata.inputs) || metadata.inputs.length < 1 || metadata.inputs.length > 2048
|
||||
|| !Array.isArray(metadata.externalImports)
|
||||
|| metadata.externalImports.some(name => typeof name !== 'string' || !builtin.has(name.replace(/^node:/, '')))) throw new Error('metadata')
|
||||
const inputNames = new Set()
|
||||
for (const input of metadata.inputs) {
|
||||
if (typeof input.path !== 'string' || input.path.length < 1 || input.path.length > 512
|
||||
|| input.path.includes('\\') || input.path.split('/').some(part => !part || part === '.' || part === '..')
|
||||
|| /[\x00-\x1f:]/.test(input.path) || !sha256.test(input.sha256 ?? '') || inputNames.has(input.path)) throw new Error('input')
|
||||
inputNames.add(input.path)
|
||||
}
|
||||
if (crypto.createHash('sha256').update(fs.readFileSync(tool)).digest('hex') !== metadata.sha256) throw new Error('tool_digest')
|
||||
const scratch = fs.mkdtempSync(path.join(process.env.RUNNER_TEMP, 'dc-runtime-publication-'))
|
||||
const home = path.join(scratch, 'home'), hooks = path.join(scratch, 'hooks'), template = path.join(scratch, 'template')
|
||||
for (const dir of [home, hooks, template]) fs.mkdirSync(dir)
|
||||
const runtimeDir = path.join(scratch, 'runtime.git')
|
||||
const cleanEnv = Object.fromEntries(['PATH', 'Path', 'SystemRoot', 'windir', 'TEMP', 'TMP', 'TMPDIR']
|
||||
.flatMap(key => process.env[key] === undefined ? [] : [[key, process.env[key]]]))
|
||||
const gitEnv = { ...cleanEnv, HOME: home, USERPROFILE: home, XDG_CONFIG_HOME: home,
|
||||
GIT_CONFIG_GLOBAL: os.devNull, GIT_CONFIG_SYSTEM: os.devNull, GIT_CONFIG_NOSYSTEM: '1',
|
||||
GIT_TEMPLATE_DIR: template, GIT_TERMINAL_PROMPT: '0', GIT_ASKPASS: '', GIT_ALLOW_PROTOCOL: 'https',
|
||||
GIT_OPTIONAL_LOCKS: '0', GIT_NO_REPLACE_OBJECTS: '1', GIT_NO_LAZY_FETCH: '1' }
|
||||
const runGit = args => cp.execFileSync('git', ['-c', `core.hooksPath=${hooks}`, '-c', 'core.fsmonitor=false',
|
||||
'-c', 'gc.auto=0', '-c', 'maintenance.auto=false', ...args], { cwd: scratch, env: gitEnv,
|
||||
encoding: 'utf8', stdio: 'pipe', timeout: 60000, maxBuffer: 8 * 1024 * 1024 }).trim()
|
||||
const url = 'https://github.com/desirecore/config-center.git'
|
||||
const line = runGit(['ls-remote', '--refs', url, 'refs/heads/runtime'])
|
||||
const expectedRuntimeCommit = line ? line.split('\t')[0] : null
|
||||
if (line && (!sha1.test(expectedRuntimeCommit) || line !== `${expectedRuntimeCommit}\trefs/heads/runtime`)) throw new Error('runtime_ref')
|
||||
runGit(['init', '--bare', '--object-format=sha1', '--initial-branch=runtime', runtimeDir])
|
||||
if (expectedRuntimeCommit) {
|
||||
// 不抓作者 main、不抓 tags、不给 depth;publisher 会核整条纯 runtime 历史。
|
||||
runGit([`--git-dir=${runtimeDir}`, 'fetch', '--no-tags', '--no-recurse-submodules', '--no-write-fetch-head',
|
||||
url, 'refs/heads/runtime:refs/heads/runtime'])
|
||||
if (runGit([`--git-dir=${runtimeDir}`, 'rev-parse', 'refs/heads/runtime']) !== expectedRuntimeCommit) throw new Error('runtime_moved')
|
||||
}
|
||||
const source = path.join(process.env.GITHUB_WORKSPACE, 'runtime-source')
|
||||
// 使用已验证公开来源提交的作者身份,不读取 runner 或用户的全局身份配置。
|
||||
const author = runGit(['-C', source, 'show', '-s', '--format=%an%x00%ae', process.env.SOURCE_SHA]).split('\0')
|
||||
if (author.length !== 2) throw new Error('author')
|
||||
const stdout = cp.execFileSync(process.execPath, [tool, '--source', source, '--source-commit', process.env.SOURCE_SHA,
|
||||
'--output', runtimeDir, '--apply', '--author-name', author[0], '--author-email', author[1]], {
|
||||
env: cleanEnv, encoding: 'utf8', stdio: 'pipe', timeout: 300000, maxBuffer: 2 * 1024 * 1024,
|
||||
})
|
||||
const result = JSON.parse(stdout.split('\n')[0])
|
||||
if (result.sourceCommit !== process.env.SOURCE_SHA || result.dryRun !== false || typeof result.changed !== 'boolean'
|
||||
|| !sha1.test(result.runtimeCommit ?? '') || !sha256.test(result.payloadDigest ?? '')
|
||||
|| !Number.isSafeInteger(result.dataVersion) || result.dataVersion < 1
|
||||
|| !Number.isSafeInteger(result.fileCount) || result.fileCount < 1 || result.fileCount > 4096
|
||||
|| result.validationBoundary !== 'runtime-payload-and-client-contract-only') throw new Error('result')
|
||||
fs.writeFileSync(path.join(process.env.RUNNER_TEMP, 'config-center-runtime-publication.json'), JSON.stringify({
|
||||
sourceCommit: result.sourceCommit, runtimeDir, home, hooks, template, expectedRuntimeCommit,
|
||||
runtimeCommit: result.runtimeCommit, changed: result.changed, payloadDigest: result.payloadDigest,
|
||||
dataVersion: result.dataVersion, fileCount: result.fileCount,
|
||||
}), { flag: 'wx', mode: 0o600 })
|
||||
console.log(result.changed ? '纯运行字节已改变;本地候选已派生,尚未推送。' : '纯运行字节未改变;没有新增 runtime 提交。')
|
||||
}
|
||||
try { main() }
|
||||
catch { console.error('config_center_runtime_ci_derivation_failed'); process.exitCode = 1 }
|
||||
NODE
|
||||
- name: 仅快进发布 runtime ref
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
SOURCE_SHA: ${{ needs.verify.outputs.source_sha }}
|
||||
shell: bash
|
||||
run: |
|
||||
node <<'NODE'
|
||||
const fs = require('node:fs'), path = require('node:path'), os = require('node:os'), cp = require('node:child_process')
|
||||
function main() {
|
||||
const receipt = JSON.parse(fs.readFileSync(path.join(process.env.RUNNER_TEMP, 'config-center-runtime-publication.json'), 'utf8'))
|
||||
const sha1 = /^[a-f0-9]{40}$/
|
||||
if (receipt.sourceCommit !== process.env.SOURCE_SHA || !sha1.test(receipt.sourceCommit)
|
||||
|| !sha1.test(receipt.runtimeCommit) || typeof receipt.changed !== 'boolean'
|
||||
|| (receipt.expectedRuntimeCommit !== null && !sha1.test(receipt.expectedRuntimeCommit))) throw new Error('receipt')
|
||||
if (!receipt.changed) { console.log('无运行内容变化;跳过推送。'); return }
|
||||
const cleanEnv = Object.fromEntries(['PATH', 'Path', 'SystemRoot', 'windir', 'TEMP', 'TMP', 'TMPDIR']
|
||||
.flatMap(key => process.env[key] === undefined ? [] : [[key, process.env[key]]]))
|
||||
const gitEnv = { ...cleanEnv, HOME: receipt.home, USERPROFILE: receipt.home, XDG_CONFIG_HOME: receipt.home,
|
||||
GIT_CONFIG_GLOBAL: os.devNull, GIT_CONFIG_SYSTEM: os.devNull, GIT_CONFIG_NOSYSTEM: '1', GIT_TEMPLATE_DIR: receipt.template,
|
||||
GIT_TERMINAL_PROMPT: '0', GIT_ASKPASS: '', GIT_ALLOW_PROTOCOL: 'https', GIT_OPTIONAL_LOCKS: '0',
|
||||
GIT_NO_REPLACE_OBJECTS: '1', GIT_NO_LAZY_FETCH: '1' }
|
||||
const runGit = (args, auth = false) => cp.execFileSync('git', ['-c', `core.hooksPath=${receipt.hooks}`,
|
||||
'-c', 'core.fsmonitor=false', '-c', 'gc.auto=0', '-c', 'maintenance.auto=false', ...args], {
|
||||
cwd: process.env.RUNNER_TEMP, encoding: 'utf8', stdio: 'pipe', timeout: 60000, maxBuffer: 1024 * 1024,
|
||||
env: auth ? { ...gitEnv, GIT_CONFIG_COUNT: '1', GIT_CONFIG_KEY_0: 'http.https://github.com/.extraheader',
|
||||
GIT_CONFIG_VALUE_0: `AUTHORIZATION: basic ${Buffer.from(`x-access-token:${process.env.GH_TOKEN}`).toString('base64')}` } : gitEnv,
|
||||
}).trim()
|
||||
const url = 'https://github.com/desirecore/config-center.git'
|
||||
const ref = 'refs/heads/runtime', gitDir = `--git-dir=${receipt.runtimeDir}`
|
||||
const remoteOid = name => {
|
||||
const line = runGit(['ls-remote', '--refs', url, name])
|
||||
if (!line) return null
|
||||
const oid = line.split('\t')[0]
|
||||
if (!sha1.test(oid) || line !== `${oid}\t${name}`) throw new Error('ref')
|
||||
return oid
|
||||
}
|
||||
if (remoteOid('refs/heads/main') !== receipt.sourceCommit) {
|
||||
console.log('main 已前进;候选保留于本轮临时目录,不发布旧来源。'); return
|
||||
}
|
||||
if (remoteOid(ref) !== receipt.expectedRuntimeCommit) throw new Error('runtime_changed')
|
||||
const expectedParents = receipt.expectedRuntimeCommit ? `${receipt.runtimeCommit} ${receipt.expectedRuntimeCommit}` : receipt.runtimeCommit
|
||||
if (runGit([gitDir, 'rev-parse', ref]) !== receipt.runtimeCommit
|
||||
|| runGit([gitDir, 'rev-list', '--parents', '-1', receipt.runtimeCommit]) !== expectedParents) throw new Error('parent')
|
||||
if (!process.env.GH_TOKEN) throw new Error('token')
|
||||
// 只发送一个固定 ref,不用 +/force/lease/mirror;远端须另行禁止回退及删除。
|
||||
// 普通快进 push 不冒称跨管理员外部回退的绝对 expected-old lease。
|
||||
runGit([gitDir, 'push', '--porcelain', '--no-follow-tags', url, `${receipt.runtimeCommit}:${ref}`], true)
|
||||
if (remoteOid(ref) !== receipt.runtimeCommit) throw new Error('published_ref_changed')
|
||||
console.log(`已发布纯运行数据 ${receipt.dataVersion},runtime ${receipt.runtimeCommit};本流程未写作者 main。`)
|
||||
}
|
||||
try { main() }
|
||||
catch { console.error('config_center_runtime_ci_push_failed'); process.exitCode = 1 }
|
||||
NODE
|
||||
Reference in New Issue
Block a user