Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Add support for providing custom hash to the cursor deploy #248

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ on:
required: true
description: "Name of the S3 registry bucket"
type: string
custom_hash:
required: false
description: "Custom hash used to cache the action on successful build"
type: string
build_dir:
required: true
description: "Location of the deploy bundle after running the build command"
Expand Down Expand Up @@ -63,11 +67,12 @@ jobs:
- uses: actions/[email protected]

- name: Check S3 Cache
uses: pleo-io/s3-cache-action@v3.0.0
uses: pleo-io/s3-cache-action@v3.1.0
id: s3-cache
with:
bucket-name: ${{ inputs.bucket_name }}
key-prefix: build/${{ inputs.app_name }}
custom-hash: ${{ inputs.custom_hash }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_FRONTEND_REGISTRY }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_FRONTEND_REGISTRY }}
aws-region: eu-west-1
Expand Down
66 changes: 46 additions & 20 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ on:
default: "dist"
description: "Directory where the bundle should be unpacked"
type: string
tree_hash:
deploy_hash:
required: true
description: "Tree hash of the code to deploy"
type: string
Expand Down Expand Up @@ -65,6 +65,25 @@ jobs:
registry-url: "https://npm.pkg.github.com"
scope: ${{ inputs.registry_scope }}

- name: Setup AWS Credentials for Origin Bucket Access
uses: aws-actions/[email protected]
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: eu-west-1

- name: Check version already deployed
id: is-version-already-deployed
run: |
DEPLOY_EXISTS=$(aws s3 ls s3://${{ inputs.bucket_name }}/html/${{ inputs.deploy_hash }}/ || true)
if [ -z "$DEPLOY_EXISTS" ]; then
echo "Version ${{ inputs.deploy_hash }} not yet deployed"
echo "is_deployed=false" >> $GITHUB_OUTPUT
else
echo "Version ${{ inputs.deploy_hash }} already deployed"
echo "is_deployed=true" >> $GITHUB_OUTPUT
fi

# For feature preview deployments we're using the permalink as the deploy URL for the GitHub deployment
# For deployments on the default branch we're using the domain name passed via inputs.
- name: Get Deployment URL
Expand All @@ -75,17 +94,19 @@ jobs:
echo "Could not determine default branch"
exit 1
fi
SUBDOMAIN=$([[ $GITHUB_REF = "refs/heads/$DEFAULT_BRANCH" || $GITHUB_REF = "refs/heads/change-freeze-emergency-deploy" ]] && echo "" || echo "preview-${{ inputs.tree_hash }}.")
SUBDOMAIN=$([[ $GITHUB_REF = "refs/heads/$DEFAULT_BRANCH" || $GITHUB_REF = "refs/heads/change-freeze-emergency-deploy" ]] && echo "" || echo "preview-${{ inputs.deploy_hash }}.")
echo "url=https://${SUBDOMAIN}${{ inputs.domain_name }}" >> $GITHUB_OUTPUT

- name: Setup AWS Credentials for Registry Bucket Access
uses: aws-actions/[email protected]
if: ${{ steps.is-version-already-deployed.outputs.is_deployed == 'false'}}
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_FRONTEND_REGISTRY }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_FRONTEND_REGISTRY }}
aws-region: eu-west-1

- name: Download & Unpack Bundle
if: ${{ steps.is-version-already-deployed.outputs.is_deployed == 'false'}}
run: |
aws s3 cp ${{ inputs.bundle_uri }} bundle.tar.gz
mkdir ${{ inputs.bundle_dir }} && tar -xvzf bundle.tar.gz -C ${{ inputs.bundle_dir }}
Expand All @@ -98,45 +119,57 @@ jobs:
aws-region: eu-west-1

- name: Inject Environment Config
if: inputs.inject_config_cmd
if: inputs.inject_config_cmd && ${{ steps.is-version-already-deployed.outputs.is_deployed == 'false'}}
env:
NODE_AUTH_TOKEN: ${{ secrets.GH_REGISTRY_NPM_TOKEN }}
SPA_BUNDLE_DIR: ${{ inputs.bundle_dir }}
SPA_ENV: ${{inputs.environment}}
SPA_TREE_HASH: ${{ inputs.tree_hash}}
SPA_TREE_HASH: ${{ inputs.deploy_hash}}
run: ${{inputs.inject_config_cmd }}

- name: Copy Static Files
if: ${{ steps.is-version-already-deployed.outputs.is_deployed == 'false'}}
run: |
aws s3 cp ${{ inputs.bundle_dir }}/static s3://${{ inputs.bucket_name }}/static \
--cache-control 'public,max-age=31536000,immutable' \
--recursive

- name: Copy HTML Files
if: ${{ steps.is-version-already-deployed.outputs.is_deployed == 'false'}}
run: |
aws s3 cp ${{ inputs.bundle_dir }}/ s3://${{ inputs.bucket_name }}/html/${{ inputs.tree_hash }} \
aws s3 cp ${{ inputs.bundle_dir }}/ s3://${{ inputs.bucket_name }}/html/${{ inputs.deploy_hash }} \
--cache-control 'public,max-age=31536000,immutable' \
--recursive \
--exclude "static/*"

- name: Update .well-known Files If Exists
if: ${{ steps.is-version-already-deployed.outputs.is_deployed == 'false'}}
run: |
aws s3 cp \
s3://${{ inputs.bucket_name }}/html/${{ inputs.tree_hash }}/.well-known/apple-app-site-association \
s3://${{ inputs.bucket_name }}/html/${{ inputs.tree_hash }}/.well-known/apple-app-site-association \
s3://${{ inputs.bucket_name }}/html/${{ inputs.deploy_hash }}/.well-known/apple-app-site-association \
s3://${{ inputs.bucket_name }}/html/${{ inputs.deploy_hash }}/.well-known/apple-app-site-association \
--content-type 'application/json' \
--cache-control 'public,max-age=3600' \
--metadata-directive REPLACE || echo "Failed updating .well-known files"

# We always copy deploy hash file even if we don't do an actual deployemnt as part of this commit
# This is used to identify which version of the deployed app is associated with the current commit
# In case we trigger rollback to this commit, we know what deployment to use based on the stored deploy_hash for this commit
- name: Copy Deploy Hash File
run: |
echo ${{ inputs.deploy_hash }} >> ${{ github.sha }}
aws s3 cp ${{ github.sha }} s3://${{ inputs.bucket_name }}/deploys/commits/${{ github.sha }}

- name: Update Cursor File
id: cursor-update
uses: pleo-io/spa-tools/actions/[email protected]
uses: pleo-io/spa-tools/actions/[email protected]
if: ${{ steps.is-version-already-deployed.outputs.is_deployed == 'false'}}
with:
bucket_name: ${{ inputs.bucket_name }}

- name: Update PR Description
uses: pleo-io/spa-tools/actions/post-preview-urls@spa-github-actions-v9.0.2
if: github.event_name == 'pull_request'
uses: pleo-io/spa-tools/actions/post-preview-urls@spa-github-actions-v10.0.0
if: github.event_name == 'pull_request' && ${{ steps.is-version-already-deployed.outputs.is_deployed == 'false'}}
with:
app_name: ${{ inputs.app_name }}
links: |
Expand All @@ -145,15 +178,8 @@ jobs:
{"name": "Current Permalink", "url": "${{ steps.deployment-url.outputs.url }}"}
]

- name: Upload Deployed Bundle as Artifact
uses: actions/[email protected]
with:
name: bundle-${{ inputs.tree_hash }}-${{ inputs.environment }}
path: bundle.tar.gz

- name: Verify app deployment
if: inputs.inject_config_cmd
if: inputs.inject_config_cmd && ${{ steps.is-version-already-deployed.outputs.is_deployed == 'false'}}
# We expect the injection to insert the version in the HTML.
run:
curl --silent --show-error ${{ steps.deployment-url.outputs.url }} |
grep -q ${{ inputs.tree_hash }}
run: curl --silent --show-error ${{ steps.deployment-url.outputs.url }} |
grep -q ${{ inputs.deploy_hash }}
47 changes: 29 additions & 18 deletions actions/cursor-deploy/dist/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24280,6 +24280,12 @@ async function copyFileToS3({
}) {
await (0, import_exec.exec)("aws s3 cp", [path, `s3://${bucket}/${key}`]);
}
async function readFileFromS3({ key, bucket }) {
await (0, import_exec.exec)("aws s3 cp", [`s3://${bucket}/${key}`, "tmp"]);
const data = await execReadOutput("cat", ["tmp"]);
await (0, import_exec.exec)("rm", ["tmp"]);
return data;
}
async function removeFileFromS3({ key, bucket }) {
await (0, import_exec.exec)("aws s3 rm", [`s3://${bucket}/${key}`]);
}
Expand All @@ -24298,11 +24304,8 @@ async function runAction(action) {
async function isHeadAncestor(commitHash) {
return execIsSuccessful("git merge-base", [`--is-ancestor`, commitHash, `HEAD`]);
}
async function getTreeHashForCommitHash(commit) {
return execReadOutput("git rev-parse", [`${commit}:`]);
}
async function getCurrentRepoTreeHash() {
return getTreeHashForCommitHash("HEAD");
async function getCommitHashFromRef(ref) {
return execReadOutput(`git rev-parse`, [ref]);
}

// cursor-deploy/index.ts
Expand All @@ -24318,7 +24321,7 @@ runAction(async () => {
rollbackCommitHash,
ref: ((_b = (_a2 = github.context.payload) == null ? void 0 : _a2.pull_request) == null ? void 0 : _b.head.ref) ?? github.context.ref
});
core2.setOutput("tree_hash", output.treeHash);
core2.setOutput("deploy_hash", output.deployHash);
core2.setOutput("branch_label", output.branchLabel);
});
async function cursorDeploy({
Expand All @@ -24329,7 +24332,8 @@ async function cursorDeploy({
}) {
const deployMode = getDeployMode(deployModeInput);
const branchLabel = branchNameToHostnameLabel(ref);
const treeHash = await getDeploymentHash(deployMode, rollbackCommitHash);
const commitHash = await getDeployCommitHash(deployMode, rollbackCommitHash);
const deployHash = await getDeploymentHashFromCommitHash(bucket, commitHash);
const rollbackKey = `rollbacks/${branchLabel}`;
const deployKey = `deploys/${branchLabel}`;
if (deployMode === "default" || deployMode === "unblock") {
Expand All @@ -24341,9 +24345,9 @@ async function cursorDeploy({
throw new Error(`${branchLabel} does not have an active rollback, you can't unblock.`);
}
}
await writeLineToFile({ text: treeHash, path: branchLabel });
await writeLineToFile({ text: deployHash, path: branchLabel });
await copyFileToS3({ path: branchLabel, bucket, key: deployKey });
core2.info(`Tree hash ${treeHash} is now the active deployment for ${branchLabel}.`);
core2.info(`Deploy hash ${deployHash} is now the active deployment for ${branchLabel}.`);
if (deployMode === "rollback") {
await copyFileToS3({ path: branchLabel, bucket, key: rollbackKey });
core2.info(`${branchLabel} marked as rolled back, automatic deploys paused.`);
Expand All @@ -24352,7 +24356,7 @@ async function cursorDeploy({
await removeFileFromS3({ bucket, key: rollbackKey });
core2.info(`${branchLabel} has automatic deploys resumed.`);
}
return { treeHash, branchLabel };
return { deployHash, branchLabel };
}
function getDeployMode(deployMode) {
function assertDeployMode(value) {
Expand All @@ -24363,19 +24367,26 @@ function getDeployMode(deployMode) {
assertDeployMode(deployMode);
return deployMode;
}
async function getDeploymentHash(deployMode, rollbackCommitHash) {
async function getDeployCommitHash(deployMode, rollbackCommitHash) {
if (deployMode === "rollback") {
if (!!rollbackCommitHash && !await isHeadAncestor(rollbackCommitHash)) {
throw new Error("The selected rollback commit is not present on the branch");
}
const commit = rollbackCommitHash || "HEAD^";
const treeHash2 = await getTreeHashForCommitHash(commit);
core2.info(`Rolling back to tree hash ${treeHash2} (commit ${commit})`);
return treeHash2;
return getCommitHashFromRef(rollbackCommitHash || "HEAD^");
}
return getCommitHashFromRef("HEAD");
}
async function getDeploymentHashFromCommitHash(bucket, commitHash) {
const commitKey = `deploys/commits/${commitHash}`;
const deployHash = await readFileFromS3({
bucket,
key: commitKey
});
if (!deployHash) {
throw Error(`Deploy hash for commit ${commitHash} not found.`);
}
const treeHash = await getCurrentRepoTreeHash();
core2.info(`Using current root tree hash ${treeHash}`);
return treeHash;
core2.info(`Using deploy hash ${deployHash} (commit ${commitHash})`);
return deployHash;
}
function branchNameToHostnameLabel(ref) {
var _a2, _b;
Expand Down
Loading
Loading