diff --git a/.github/workflows/desync.yml b/.github/workflows/desync.yml new file mode 100644 index 0000000..bdf4f7a --- /dev/null +++ b/.github/workflows/desync.yml @@ -0,0 +1,86 @@ +name: De-sync +on: + pull_request: + branches: + - main + types: + - closed + +concurrency: codegen-sync-${{ github.event.number }} + +env: + TECO_BRANCH: codegen-pr-${{ github.event.number }} + +jobs: + create-pr: + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.merged }} + strategy: + matrix: + repo: + - teco + - teco-core + steps: + - name: TODO + run: echo "Hello world!" > /dev/null + delete-branch: + runs-on: ubuntu-latest + if: ${{ !github.event.pull_request.merged }} + strategy: + matrix: + repo: + - teco + - teco-core + steps: + - name: Generate app token for bot + uses: tibdex/github-app-token@v1 + id: generate-token + with: + app_id: ${{ secrets.BOT_APP_ID }} + private_key: ${{ secrets.BOT_APP_PRIVATE_KEY }} + - name: Delete branch protection rule + uses: actions/github-script@v6 + env: + TECO_REPO: ${{ matrix.repo }} + with: + github-token: ${{ steps.generate-token.outputs.token }} + script: | + github.rest.repos.deleteBranchProtection({ + owner: context.repo.owner, + repo: process.env.TECO_REPO, + branch: process.env.TECO_BRANCH + }); + - name: Delete sync branch + uses: actions/github-script@v6 + env: + TECO_REPO: ${{ matrix.repo }} + with: + github-token: ${{ steps.generate-token.outputs.token }} + script: | + github.rest.git.deleteRef({ + owner: context.repo.owner, + repo: process.env.TECO_REPO, + ref: `heads/${process.env.TECO_BRANCH}` + }); + delete-label: + runs-on: ubuntu-latest + needs: + - delete-branch + steps: + - name: Generate app token for bot + uses: tibdex/github-app-token@v1 + id: generate-token + with: + app_id: ${{ secrets.BOT_APP_ID }} + private_key: ${{ secrets.BOT_APP_PRIVATE_KEY }} + - name: Delete "in sync" label + uses: actions/github-script@v6 + with: + github-token: ${{ steps.generate-token.outputs.token }} + script: | + github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + name: 'in sync' + }); diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 0000000..e0d7653 --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,254 @@ +name: Sync +on: + pull_request: + branches: + - main + +concurrency: codegen-sync-${{ github.event.number }} + +env: + TECO_BRANCH: codegen-pr-${{ github.event.number }} + +jobs: + create-branch: + runs-on: ubuntu-latest + if: ${{ github.event.action == 'opened' || github.event.action == 'reopened' }} + strategy: + matrix: + repo: + - teco + - teco-core + steps: + - name: Generate app token for bot + uses: tibdex/github-app-token@v1 + id: generate-token + with: + app_id: ${{ secrets.BOT_APP_ID }} + private_key: ${{ secrets.BOT_APP_PRIVATE_KEY }} + - name: Check if PR author is collaborator + uses: actions/github-script@v6 + id: check-collaborator + env: + TECO_REPO: ${{ matrix.repo }} + with: + github-token: ${{ steps.generate-token.outputs.token }} + script: | + try { + await github.rest.repos.checkCollaborator({ + owner: context.repo.owner, + repo: process.env.TECO_REPO, + username: context.payload.pull_request.user.login + }); + return true; + } catch { + const title = 'Pull request author is not collaborator'; + const message = 'The author does not have push access to the sync branch at the moment. If the generated codes need patching, please contact organization owners.'; + console.log(`::warning title=${title}::${message}`); + return false; + } + - name: Create sync branch + uses: actions/github-script@v6 + env: + TECO_REPO: ${{ matrix.repo }} + with: + github-token: ${{ steps.generate-token.outputs.token }} + script: | + const base_ref = context.payload.pull_request.base.ref; + const owner = context.repo.owner; + const repo = process.env.TECO_REPO; + const ref = `refs/heads/${process.env.TECO_BRANCH}`; + const { data: branches } = await github.rest.repos.listBranches({ owner, repo }); + var sha = branches.find((branch) => branch.name === 'main').commit.sha; + if (branches.map((branch) => branch.name).includes(base_ref)) { + sha = branches.find((branch) => branch.name === base_ref).commit.sha; + } else { + const title = 'Base branch not found'; + const message = `Base branch "${base_ref}" doesn't exist in repository ${owner}/${repo}. Available branches: ${JSON.stringify(branches.map((branch) => branch.name))}`; + console.log(`::warning title=${title}::${message}`); + } + github.rest.git.createRef({ owner, repo, ref, sha }); + - name: Create branch protection rule + uses: actions/github-script@v6 + env: + TECO_REPO: ${{ matrix.repo }} + with: + github-token: ${{ steps.generate-token.outputs.token }} + script: | + github.rest.repos.updateBranchProtection({ + owner: context.repo.owner, + repo: process.env.TECO_REPO, + branch: process.env.TECO_BRANCH, + required_status_checks: null, + enforce_admins: null, + required_pull_request_reviews: null, + restrictions: { + users: [context.payload.pull_request.user.login], + teams: [], + apps: ['teco-automation'] + }, + required_linear_history: true, + allow_force_pushes: true, + allow_deletions: true + }); + create-label: + runs-on: ubuntu-latest + needs: + - create-branch + steps: + - name: Generate app token for bot + uses: tibdex/github-app-token@v1 + id: generate-token + with: + app_id: ${{ secrets.BOT_APP_ID }} + private_key: ${{ secrets.BOT_APP_PRIVATE_KEY }} + - name: Add "in sync" label + uses: actions/github-script@v6 + with: + github-token: ${{ steps.generate-token.outputs.token }} + script: | + github.rest.issues.addLabels({ + issue_number: context.payload.pull_request.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['in sync'] + }); + generate-and-commit: + runs-on: ubuntu-latest + if: ${{ success() || needs.create-branch.result == 'skipped' }} + needs: + - create-branch + - create-label + steps: + - name: Generate app token for bot + uses: tibdex/github-app-token@v1 + id: generate-token + with: + app_id: ${{ secrets.BOT_APP_ID }} + private_key: ${{ secrets.BOT_APP_PRIVATE_KEY }} + - name: Import bot's GPG key for signing commits + id: import-gpg + uses: crazy-max/ghaction-import-gpg@v5 + with: + gpg_private_key: ${{ secrets.BOT_GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.BOT_GPG_PASSPHRASE }} + git_config_global: true + git_user_signingkey: true + git_commit_gpgsign: true + - name: Install dependency + run: sudo apt-get install git-filter-repo -y + - name: Checkout + uses: actions/checkout@v3 + - name: Detect Swift package information + id: swift-package + run: | + echo "BUILD_PATH=$(swift build --show-bin-path -c release)" >> $GITHUB_ENV + echo 'product-paths<<$EOF' >> $GITHUB_OUTPUT + swift package describe --type json \ + | jq '.products[].name' -r \ + | sed "s#^#$(swift build --show-bin-path -c release)/#" \ + >> $GITHUB_OUTPUT + echo '$EOF' >> $GITHUB_OUTPUT + - name: Apply cache + uses: actions/cache@v3 + id: build-cache + with: + path: ${{ steps.swift-package.outputs.product-paths }} + key: ${{ runner.os }}-${{ hashFiles('Package.*', 'Sources/**') }} + - name: Build + if: ${{ !steps.build-cache.outputs.cache-hit }} + run: swift build -c release + - name: Checkout API models + uses: actions/checkout@v3 + with: + repository: ${{ github.repository_owner }}/teco-api-models + path: ./models + ref: refs/tags/current + - name: Checkout teco-core + uses: actions/checkout@v3 + with: + repository: ${{ github.repository_owner }}/teco-core + ref: ${{ env.TECO_BRANCH }} + path: ./teco-core + token: ${{ steps.generate-token.outputs.token }} + fetch-depth: 0 + - name: Drop bot commits in teco-core + env: + BOT_COMMITTER_EMAIL: ${{ steps.import-gpg.outputs.email }} + working-directory: ./teco-core + run: | + git filter-repo --refs HEAD --commit-callback ' + import subprocess; import os + p = subprocess.run(["git", "merge-base", "--is-ancestor", commit.original_id, "origin/" + os.environ["GITHUB_BASE_REF"]]) + if p.returncode != 0 and commit.committer_email.decode() == os.environ["BOT_COMMITTER_EMAIL"]: + commit.skip() + ' + git rebase "origin/$GITHUB_BASE_REF" --quiet + - name: Generate teco-core + run: | + ${{ env.BUILD_PATH }}/teco-common-error-generator \ + --error-file ./models/zh-CN/error-codes.json \ + --output ./teco-core/Sources/TecoCore/Errors/TCCommonError.swift + ${{ env.BUILD_PATH }}/teco-date-wrapper-generator \ + --output-dir './teco-core/Sources/TecoDateHelpers/Property Wrappers' + ${{ env.BUILD_PATH }}/teco-region-generator \ + --output ./teco-core/Sources/TecoCore/Common/TCRegion.swift + - name: Checkout teco + uses: actions/checkout@v3 + with: + repository: ${{ github.repository_owner }}/teco + ref: ${{ env.TECO_BRANCH }} + path: ./teco + token: ${{ steps.generate-token.outputs.token }} + fetch-depth: 0 + - name: Drop bot commits in teco + env: + BOT_COMMITTER_EMAIL: ${{ steps.import-gpg.outputs.email }} + working-directory: ./teco + run: | + git filter-repo --refs HEAD --commit-callback ' + import subprocess; import os + p = subprocess.run(["git", "merge-base", "--is-ancestor", commit.original_id, "origin/" + os.environ["GITHUB_BASE_REF"]]) + if p.returncode != 0 and commit.committer_email.decode() == os.environ["BOT_COMMITTER_EMAIL"]: + commit.skip() + ' + git rebase "origin/$GITHUB_BASE_REF" --quiet + - name: Generate teco + run: | + ${{ env.BUILD_PATH }}/teco-package-generator \ + --model-dir ./models/zh-CN \ + --service-generator ${{ env.BUILD_PATH }}/teco-service-generator \ + --package-dir ./teco + if ! git diff --exit-code Package.swift + then + swift package resolve + fi + - name: Get pull request author information + uses: actions/github-script@v6 + id: author-info + with: + script: | + const { data: user } = await github.rest.users.getByUsername({ + username: context.payload.pull_request.user.login + }); + core.setOutput('name', user.name); + core.setOutput('email', user.email); + - name: Commit and push to teco-core + uses: EndBug/add-and-commit@v9 + with: + cwd: ./teco-core + author_name: ${{ steps.author-info.outputs.name }} + author_email: ${{ steps.author-info.outputs.email }} + committer_name: ${{ steps.import-gpg.outputs.name }} + committer_email: ${{ steps.import-gpg.outputs.email }} + message: ${{ github.event.pull_request.title }} + push: --force + - name: Commit and push to teco + uses: EndBug/add-and-commit@v9 + with: + cwd: ./teco + author_name: ${{ steps.author-info.outputs.name }} + author_email: ${{ steps.author-info.outputs.email }} + committer_name: ${{ steps.import-gpg.outputs.name }} + committer_email: ${{ steps.import-gpg.outputs.email }} + message: ${{ github.event.pull_request.title }} + push: --force diff --git a/Sources/TecoCodeGeneratorCommons/Utils.swift b/Sources/TecoCodeGeneratorCommons/Utils.swift index aca1e36..7ad97e7 100644 --- a/Sources/TecoCodeGeneratorCommons/Utils.swift +++ b/Sources/TecoCodeGeneratorCommons/Utils.swift @@ -76,6 +76,8 @@ extension SourceFileSyntax { // THIS FILE IS AUTOMATICALLY GENERATED by \(GeneratorContext.generator). // DO NOT EDIT. + // Test PR teco-project/teco-code-generator#23. + """ return self.with(\.leadingTrivia, .lineComment(header) + (self.leadingTrivia ?? []))