From 817617e0489d98a5cd0c701a97a699ed8e6ea300 Mon Sep 17 00:00:00 2001 From: huyhuynh3103 Date: Tue, 16 Apr 2024 10:08:06 +0700 Subject: [PATCH] chore: add ci script --- .DS_Store | Bin 0 -> 6148 bytes .github/template/create-pull-request.md | 2 + .../workflows/create-PR-deploy-to-release.yml | 64 +++++++++++++ .../create-PR-implement-to-feature.yml | 50 +++++++++++ .../create-PR-release-to-feature.yml | 84 ++++++++++++++++++ .../create-PR-release-to-network.yml | 47 ++++++++++ .github/workflows/create-release-tag.yml | 64 +++++++++++++ .github/workflows/test.yml | 54 +++++++++++ .husky/generate-layout.sh | 16 ++++ .husky/pre-commit | 10 +-- .husky/pre-push | 26 ++++++ .husky/storage-logger.js | 60 +++++++++++++ 12 files changed, 471 insertions(+), 6 deletions(-) create mode 100644 .DS_Store create mode 100644 .github/template/create-pull-request.md create mode 100644 .github/workflows/create-PR-deploy-to-release.yml create mode 100644 .github/workflows/create-PR-implement-to-feature.yml create mode 100644 .github/workflows/create-PR-release-to-feature.yml create mode 100644 .github/workflows/create-PR-release-to-network.yml create mode 100644 .github/workflows/create-release-tag.yml create mode 100644 .github/workflows/test.yml create mode 100755 .husky/generate-layout.sh create mode 100755 .husky/pre-push create mode 100644 .husky/storage-logger.js diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8a7670fb140235cdc57659293e306986a1f3e06a GIT binary patch literal 6148 zcmeHKI|@QE5ZqM}!N$@uClI_r5Iunx5Va5#EX2a@Ri4YEIr}M;r=1qcENmv(>?CB0 zw{Jy6XXouyWF#U3xS?EaXq)Yux2%&91;TO0UM}0y=CD2<53TIi0ps>%B0D+D>5*?6 zG%7#^r~nn90#xAR3S@~L3_pG{52ONA;MWzf??Ztb*2E^zKOGpn1pxLCcEj9z31G1R zuqHNvh`=> $GITHUB_ENV + echo "VERSION=$(echo -n ${{ env.HEAD_BRANCH }} | sed 's/.*deploy\///' | cut -d'-' -f1)" >> $GITHUB_ENV + echo "NETWORK=$(echo -n ${{ env.HEAD_BRANCH }} | sed 's/.*deploy\/v[0-9\.]*-\(.*\)/\1/')" >> $GITHUB_ENV + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: 'release/${{ env.VERSION }}' + fetch-depth: 0 + fetch-tags: 'true' + + - name: Get Testnet Latest Tag + if: ${{ env.NETWORK == 'testnet' }} + run: | + echo "LATESTTAG=$(git describe --tags --match "*testnet*" --abbrev=0)" >> $GITHUB_ENV + + - name: Get Mainnet Latest Tag + if: ${{ env.NETWORK == 'mainnet' }} + run: | + echo "LATESTTAG=$(git describe --tags --match "*mainnet*" --abbrev=0)" >> $GITHUB_ENV + + - name: Reset promotion branch + run: | + git fetch origin ${HEAD_BRANCH}:${HEAD_BRANCH} + git reset --hard ${HEAD_BRANCH} + + - name: Generate Release note + id: template + run: | + echo "VERSION=${{ env.VERSION }} - SHA: ${{ github.sha }}" > CHANGELOG.md + + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v6.0.1 + with: + labels: automated PR + delete-branch: true + title: 'chore(`release/${{ env.VERSION }}`): merge from `${{ env.HEAD_BRANCH}}`' + body: ${{ steps.template.outputs.result }} + branch: ${{ env.PR_BRANCH }} \ No newline at end of file diff --git a/.github/workflows/create-PR-implement-to-feature.yml b/.github/workflows/create-PR-implement-to-feature.yml new file mode 100644 index 0000000..85cbbd6 --- /dev/null +++ b/.github/workflows/create-PR-implement-to-feature.yml @@ -0,0 +1,50 @@ +name: Create Pull Request From Implement To Feature +on: + push: + branches: + - 'implement-feature/**' + - 'implement-feature/**/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +env: + HEAD_BRANCH: ${{ github.head_ref || github.ref_name }} + +jobs: + createPullRequest: + runs-on: ubuntu-latest + steps: + - name: Set env + run: | + echo "FEATURE_NAME=$(echo ${HEAD_BRANCH} | cut -d'/' -f2)" >> $GITHUB_ENV + echo "FEATURE_BRANCH=feature/$(echo ${HEAD_BRANCH} | cut -d'/' -f2)" >> $GITHUB_ENV + echo "IMPLEMENT_NAME=$(echo ${HEAD_BRANCH} | cut -d'/' -f3)" >> $GITHUB_ENV + + - uses: actions/checkout@v3 + with: + ref: ${{env.FEATURE_BRANCH}} + + - name: Reset promotion branch + run: | + git fetch origin ${HEAD_BRANCH}:${HEAD_BRANCH} + git reset --hard ${HEAD_BRANCH} + + - name: Render template + id: template + uses: chuhlomin/render-template@v1.4 + with: + template: .github/template/create-pull-request.md + vars: | + fromBranch: ${{env.HEAD_BRANCH}} + toBranch: ${{ env.FEATURE_BRANCH }} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v6.0.1 + with: + branch: ${{ env.HEAD_BRANCH }} + base: ${{env.FEATURE_BRANCH}} + labels: automated PR + title: 'feat(${{env.FEATURE_NAME}}): implement `${{env.IMPLEMENT_NAME}}`' + body: ${{ steps.template.outputs.result }} \ No newline at end of file diff --git a/.github/workflows/create-PR-release-to-feature.yml b/.github/workflows/create-PR-release-to-feature.yml new file mode 100644 index 0000000..b9dec20 --- /dev/null +++ b/.github/workflows/create-PR-release-to-feature.yml @@ -0,0 +1,84 @@ +name: Create Pull Request From Release to Feature +on: + push: + branches: + - 'release/*' + - 'release*/*' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +env: + HEAD_BRANCH: ${{ github.head_ref || github.ref_name }} + +jobs: + fetchAllFeatureBranches: + runs-on: ubuntu-latest + + steps: + - id: step1 + name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - id: step2 + name: List all the remote feature branches + run: | + branches=$(git branch -r | grep -E '.*origin\/feature\/.*' | sed -e "s/.*origin\///" | tr "\n" " ") + JSON="[" + for branch in ${branches[@]}; do + echo $branch + JSONline="\"$branch\"," + # we don't need to iterate on the same branch over and over, so + # onnly include it when it wasn't included + if [[ "$JSON" != *"$JSONline"* ]]; then + JSON="$JSON$JSONline" + fi + done + # Remove last "," and add the closing bracket + if [[ $JSON == *, ]]; then + JSON="${JSON%?}" + fi + JSON="$JSON]" + echo $JSON + echo "BRANCHES={\"branch_name\": $( echo "$JSON" )}" >> "$GITHUB_OUTPUT" + outputs: + BRANCHES: ${{ steps.step2.outputs.BRANCHES }} + + mergeRelease2FeatureRepo: + runs-on: ubuntu-latest + needs: fetchAllFeatureBranches + strategy: + matrix: ${{ fromJSON(needs.fetchAllFeatureBranches.outputs.BRANCHES) }} + steps: + - name: Set env + run: | + echo "PR_BRANCH=merge/${HEAD_BRANCH}-${{matrix.branch_name}}" >> $GITHUB_ENV + echo "FEATURE_NAME=$(echo ${{matrix.branch_name}} | cut -d'/' -f2)" >> $GITHUB_ENV + - uses: actions/checkout@v3 + with: + ref: ${{matrix.branch_name}} + - name: Reset promotion branch + run: | + git fetch origin ${HEAD_BRANCH}:${HEAD_BRANCH} + git reset --hard ${HEAD_BRANCH} + + - name: Render template + id: template + uses: chuhlomin/render-template@v1.4 + with: + template: .github/template/create-pull-request.md + vars: | + fromBranch: ${{env.HEAD_BRANCH}} + toBranch: ${{matrix.branch_name}} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v6.0.1 + with: + labels: automated PR + delete-branch: true + title: 'chore(`${{env.FEATURE_NAME}}`): merge from `${{env.HEAD_BRANCH}}`' + body: ${{ steps.template.outputs.result }} + branch: ${{env.PR_BRANCH}} diff --git a/.github/workflows/create-PR-release-to-network.yml b/.github/workflows/create-PR-release-to-network.yml new file mode 100644 index 0000000..e291a52 --- /dev/null +++ b/.github/workflows/create-PR-release-to-network.yml @@ -0,0 +1,47 @@ +name: Create PR from release to network + +on: + pull_request: + branches: + - release/* + types: + - closed + +permissions: + contents: write + pull-requests: write + +env: + HEAD_BRANCH: ${{ github.head_ref || github.ref_name }} + RELEASE_BRANCH: ${{ github.event.pull_request.base.ref}} + +jobs: + merge-release-to-network: + runs-on: ubuntu-latest + if: ${{ (github.event.pull_request.merged == true) && (contains(github.head_ref, 'deploy') || contains(github.ref_name, 'deploy')) }} + steps: + - name: Set Env + run: | + echo "PR_BRANCH=merge/${HEAD_BRANCH}" >> $GITHUB_ENV + echo "VERSION=$(echo -n ${{ env.HEAD_BRANCH }} | sed 's/.*deploy\///' | cut -d'-' -f1)" >> $GITHUB_ENV + echo "NETWORK=$(echo -n ${{ env.HEAD_BRANCH }} | sed 's/.*deploy\/v[0-9\.]*-\(.*\)/\1/')" >> $GITHUB_ENV + + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ env.NETWORK }} + + - name: Reset promotion branch + run: | + git fetch origin ${RELEASE_BRANCH}:${RELEASE_BRANCH} + git reset --hard ${RELEASE_BRANCH} + + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v6.0.1 + with: + labels: automated PR + delete-branch: true + title: 'chore(`${{ env.NETWORK }}`): merge from `${{ env.HEAD_BRANCH}}`' + body: ${{ steps.template.outputs.result }} + branch: ${{env.PR_BRANCH}} diff --git a/.github/workflows/create-release-tag.yml b/.github/workflows/create-release-tag.yml new file mode 100644 index 0000000..d720027 --- /dev/null +++ b/.github/workflows/create-release-tag.yml @@ -0,0 +1,64 @@ +name: Create Release Tag + +on: + pull_request: + types: [closed] + branches: + - mainnet + - testnet + +env: + HEAD_BRANCH: ${{ github.head_ref || github.ref_name }} + +permissions: + contents: write + pull-requests: write + +jobs: + create-release-tag: + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true + steps: + - name: Set Env + run: | + echo "VERSION=$(echo -n ${{ env.HEAD_BRANCH }} | sed 's/.*deploy\///' | cut -d'-' -f1)" >> $GITHUB_ENV + echo "NETWORK=$(echo -n ${{ env.HEAD_BRANCH }} | sed 's/.*deploy\/v[0-9\.]*-\(.*\)/\1/')" >> $GITHUB_ENV + + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ env.NETWORK }} + fetch-depth: 0 + fetch-tags: 'true' + + - name: Set Mainnet Tag + if: ${{ env.NETWORK == 'mainnet' }} + run: | + echo "TAG=${{ env.VERSION }}" >> $GITHUB_ENV + + - name: Set Testnet Tag + if: ${{ env.NETWORK == 'testnet' }} + run: | + echo "TAG=${{ env.VERSION }}-testnet" >> $GITHUB_ENV + + - name: Get Testnet Latest Tag + if: ${{ env.NETWORK == 'testnet' }} + run: | + echo "LATESTTAG=$(git describe --tags --match "*testnet*" --abbrev=0)" >> $GITHUB_ENV + + - name: Get Mainnet Latest Tag + if: ${{ env.NETWORK == 'mainnet' }} + run: | + echo "LATESTTAG=$(git describe --tags --match "*mainnet*" --abbrev=0)" >> $GITHUB_ENV + + - name: Create release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ env.TAG }} + run: | + gh release create "$tag" \ + --repo "$GITHUB_REPOSITORY" \ + --title "${{ env.NETWORK }} release ${{ env.VERSION }}" \ + --target "${{ env.NETWORK }}" \ + --notes-start-tag "${{ env.LATESTTAG }}" \ + --generate-notes diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..876b75f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,54 @@ +name: test + +on: + push: + branches: + - mainnet + - testnet + - 'feature/*' + - 'features/*' + pull_request: + branches: + - mainnet + - testnet + - 'feature/*' + - 'features/*' + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: [self-hosted, dockerize] + steps: + - id: 'gh-app' + name: 'Get Token' + uses: 'tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a' #v1.7.0 + with: + app_id: ${{ secrets.GH_APP_ID }} + private_key: ${{ secrets.GH_PRIVATE_KEY }} + + - uses: actions/checkout@v4.1.1 + with: + submodules: recursive + token: ${{ steps.gh-app.outputs.token }} + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run Forge build + run: | + forge --version + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/.husky/generate-layout.sh b/.husky/generate-layout.sh new file mode 100755 index 0000000..0e66916 --- /dev/null +++ b/.husky/generate-layout.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +rm -rf logs/storage/* +dirOutputs=$(ls out | grep '^[^.]*\.sol$') # assuming the out dir is at 'out' +while IFS= read -r contractDir; do + innerdirOutputs=$(ls out/"$contractDir") + + while IFS= read -r jsonFile; do + fileIn=out/"$contractDir"/$jsonFile + fileOut=logs/storage/"$contractDir":${jsonFile%.json}.log + node .husky/storage-logger.js "$fileIn" "$fileOut" & + done <<< "$innerdirOutputs" +done <<< "$dirOutputs" + +# Wait for all background jobs to finish +wait \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index a77b1e6..259dc79 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,7 +1,5 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" -set -ex - -yarn lint-staged -yarn compile +forge build +npx lint-staged diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 0000000..6397927 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,26 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +set -ex + +# Workaround: git stash no changes doesn't cause failure but git stash pop cause +output=$(git stash) +stashed=true +if [[ $output == *"No local changes to save"* ]]; then + stashed=false +fi + +forge build --sizes 2>&1 | sed -n '/Contract/,$p' > logs/contract-code-sizes.log +.husky/generate-layout.sh + +git add logs + +output=$(git status -s) +line_count=$(echo "$output" | wc -l) +if [ "$line_count" -gt 1 ]; then + git commit -m "chore: storage layout" +fi + +if $stashed; then + git stash pop +fi \ No newline at end of file diff --git a/.husky/storage-logger.js b/.husky/storage-logger.js new file mode 100644 index 0000000..0ff423c --- /dev/null +++ b/.husky/storage-logger.js @@ -0,0 +1,60 @@ +const fs = require('fs'); +const fileIn = process.argv[2]; +const fileOut = process.argv[3]; + +if (!fileIn) { + console.error('Invalid input'); +} + +fs.readFile(fileIn, 'utf8', (err, data) => { + if (err) { + console.error('Error reading file:', err); + return; + } + + try { + const jsonData = JSON.parse(data); + + if (typeof jsonData.storageLayout == 'undefined') return; + if (jsonData.storageLayout.storage.length == 0) return; + + const hasAst = typeof jsonData.ast != 'undefined'; + + let storageLayout; + if (hasAst) { + const absolutePath = jsonData.ast.absolutePath; + if (typeof absolutePath == 'undefined') return; + if (!absolutePath.startsWith('src')) return; + storageLayout = jsonData.storageLayout.storage; + } else { + // filter only contracts in src/* + storageLayout = jsonData.storageLayout.storage.filter(({ contract }) => + contract.startsWith('src') + ); + } + + const outputData = storageLayout + .map(({ contract, label, offset, slot, type: typeId }) => { + const typeObj = jsonData.storageLayout.types[typeId]; + const typeLabel = typeObj.label; + const numberOfBytes = typeObj.numberOfBytes; + return `${contract}:${label} (storage_slot: ${slot}) (offset: ${offset}) (type: ${typeLabel}) (numberOfBytes: ${numberOfBytes})`; + }) + .join('\n'); + + if (outputData == '') return; + + if (!fileOut) { + console.log(outputData); + } else { + fs.writeFile(fileOut, outputData, 'utf-8', (err) => { + if (err) { + console.error('Error writing file:', err); + return; + } + }); + } + } catch (err) { + console.error('Error parsing JSON:', err); + } +});