diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..61ead86 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..479b43a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + // 使用 IntelliSense 以得知可用的屬性。 + // 暫留以檢視現有屬性的描述。 + // 如需詳細資訊,請瀏覽: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for XDebug", + "type": "php", + "request": "launch", + // "hostname": "jianmiau.tk", + "port": 9000, + "pathMappings": { + "web/MyWeb/GiteaRepoManager": "${workspaceRoot}", + }, + }, + { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 9003, + "pathMappings": { + "web/MyWeb/GiteaRepoManager": "${workspaceRoot}", + }, + } + ] +} \ No newline at end of file diff --git a/actions_settings.json b/actions_settings.json new file mode 100644 index 0000000..381b831 --- /dev/null +++ b/actions_settings.json @@ -0,0 +1,44 @@ +{ + "project_settings": { + "secrets": { + "PROJECT_NAME": "excel:1082812249:$B", + "PLATFORM": "excel:1082812249:$C" + } + }, + "repos": [ + { + "name": "Resource-B2B", + "secrets": { + "ENV": "excel:1659610869:$C", + "ACTION": "excel:1082812249:$D", + "POS": "B4", + "REPO": "eI4Z6Iuhuf" + } + }, + { + "name": "Resource-Out", + "secrets": { + "ENV": "excel:1866603134:$C", + "POS": "D4", + "REPO": "eI4Z6Iuhuf" + } + }, + { + "name": "Official-B2B", + "secrets": { + "ENV": "excel:1659610869:$C", + "ACTION": "excel:1082812249:$D", + "POS": "D4", + "REPO": "xZcvkubN1o" + } + }, + { + "name": "Official-Out", + "secrets": { + "ENV": "excel:1866603134:$C", + "POS": "D4", + "REPO": "xZcvkubN1o" + } + } + ] +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..4fad37e --- /dev/null +++ b/composer.json @@ -0,0 +1,17 @@ +{ + "name": "user/gitearepomanager", + "autoload": { + "psr-4": { + "User\\GiteaRepoManager\\": "src/" + } + }, + "authors": [ + { + "name": "JianMiau", + "email": "bir840124@gmail.com" + } + ], + "require": { + "google/apiclient": "^2.14" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..12c6fb5 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1281 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "f81f2aaf5c65505d7df8510c288ab652", + "packages": [ + { + "name": "firebase/php-jwt", + "version": "v6.11.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + }, + "time": "2025-04-09T20:32:01+00:00" + }, + { + "name": "google/apiclient", + "version": "v2.18.3", + "source": { + "type": "git", + "url": "https://github.com/googleapis/google-api-php-client.git", + "reference": "4eee42d201eff054428a4836ec132944d271f051" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/google-api-php-client/zipball/4eee42d201eff054428a4836ec132944d271f051", + "reference": "4eee42d201eff054428a4836ec132944d271f051", + "shasum": "" + }, + "require": { + "firebase/php-jwt": "^6.0", + "google/apiclient-services": "~0.350", + "google/auth": "^1.37", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.6", + "monolog/monolog": "^2.9||^3.0", + "php": "^8.0", + "phpseclib/phpseclib": "^3.0.36" + }, + "require-dev": { + "cache/filesystem-adapter": "^1.1", + "composer/composer": "^1.10.23", + "phpcompatibility/php-compatibility": "^9.2", + "phpspec/prophecy-phpunit": "^2.1", + "phpunit/phpunit": "^9.6", + "squizlabs/php_codesniffer": "^3.8", + "symfony/css-selector": "~2.1", + "symfony/dom-crawler": "~2.1" + }, + "suggest": { + "cache/filesystem-adapter": "For caching certs and tokens (using Google\\Client::setCache)" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/aliases.php" + ], + "psr-4": { + "Google\\": "src/" + }, + "classmap": [ + "src/aliases.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Client library for Google APIs", + "homepage": "http://developers.google.com/api-client-library/php", + "keywords": [ + "google" + ], + "support": { + "issues": "https://github.com/googleapis/google-api-php-client/issues", + "source": "https://github.com/googleapis/google-api-php-client/tree/v2.18.3" + }, + "time": "2025-04-08T21:59:36+00:00" + }, + { + "name": "google/apiclient-services", + "version": "v0.407.0", + "source": { + "type": "git", + "url": "https://github.com/googleapis/google-api-php-client-services.git", + "reference": "8366037e450b62ffc1c5489459f207640acca2b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/8366037e450b62ffc1c5489459f207640acca2b4", + "reference": "8366037e450b62ffc1c5489459f207640acca2b4", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6" + }, + "type": "library", + "autoload": { + "files": [ + "autoload.php" + ], + "psr-4": { + "Google\\Service\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Client library for Google APIs", + "homepage": "http://developers.google.com/api-client-library/php", + "keywords": [ + "google" + ], + "support": { + "issues": "https://github.com/googleapis/google-api-php-client-services/issues", + "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.407.0" + }, + "time": "2025-06-04T17:28:44+00:00" + }, + { + "name": "google/auth", + "version": "v1.47.1", + "source": { + "type": "git", + "url": "https://github.com/googleapis/google-auth-library-php.git", + "reference": "d7a0a215ec42ca0c8cb40e9ae0c5960aa9a024b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/d7a0a215ec42ca0c8cb40e9ae0c5960aa9a024b7", + "reference": "d7a0a215ec42ca0c8cb40e9ae0c5960aa9a024b7", + "shasum": "" + }, + "require": { + "firebase/php-jwt": "^6.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.4.5", + "php": "^8.0", + "psr/cache": "^2.0||^3.0", + "psr/http-message": "^1.1||^2.0", + "psr/log": "^3.0" + }, + "require-dev": { + "guzzlehttp/promises": "^2.0", + "kelvinmo/simplejwt": "0.7.1", + "phpseclib/phpseclib": "^3.0.35", + "phpspec/prophecy-phpunit": "^2.1", + "phpunit/phpunit": "^9.6", + "sebastian/comparator": ">=1.2.3", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^6.0||^7.0", + "webmozart/assert": "^1.11" + }, + "suggest": { + "phpseclib/phpseclib": "May be used in place of OpenSSL for signing strings or for token management. Please require version ^2." + }, + "type": "library", + "autoload": { + "psr-4": { + "Google\\Auth\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Google Auth Library for PHP", + "homepage": "https://github.com/google/google-auth-library-php", + "keywords": [ + "Authentication", + "google", + "oauth2" + ], + "support": { + "docs": "https://cloud.google.com/php/docs/reference/auth/latest", + "issues": "https://github.com/googleapis/google-auth-library-php/issues", + "source": "https://github.com/googleapis/google-auth-library-php/tree/v1.47.1" + }, + "time": "2025-07-09T15:26:02+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.9.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2025-03-27T13:37:11+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.2.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2025-03-27T13:27:01+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2025-03-27T12:30:47+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.9.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.9.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2025-03-24T10:02:05+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", + "shasum": "" + }, + "require": { + "php": "^8" + }, + "require-dev": { + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4|^5" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2024-05-08T12:36:18+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.46", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2|^3", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2025-06-26T16:29:55+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/favicon.svg b/favicon.svg new file mode 100644 index 0000000..4329134 --- /dev/null +++ b/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/google-service-key.json b/google-service-key.json new file mode 100644 index 0000000..18bf970 --- /dev/null +++ b/google-service-key.json @@ -0,0 +1,14 @@ +{ + "type": "service_account", + "project_id": "fresh-strategy-369406", + "private_key_id": "a816afe3911c78b8618ca1846f703aefba3519b2", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDXzAYyDqjMNbSM\noGMuFKxagDhJH9TKoV2yyj4AAJZ2wGYOTKar5pJ78KW7GCwPr+w3Oom2YJX6QDXe\nxnrguikmku9bPUmHkoVb7ZK5R8aWqR6tjn+czRHU5wlU5FVv2Bn/uxI94vnfRb48\nZmKP6BG6RcLdoBBE7TFb8Kny+D0O7w3Q63r7hbJFssnfm/YBXyPpCOnUuB30GJmQ\n0rPNT37+BfbuLDH6mX9KCAfz4VTKPNQbje3r8b+7Il/woPs9DtLmJtkiHDMbxkjt\nFJCqwyUwjjAFE5Youepjv5Tpm5Q1IWSCKhE9rQ/XeuD0pezeF8wzaQMHfUPM9WxS\nYrVYP0odAgMBAAECggEAFNdqE8SRreNT9C77VTR+7uCqTvmpigZqr71Tnplv7rkn\nQiNKB5kltZ2oy/iKLNuvQyg+q6QJaBlyenkN3g1sswKG5nd1VggjJB0+SfGyLtPX\nmCiGj6TIn5jOsGm7DKnA3Q96tAprWpJ4TIoQ49gkeiqJpvDyEU4dMcV9DG/IKdxk\nKs99yw5dIcMOAq8o1C6ZM7eEJGcG9iyshlB86Bg0hPY8EfEIuP1dF/kTGELhBNKz\nbku7UQsBJdeBGb31Qa361Qx9tbifPK2zl1gpjG6dZmgTHgxYb2xEEAcUdzyKsWyR\n18D3PRmGHKLsCxYFmj9jzwYaxaEarYx9ap2VHPS+wQKBgQD6EWrN56oGUiujbwQC\nCM0a+NchGQ0LWF+09anDHohIKEA+Nto54k3FxqAkRfTC0flBfe0AzYmPNxbOSb8Q\ncb/FIaqbLRdIfNjnbpse8bNFh27UVMQDr18jm0ab2t7BKSyoKN4eVok6soxCRqlZ\nNSUPxspsC+zO9S69KkGP0SqO4QKBgQDc6n1lU0U06jz2R52IshOq47+nC5TvK3r7\nqRfIRjjSMyabhwtTCSU8xALAqQLS+lFauFtGRRSFLK07aXvaWPWwW37xADq7ICc6\nLdsvaJv2rFvx0Uuwp+EJOSXl5NDEU27PTf/44tzC4Gp/SyfD1wQVu0TzVKFe1ZXT\n0P1X0fOOvQKBgGSGJeIZ035w/7vWP801joXeLFTQxi6eWvLaomCeYHhpPdIEqNsF\n/u+XNf7+5DKAx+ss3N4qwbaBlbhdauIIZ+et7fAtQyPPlD4Md20MCl3T4JiYbqdw\nkxU0MUErzcnmbF4493lInieraLinwSHsPDbIWczvSkWzyBMg7nQKyEnhAoGBAI82\nAAZQnfu4ob5yHKDB+Ff+/n4W1vzY7gf4zS8KvskdWbjXKbMxqY8j7jjhF7CXj2fF\nPX5nR+8xUDfEoQKiStuB5N/s6yXlqShhE8c/BGQ7xfsUWAH0QsEM6BGJbQDoqVwA\nT6ETyFMY0lEk8mlVmRNRbFhmE5p70X4X7DQjKcXtAoGBAMlyvK404Ne9hc/cvoLp\n25zliltf0s5599xdX4tHINbKupGqAIr8KlMxEKZWr49Fys9D1U4SkMck7OKvWSSX\n8kQWwJZVfkJWcdbdNZWcO3QPvF7UQ2w4D+an2t/DngjecMlNEPfS9AO6UhN5LCft\nJ0ldwDas8FggZEoFqmG+VGbC\n-----END PRIVATE KEY-----\n", + "client_email": "google-sheet@fresh-strategy-369406.iam.gserviceaccount.com", + "client_id": "110536180295168446962", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/google-sheet%40fresh-strategy-369406.iam.gserviceaccount.com", + "universe_domain": "googleapis.com" + } + \ No newline at end of file diff --git a/index.html b/index.html index 6361c4b..576b350 100644 --- a/index.html +++ b/index.html @@ -2,294 +2,44 @@ - + 手動輸入組織,批次建立 Gitea 儲存庫與團隊 - + + -

- 輸入組織名稱並從 JSON 建立儲存庫+設定團隊 -
- PS.請先建好團隊(或使用下方按鈕批次建立) -

+

輸入組織名稱並從 JSON 建立儲存庫+設定團隊
PS.請先建好團隊(或使用下方按鈕批次建立)

- - - - + + + +
diff --git a/run.php b/run.php new file mode 100644 index 0000000..6f89e0d --- /dev/null +++ b/run.php @@ -0,0 +1,301 @@ +setAuthConfig($SERVICE_KEY); + $client->addScope(\Google\Service\Sheets::SPREADSHEETS_READONLY); + $service = new \Google\Service\Sheets($client); + + $spreadsheet = $service->spreadsheets->get($SHEET_ID); + $sheetName = null; + foreach ($spreadsheet->getSheets() as $sheet) { + if ($sheet->getProperties()->getSheetId() === intval($gid)) { + $sheetName = $sheet->getProperties()->getTitle(); + break; + } + } + if (!$sheetName) { logMsg("⚠️ 找不到 GID: $gid"); return null; } + + $response = $service->spreadsheets_values->get($SHEET_ID, $sheetName); + $values = $response->getValues() ?? []; + + $lastNonEmptyCol = []; // 保存每個欄位最後非空值 + + foreach ($values as $r => &$row) { + // 保證列長度至少到目標欄位 + for ($c = 0; $c <= $colIndex; $c++) { + $cell = $row[$c] ?? ''; + $cell = trim($cell); + if ($cell !== '') { + $lastNonEmptyCol[$c] = $cell; + } else if (isset($lastNonEmptyCol[$c])) { + $row[$c] = $lastNonEmptyCol[$c]; // 更新列 + } else { + $row[$c] = ''; // 避免未設定 + } + // logMsg("DEBUG row={$r} col={$c} value='{$row[$c]}'"); + } + + $cellOrg = $row[0] ?? ''; + if ($cellOrg === $orgInput) { + $value = $row[$colIndex] ?? ''; + // logMsg("DEBUG 找到 org='{$orgInput}',回傳 col={$colIndex} 值='{$value}'"); + return $value; + } + } + unset($row); + + logMsg("⚠️ Excel 找不到 org={$orgInput} 的資料 (sheet='{$sheetName}')"); + return ''; + } catch (\Exception $e) { + logMsg("⚠️ Excel 讀取錯誤: " . $e->getMessage()); + return ''; + } +} + +function fetchJSON($url, $method='GET', $data=null) { + global $GITEA_TOKEN; + $ch = curl_init($url); + $headers = ["Content-Type: application/json"]; + if ($GITEA_TOKEN) $headers[] = "Authorization: token $GITEA_TOKEN"; + + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + if ($method === 'POST') curl_setopt($ch, CURLOPT_POST, true); + if ($method === 'PUT') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); + if ($method === 'PATCH') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH'); + if ($data) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); + + $res = curl_exec($ch); + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + return [$code, json_decode($res, true)]; +} + +function createTeams($org) { + global $GITEA_URL; + $teams = json_decode(file_get_contents('teams.json'), true) ?? []; + + logMsg("🟢 開始執行 createTeams..."); + + // 先抓全部團隊 + [$codeAll, $allTeams] = fetchJSON("$GITEA_URL/orgs/$org/teams", 'GET'); + $allTeams = $allTeams ?? []; + + foreach ($teams as $team) { + $foundTeam = null; + foreach ($allTeams as $t) { + if (strcasecmp($t['name'], $team['name']) === 0) { + $foundTeam = $t; + break; + } + } + + if ($foundTeam) { + logMsg("🔄 團隊已存在,更新權限: {$team['name']}"); + // 更新權限 + fetchJSON("$GITEA_URL/teams/{$foundTeam['id']}", 'PATCH', [ + 'units_map' => $team['units_map'] ?? [], + 'permission' => $team['permission'] ?? 'write' + ]); + + // 補齊成員 + [$codeMembers, $resMembers] = fetchJSON("$GITEA_URL/teams/{$foundTeam['id']}/members", 'GET'); + $existingMembers = array_column($resMembers ?? [], 'username'); + foreach ($team['members'] ?? [] as $member) { + if (!in_array($member, $existingMembers)) { + [$codeAdd, $resAdd] = fetchJSON("$GITEA_URL/teams/{$foundTeam['id']}/members/$member", 'PUT'); + if ($codeAdd === 204) { + logMsg("  ✅ 新增成員: $member"); + } else { + logMsg("  ⚠️ 新增成員失敗: $member → " . json_encode($resAdd, JSON_UNESCAPED_UNICODE)); + } + } + } + + } else { + logMsg("➕ 建立團隊: {$team['name']}"); + [$codeCreate, $resCreate] = fetchJSON("$GITEA_URL/orgs/$org/teams", 'POST', [ + 'name' => $team['name'], + 'units_map' => $team['units_map'] ?? [], + 'permission' => $team['permission'] ?? 'write' + ]); + + if ($codeCreate === 201) { + $teamId = $resCreate['id']; + foreach ($team['members'] ?? [] as $member) { + [$codeAdd, $resAdd] = fetchJSON("$GITEA_URL/teams/$teamId/members/$member", 'PUT'); + if ($codeAdd === 204) { + logMsg("  ✅ 新增成員: $member"); + } else { + logMsg("  ⚠️ 新增成員失敗: $member → " . json_encode($resAdd, JSON_UNESCAPED_UNICODE)); + } + } + } else { + logMsg("⚠️ 建立團隊失敗: {$team['name']} → " . json_encode($resCreate, JSON_UNESCAPED_UNICODE)); + } + } + } + + logMsg("✅ 所有團隊設定完成!"); +} + +function createRepos($org) { + global $GITEA_URL; + + $repos = json_decode(file_get_contents('repos.json'), true) ?? []; + + logMsg("🟢 開始執行 createRepos..."); + + // 先抓全部 repo,避免重複建立 + [$codeAll, $allRepos] = fetchJSON("$GITEA_URL/orgs/$org/repos"); + $allRepos = $allRepos ?? []; + + foreach ($repos as $repo) { + $repoExists = false; + foreach ($allRepos as $r) { + if ($r['name'] === $repo['name']) { + $repoExists = true; + break; + } + } + + if ($repoExists) { + logMsg("⚠️ 儲存庫已存在,略過建立:{$repo['name']}"); + continue; + } + + $payload = [ + 'name' => $repo['name'], + 'description' => $repo['description'] ?? '', + 'default_branch' => $repo['default_branch'] ?? 'master', + 'private' => false, + 'auto_init' => true + ]; + + [$codeCreate, $resCreate] = fetchJSON("$GITEA_URL/orgs/$org/repos", 'POST', $payload); + + if ($codeCreate === 201) { + $repoName = $resCreate['name']; + logMsg("✅ 建立儲存庫:{$repoName}"); + + // 加入團隊 + foreach ($repo['teams'] ?? [] as $teamName) { + [$codeTeams, $teamsList] = fetchJSON("$GITEA_URL/orgs/$org/teams"); + $targetTeam = null; + foreach ($teamsList ?? [] as $t) { + if ($t['name'] === $teamName) { + $targetTeam = $t; + break; + } + } + + if ($targetTeam) { + [$codeAdd, $resAdd] = fetchJSON("$GITEA_URL/teams/{$targetTeam['id']}/repos/$org/$repoName", 'PUT'); + if (in_array($codeAdd, [200,204])) { + logMsg("  👥 已加入團隊:{$teamName}"); + } else { + logMsg("  ⚠️ 加入團隊失敗:{$teamName} → " . json_encode($resAdd, JSON_UNESCAPED_UNICODE)); + } + } else { + logMsg("  ⚠️ 找不到團隊:{$teamName}"); + } + } + + // 建立 Issue + if (!empty($repo['issue'])) { + $issuePayload = [ + 'title' => $repo['issue']['title'] ?? '', + 'body' => $repo['issue']['content'] ?? '' + ]; + [$codeIssue, $resIssue] = fetchJSON("$GITEA_URL/repos/$org/$repoName/issues", 'POST', $issuePayload); + if ($codeIssue === 201) logMsg("  📌 已建立 Issue:{$issuePayload['title']}"); + else logMsg("  ⚠️ 建立 Issue 失敗:{$issuePayload['title']} → " . json_encode($resIssue, JSON_UNESCAPED_UNICODE)); + } + + } else { + logMsg("⚠️ 建立儲存庫失敗:{$repo['name']} → " . json_encode($resCreate, JSON_UNESCAPED_UNICODE)); + } + } + + logMsg("✅ 所有儲存庫處理完成!"); +} + +function setActions($org) { + global $GITEA_URL; + $actions = json_decode(file_get_contents('actions_settings.json'), true) ?? []; + + logMsg("🟢 開始執行 setActions..."); + + $projectSecrets = $actions['project_settings']['secrets'] ?? []; + logMsg("🔧 設定 project"); + foreach($projectSecrets as $key=>$value) { + $finalValue = (is_string($value) && str_starts_with($value,'excel:')) + ? getExcelValue($value,$org) + : $value; + if(trim($finalValue)==='') { logMsg("  ⚠️ 略過空值 Project Secret: $key"); continue; } + + [$code,$res] = fetchJSON("$GITEA_URL/orgs/$org/actions/secrets/$key",'PUT',['data'=>$finalValue]); + if (in_array($code, [200,204,201])) logMsg("  ✅ Project Secret 設定成功: $key"); + else logMsg("  ⚠️ Project Secret 設定失敗: $key → ".json_encode($res,JSON_UNESCAPED_UNICODE)); + } + + $reposArr = $actions['repos'] ?? []; + foreach($reposArr as $repo) { + $repoName = $repo['name']; + logMsg("🔧 設定 repo: $repoName"); + $secrets = $repo['secrets'] ?? []; + foreach($secrets as $key=>$value) { + $finalValue = (is_string($value) && str_starts_with($value,'excel:')) + ? getExcelValue($value,$org) + : $value; + if(trim($finalValue)==='') { logMsg("  ⚠️ 略過空值 Repo Secret: $key"); continue; } + + [$code,$res] = fetchJSON("$GITEA_URL/repos/$org/$repoName/actions/secrets/$key",'PUT',['data'=>$finalValue]); + if (in_array($code, [200,204,201])) logMsg("  ✅ Repo Secret 設定成功: $key"); + else logMsg("  ⚠️ Repo Secret 設定失敗: $key → ".json_encode($res,JSON_UNESCAPED_UNICODE)); + } + } + logMsg("✅ 所有 Actions 設定完成!"); +} + +$org = $_GET['org'] ?? ''; +$action = $_GET['action'] ?? ''; + +if (!$org) { logMsg("❌ 請輸入組織名稱"); exit; } + +switch($action) { + case 'createTeams': createTeams($org); break; + case 'createRepos': createRepos($org); break; + case 'setActions': setActions($org); break; + default: logMsg("⚠️ 未知操作: $action"); break; +} + +logMsg("✅ 任務完成"); diff --git a/style.css b/style.css new file mode 100644 index 0000000..08075fa --- /dev/null +++ b/style.css @@ -0,0 +1,26 @@ +body { + font-family: sans-serif; +} + +#log { + white-space: pre-wrap; + background: #f0f0f0; + padding: 1em; + border-radius: 8px; + height: 600px; + overflow-y: auto; + margin-top: 1em; +} + +label, +input { + font-size: 1rem; +} + +input { + margin-left: 0.5em; +} + +button { + margin-left: 1em; +}