diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..a16912a --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @adeherysh diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..0dd0a78 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,20 @@ +## What does this PR do? +_Place what this pull request changes and anything affected. If your PR block or require another PR, also need to mention here_ + +## Why are we doing this? Any context or related work? +_You may put your link or another here_ + +## Where should a reviewer start? +_optional -- if your changes affected so much files, it is encouraged to give helper for reviewer_ + +## Screenshots +_optional -- You may put the database, sequence or any diagram needed_ + +## Manual testing steps? +_Steps to do tests. including all possible that can hape_ + +## Config changes +_optional -- If there's config changes, put it here_ + +## Deployment instructions +_optional -- Better to put it if there's some 'special case' for deployment_ diff --git a/.github/workflows/release-please.yaml b/.github/workflows/release-please.yaml new file mode 100644 index 0000000..1784e21 --- /dev/null +++ b/.github/workflows/release-please.yaml @@ -0,0 +1,16 @@ +name: Release Please + +on: + push: + branches: + - main + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - name: Release please + uses: googleapis/release-please-action@v4 + with: + release-type: simple + token: ${{ secrets.RELEASE_PLEASE_TOKEN }} diff --git a/.gitignore b/.gitignore index 5d947ca..8a52cd5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ bin-release/ # Other files and folders .settings/ +.DS_Store # Executables *.swf diff --git a/README.md b/README.md index 58606d2..6a64e7f 100644 --- a/README.md +++ b/README.md @@ -1 +1,95 @@ -# cloudflare-pages-action \ No newline at end of file +# Cloudflare Pages Action + +Deploy your project to Cloudflare Pages with automatic project creation and custom domain + +## Usage + +### Deploy preview + +```yaml +on: + pull_request: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + name: Deploy + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build + run: pnpm build + + - name: Deploy + uses: kitabisa/cloudflare-pages-action@v1 + with: + api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + branch: ${{ github.ref }} + production-branch: main + package-manager: pnpm + build-directory: ./out + project-name: your-cloudflare-project + zone-name: example.com + custom-domain: dev-${{ github.event.pull_request.number }}.example.com + working-directory: ./ +``` + +### Deploy production + +```yaml +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + name: Deploy + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build + run: pnpm build + + - name: Deploy + uses: kitabisa/cloudflare-pages-action@v1 + with: + api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + branch: main + production-branch: main + package-manager: pnpm + build-directory: ./out + project-name: your-cloudflare-project + zone-name: example.com + custom-domain: prod.example.com + working-directory: ./ +``` + +## Inputs + +| Name | Description | Default | Examples | +| --------------------- | --------------------------------- | ------------- | ------------------------------------- | +| `api-token` | Define Cloudflare api token. | `undefined` | `sjcbjxbBJKBJKBbkjbjkbkBkbjkBjkbk` | +| `account-id` | Define Cloudflare account id. | `undefined` | `023e105f4ecef8ad9ca31a8372d0c353` | +| `branch` | Branch for current deployment. | `undefined` | `${{ github.ref }}` | +| `production-branch` | Branch for production deployment. | `undefined` | `main` | +| `package-manager` | Setup package manager. | `undefined` | `npm`, `yarn`, `pnpm`, `bun` | +| `build-directory` | Define output build directory. | `undefined` | `./out` | +| `project-name` | Setup project name. | `undefined` | `kitabisa-accounts` | +| `zone-name` | Define Cloudflare zone name. | `""` | `kitabisa.com` | +| `custom-domain` | Setup custom domain. | `""` | `accounts.kitabisa.com` | +| `working-directory` | Setup working directory. | `"."` | `./apps/accounts` | + +## Outputs + +| Name | Description | Example | +| ----------------- | --------------------------------------------------------- | ----------------------------- | +| `deployment-url` | The output deployment url from custom domain (if set). | `accounts.kitabisa.com` | +| `pages-url` | The output deployment url from cloudflare pages. | `kitabisa-accounts.pages.dev` | diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..18fd650 --- /dev/null +++ b/action.yml @@ -0,0 +1,178 @@ +name: Cloudflare Pages Action +description: Deploy your project to Cloudflare Pages with automatic project creation and custom domain +author: Ade Hery Shopyan + +branding: + icon: 'upload-cloud' + color: 'blue' + +inputs: + api-token: + required: true + description: "Set api token" + type: string + + account-id: + required: true + description: "Set account id" + type: string + + branch: + required: true + description: "Set branch" + type: string + + production-branch: + required: true + description: "Set production branch" + type: string + + build-directory: + required: true + description: "Set build directory" + type: string + + package-manager: + required: true + description: "Set package manager" + type: string + + project-name: + required: true + description: "Set project name" + type: string + + zone-name: + required: false + description: "Set zone name" + default: "" + type: string + + custom-domain: + required: false + description: "Set custom domain" + default: "" + type: string + + working-directory: + required: false + description: "Set working directory" + default: "." + type: string + +outputs: + deployment-url: + description: "Define deployment url" + value: ${{ steps.url.outputs.deployment-url }} + + pages-url: + description: "Define pages url" + value: ${{ steps.url.outputs.pages-url }} + +runs: + using: composite + steps: + - name: Deploy + id: deploy + uses: cloudflare/wrangler-action@v3 + with: + workingDirectory: ${{ inputs.working-directory }} + apiToken: ${{ inputs.api-token }} + accountId: ${{ inputs.account-id }} + preCommands: wrangler pages project create ${{ inputs.project-name }} --production-branch=${{ inputs.production-branch }} || true + command: pages deploy ${{ inputs.build-directory }} --project-name=${{ inputs.project-name }} --branch=${{ inputs.branch }} --commit-dirty=true + packageManager: ${{ inputs.package-manager }} + + - name: Get Cloudflare Zone ID + id: get-zone-id + if: "${{ inputs.zone-name != '' }}" + shell: bash + env: + CLOUDFLARE_API_TOKEN: ${{ inputs.api-token }} + ZONE_DOMAIN: ${{ inputs.zone-name }} + run: | + response=$(curl -X GET "https://api.cloudflare.com/client/v4/zones?name=$ZONE_DOMAIN" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json") + zone_id=$(echo $response | jq -r '.result[0].id') + echo "id=$zone_id" >> $GITHUB_OUTPUT + + - name: Set Cloudflare DNS Record & Custom Domain + id: dnsrecord + if: "${{ steps.get-zone-id.outputs.id != 'null' && inputs.zone-name != '' && inputs.custom-domain != '' }}" + shell: bash + env: + CLOUDFLARE_API_TOKEN: ${{ inputs.api-token }} + CLOUDFLARE_ACCOUNT_ID: ${{ inputs.account-id }} + CLOUDFLARE_ZONE_ID: ${{ steps.get-zone-id.outputs.id }} + PROJECT_NAME: ${{ inputs.project-name }} + CUSTOM_DOMAIN: ${{ inputs.custom-domain }} + run: | + echo "Check DNS record exist or not for the domain: ${CUSTOM_DOMAIN}" + res_record=$(curl -X GET "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records?type=CNAME&name=$CUSTOM_DOMAIN" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json") + dns_record_id=$(echo $res_record | jq -r '.result[0].id') + + if [ "$dns_record_id" == "null" ] || [ -z "$dns_record_id" ]; then + echo "DNS record not found for the domain: ${CUSTOM_DOMAIN}" + response=$(curl -X POST "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + --data '{ + "type": "CNAME", + "name": "'"$CUSTOM_DOMAIN"'", + "content": "'"$PROJECT_NAME"'.pages.dev", + "ttl": 1, + "proxied": true + }') + id=$(echo $response | jq -r '.result.id') + echo "id=$id" >> $GITHUB_OUTPUT + + echo "Set Cloudflare Pages Custom Domain for: ${CUSTOM_DOMAIN}" + curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/pages/projects/$PROJECT_NAME/domains" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + --data '{"name": "'$CUSTOM_DOMAIN'"}' + else + echo "DNS record found for the domain: ${CUSTOM_DOMAIN}" + echo "id=$dns_record_id" >> $GITHUB_OUTPUT + fi + + - name: Update Cloudflare DNS Record + if: "${{ steps.get-zone-id.outputs.id != 'null' && inputs.zone-name != '' && inputs.custom-domain != '' && inputs.branch != inputs.production-branch }}" + shell: bash + env: + CLOUDFLARE_API_TOKEN: ${{ inputs.api-token }} + CLOUDFLARE_ZONE_ID: ${{ steps.get-zone-id.outputs.id }} + CUSTOM_DOMAIN: ${{ inputs.custom-domain }} + DNS_RECORD_ID: ${{ steps.dnsrecord.outputs.id }} + PAGES_URL: ${{ steps.deploy.outputs.deployment-url }} + run: | + DNS_RECORD_CONTENT=$(echo "$PAGES_URL" | sed 's/^https:\/\///') + curl -X PATCH "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records/$DNS_RECORD_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + --data '{ + "type": "CNAME", + "name": "'"$CUSTOM_DOMAIN"'", + "content": "'"$DNS_RECORD_CONTENT"'", + "id": "'"$DNS_RECORD_ID"'", + "ttl": 1, + "proxied": true + }' + + - name: Get deployment url + id: url + shell: bash + env: + DEPLOYMENT_URL: ${{ inputs.custom-domain }} + PAGES_URL: ${{ steps.deploy.outputs.deployment-url }} + run: | + if [ "$DEPLOYMENT_URL" != "" ]; then + echo "deployment-url=https://$DEPLOYMENT_URL" >> $GITHUB_OUTPUT + echo "pages-url=$PAGES_URL" >> $GITHUB_OUTPUT + else + echo "deployment-url=$PAGES_URL" >> $GITHUB_OUTPUT + echo "pages-url=$PAGES_URL" >> $GITHUB_OUTPUT + fi \ No newline at end of file