Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/jsonnet/GIT_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
85284ade969df3f45c01eb0a9c513267c0a60b81
358da535b48d02d139dee0974843d2e646b972fe
6 changes: 4 additions & 2 deletions .github/jsonnet/base.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,10 @@ local misc = import 'misc.jsonnet';
* @param {string} [id=null] - Unique identifier for this step (used to reference outputs)
* @param {string} [ifClause=null] - Conditional expression to determine if step should run
* @param {boolean} [continueOnError=null] - Whether to continue job if this step fails
* @param {number} [timeoutMinutes=null] - Maximum minutes to run this step before failing
* @returns {steps} - Array containing a single step object
*/
action(name, uses, env=null, with=null, id=null, ifClause=null, continueOnError=null)::
action(name, uses, env=null, with=null, id=null, ifClause=null, continueOnError=null, timeoutMinutes=null)::
[
{
name: name,
Expand All @@ -179,6 +180,7 @@ local misc = import 'misc.jsonnet';
+ (if with != null && with != {} then { with: with } else {})
+ (if id != null then { id: id } else {})
+ (if ifClause != null then { 'if': ifClause } else {})
+ (if continueOnError == null then {} else { 'continue-on-error': continueOnError }),
+ (if continueOnError == null then {} else { 'continue-on-error': continueOnError })
+ (if timeoutMinutes == null then {} else { 'timeout-minutes': timeoutMinutes })
],
}
91 changes: 87 additions & 4 deletions .github/jsonnet/cache.jsonnet
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
local base = import 'base.jsonnet';
local images = import 'images.jsonnet';
local misc = import 'misc.jsonnet';

{
/**
* Fetch a cache from the cache server.
*
*
* This is a generic function that can be used to fetch any cache. It is advised to wrap this function
* in a more specific function that fetches a specific cache, setting the cacheName and folders parameters.
*
*
* To be paired with the uploadCache function.
*
* @param {string} cacheName - The name of the cache to fetch. The name of the repository is usually a good option.
Expand Down Expand Up @@ -82,10 +84,10 @@ local base = import 'base.jsonnet';

/**
* Uploads a cache to the cache server.
*
*
* This is a generic function that can be used to upload any cache. It is advised to wrap this function
* in a more specific function that uploads a specific cache, setting the cacheName and folders parameters.
*
*
* To be paired with the fetchCache function.
*
* @param {string} cacheName - The name of the cache to upload. The name of the repository is usually a good option.
Expand Down Expand Up @@ -141,4 +143,85 @@ local base = import 'base.jsonnet';
'echo "Cache removed"\n',
ifClause=ifClause,
),

/**
* Daily (weekday) backup of the full repository (working tree + .git) to GCS as a zstd tar archive,
* then refreshes a 7-day signed HTTPS URL into the GIT_HTTPS_ARCHIVE_MIRROR repo secret.
*
* @param {string} [cron='0 1 * * 1-5'] - Schedule (UTC). Default 01:00 on weekdays.
* @param {string} [bucketPath='gs://gynzy-internal-files/git-mirror'] - Destination prefix.
* @returns {workflows} - GitHub Actions pipeline that mirrors the repository to GCS.
*/
updateGitCacheCron(
cron='0 1 * * 1-5',
)::
local secret = self.secret;
local destUrl = 'gs://gynzy-internal-files/git-mirror/${GITHUB_REPOSITORY}.tar.zst';
base.pipeline(
'git-mirror-backup',
[
base.ghJob(
'git-mirror-backup',
image=images.cloud_sdk_image,
useCredentials=true,
timeoutMinutes=60,
steps=[
// blobless=false: a backup must contain ALL git objects, not lazily-fetched blobs.
misc.checkout(fullClone=true, blobless=false, cloneTimeout=30, skipSeed=true),
base.step(
'authenticate gcloud',
|||
set -euo pipefail
# Write the key OUTSIDE the workspace so it never lands in the archive.
printf '%s' "$SERVICE_JSON" > "$RUNNER_TEMP/gce.json"
gcloud auth activate-service-account --key-file="$RUNNER_TEMP/gce.json"
|||,
shell='bash',
env={ SERVICE_JSON: misc.secret('SERVICE_JSON') },
),
base.step(
'create and upload archive',
|||
set -euo pipefail
# checkStat=minimal makes git compare only mtime+size (which tar preserves),
# not ctime/inode. Consumers that seed from this archive then see a clean tree
# and only write the real diff during checkout instead of rewriting every file.
git -C "$GITHUB_WORKSPACE" config --local core.checkStat minimal

DEST="%s"

# Upload to a temp object first, then atomically move into place so a partial
# upload can never sit at the final destination.
tar -C "$GITHUB_WORKSPACE" -c . | zstdmt -10 | gcloud storage cp - "${DEST}.tmp"
gcloud storage mv "${DEST}.tmp" "$DEST"
||| % destUrl,
shell='bash',
),
base.step(
'refresh signed-url secret',
|||
set -euo pipefail
DEST="%s"
URL="$(gcloud storage sign-url "$DEST" \
--private-key-file="$RUNNER_TEMP/gce.json" --http-verb=GET --duration=7d \
--format='value(signed_url)')"
echo "::add-mask::$URL"
gh secret set "GIT_HTTPS_ARCHIVE_MIRROR" --repo "$GITHUB_REPOSITORY" --body "$URL"
||| % destUrl,
shell='bash',
env={
GH_TOKEN: misc.secret('VIRKO_GITHUB_TOKEN'),
},
),
base.step(
'remove credentials',
'rm -f "$RUNNER_TEMP/gce.json"',
ifClause='${{ always() }}',
),
],
),
],
event={ schedule: [{ cron: cron }], workflow_dispatch: {} },
permissions={ contents: 'read' },
),
}
36 changes: 27 additions & 9 deletions .github/jsonnet/complete-workflows.jsonnet
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local base = import 'base.jsonnet';
local misc = import 'misc.jsonnet';
local pnpm = import 'pnpm.jsonnet';
local yarn = import 'yarn.jsonnet';

{
Expand All @@ -11,32 +12,49 @@ local yarn = import 'yarn.jsonnet';
* 2. 'publish-prod' - Production package publishing on branch push
* 3. 'pr' - Pull request preview publishing and testing
*
* @param {array} [repositories=['gynzy']] - The repositories to publish to
* @param {array} [repositories=['github']] - The repositories to publish to
* @param {boolean} [isPublicFork=true] - Whether the repository is a public fork (affects runner selection)
* @param {boolean} [checkVersionBump=true] - Whether to assert if the version was bumped (recommended)
* @param {jobs} [testJob=null] - A job to be run during PR to assert tests. Can be an array of jobs
* @param {string} [branch='main'] - The branch to run the publish-prod job on
* @param {string} [branch='main'] - The branch that triggers the publish-prod job; publishes the package <VERSION> specified in package.json and sets latest tag
* @param {string} [packageManager='yarn'] - Package manager to use ('yarn' or 'pnpm')
* @param {string} [image=null] - Docker image override for publish jobs; null uses the PM-specific default
* @param {array} [buildSteps=null] - Build steps override; null uses the PM-specific default. Pass `[]` to skip build.
* @returns {workflows} - Complete set of GitHub Actions workflows for JavaScript package lifecycle
*/
workflowJavascriptPackage(repositories=['gynzy'], isPublicFork=true, checkVersionBump=true, testJob=null, branch='main')::
workflowJavascriptPackage(
repositories=['github'],
isPublicFork=true,
checkVersionBump=true,
testJob=null,
branch='main',
packageManager='yarn',
image='mirror.gcr.io/node:24',
buildSteps=null,
)::
local runsOn = (if isPublicFork then 'ubuntu-latest' else null);
local defaultBuildSteps = if packageManager == 'pnpm' then [base.step('build', 'pnpm run build')]
else [base.step('build', 'yarn build')];
local effectiveBuildSteps = if buildSteps != null then buildSteps else defaultBuildSteps;
local publishJob = if packageManager == 'pnpm'
then pnpm.pnpmPublishJob(repositories=repositories, runsOn=runsOn, image=image, buildSteps=effectiveBuildSteps, publishBranch=branch)
else yarn.yarnPublishJob(repositories=repositories, runsOn=runsOn, image=image, buildSteps=effectiveBuildSteps, publishBranch=branch);
local previewJob = if packageManager == 'pnpm'
then pnpm.pnpmPublishPreviewJob(repositories=repositories, runsOn=runsOn, checkVersionBump=checkVersionBump, image=image, buildSteps=effectiveBuildSteps)
else yarn.yarnPublishPreviewJob(repositories=repositories, runsOn=runsOn, checkVersionBump=checkVersionBump, image=image, buildSteps=effectiveBuildSteps);

base.pipeline(
'misc',
[misc.verifyJsonnet(fetch_upstream=false, runsOn=runsOn)],
) +
base.pipeline(
'publish-prod',
[
yarn.yarnPublishJob(repositories=repositories, runsOn=runsOn),
],
[publishJob],
event={ push: { branches: [branch] } },
) +
base.pipeline(
'pr',
[
yarn.yarnPublishPreviewJob(repositories=repositories, runsOn=runsOn, checkVersionBump=checkVersionBump),
] +
[previewJob] +
(if testJob != null then
[testJob]
else [])
Expand Down
5 changes: 3 additions & 2 deletions .github/jsonnet/deployment.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,11 @@ local notifications = import 'notifications.jsonnet';
* Generate a GitHub ifClause for the provided deployment targets.
*
* @param {array} targets - Array of deployment target environment names
* @param {boolean} [virkoOnly=true] - If true, also require the deployment event to be created by 'gynzy-virko', preventing manually created deployment events from triggering the job
* @returns {string} - GitHub Actions conditional expression that matches any of the provided targets
*/
deploymentTargets(targets)::
'${{ ' + std.join(' || ', std.map(function(target) "github.event.deployment.environment == '" + target + "'", targets)) + ' }}',
deploymentTargets(targets, virkoOnly=true)::
'${{ (' + std.join(' || ', std.map(function(target) "github.event.deployment.environment == '" + target + "'", targets)) + ')' + (if virkoOnly then " && github.event.deployment.creator.login == 'gynzy-virko'" else '') + ' }}',

/**
* Creates a step to update deployment status (success/failure) based on the result from the current job
Expand Down
9 changes: 5 additions & 4 deletions .github/jsonnet/helm.jsonnet
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
local base = import 'base.jsonnet';
local clusters = import 'clusters.jsonnet';
local databases = import 'databases.jsonnet';
local deployment = import 'deployment.jsonnet';
local images = import 'images.jsonnet';
local misc = import 'misc.jsonnet';
local services = import 'services.jsonnet';
Expand Down Expand Up @@ -151,7 +152,7 @@ local services = import 'services.jsonnet';
base.ghJob(
'deploy-prod',
runsOn=runsOn,
ifClause="${{ github.event.deployment.environment == '" + environment + "' }}",
ifClause=deployment.deploymentTargets([environment]),
image=image,
useCredentials=useCredentials,
steps=[
Expand Down Expand Up @@ -234,7 +235,7 @@ local services = import 'services.jsonnet';
base.ghJob(
'deploy-test',
runsOn=runsOn,
ifClause="${{ github.event.deployment.environment == 'test' }}",
ifClause=deployment.deploymentTargets(['test']),
image=image,
useCredentials=useCredentials,
steps=[
Expand Down Expand Up @@ -515,7 +516,7 @@ local services = import 'services.jsonnet';
runsOn=runsOn,
image=image,
useCredentials=useCredentials,
ifClause="${{ github.event.deployment.environment == 'canary' }}",
ifClause=deployment.deploymentTargets(['canary']),
steps=[
misc.checkout(),
self.helmDeployCanary(
Expand Down Expand Up @@ -597,7 +598,7 @@ local services = import 'services.jsonnet';
base.ghJob(
'kill-canary',
runsOn=runsOn,
ifClause="${{ github.event.deployment.environment == 'kill-canary' || github.event.deployment.environment == 'production' }}",
ifClause=deployment.deploymentTargets(['kill-canary', 'production']),
image=images.default_job_image,
useCredentials=false,
steps=[
Expand Down
1 change: 1 addition & 0 deletions .github/jsonnet/images.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
default_mongodb_image: 'europe-docker.pkg.dev/unicorn-985/private-images/docker-images_mongo8-replicated:v1',
mongo_job_image: 'europe-docker.pkg.dev/unicorn-985/public-images/docker-images_mongo-cloner-job:v1',
default_python_image: 'mirror.gcr.io/python:3.12.1',
cloud_sdk_image: 'europe-docker.pkg.dev/unicorn-985/private-images/docker-images_gcloud:v2',
default_pulumi_node_image: 'mirror.gcr.io/node:22',
job_poster_image: 'europe-docker.pkg.dev/unicorn-985/public-images/docker-images_job-poster:v2',
}
79 changes: 69 additions & 10 deletions .github/jsonnet/misc.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,22 @@ local images = import 'images.jsonnet';
* @param {string} [ref=null] - Specific git ref/branch/tag to checkout
* @param {boolean} [preferSshClone=true] - Whether to attempt SSH clone first
* @param {boolean} [includeSubmodules=true] - Whether to checkout git submodules
* @param {boolean} [blobless=null] - Whether to perform a blobless clone (--filter=blob:none); null uses default (false)
* @param {number} [retryAttempts=null] - Number of additional checkout attempts on failure; null uses default (0)
* @param {number} [cloneTimeout=null] - Timeout for git clone operation in minutes; null uses default (10)
* @param {boolean} [skipSeed=false] - Boolean where the archive based git initialization can be skipped
* @returns {steps} - GitHub Actions steps for repository checkout
*/
checkout(ifClause=null, fullClone=false, ref=null, preferSshClone=true, includeSubmodules=true)::
checkout(ifClause=null, fullClone=false, ref=null, preferSshClone=true, includeSubmodules=true, blobless=null, retryAttempts=null, cloneTimeout=null, skipSeed=false)::
local secret = self.secret;
local actualBlobless = if blobless == null then false else blobless;
local actualRetryAttempts = if retryAttempts == null then 0 else retryAttempts;
local actualCloneTimeout = if cloneTimeout == null then 10 else cloneTimeout;
local with =
(if fullClone then { 'fetch-depth': 0 } else {}) +
(if ref != null then { ref: ref } else {}) +
(if includeSubmodules then { submodules: 'recursive' } else {});
(if includeSubmodules then { submodules: 'recursive' } else {}) +
(if actualBlobless then { filter: 'blob:none' } else {});
local sshSteps = (if (preferSshClone) then
base.step(
'check for ssh/git binaries',
Expand Down Expand Up @@ -66,25 +75,75 @@ local images = import 'images.jsonnet';
id='check-binaries',
) else []);

// Cache seed: unpack the mirror archive (full working tree + .git) so the checkout
// below only fetches the delta. Skipped at runtime when the mirror secret is empty.
// Any failure leaves a clean workspace and continues normally.
local seedSteps = (if skipSeed then [] else base.step(
'fetch git-mirror archive',
|||
seed() {
if ! command -v zstd >/dev/null 2>&1; then
if command -v apk >/dev/null 2>&1; then apk add --no-cache zstd tar wget || return 1;
elif command -v apt >/dev/null 2>&1; then apt update && apt install -y zstd tar wget || return 1;
else return 1; fi
fi
wget -q -O - "$GIT_HTTPS_ARCHIVE_MIRROR" | tar -C "$GITHUB_WORKSPACE" --extract --zstd -f - || return 1
}
if ! seed; then
echo "git-mirror seed failed; continuing with a clean checkout"
find "$GITHUB_WORKSPACE" -mindepth 1 -delete 2>/dev/null || true
exit 1
fi
echo "git-mirror seed succeeded"
exit 0
|||,
env={ GIT_HTTPS_ARCHIVE_MIRROR: secret('GIT_HTTPS_ARCHIVE_MIRROR') },
ifClause="${{ env.GIT_HTTPS_ARCHIVE_MIRROR != '' }}",
shell='bash',
continueOnError=true,
));

// strip the ${{ }} from the IfClause so we can inject and add our own if clause
local localIfClause = (if ifClause == null then null else std.strReplace(std.strReplace(ifClause, '${{ ', ''), ' }}', ''));
local userIfPart = if ifClause == null then '' else '( ' + localIfClause + ' ) && ';

// Generate `retryAttempts + 1` attempts of the same checkout action. Each non-final attempt
// sets continue-on-error so the workflow proceeds to the next attempt; retries are gated on
// the previous attempt's outcome being 'failure'.
local retrySteps(name, withParams, baseIf, idPrefix) = std.flatMap(
function(i)
local isLast = (i == actualRetryAttempts);
local previousFailed = if i == 0 then '' else " && steps." + idPrefix + (i - 1) + ".outcome == 'failure'";
base.action(
name + (if i == 0 then '' else ' (retry ' + i + ')'),
actions.checkout_action,
with=withParams,
id=idPrefix + i,
ifClause='${{ ' + userIfPart + '( ' + baseIf + ' )' + previousFailed + ' }}',
timeoutMinutes=actualCloneTimeout,
continueOnError=(if isLast then null else true),
),
std.range(0, actualRetryAttempts)
);

if (preferSshClone) then
seedSteps +
sshSteps +
base.action(
retrySteps(
'Check out repository code via ssh',
actions.checkout_action,
with=with + (if preferSshClone then { 'ssh-key': '${{ secrets.VIRKO_GITHUB_SSH_KEY }}' } else {}),
ifClause='${{ ' + (if ifClause == null then '' else '( ' + localIfClause + ' ) && ') + " ( steps.check-binaries.outputs.sshBinaryExists == 'true' && steps.check-binaries.outputs.gitBinaryExists == 'true' ) }}",
with + { 'ssh-key': '${{ secrets.VIRKO_GITHUB_SSH_KEY }}' },
"steps.check-binaries.outputs.sshBinaryExists == 'true' && steps.check-binaries.outputs.gitBinaryExists == 'true'",
'checkout-ssh-',
) +
base.action(
retrySteps(
'Check out repository code via https',
actions.checkout_action,
with=with,
ifClause='${{ ' + (if ifClause == null then '' else '( ' + localIfClause + ' ) && ') + " ( steps.check-binaries.outputs.sshBinaryExists == 'false' || steps.check-binaries.outputs.gitBinaryExists == 'false' ) }}",
with,
"steps.check-binaries.outputs.sshBinaryExists == 'false' || steps.check-binaries.outputs.gitBinaryExists == 'false'",
'checkout-https-',
) +
base.step('git safe directory', "command -v git && git config --global --add safe.directory '*' || true")
else
seedSteps +
self.checkoutWithoutSshMagic(ifClause, fullClone, ref),

/**
Expand Down
Loading
Loading