Merge pull request #1661 from Shuffle/nightly

Sync for 2.0.1
This commit is contained in:
Frikky
2025-03-21 02:09:44 +01:00
committed by GitHub
32 changed files with 1159 additions and 415 deletions
+54 -23
View File
@@ -14,17 +14,18 @@ jobs:
matrix:
os: [ubuntu-latest]
architecture: [x64, arm64]
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install Docker Compose
run: |
sudo apt-get update
sudo apt-get install -y docker-compose
docker-compose --version
- name: Set up opensearch directory
run: mkdir shuffle-database
continue-on-error: true
- name: Elevate rights for it
run: chmod -R 777 shuffle-database
run: chmod -R 777 shuffle-database
- name: Build the stack
run: docker compose up -d
@@ -59,7 +60,6 @@ jobs:
echo "The word 'Shuffle' was not found in the response."
exit 1
fi
- name: Register a user and check the status code
run: |
MAX_RETRIES=30
@@ -100,31 +100,36 @@ jobs:
echo "User registration failed after $MAX_RETRIES attempts."
exit 1
- name: Run Selenium testing for frontend
run: |
cd $GITHUB_WORKSPACE/frontend
# write some log to see the current directory
chmod +x frontend-testing.sh
./frontend-testing.sh
- name: Get the API key and run a health check
id: health_check
run: |
RESPONSE=$(curl -s -k -u admin:StrongShufflePassword321! 'https://localhost:9200/users/_search')
echo "Raw Response: $RESPONSE"
API_KEY=$(echo "$RESPONSE" | jq -r '.hits.hits[0]._source.apikey')
if [ -n "$API_KEY" ]; then
if [ -n "$API_KEY" ] && [ "$API_KEY" != "null" ]; then
echo "Admin API key: $API_KEY"
echo "API_KEY=$API_KEY" >> $GITHUB_ENV
else
echo "Failed to retrieve the API key for the admin user."
exit 1
fi
echo "Waiting 1 minute before sending the health API request..."
sleep 60
echo "Checking health API..."
HEALTH_RESPONSE=$(curl -s 'http://localhost:3001/api/v1/health?force=true' \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
-H 'Accept-Language: en-US,en;q=0.9' \
-H 'Connection: keep-alive' \
-H 'Sec-Fetch-Dest: document' \
-H 'Sec-Fetch-Mode: navigate' \
-H 'Sec-Fetch-Site: none' \
-H "Authorization: Bearer $API_KEY" \
-H 'Sec-Fetch-User: ?1' \
-H 'Upgrade-Insecure-Requests: 1' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' \
-H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "macOS"')
-H "Authorization: Bearer $API_KEY")
echo "Health API Response: $HEALTH_RESPONSE"
WORKFLOWS_CREATE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.create')
WORKFLOWS_RUN=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run')
@@ -132,9 +137,35 @@ jobs:
WORKFLOWS_RUN_STATUS=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_status')
WORKFLOWS_DELETE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.delete')
echo "WORKFLOWS_CREATE: $WORKFLOWS_CREATE"
echo "WORKFLOWS_RUN: $WORKFLOWS_RUN"
echo "WORKFLOWS_RUN_FINISHED: $WORKFLOWS_RUN_FINISHED"
echo "WORKFLOWS_RUN_STATUS: $WORKFLOWS_RUN_STATUS"
echo "WORKFLOWS_DELETE: $WORKFLOWS_DELETE"
if [ "$WORKFLOWS_CREATE" = "true" ] && [ "$WORKFLOWS_RUN" = "true" ] && [ "$WORKFLOWS_RUN_FINISHED" = "true" ] && [ "$WORKFLOWS_RUN_STATUS" = "FINISHED" ] && [ "$WORKFLOWS_DELETE" = "true" ]; then
echo "Health endpoint check was successful."
exit 0
else
echo "Health endpoint check failed. Response did not meet expected criteria."
echo "Health check failed."
exit 1
fi
notify:
needs: build
if: failure()
runs-on: ubuntu-latest
steps:
- name: Send Twilio Alert
run: |
WORKFLOW_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
MESSAGE="🚨 Shuffle Workflow Failed!
Repository: ${{ github.repository }}
Branch: ${{ github.ref_name }}
Workflow URL: $WORKFLOW_URL"
curl -s -X POST https://api.twilio.com/2010-04-01/Accounts/${{ secrets.TWILIO_ACCOUNT_SID }}/Messages.json \
--data-urlencode "To=${{ secrets.TWILIO_TO_NUMBER }}" \
--data-urlencode "From=${{ secrets.TWILIO_FROM_NUMBER }}" \
--data-urlencode "Body=$MESSAGE" \
-u "${{ secrets.TWILIO_ACCOUNT_SID }}:${{ secrets.TWILIO_AUTH_TOKEN }}" > /dev/null
-7
View File
@@ -984,11 +984,4 @@ func activateWorkflowAppDocker(resp http.ResponseWriter, request *http.Request)
log.Printf("[INFO] User %s (%s) is activating %s. Public: %t, Shared: %t", user.Username, user.Id, app.Name, app.Public, app.Sharing)
buildSwaggerApp(resp, []byte(openApiApp.Body), user, true)
//app.Active = true
//app.Generated = true
//app, err := shuffle.SetApp(ctx, app)
//resp.WriteHeader(200)
//resp.Write([]byte(`{"success": true}`))
}
+11 -9
View File
@@ -1,7 +1,9 @@
module shuffle
go 1.22.2
toolchain go1.24.1
go 1.23.0
toolchain go1.23.7
//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared
@@ -19,8 +21,8 @@ require (
github.com/gorilla/mux v1.8.1
github.com/h2non/filetype v1.1.3
github.com/satori/go.uuid v1.2.0
github.com/shuffle/shuffle-shared v0.8.12
golang.org/x/crypto v0.35.0
github.com/shuffle/shuffle-shared v0.8.18
golang.org/x/crypto v0.36.0
google.golang.org/api v0.176.1
google.golang.org/grpc v1.68.1
gopkg.in/src-d/go-git.v4 v4.13.1
@@ -121,12 +123,12 @@ require (
go.opentelemetry.io/proto/otlp v0.11.0 // indirect
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/appengine v1.6.8 // indirect
+5 -14
View File
@@ -347,8 +347,6 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdR
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shuffle/shuffle-shared v0.8.12 h1:fg6jGAuevOGLgR7kpoZDJh//tzFjrEqj/bpxRFzxUTw=
github.com/shuffle/shuffle-shared v0.8.12/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
@@ -423,8 +421,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -480,8 +476,7 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -497,8 +492,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -527,14 +521,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -545,8 +537,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+74
View File
@@ -0,0 +1,74 @@
SERVER_URL="http://localhost:3000"
declare -a Routes=($(grep -oP '(?<=path=")[^"]*' src/App.jsx | grep -E '^[a-zA-Z0-9/_:-]+$' | grep -vE '^/$'))
for i in "${!Routes[@]}"; do
Routes[$i]="${Routes[$i]#/}"
done
# # Add all tab routes and unique routes here
Routes+=(
'workflows?tab=org_workflows'
'workflows?tab=my_workflows'
'workflows?tab=all_workflows'
'apps/gmail'
'apis/gmail'
'apps?tab=my_apps'
'apps?tab=all_apps'
'search?tab=org_apps'
'search?tab=my_apps'
'search?tab=workflows'
'search?tab=docs'
'search?tab=creators'
'search?tab=discord'
"admin?tab=organization"
"admin?tab=users"
"admin?tab=app_auth"
"admin?tab=datastore"
"admin?tab=files"
"admin?tab=triggers"
"admin?tab=locations"
"admin?tab=tenants"
"admin?admin_tab=org_config"
"admin?admin_tab=sso"
"admin?admin_tab=notifications"
"admin?admin_tab=billingstats"
"admin?admin_tab=branding(beta)"
)
# Stop frontend container so it test unpushed changes
docker stop shuffle-frontend
# # Install all frontend dependencies
yarn add selenium-webdriver --dev && yarn install
echo "Starting frontend..."
BROWSER=none yarn start &
SERVER_PID=$!
echo "Frontend started with PID: $SERVER_PID"
echo "Waiting for 1 minute to ensure the server is fully up..."
sleep 60
echo "Server is up! Starting Selenium tests..."
echo "Starting frontend tests..."
node selenium-test.js "$SERVER_URL" "${Routes[@]}"
TEST_EXIT_CODE=$?
if [[ $TEST_EXIT_CODE -ne 0 ]]; then
echo "Selenium tests failed. Exiting..."
kill $SERVER_PID
exit 1
fi
kill $SERVER_PID
echo "Testing complete. See above logs for errors if any."
# Starting shuffle-frontend container
echo "Starting shuffle-frontend container..."
docker start shuffle-frontend
echo "shuffle-frontend started successfully."
exit 0
+1
View File
@@ -128,6 +128,7 @@
"babel-preset-es2015": "^6.24.1",
"postcss": "^8.4.38",
"promise-window": "^1.2.1",
"selenium-webdriver": "^4.29.0",
"webpack-cli": "^5.1.4"
}
}
+253
View File
@@ -0,0 +1,253 @@
const { Builder, By, until } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const fs = require('fs');
const path = require('path');
console.log('Starting Selenium script for testing pages...');
(async () => {
const userDataDir = path.join(__dirname, 'chrome-user-data', `${Date.now()}-${Math.random().toString(36).substring(2, 8)}`);
fs.mkdirSync(userDataDir, { recursive: true });
let options = new chrome.Options();
options.addArguments('--no-sandbox');
options.addArguments('--headless');
let driver = await new Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.build();
const SuccessfullyLoadedPath = [];
const FailedToLoadPath = [];
try {
await driver.manage().window().setRect({ width: 1600, height: 1200 });
await driver.manage().window().maximize()
await driver.manage().setTimeouts({ implicit: 15000 });
const frontendURL = process.argv[2];
const routes = process.argv.slice(3);
console.log('Frontend URL:', frontendURL);
const isCloud = frontendURL === 'http://localhost:3002' || frontendURL === 'https://sandbox.shuffler.io' || frontendURL === 'https://shuffler.io';
if (isCloud) {
// Login Credentials
const LOGIN_URL = `${frontendURL}/login`;
// put your login credentials here
const USERNAME = '';
const PASSWORD = '';
console.log('Logging in...');
await driver.get(LOGIN_URL);
try {
await driver.wait(until.elementLocated(By.css('#emailfield')), 10000);
await driver.findElement(By.css('#emailfield')).sendKeys(USERNAME);
await driver.wait(until.elementLocated(By.css('#outlined-password-input')), 10000);
await driver.findElement(By.css('#outlined-password-input')).sendKeys(PASSWORD);
await driver.wait(until.elementLocated(By.css('#loginButton')), 10000);
await driver.findElement(By.css('#loginButton')).click();
// Ensure login success by checking URL change
await driver.wait(async () => {
const url = await driver.getCurrentUrl();
return url.includes('welcome');
}, 20000);
console.log('Successfully logged in!');
} catch (error) {
console.error('Login failed:', error.message);
FailedToLoadPath.push(LOGIN_URL);
await driver.quit();
process.exit(1);
}
}else {
// Steps for onprem testing
// 1. Login
// 2. Create new workflow
// Write your own login credentials here if you are testing onprem locally
const USERNAME = 'demo@demo.io';
const PASSWORD = 'supercoolpassword';
const WORKFLOW_URL = `${frontendURL}/workflows`;
try {
// Login
console.log('Logging in...');
await driver.get(`${frontendURL}/login`);
await driver.wait(until.elementLocated(By.css('#emailfield')), 10000);
await driver.findElement(By.css('#emailfield')).sendKeys(USERNAME);
await driver.wait(until.elementLocated(By.css('#outlined-password-input')), 10000);
await driver.findElement(By.css('#outlined-password-input')).sendKeys(PASSWORD);
const loginButton = await driver.findElement(By.css('#loginButton'));
await driver.executeScript("arguments[0].scrollIntoView(true);", loginButton);
await loginButton.click();
// Ensure signup success by checking URL change
await driver.wait(async () => {
const url = await driver.getCurrentUrl();
return url.includes('welcome') || url.includes('workflows');
}, 20000);
const isParentPresent = await driver.wait(async () => {
return await driver.executeScript(
"return document.querySelector('.parent-component') !== null;"
);
}, 5000).catch(() => false);
if (!isParentPresent) {
const logs = await driver.manage().logs().get('browser');
const severeErrors = logs.filter(log => log.level.name === 'SEVERE');
if (severeErrors.length > 0) {
const crashCausingErrors = severeErrors.filter(err => {
return !err.message.includes('Warning:') &&
!err.message.includes('MUI:')
});
console.error(`Page (${frontendURL}/login) did not load correctly:`, crashCausingErrors);
FailedToLoadPath.push(`${frontendURL}/login`);
}
}
console.log('Successfully logged in!');
} catch (error) {
console.error('Failed to login:', error.message);
FailedToLoadPath.push(WORKFLOW_URL);
await driver.quit();
process.exit(1);
}
try {
// Create new workflow
console.log('Creating new workflow...');
await driver.get(WORKFLOW_URL);
await driver.sleep(1000);
await driver.wait(until.elementLocated(By.css('#create_workflow_button')), 10000);
const create_workflow_button = await driver.findElement(By.css('#create_workflow_button'));
await driver.sleep(1000);
await create_workflow_button.click();
await driver.sleep(1500);
await driver.wait(until.elementLocated(By.css('#Enter-Workflow-Name')), 10000);
const enter_workflow_name_field = await driver.findElement(By.css('#Enter-Workflow-Name'));
await driver.sleep(1000);
await enter_workflow_name_field.sendKeys("Test Workflow");
// Wait before saving
await driver.sleep(1500);
await driver.wait(until.elementLocated(By.css('#save_workflow_button')), 10000);
const save_workflow_button = await driver.findElement(By.css('#save_workflow_button'));
await driver.sleep(1000); // Delay before clicking
await save_workflow_button.click();
// Wait for the parent component to appear
await driver.sleep(2000);
const isParentPresent = await driver.wait(async () => {
return await driver.executeScript(
"return document.querySelector('.parent-component') !== null;"
);
}, 5000).catch(() => false);
if (!isParentPresent) {
const logs = await driver.manage().logs().get('browser');
const severeErrors = logs.filter(log => log.level.name === 'SEVERE');
if (severeErrors.length > 0) {
const crashCausingErrors = severeErrors.filter(err => {
return !err.message.includes('Warning:') &&
!err.message.includes('MUI:');
});
console.error(`Page (${WORKFLOW_URL}) did not load correctly:`, crashCausingErrors);
FailedToLoadPath.push(`${WORKFLOW_URL}`);
}
}
} catch (error) {
console.error('Failed to create new workflow:', error.message);
FailedToLoadPath.push(WORKFLOW_URL);
await driver.quit();
process.exit(1);
}
}
// Get routes from command line arguments
if (routes.length === 0) {
console.error('No routes found to test.');
await driver.quit();
process.exit(1);
}
console.log(`Found ${routes.length} routes to test.`);
for (const route of routes) {
const url = `${frontendURL}/${route}`;
console.log(`Testing route: ${url}`);
try {
await driver.get(url);
await driver.sleep(3000);
// Wait for document readiness
await driver.wait(async () => {
return await driver.executeScript('return document.readyState') === 'complete';
}, 10000);
const isParentPresent = await driver.wait(async () => {
return await driver.executeScript(
"return document.querySelector('.parent-component') !== null;"
);
}, 5000).catch(() => false);
if (!isParentPresent) {
const logs = await driver.manage().logs().get('browser');
const severeErrors = logs.filter(log => log.level.name === 'SEVERE');
if (severeErrors.length > 0) {
const crashCausingErrors = severeErrors.filter(err => {
return !err.message.includes('Warning:') &&
!err.message.includes('MUI:')
});
console.error(`Page (${url}) did not load correctly:`, crashCausingErrors);
FailedToLoadPath.push(url);
}
continue;
}
console.log(`Successfully loaded: ${url}`);
SuccessfullyLoadedPath.push(url);
} catch (error) {
console.error(`Failed to load ${url}:`, error.message);
}
}
} finally {
console.log("Total pages tested: ", SuccessfullyLoadedPath.length + FailedToLoadPath.length);
console.log("Successfully loaded pages: ", SuccessfullyLoadedPath.length);
if (FailedToLoadPath.length > 0) {
console.log("Failed to load pages: ", FailedToLoadPath.length);
console.log("Failed to load pages paths: ", FailedToLoadPath);
process.exit(1);
}else {
console.log("No pages failed to load. Congrats!");
}
await driver.quit();
fs.rmSync(userDataDir, { recursive: true, force: true });
}
})();
+28 -1
View File
@@ -189,6 +189,7 @@ const App = (message, props) => {
color: "rgba(255, 255, 255, 0.65)",
minHeight: "100vh",
}}
className='parent-component'
>
<ScrollToTop
getUserNotifications={getUserNotifications}
@@ -371,6 +372,7 @@ const App = (message, props) => {
element={
<Admin
isLoggedIn={isLoggedIn}
userdata={userdata}
setIsLoggedIn={setIsLoggedIn}
register={true}
isLoaded={isLoaded}
@@ -634,6 +636,32 @@ const App = (message, props) => {
<Route exact path="/forms/:key/run" element={<RunWorkflow serverside={false} userdata={userdata} globalUrl={globalUrl} isLoaded={isLoaded} isLoggedIn={isLoggedIn} surfaceColor={theme.palette.surfaceColor} inputColor={theme.palette.inputColor}{...props} />} />
<Route exact path="/forms/:key" element={<RunWorkflow serverside={false} userdata={userdata} globalUrl={globalUrl} isLoaded={isLoaded} isLoggedIn={isLoggedIn} surfaceColor={theme.palette.surfaceColor} inputColor={theme.palette.inputColor}{...props} />} />
<Route
exact
path="/legal/:key"
element={
<Docs
isMobile={isMobile}
isLoaded={isLoaded}
globalUrl={globalUrl}
isLoggedIn={isLoggedIn}
{...props}
/>
}
/>
<Route
exact
path="/legal"
element={
<Docs
isMobile={isMobile}
isLoaded={isLoaded}
globalUrl={globalUrl}
isLoggedIn={isLoggedIn}
{...props}
/>
}
/>
<Route
exact
path="/docs/:key"
@@ -651,7 +679,6 @@ const App = (message, props) => {
exact
path="/docs"
element={
//navigate(`/docs/about`)
<Docs
isMobile={isMobile}
isLoaded={isLoaded}
+8 -5
View File
@@ -860,6 +860,7 @@ const Billing = memo((props) => {
"Add Card Details"
}
</Button>
{userdata.has_card_available === true ?
<Button
fullWidth
@@ -888,7 +889,8 @@ const Billing = memo((props) => {
: null}
</span>
: null}
: null}
{showSupport ?
<Button variant="outlined" color="primary" style={{ marginTop: 20, marginBottom: 10, }} onClick={() => {
if (window.drift !== undefined) {
@@ -1204,12 +1206,13 @@ const Billing = memo((props) => {
ReactGA.event({
category: "Billing",
action: "Buy_consultation_hours",
});
})
}
const url = userdata.licensed === true
? "https://book.stripe.com/6oEeY23fw2T84M06oq"
: "https://book.stripe.com/00g6rw3fw9hwguI289";
window.open(url, "_blank");
? "https://buy.stripe.com/aEU3fk17o0L092g5kt"
: "https://buy.stripe.com/aEU3fk17o0L092g5kt"
window.open(url, "_blank")
}}
>
Book Professional Service Hours
+2
View File
@@ -302,6 +302,7 @@ const EditWorkflow = (props) => {
<Button
variant="contained"
style={{}}
id="save_workflow_button"
disabled={name.length === 0 || submitLoading === true}
onClick={() => {
setSubmitLoading(true)
@@ -406,6 +407,7 @@ const EditWorkflow = (props) => {
label="Name"
autoFocus
fullWidth
id="Enter-Workflow-Name"
/>
<div style={{ display: "flex", marginTop: 10, }}>
+11 -7
View File
@@ -57,7 +57,7 @@ const ExpandMoreAndLessIcon = "/icons/expandMoreIcon.svg";
const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
const navigate = useNavigate();
const {setLeftSideBarOpenByClick, leftSideBarOpenByClick, setSearchBarModalOpen, searchBarModalOpen} = useContext(Context);
const {setLeftSideBarOpenByClick, leftSideBarOpenByClick, setSearchBarModalOpen, searchBarModalOpen, isDocSearchModalOpen} = useContext(Context);
const [expandLeftNav, setExpandLeftNav] = useState(false);
const [activeOrgName, setActiveOrgName] = useState(
@@ -526,7 +526,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
<Divider style={{ marginBottom: 10, }} />
<Typography color="textSecondary" align="center" style={{ marginTop: 5, marginBottom: 5, fontSize: 18 }}>
Version: 2.0.0
Version: 2.0.1
</Typography>
</Menu>
</span>
@@ -773,6 +773,9 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
}, [window?.location?.pathname]);
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
return (
<div
style={{
@@ -785,12 +788,13 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
transition: "width 0.3s ease",
boxShadow: "0px 4px 12px rgba(0, 0, 0, 0.2)" ,
resize: 'both',
zoom: 0.8,
zoom: isSafari ? undefined : 0.8,
transform: isSafari ? "scale(0.8)" : undefined,
transformOrigin: isSafari ? "top left" : undefined,
height: "calc((100vh - 32px)*1.2)",
}}
>
{searchBarModalOpen ? <ModalView serverside={serverside} userdata={userdata} searchBarModalOpen={searchBarModalOpen} setSearchBarModalOpen={setSearchBarModalOpen} globalUrl={globalUrl} /> : null}
{searchBarModalOpen && !isDocSearchModalOpen ? <ModalView serverside={serverside} userdata={userdata} searchBarModalOpen={searchBarModalOpen} setSearchBarModalOpen={setSearchBarModalOpen} globalUrl={globalUrl} isDocSearchModalOpen={isDocSearchModalOpen} /> : null}
<Box
sx={{
display: "flex",
@@ -1911,11 +1915,11 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
};
export default LeftSideBar;
const ModalView = memo(({searchBarModalOpen, setSearchBarModalOpen, globalUrl, serverside, userdata}) => {
const ModalView = memo(({searchBarModalOpen, setSearchBarModalOpen, globalUrl, serverside, userdata, isDocSearchModalOpen}) => {
return (
(
<Dialog
open={searchBarModalOpen}
open={searchBarModalOpen && !isDocSearchModalOpen}
onClose={() => {
setSearchBarModalOpen(false);
}}
+7 -4
View File
@@ -909,21 +909,23 @@ const LicencePopup = (props) => {
const priceItem =
window.location.origin === "https://shuffler.io/" || "https://sandbox.shuffler.io/"
? shuffleVariant === 0
? "app_executions"
: "cores"
? "price_1PWI3uDzMUgUjxHSffUBwWCy"
: "price_1PWI8EDzMUgUjxHSfEhUB7oL"
: shuffleVariant === 0
? "price_1PZPSSEJjT17t98NLJoTMYja"
: "price_1PZPQuEJjT17t98N3yORUtd9";
const successUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=success`
const failUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=failure`
const quantity = shuffleVariant === 0 ? selectedValue / 100 : selectedValue
console.log("Priceitem: ", priceItem, shuffleVariant)
console.log("Priceitem: ", priceItem, quantity, shuffleVariant)
var checkoutObject = {
lineItems: [
{
price: priceItem,
quantity: shuffleVariant === 0 ? selectedValue / 100 : selectedValue,
quantity: quantity,
},
],
mode: "subscription",
@@ -1141,6 +1143,7 @@ const LicencePopup = (props) => {
>
View all plans
</Button>
<Button
variant="contained"
style={{ borderRadius: 4, textTransform: "capitalize", color: "#1a1a1a", backgroundColor: "#ff8544", width: "100%", fontSize: 16}}
+33 -7
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useContext, useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import {
AppBar,
@@ -35,6 +35,7 @@ import { toast } from "react-toastify";
import AddIcon from '@mui/icons-material/Add';
import Mousetrap from "mousetrap";
import LicencePopup from "../components/LicencePopup.jsx";
import { Context } from "../context/ContextApi.jsx";
const curpath = (typeof window !== "undefined" && window.location && typeof window.location.pathname === "string")
? window.location.pathname
@@ -47,7 +48,7 @@ const menuData = {
title: "Shuffle",
description:
"The most versatile automation engine with focus on security.",
icon: "images/icons/shuffleLogo.svg",
icon: "/images/icons/shuffleLogo.svg",
path: "/docs/about",
gaData: {
category: "navbar",
@@ -750,7 +751,7 @@ const Navbar = (props) => {
const topbar_var = "topbar_closed10"
const theme = useTheme();
const [searchBarModalOpen, setSearchBarModalOpen] = useState(false);
const {searchBarModalOpen, setSearchBarModalOpen, isDocSearchModalOpen} = useContext(Context)
const [pricingModalOpen, setPricingModalOpen] = useState(false);
const isTabletOrMobile = useMediaQuery(theme.breakpoints.down("lg"));
const isMobile = useMediaQuery(theme.breakpoints.down("md"));
@@ -845,7 +846,7 @@ const Navbar = (props) => {
const modalView = (
<Dialog
open={searchBarModalOpen}
open={searchBarModalOpen && !isDocSearchModalOpen}
onClose={() => {
setSearchBarModalOpen(false);
}}
@@ -1287,10 +1288,21 @@ const Navbar = (props) => {
<Button
onMouseEnter={() => handleMenuOpen(item)}
onMouseLeave={(e) => {
// Only close if not moving to dropdown
const dropdown = document.querySelector('.dropdown-content'); // Adjust the selector as needed
const rect = e.currentTarget.getBoundingClientRect();
const dropdownRect = dropdown ? dropdown.getBoundingClientRect() : null;
// Check if the cursor is moving down
const isMovingDown = e.clientY > rect.bottom;
if (!isMovingDown) {
// Check if the cursor is within the dropdown area or a buffer zone
const isOverDropdown = dropdownRect && (
(e.clientY >= dropdownRect.top - 20 && e.clientY <= dropdownRect.bottom + 20) && // Buffer zone
(e.clientX >= dropdownRect.left && e.clientX <= dropdownRect.right)
);
if (!isMovingDown && !isOverDropdown) {
handleMenuClose();
}
}}
@@ -1352,6 +1364,11 @@ const Navbar = (props) => {
onClick={() => {
if(isCloud) {
navigate("/pricing");
ReactGA.event({
category: "navbar",
action: "pricing_click",
label: "go_to_pricing",
})
} else {
window.open("https://shuffler.io/pricing", '_blank');
return;
@@ -1365,6 +1382,11 @@ const Navbar = (props) => {
onClick={() => {
if(isCloud) {
navigate("/partners");
ReactGA.event({
category: "navbar",
action: "partners_click",
label: "go_to_partners",
})
} else {
window.open("https://shuffler.io/partners", '_blank');
return;
@@ -1722,7 +1744,11 @@ const Navbar = (props) => {
}}
>
{topbar}
<Container maxWidth="xl">
<Container maxWidth="xl" sx={{ zoom: {
md: "0.7",
lg: "0.8",
xl: "1",
} }}>
<Toolbar disableGutters sx={{ justifyContent: "space-between" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Link
@@ -307,17 +307,16 @@ const OrgHeaderexpandedNew = (props) => {
const setSelectedRegion = (region) => {
// send a POST request to /api/v1/orgs/{org_id}/region with the region as the body
if (region === "US") {
region = "us-west2"
} else if (region === "EU") {
region = "europe-west2"
} else if (region === "CA") {
region = "northamerica-northeast1"
} else if (region === "UK") {
region = "europe-west2"
} else if (region === "EU-2") {
region = "europe-west3"
}
const regionMap = {
"US": "us-west2",
"EU": "europe-west2",
"CA": "northamerica-northeast1",
"UK": "europe-west2",
"EU-2": "europe-west3",
"AUS": "australia-southeast1"
};
region = regionMap[region] || region;
var data = {
dst_region: region
@@ -336,7 +335,11 @@ const OrgHeaderexpandedNew = (props) => {
timeOut: 1000
}).then((response) => {
if (response.status !== 200) {
toast("Failed to change region!")
response.json().then((reason) => {
toast.error("Failed to change region: " + reason.reason)
}).catch((err) => {
toast.error("Failed to change region")
});
}
else {
toast("Region changed successfully! Reloading in 5 seconds..")
@@ -347,7 +350,7 @@ const OrgHeaderexpandedNew = (props) => {
}
return response.json();
// return responseJson
})
}
@@ -1091,6 +1094,7 @@ const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userd
"EU-2": "eu",
"CA": "ca",
"UK": "gb",
"AUS": "au",
};
//let regiontag = "UK";
@@ -1112,6 +1116,9 @@ const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userd
} else if (regiontag === "ca") {
regiontag = "CA";
regionCode = "ca";
} else if (regiontag === "austrailia") {
regiontag = "AUS";
regionCode = "au"
}
}
@@ -1124,11 +1131,12 @@ const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userd
value={regiontag}
style={{ minWidth: 120, height: 35, borderRadius: 4 }}
onChange={(e) => {
if (userdata?.support) {
setSelectedRegion(e.target.value)
} else {
handleSendChangeRegionMail(e.target.value)
}
// if (userdata?.support) {
// setSelectedRegion(e.target.value)
// } else {
// handleSendChangeRegionMail(e.target.value)
// }
setSelectedRegion(e.target.value)
}}
>
{Object.keys(regionMapping).map((region, index) => {
@@ -1138,6 +1146,10 @@ const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userd
selectedOrganization.region = "europe-west2";
}
if (region === "AUS") {
region = "AUS (test)"
}
// Check if the current region matches the selected region
if (region === selectedOrganization.region) {
// If the region matches, set the MenuItem as selected
+1 -1
View File
@@ -2551,7 +2551,7 @@ const ParsedAction = (props) => {
{workflow.execution_variables !== undefined && workflow.execution_variables !== null && workflow.execution_variables.length > 0 ? (
<div style={{ marginTop: "20px" }}>
<Typography>Execution variable (optional)</Typography>
<Typography color="textSecondary">Runtime variable (optional)</Typography>
<Select
MenuProps={{
disableScrollLock: true,
+79 -45
View File
@@ -51,12 +51,13 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e52
const SearchData = props => {
const { serverside, globalUrl, userdata } = props
let navigate = useNavigate();
const { searchBarModalOpen, setSearchBarModalOpen } = useContext(Context);
const { searchBarModalOpen, setSearchBarModalOpen, isDocSearchModalOpen } = useContext(Context);
const borderRadius = 3
const node = useRef()
const [searchOpen, setSearchOpen] = useState(false)
const [oldPath, setOldPath] = useState("")
const [value, setValue] = useState("");
const isDocSearchModal = isDocSearchModalOpen;
const handleLinkClick = () => {
if (searchBarModalOpen) {
@@ -90,6 +91,8 @@ const SearchData = props => {
const [inputValue, setInputValue] = useState(currentRefinement);
const textFieldRef = useRef(null);
const debounceTimeoutRef = useRef(null);
const keyPressHandler = (e) => {
if (e.key === "Enter") {
e.preventDefault();
@@ -110,10 +113,25 @@ const SearchData = props => {
}, [searchOpen]);
useEffect(() => {
if (currentRefinement !== inputValue) {
refine(inputValue);
// Clear any existing timeout to prevent multiple executions
if (debounceTimeoutRef.current) {
clearTimeout(debounceTimeoutRef.current);
}
}, [currentRefinement]);
// Set a new timeout that will execute the search after a 200ms delay
debounceTimeoutRef.current = setTimeout(() => {
refine(inputValue);
setSearchOpen(inputValue.trim() !== '');
}, 200);
// Cleanup function that runs when component unmounts or when dependencies change
// This ensures we don't have any hanging timeouts
return () => {
if (debounceTimeoutRef.current) {
clearTimeout(debounceTimeoutRef.current);
}
};
}, [inputValue, refine]); // Effect runs when inputValue or refine function changes
return (
@@ -122,7 +140,7 @@ const SearchData = props => {
>
<TextField
fullWidth
style={{ zIndex: 1100, marginTop: -20, marginBottom: 200, position: "fixed", backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, width: 685, }}
style={{ zIndex: 1100, marginTop: -20, marginBottom: 200, position: "fixed", backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, width: 690, }}
InputProps={{
style: {
color: "white",
@@ -149,7 +167,7 @@ const SearchData = props => {
autoComplete='off'
type="search"
color="primary"
placeholder="Find Public Apps, Workflows, Documentation..."
placeholder={isDocSearchModal ? "Type to search documentation..." : "Find Public Apps, Workflows, Documentation..."}
value={inputValue}
id="shuffle_search_field"
onClick={(event) => {
@@ -163,12 +181,6 @@ const SearchData = props => {
onChange={(event) => {
const newValue = event.target.value;
setInputValue(newValue);
refine(newValue);
if (newValue.trim() !== '') {
setSearchOpen(true);
} else {
setSearchOpen(false);
}
}}
onKeyDown={keyPressHandler}
inputRef={textFieldRef}
@@ -605,7 +617,11 @@ const SearchData = props => {
}
if (hits.length > 4) {
hits = hits.slice(0, 4)
if(!isDocSearchModal) {
hits = hits.slice(0, 4)
} else {
hits = hits.slice(0, 15)
}
}
const type = "documentation"
@@ -614,15 +630,19 @@ const SearchData = props => {
//console.log(type, hits.length, hits)
return (
<Card elevation={0} style={{ marginRight: 10, marginTop: 50, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 470, boxShadows: "none", }}>
<Card elevation={0} style={{ marginRight: 10, marginTop: isDocSearchModal ? 0 : 50, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 470, boxShadows: "none", }}>
{/* <IconButton style={{ zIndex: 5000, position: "absolute", right: 14, color: "grey" }} onClick={() => {
setSearchOpen(false)
}}>
<CloseIcon />
</IconButton> */}
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", color: "#FF8444", borderBottom: "1px solid", width: 152 }}>
Documentation
</Typography>
{
!isDocSearchModal && (
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", color: "#FF8444", borderBottom: "1px solid", width: 152 }}>
Documentation
</Typography>
)
}
{/*
<IconButton edge="end" aria-label="delete" style={{position: "absolute", top: 5, right: 15,}} onClick={() => {
setSearchOpen(false)
@@ -630,7 +650,7 @@ const SearchData = props => {
<DeleteIcon />
</IconButton>
*/}
<List style={{ backgroundColor: theme.palette.inputColor, }}>
<List style={{ backgroundColor: theme.palette.inputColor, marginTop: isDocSearchModal ? 35 : 0, }}>
{hits.length === 0 ?
<ListItem style={outerlistitemStyle}>
<ListItemAvatar onClick={() => console.log(hits)}>
@@ -654,8 +674,8 @@ const SearchData = props => {
cursor: "pointer",
marginLeft: 5,
marginRight: 5,
maxHeight: 75,
minHeight: 75,
maxHeight: isDocSearchModal ? 100 : 75,
minHeight: isDocSearchModal ? 100 : 75,
maxWidth: 420,
minWidth: "100%",
}
@@ -666,9 +686,13 @@ const SearchData = props => {
(hit.name.charAt(0).toUpperCase() + hit.name.slice(1)).replaceAll("_", " ")
if (name.length > 30) {
name = name.slice(0, 30) + "..."
if(isDocSearchModal) {
name = name.slice(0, 70) + "..."
} else {
name = name.slice(0, 30) + "..."
}
}
const secondaryText = hit.data !== undefined ? hit.data.slice(0, 40) + "..." : ""
const secondaryText = hit.data !== undefined ? hit.data.slice(0, 80) + "..." : ""
const avatar = hit.image_url === undefined ?
baseImage
:
@@ -719,7 +743,7 @@ const SearchData = props => {
</ListItemAvatar>
<ListItemText
primary={name}
secondary={secondaryText}
secondary={<div style={{ padding: '8px 0' }}>{secondaryText}</div>}
/>
{/*
<ListItemSecondaryAction>
@@ -897,28 +921,38 @@ const SearchData = props => {
const modalView = (
<div>
<Grid container style={{ display: "contents", }}>
<Grid item xs="auto" style={{}}>
<Index indexName="appsearch">
<CustomAppHits />
</Index>
{
!isDocSearchModal ? (
<Grid container style={{ display: "contents", }}>
<Grid item xs="auto" style={{}}>
<Index indexName="appsearch">
<CustomAppHits />
</Index>
</Grid>
<Grid item xs="auto" style={{}}>
<Index indexName="workflows">
<CustomWorkflowHits />
</Index>
</Grid>
<Grid item xs="auto" style={{}}>
<Index indexName="documentation">
<CustomDocHits />
</Index>
</Grid>
<Grid item xs="auto" style={{}}>
<Index indexName="discord_chat">
<CustomDiscordHits />
</Index>
</Grid>
</Grid>
<Grid item xs="auto" style={{}}>
<Index indexName="workflows">
<CustomWorkflowHits />
</Index>
</Grid>
<Grid item xs="auto" style={{}}>
<Index indexName="documentation">
<CustomDocHits />
</Index>
</Grid>
<Grid item xs="auto" style={{}}>
<Index indexName="discord_chat">
<CustomDiscordHits />
</Index>
</Grid>
</Grid>
) : (
<Grid item xs="auto" style={{width: "100%"}}>
<Index indexName="documentation">
<CustomDocHits />
</Index>
</Grid>
)
}
</div>
)
@@ -932,7 +966,7 @@ const SearchData = props => {
<CustomSearchBox />
{modalView}
</InstantSearch>
{gettingStartData}
{!isDocSearchModal && gettingStartData}
</div>
)
}
@@ -2476,7 +2476,8 @@ const CodeEditor = (props) => {
var fixedcodedata = localcodedata
const valid = validateJson(localcodedata, true)
if (valid.valid) {
fixedcodedata = JSON.stringify(valid.result, null, 2)
//fixedcodedata = JSON.stringify(valid.result, null, 2)
fixedcodedata = JSON.stringify(valid.result)
}
// console.log(codedata)
@@ -398,8 +398,9 @@ const WorkflowValidationTimeline = (props) => {
nodecolor = green
branchcolor = green
} else {
nodecolor = grey
branchcolor = grey
// Green by default
nodecolor = green
branchcolor = green
}
} else {
nodecolor = green
@@ -561,8 +562,6 @@ const WorkflowValidationTimeline = (props) => {
nodecolor = green
branchcolor = green
}
var flex = index !== 0 && index !== relevantactions.length - 1 ? 1 : 3
if (nodecolor === green) {
@@ -609,6 +608,8 @@ const WorkflowValidationTimeline = (props) => {
const ballsize = 8
const topMargin = 20
console.log("CHIP: ", index, chipColor, chipBackground)
const chipStyle = {
height: 40,
minWidth: 125,
@@ -676,7 +677,6 @@ const WorkflowValidationTimeline = (props) => {
{founderror.length > 0 ? founderror : `App: ${appname} - Action: ${action.label}`}
</Typography>
} placement="top">
<Chip label={`${appname}`} style={chipStyle} icon={
<Avatar
variant="round"
+81 -34
View File
@@ -7,11 +7,7 @@ import {
Tooltip,
TextField,
Grid,
Skeleton,
Dialog,
DialogTitle,
DialogContent,
Box,
Checkbox
} from "@mui/material";
import { makeStyles } from "@mui/styles";
import { Link } from "react-router-dom";
@@ -30,6 +26,8 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
const classes = useStyles();
const [show2faSetup, setShow2faSetup] = React.useState(false);
const [autoPrivision, setAutoProvision] = React.useState(selectedOrganization?.sso_config?.auto_provision)
const [showOpenIdCred, setShowOpenIdCred] = React.useState(false);
const [showSamlCred, setShowSamlCred] = React.useState(false);
const [ssoEntrypoint, setSsoEntrypoint] = React.useState(
selectedOrganization.sso_config === undefined
? ""
@@ -393,7 +391,27 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
</span>
<Typography style={{ textAlign: "left", fontSize: 16, marginTop: 8, color: "rgba(158, 158, 158, 1)", fontWeight: 400 }}>
IdP URL for Shuffle OpenID: <Link to={`${globalUrl}/api/v1/login_openid`} target="_blank" style={{ color: "rgba(241, 241, 241, 1)", textDecoration: "none", fontSize: 16,}}>{`${globalUrl}/api/v1/login_openid`}</Link>
</Typography>
</Typography>
<div style={{ display: 'flex', marginTop: 10, }}>
<Typography
style={{
textAlign: "left",
fontSize: 16,
marginTop: 8,
color: "rgba(158, 158, 158, 1)",
fontWeight: 400,
}}
>
Show OpenID Credentials
</Typography>
<Checkbox
checked={showOpenIdCred}
onChange={(e) => setShowOpenIdCred(e.target.checked)}
name="showOpenIdCred"
color="primary"
style={{ color: "rgba(255, 255, 255, 1)", }}
/>
</div>
<Grid container style={{ marginTop: 8, }} spacing={2}>
<Grid item xs={6} style={{}}>
<span>
@@ -407,30 +425,29 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
}}
fullWidth={true}
type="name"
multiline={true}
rows={2}
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
placeholder="The OpenID client ID from the identity provider"
value={openidClientId}
onChange={(e) => {
setOpenidClientId(e.target.value);
}}
value={showOpenIdCred ? openidClientId : openidClientId?.length > 0 ? "•".repeat(50) : ""}
onChange={(e) => setOpenidClientId(e.target.value)}
onFocus={(e) => setShowOpenIdCred(true)}
onBlur={(e) => setShowOpenIdCred(false)}
InputProps={{
classes: {
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
},
style: {
color: "white",
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
fontWeight: 400,
fontSize: 16,
borderRadius: 4,
color: "white",
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
fontWeight: 400,
fontSize: 16,
borderRadius: 4,
},
}}
/>
/>
</span>
</Grid>
<Grid item xs={6} style={{}}>
@@ -445,17 +462,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
}}
fullWidth={true}
type="name"
type={showOpenIdCred ? "text" : "password"}
multiline={true}
rows={2}
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
placeholder="The OpenID client secret - DONT use this if dealing with implicit auth / PKCE"
value={openidClientSecret}
value={showOpenIdCred ? openidClientSecret : openidClientSecret?.length > 0 ? "•".repeat(50) : ""}
onChange={(e) => {
setOpenidClientSecret(e.target.value);
}}
}}
onFocus={(e) => setShowOpenIdCred(true)}
onBlur={(e) => setShowOpenIdCred(false)}
InputProps={{
classes: {
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
@@ -485,17 +504,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
}}
fullWidth={true}
type="name"
type={showOpenIdCred ? "text" : "password"}
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
multiline={true}
rows={2}
placeholder="The OpenID authorization URL (usually ends with /authorize)"
value={openidAuthorization}
value={showOpenIdCred ? openidAuthorization : openidAuthorization?.length > 0 ? "•".repeat(50) : ""}
onChange={(e) => {
setOpenidAuthorization(e.target.value)
}}
}}
onFocus={(e) => setShowOpenIdCred(true)}
onBlur={(e) => setShowOpenIdCred(false)}
InputProps={{
classes: {
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
@@ -523,17 +544,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
}}
fullWidth={true}
type="name"
type={showOpenIdCred ? "text" : "password"}
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
multiline={true}
rows={2}
placeholder="The OpenID token URL (usually ends with /token)"
value={openidToken}
value={showOpenIdCred ? openidToken : openidToken?.length > 0 ? "•".repeat(50) : ""}
onChange={(e) => {
setOpenidToken(e.target.value)
}}
}}
onFocus={(e) => setShowOpenIdCred(true)}
onBlur={(e) => setShowOpenIdCred(false)}
InputProps={{
classes: {
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
@@ -557,7 +580,27 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
<Typography variant="h4" style={{ textAlign: "left", color: "rgba(241, 241, 241, 1)", fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontSize: 24, fontWeight: 600, }}>SAML SSO (v1.1)</Typography>
<Typography variant="body2" style={{ textAlign: "left", marginTop: 8, color: "rgba(241, 241, 241, 1)", fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontSize: 16, fontWeight: 400, color: "rgba(158, 158, 158, 1)" }} color="textSecondary">
IdP URL for Shuffle SAML/SSO: <Link to={`${globalUrl}/api/v1/login_sso`} target="_blank" style={{ color: "rgba(241, 241, 241, 1)", textDecoration: "none" }}>{`${globalUrl}/api/v1/login_sso`}</Link>
</Typography>
</Typography>
<div style={{ display: 'flex', marginTop: 10, }}>
<Typography
style={{
textAlign: "left",
fontSize: 16,
marginTop: 8,
color: "rgba(158, 158, 158, 1)",
fontWeight: 400,
}}
>
Show SAML Credentials
</Typography>
<Checkbox
checked={showSamlCred}
onChange={(e) => setShowSamlCred(e.target.checked)}
name="showSamlCred"
color="primary"
style={{ color: "rgba(255, 255, 255, 1)", }}
/>
</div>
<Grid container style={{ marginTop: 10, }} spacing={2}>
<Grid item xs={6} style={{}}>
<span>
@@ -571,17 +614,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
}}
fullWidth={true}
type="name"
type={showSamlCred ? "text" : "password"}
multiline={true}
rows={2}
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
placeholder="The entrypoint URL from your provider"
value={ssoEntrypoint}
value={showSamlCred ? ssoEntrypoint : ssoEntrypoint?.length > 0 ? "•".repeat(50) : ""}
onChange={(e) => {
setSsoEntrypoint(e.target.value);
}}
}}
onFocus={(e) => setShowSamlCred(true)}
onBlur={(e) => setShowSamlCred(false)}
InputProps={{
classes: {
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
@@ -609,14 +654,16 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
}}
fullWidth={true}
type="name"
type={showSamlCred ? "text" : "password"}
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
multiline={true}
rows={2}
placeholder="The X509 certificate to use"
value={ssoCertificate}
value={showSamlCred ? ssoCertificate : ssoCertificate?.length > 0 ? "•".repeat(50) : ""}
onFocus={(e) => setShowSamlCred(true)}
onBlur={(e) => setShowSamlCred(false)}
onChange={(e) => {
setSsoCertificate(e.target.value);
}}
@@ -646,4 +693,4 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
)
}
export default SSOTab
export default SSOTab
+3
View File
@@ -8,6 +8,7 @@ export const AppContext = (props) => {
// Left side bar global states
const [searchBarModalOpen, setSearchBarModalOpen] = useState(false);
const [isDocSearchModalOpen, setIsDocSearchModalOpen] = useState(false);
const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(currentLocation?.includes('/workflows/') ? false : true)
const [windowWidth, setWindowWidth] = useState(serverside === true ? 100 : window.innerWidth);
@@ -37,6 +38,8 @@ export const AppContext = (props) => {
return (
<Context.Provider value={{
isDocSearchModalOpen,
setIsDocSearchModalOpen,
searchBarModalOpen,
setSearchBarModalOpen,
leftSideBarOpenByClick,
+1 -1
View File
@@ -81,7 +81,7 @@ const theme = createTheme(adaptV4Theme({
singulBlackWhite: "/images/singul_black_white.png",
},
typography: {
fontFamily: `"Roboto", "Helvetica", "Arial", "inter", sans-serif`,
fontFamily: `"inter", "Roboto", "Helvetica", "Arial", sans-serif`,
useNextVariants: true,
fontWeightLight: 300,
fontWeightRegular: 400,
+72 -35
View File
@@ -386,6 +386,8 @@ const useStyles = makeStyles({
const splitter = "|~|";
const svgSize = 24;
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
//const referenceUrl = "https://shuffler.io/functions/webhooks/"
//const referenceUrl = window.location.origin+"/api/v1/hooks/"
@@ -1320,33 +1322,45 @@ const AngularWorkflow = (defaultprops) => {
const allTriggersInOne = useTriggers.pipelines.concat(useTriggers.schedules).concat(useTriggers.webhooks)
for (let triggerkey in allTriggersInOne) {
const curtrigger = allTriggersInOne[triggerkey]
if (curtrigger.id === selectedTrigger.id) {
if (curtrigger.status === undefined || curtrigger.status === null) {
continue
}
if (curtrigger.running === undefined || curtrigger.running === null) {
continue
}
var changed = false
if (curtrigger.status !== selectedTrigger.status) {
changed = true
selectedTrigger.status = curtrigger.status
}
if (curtrigger.running !== selectedTrigger.running) {
changed = true
selectedTrigger.running = curtrigger.running
}
if (changed) {
//console.log("TRIGGER FIX: ", selectedTrigger)
setSelectedTrigger(selectedTrigger)
}
break
if (curtrigger.id !== selectedTrigger.id) {
continue
}
if (curtrigger.status === undefined || curtrigger.status === null) {
continue
}
var changed = false
if (selectedTrigger?.parameters === undefined || selectedTrigger?.parameters === null) {
changed = true
if (selectedTrigger?.trigger_type === "SCHEDULE" && curtrigger?.frequency !== undefined && curtrigger?.frequency !== null) {
selectedTrigger.parameters = [{
name: "cron",
value: curtrigger.frequency,
},
{
name: "execution_argument",
value: curtrigger.argument,
}]
}
}
if (curtrigger.status !== selectedTrigger.status) {
changed = true
selectedTrigger.status = curtrigger.status
}
if (curtrigger.running !== selectedTrigger.running) {
changed = true
selectedTrigger.running = curtrigger.running
}
if (changed) {
setSelectedTrigger(selectedTrigger)
}
break
}
}
}
@@ -2799,11 +2813,11 @@ const AngularWorkflow = (defaultprops) => {
// FIXME: Check if any node contains $exec in a param
// If they do, show a popup asking if they want to execute it without an execution argument, or to use a previous one
if (skip_popup !== true && executionArgument === undefined || executionArgument === null || executionArgument.length === 0)
if (skip_popup !== true && executionArgument === undefined || executionArgument === null || executionArgument?.length === 0)
if (workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) {
if (workflow?.actions !== undefined && workflow?.actions !== null && workflow?.actions?.length > 0) {
var foundmissing = false
for (let actionkey in workflow.actions) {
for (let actionkey in workflow?.actions) {
if (workflow.actions[actionkey].parameters === undefined || workflow.actions[actionkey].parameters === null || workflow.actions[actionkey].parameters.length === 0) {
continue
}
@@ -12236,7 +12250,9 @@ const AngularWorkflow = (defaultprops) => {
overflowAnchor: "none",
minWidth: minSize,
width: isMobile ? "100%" : minSize,
zoom: 0.9,
zoom: isSafari ? undefined : 0.9,
transform: isSafari ? "scale(0.9)" : undefined,
transformOrigin: isSafari ? "top right" : undefined,
};
const setTriggerFolderWrapperMulti = (event) => {
@@ -18011,10 +18027,10 @@ const AngularWorkflow = (defaultprops) => {
const topBarStyle = {
position: "absolute",
top: isMobile ? 30 : 25,
top: isMobile ? 30 : isSafari ? 15 : 25,
transform: isMobile ? "translateX(20px)" : `translateX(${leftBarSize}px)`,
transition: "all 0.3s ease",
zoom: 0.9,
zoom: isSafari ? undefined : 0.9,
}
const TopCytoscapeBar = (props) => {
@@ -21834,13 +21850,23 @@ const AngularWorkflow = (defaultprops) => {
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
const chosenNodeId = new URLSearchParams(cursearch).get("node");
const highlightNode = chosenNodeId !== null && chosenNodeId !== undefined && chosenNodeId !== "" && chosenNodeId === data.action.id
var relevant_errors = []
if (data?.action?.parameters !== undefined && data?.action?.parameters !== null && data?.action?.parameters.length > 0) {
for (var i = 0; i < data.action.parameters.length; i++) {
// Specific error patterns in params
const param = data.action.parameters[i]
if (param?.name?.endsWith("_error") && (param?.name?.startsWith("shuffle_") || param?.name?.startsWith("liquid_"))) {
relevant_errors.push(param)
}
}
}
return (
<div
key={index}
style={{
marginBottom: 20,
border: highlightNode ? "2px solid red"
border: highlightNode ? `2px solid ${red}`
:
data.action.sub_action === true
? "1px solid rgba(255,255,255,0.3)"
@@ -21913,11 +21939,17 @@ const AngularWorkflow = (defaultprops) => {
>
<Tooltip
color="primary"
title="Expand result window"
title={
<Typography variant="body1">
Expand result window. Errors: {relevant_errors.length}
</Typography>
}
placement="top"
style={{ zIndex: 50000 }}
>
<ArrowLeftIcon style={{ color: "white" }} />
<ArrowLeftIcon style={{
color: relevant_errors.length > 0 ? yellow : "rgba(255,255,255,0.7)",
}} />
</Tooltip>
</IconButton>
{actionimg}
@@ -22087,6 +22119,11 @@ const AngularWorkflow = (defaultprops) => {
const checked = validateJson(data.value.trim())
if (data.name === "shuffle_action_logs" && data.value !== undefined && data.value !== null && data.value.length > 0 && data.value.includes("add env SHUFFLE_LOGS_DISABLED")) {
if (isCloud) {
return null
}
return (
<div style={{ maxWidth: 600, marginTop: 75, overflowX: "hidden", }}>
<Typography
+179 -23
View File
@@ -23,7 +23,12 @@ import {
List,
Collapse,
ListItemButton,
ListItemText
ListItemText,
Dialog,
DialogTitle,
Box,
DialogContent,
InputAdornment
} from "@mui/material";
import {
Link as LinkIcon,
@@ -32,9 +37,12 @@ import {
ExpandMore as ExpandMoreIcon,
FileCopy as FileCopyIcon,
ChevronLeft,
ChevronRight
ChevronRight,
Search as SearchIcon,
Close as CloseIcon
} from "@mui/icons-material";
import { fontGrid } from "@mui/material/styles/cssUtils.js";
import SearchBox from "../components/SearchData.jsx";
const Body = {
//maxWidth: 1000,
@@ -158,12 +166,13 @@ export const OuterLink = (props) => {
export const Img = (props) => {
// Find parent container and check width
const isArticlePage = window.location.pathname.includes("/articles/");
const isArticlePage = window.location.pathname.includes("/articles/")
const isFormPage = window.location.pathname.includes("/forms/")
var height = "auto"
var width = isArticlePage ? 1000 : 750
var width = isArticlePage ? 1000 : isFormPage ? 400: 750
const docsImageStyle = {
border: "1px solid rgba(255,255,255,0.3)",
border: isFormPage ? null : "1px solid rgba(255,255,255,0.3)",
borderRadius: theme.palette?.borderRadius,
width: width,
maxWidth: width,
@@ -191,11 +200,13 @@ export const Img = (props) => {
}
return(
<img
style={isArticlePage ? articleImageStyle : docsImageStyle}
alt={props.alt}
src={props.src}
/>
<div style={{width: "100%", textAlign: "center", }}>
<img
style={isArticlePage ? articleImageStyle : docsImageStyle}
alt={props.alt}
src={props.src}
/>
</div>
)
}
@@ -278,7 +289,7 @@ export const CodeHandler = (props) => {
const Docs = (defaultprops) => {
const { globalUrl, selectedDoc, serverside, serverMobile, isLoggedIn, isLoaded, userdata } = defaultprops;
console.log("\n\nSELECTED DOC\n", selectedDoc, "\n\n")
const { searchBarModalOpen, setSearchBarModalOpen, isDocSearchModalOpen, setIsDocSearchModalOpen, leftSideBarOpenByClick } = useContext(Context);
let navigate = useNavigate();
const location = useLocation();
@@ -316,20 +327,36 @@ const Docs = (defaultprops) => {
const [activeSubItem, setActiveSubItem] = useState(false);
const headingElementsRef = useRef({})
var isArticlePage = window.location.pathname.includes("/articles/") || window.location.pathname === "/articles" ? true : false;
const searchFieldRef = useRef(null);
useEffect(() => {
fetchDocList();
setSidebarOpen(false);
if (props.match.params === undefined || props.match.params === null) {
return
}
const propkey = props.match.params.key
if (propkey === undefined || propkey === null) {
return
}
if (location.pathname.includes("/docs/")) {
if (propkey === "cookie_policy" || propkey === "compliance" || propkey === "privacy_policy" || propkey === "terms_of_service") {
navigate(`/legal/${propkey}`)
}
}
}, [location]);
useEffect(() => {
//if (params["key"] === undefined) {
// navigate("/docs/about")
// return
//}
}, [])
return () => {
setIsDocSearchModalOpen(false);
setSearchBarModalOpen(false);
};
}, []);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
}
@@ -459,6 +486,71 @@ const Docs = (defaultprops) => {
return headings;
};
const modalView = (
<Dialog
open={searchBarModalOpen && isDocSearchModalOpen}
onClose={() => {
setSearchBarModalOpen(false);
if (searchFieldRef.current) {
searchFieldRef.current.blur();
}
}}
PaperProps={{
style: {
color: "white",
minWidth: 750,
minHeight: "180px",
maxHeight: "85vh",
borderRadius: 16,
border: "1px solid var(--Container-Stroke, #494949)",
background: "var(--Container, #000000)",
boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)",
position: "fixed",
top: "70px",
left: "50%",
transform: "translateX(-50%)",
},
}}
sx={{
zIndex: 50005,
'& .MuiBackdrop-root': {
backgroundColor: 'rgba(0, 0, 0, 0.8)',
},
}}
>
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", px: 2, pr: 3, pt: 2 }}>
<DialogTitle
sx={{
color: "var(--Paragraph-text, #C8C8C8)",
p: 0,
m: 0,
ml: 1.5,
fontFamily: theme.typography.fontFamily,
}}
>
Search for Documentation
</DialogTitle>
<IconButton
onClick={() => setSearchBarModalOpen(false)}
sx={{
color: 'white',
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.1)'
}
}}
>
<CloseIcon />
</IconButton>
</Box>
<DialogContent>
<Box sx={{ pt: 3 }}>
<SearchBox globalUrl={globalUrl} serverside={serverside} userdata={userdata} />
</Box>
</DialogContent>
<Divider sx={{ backgroundColor: 'rgba(255, 255, 255, 0.1)' }}/>
</Dialog>
);
// extract TOC from actual markdown
const tocvalue = (markdown) => {
@@ -761,7 +853,13 @@ const Docs = (defaultprops) => {
}
const fetchDocList = (resetCache = false) => {
const url = isArticlePage ? `${globalUrl}/api/v1/articles?resetCache=${resetCache}` : `${globalUrl}/api/v1/docs`;
var url = `${globalUrl}/api/v1/docs`
if (location.pathname.includes("/legal")) {
url = `${globalUrl}/api/v1/docs?folder=legal&resetCache=${resetCache}`
} else if (location.pathname.includes("/articles")) {
url = `${globalUrl}/api/v1/docs?folder=articles&resetCache=${resetCache}`
}
fetch(url, {
method: "GET",
headers: {
@@ -782,8 +880,18 @@ const Docs = (defaultprops) => {
.catch((error) => { });
};
const fetchDocs = (docId) => {
const url = isArticlePage ? `${globalUrl}/api/v1/articles/${docId}` : `${globalUrl}/api/v1/docs/${docId}`;
const fetchDoc = (docId) => {
if (docId === undefined) {
return
}
var url = `${globalUrl}/api/v1/docs/${docId}`
if (location.pathname.includes("/legal")) {
url = `${globalUrl}/api/v1/docs/${docId}?folder=legal`
} else if (location.pathname.includes("/articles")) {
url = `${globalUrl}/api/v1/docs/${docId}?folder=articles`
}
fetch(url, {
method: "GET",
headers: {
@@ -902,7 +1010,7 @@ const Docs = (defaultprops) => {
});
} else {
console.log("DOCID: ", props.match.params.key)
fetchDocs(props.match.params.key)
fetchDoc(props.match.params.key)
}
}
}
@@ -911,7 +1019,7 @@ const Docs = (defaultprops) => {
// Handles search-based changes that origin from outside this file
if (serverside !== true && window.location.href !== baseUrl) {
setBaseUrl(window.location.href);
fetchDocs(props.match.params.key);
fetchDoc(props.match.params.key);
}
const markdownStyle = {
@@ -1093,9 +1201,50 @@ const Docs = (defaultprops) => {
<div style={Body}>
<div style={!isArticlePage ? SideBar : (sidebarOpen ? SideBar : collapsedSideBar)}>
<Paper style={SidebarPaperStyle}>
{modalView}
{
!isArticlePage && (
<TextField
variant="outlined"
placeholder="Search Docs..."
size="small"
onClick={() => {
setIsDocSearchModalOpen(true)
setSearchBarModalOpen(true)
}}
inputRef={searchFieldRef}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<SearchIcon style={{ color: "white" }} />
</InputAdornment>
),
style: {
backgroundColor: "#212121",
borderRadius: 4,
color: "white",
},
}}
style={{
marginTop: 10,
marginLeft: 10,
borderRadius: 4,
width: !isLoggedIn ? "14%" : !leftSideBarOpenByClick ? "12%" : "11%",
maxWidth: "285px",
position: "fixed",
zIndex: 1100,
}}
inputProps={{
readOnly: true,
}}
/>
)
}
<List style={{
listStyle: "none",
paddingLeft: "0",
paddingTop: !isArticlePage ? "60px" : undefined,
paddingBottom: !isArticlePage ? "30px" : undefined,
display: isArticlePage ? sidebarOpen ? "block" : "none" : undefined,
opacity: isArticlePage ? sidebarOpen ? 1 : 0 : undefined,
transition: isArticlePage ? "opacity 0.3s ease" : undefined,
@@ -1106,7 +1255,14 @@ const Docs = (defaultprops) => {
if (item === undefined) {
return null;
}
const path = "/docs/" + item;
var path = "/docs/" + item;
if (location.pathname.includes("/legal")) {
path = "/legal/" + item
} else if (location.pathname.includes("/articles")) {
path = "/articles/" + item
}
const newname =
item.charAt(0).toUpperCase() +
item.substring(1).split("_").join(" ").split("-").join(" ");
+109 -63
View File
@@ -5,8 +5,8 @@ import { useNavigate, Link, useParams } from "react-router-dom";
import { isMobile } from "react-device-detect";
import { toast } from 'react-toastify';
import { useInterval } from "react-powerhooks";
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';
import {
ConnectingAirportsOutlined,
ConstructionOutlined,
@@ -81,21 +81,6 @@ const useStyles = makeStyles({
notchedOutline: {
borderColor: "#f85a3e !important"
},
marketplaceButton: {
width: "100%",
justifyContent: "flex-start",
padding: "12px",
marginBottom: "12px",
backgroundColor: "#1A1A1A",
border: "1px solid #494949",
borderRadius: "8px",
color: "white",
opacity: 0.7,
'&:hover': {
opacity: 1,
cursor: "not-allowed",
},
},
marketplaceIcon: {
width: 28,
height: 28,
@@ -174,6 +159,14 @@ const FreePlanCard = ({ classes }) => {
const MarketplaceCard = ({ classes }) => {
const marketplaceOptions = [
{
name: "Open Source Install",
logo: "https://static.cdnlogo.com/logos/g/69/github-icon.svg",
tooltipText: "Click to install!",
valid: true,
link: "https://github.com/shuffle/shuffle/blob/main/.github/install-guide.md"
},
{
name: "Amazon Web Services",
logo: "https://cdn.cdnlogo.com/logos/a/19/aws.svg",
@@ -213,31 +206,50 @@ const MarketplaceCard = ({ classes }) => {
Self Host
</h2>
{marketplaceOptions.map((option, index) => (
<Tooltip
key={index}
title={option.tooltipText}
placement="top"
arrow
>
<button
className={classes.marketplaceButton}
disabled
{marketplaceOptions.map((option, index) => {
return (
<Tooltip
key={index}
title={option.tooltipText}
placement="top"
arrow
>
<img
src={option.logo}
alt={option.name}
className={classes.marketplaceIcon}
style={{
filter: option.valid === true ? null : "grayscale(1)",
<button
onClick={() => {
if (option.valid === true) {
window.open(option.link, "_blank")
}
}}
/>
<Typography className={classes.marketplaceText}>
{option.name}
</Typography>
</button>
</Tooltip>
))}
style={{
width: "100%",
justifyContent: "flex-start",
padding: "12px",
marginBottom: "12px",
backgroundColor: "#1A1A1A",
border: "1px solid #494949",
borderRadius: "8px",
color: "white",
opacity: 0.7,
cursor: option.valid === true ? "pointer" : "not-allowed",
}}
disabled={option.valid === false}
>
<img
src={option.logo}
alt={option.name}
className={classes.marketplaceIcon}
style={{
filter: option.valid === true ? null : "grayscale(1)",
}}
/>
<Typography className={classes.marketplaceText}>
{option.name}
</Typography>
</button>
</Tooltip>
)
})}
<div style={{
marginTop: "0.5rem",
@@ -256,7 +268,7 @@ const MarketplaceCard = ({ classes }) => {
</a>
*/}
<a
href="/docs/configuration#installing-shuffle"
href="/docs/configuration#installation"
target="_blank"
style={{ color: "rgba(255, 132, 68, 1)", textDecoration: "underline" }}
>
@@ -282,7 +294,7 @@ const LoginPage = props => {
const [register, setRegister] = useState(inregister);
const [checkboxClicked, setCheckboxClicked] = useState(false);
const [loginWithSSO, setLoginWithSSO] = useState(false)
const [showPassword, setShowPassword] = useState(false)
const [ssoUrl, setSSOUrl] = useState("");
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io";
@@ -315,7 +327,15 @@ const LoginPage = props => {
} else {
document.title = "Register to Shuffle SaaS"
}
}
}
useEffect(() => {
if (loginWithSSO && window?.location?.pathname === "/register") {
setLoginWithSSO(false)
}
},[window?.location?.pathname])
// Just a way to force location loading properly
// Register & login should be split :3
@@ -354,7 +374,8 @@ const LoginPage = props => {
color: "white",
padding: "40px",
flex: 1,
maxWidth: isMobile ? "100%" : "550px",
maxWidth: isMobile ? "100%" : 410,
minWidth: 410,
background: "#212121",
borderRadius: "12px",
display: "flex",
@@ -461,7 +482,6 @@ const LoginPage = props => {
})
}
const onSubmit = (e) => {
//toast("Testing from login page")
@@ -650,26 +670,16 @@ const LoginPage = props => {
setLoginWithSSO(true)
}
//const onClickRegister = () => {
// if (props.location.pathname === "/login") {
// window.location.pathname = "/register"
// } else {
// window.location.pathname = "/login"
// }
// setLoginCheck(!register)
//}
//var loginChange = register ? (<div><p onClick={setLoginCheck(false)}>Want to register? Click here.</p></div>) : (<div><p onClick={setLoginCheck(true)}>Go back to login? Click here.</p></div>);
var formtitle = register ? <div>Welcome Back!</div> : <div>Create your account</div>
var formButton = !isCloud ? "" : register ? <div style={{ display: "flex" }}> <div style={{ fontSize: "14px", paddingRight: "7px", textDecoration: "none", }}>Dont have an account yet?</div> <Link style={hrefStyle} to={`/register${parsedsearch}`}><div>Register here</div></Link></div> : <>
<div style={{ display: "flex", marginTop: 40, marginBottom: -10 }}> <div style={{ fontSize: "14px", paddingRight: "7px", textDecoration: "none", }}>Already have an account?</div> <Link style={hrefStyle} to={`/login${parsedsearch}`}><div>Login here</div></Link></div>
</>
//<Link to={`/login${parsedsearch}`} style={hrefStyle}><div>Click here to Login</div></Link>
// <DialogTitle>{formtitle}</DialogTitle>
const buttonBackground = "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)"
const buttonBackground = "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)"
const buttonStyle = { borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(username, password) || loginLoading || (checkboxClicked && register) ? buttonBackground : "grey", color: "white" }
//<Button variant="contained" type="submit" fullWidth style={buttonStyle} disabled={!handleValidateForm() || loginLoading}>
@@ -775,15 +785,29 @@ const LoginPage = props => {
color: "white",
fontSize: "1em",
},
endAdornment: (
<Tooltip title={showPassword ? "Hide password" : "Show password"} arrow>
<div
style={{
cursor: "pointer",
marginRight: 10,
color: "white"
}}
onClick={() => setShowPassword(!showPassword)}
>
{showPassword ? <VisibilityOutlinedIcon /> : <VisibilityOffOutlinedIcon />}
</div>
</Tooltip>
)
}}
required
id="outlined-password-input"
fullWidth={true}
type="password"
autoComplete="current-password"
placeholder="at least 10 characters"
margin="normal"
variant="outlined"
type={showPassword ? "text" : "password"}
onChange={onChangePass}
helperText={
handleValidateForm(username, password)
@@ -907,7 +931,10 @@ const LoginPage = props => {
{loginInfo}
</div>
{ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0 ? (
{(
ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length) > 0
//|| (isCloud && !loginWithSSO && window?.location?.pathname !== "/register")
? (
<div>
<Typography style={{ textAlign: "center" }}>Or</Typography>
<div style={{ textAlign: "center", margin: 10 }}>
@@ -917,11 +944,17 @@ const LoginPage = props => {
color="secondary"
variant="outlined"
type="button"
style={{ flex: "1", marginTop: 5 }}
onClick={() => {
style={{ flex: "1", marginTop: 5, textTransform: 'none', fontSize: 16 }}
onClick={(e) => {
//console.log("CLICK SSO");
window.location.href = ssoUrl
e.preventDefault();
//navigate(ssoUrl)
if (isCloud) {
setLoginWithSSO(true)
setPassword("")
}else {
window.location.href = ssoUrl
}
}}
>
Use SSO
@@ -929,6 +962,19 @@ const LoginPage = props => {
</div>
</div>
) : null}
{isCloud && loginWithSSO && (
<Button
variant="outlined"
color="secondary"
onClick={() => {
setLoginWithSSO(false)
setLoginInfo("")
}}
style={{ width: '100%', marginTop: 5, flex: "1", textTransform: 'none', fontSize: 16 }}
>
Back to login
</Button>
)}
</form>
</Paper>
@@ -952,7 +998,7 @@ const LoginPage = props => {
</div>
return (
<div style={{zoom: 0.8, paddingTop: 75, paddingBottom: 90 }}>
<div style={{zoom: 0.8, minHeight: "75vh", paddingTop: 75, paddingBottom: 90 }}>
{loadedCheck}
</div>
)
+2
View File
@@ -1197,6 +1197,7 @@ const RunWorkflow = (defaultprops) => {
<form onSubmit={(e) => {onSubmit(e)}} style={{margin: "25px 0px 15px 0px",}}>
{workflowQuestion !== "" || (workflow.form_control.input_markdown !== undefined && workflow.form_control.input_markdown !== null && workflow.form_control.input_markdown.length > 0) ? null :
<div>
{/*
<img
alt={workflow.name}
src={image}
@@ -1211,6 +1212,7 @@ const RunWorkflow = (defaultprops) => {
left: 200,
}}
/>
*/}
<Typography variant="h6" style={{marginBottom: 10, marginTop: 50, textAlign: "center", }}>
{organization}
+14 -4
View File
@@ -724,6 +724,8 @@ const Workflows2 = (props) => {
const [apps, setApps] = React.useState([]);
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
document.title = "Shuffle - Workflows";
useEffect(() => {
@@ -2327,7 +2329,7 @@ const Workflows2 = (props) => {
key={"explore forms"}
>
<EditNoteIcon style={{ marginLeft: 0, marginRight: 8 }} />
{"Create Form"}
{"Edit Form"}
</MenuItem>
<Divider />
@@ -4289,7 +4291,7 @@ const Workflows2 = (props) => {
display: "flex",
flexDirection: "column",
width: "100%",
maxWidth: "70%",
maxWidth: isSafari ? "100%" : "70%",
margin: "auto",
}}>
<Typography variant="h4" style={{ marginBottom: 20, paddingLeft: 15, textTransform: 'none', fontFamily: theme?.typography?.fontFamily }}>
@@ -4616,6 +4618,7 @@ const Workflows2 = (props) => {
variant="contained"
color="primary"
onClick={handleCreateWorkflow}
id="create_workflow_button"
style={{
borderRadius: 4,
flex: 0.8,
@@ -5060,8 +5063,9 @@ const Workflows2 = (props) => {
</iframe>
</Dialog>
//isLoaded && isLoggedIn && workflowDone ? (
const loadedCheck =
isLoaded && isLoggedIn && workflowDone ? (
workflowDone ? (
<div>
{/*
<ShepherdTour steps={newSteps} tourOptions={tourOptions}>
@@ -5128,8 +5132,14 @@ const Workflows2 = (props) => {
</div>
);
const safariStyle = {
transform: 'scale(0.7)',
transformOrigin: 'top',
width: '100%',
height: '100%',
}
// Maybe use gridview or something, idk
return <div style={{zoom: 0.7, }}>{loadedCheck}</div>;
return <div style={isSafari ? safariStyle : {zoom: 0.7, minHeight: "80vh",}}>{loadedCheck}</div>;
};
+10 -9
View File
@@ -1,7 +1,8 @@
module orborus
go 1.22.0
toolchain go1.24.1
go 1.23.0
toolchain go1.23.6
//replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared
@@ -9,7 +10,7 @@ require (
github.com/docker/docker v27.5.0+incompatible
github.com/docker/go-connections v0.5.0
github.com/satori/go.uuid v1.2.0
github.com/shuffle/shuffle-shared v0.8.7
github.com/shuffle/shuffle-shared v0.8.18
k8s.io/api v0.30.2
k8s.io/apimachinery v0.30.2
)
@@ -96,14 +97,14 @@ require (
go.opentelemetry.io/otel/trace v1.33.0 // indirect
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
-14
View File
@@ -301,8 +301,6 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fc
github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shuffle/shuffle-shared v0.8.7 h1:+UdRx7b/KUy/E92ODNr87UvCI2Dxfnv0samQWuggJu4=
github.com/shuffle/shuffle-shared v0.8.7/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
@@ -366,8 +364,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -423,8 +419,6 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -440,8 +434,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -468,14 +460,10 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -486,8 +474,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
+72 -57
View File
@@ -456,6 +456,7 @@ func deployServiceWorkers(image string) {
}
}
/*
isMemcachedRunning, err := checkMemcached(ctx, dockercli)
if err != nil {
log.Printf("[ERROR] Failed checking memcached: %s", err)
@@ -466,8 +467,10 @@ func deployServiceWorkers(image string) {
}
ip := "shuffle-cache"
os.Setenv("SHUFFLE_MEMCACHED", fmt.Sprintf("%s:11211", ip))
if len(os.Getenv("SHUFFLE_MEMCACHED")) == 0 {
os.Setenv("SHUFFLE_MEMCACHED", fmt.Sprintf("%s:11211", ip))
}
*/
defaultNetworkAttach := false
if containerId != "" {
@@ -608,7 +611,7 @@ func deployServiceWorkers(image string) {
fmt.Sprintf("SHUFFLE_LOGS_DISABLED=%s", os.Getenv("SHUFFLE_LOGS_DISABLED")),
fmt.Sprintf("DEBUG_MEMORY=%s", os.Getenv("DEBUG_MEMORY")),
fmt.Sprintf("SHUFFLE_APP_SDK_TIMEOUT=%s", os.Getenv("SHUFFLE_APP_SDK_TIMEOUT")),
fmt.Sprintf("SHUFFLE_MAX_SWARM_NODES=%d", os.Getenv("SHUFFLE_MAX_SWARM_NODES")),
fmt.Sprintf("SHUFFLE_MAX_SWARM_NODES=%s", os.Getenv("SHUFFLE_MAX_SWARM_NODES")),
fmt.Sprintf("SHUFFLE_BASE_IMAGE_NAME=%s", os.Getenv("SHUFFLE_BASE_IMAGE_NAME")),
fmt.Sprintf("SHUFFLE_APP_REQUEST_TIMEOUT=%s", os.Getenv("SHUFFLE_APP_REQUEST_TIMEOUT")),
},
@@ -766,42 +769,40 @@ func handleBackendImageDownload(ctx context.Context, images string) error {
// Remove the image
handled := []string{}
log.Printf("[DEBUG] Should remove existing image (s): %s. Waiting 30 seconds to ensure backend has the latest images built and ready to distribute.", images)
removeOptions := image.RemoveOptions{}
time.Sleep(time.Duration(30) * time.Second)
log.Printf("[DEBUG] Removing existing image (s): %s. Waiting 30 seconds before starting to ensure backend has the latest images built and ready to distribute.", images)
//time.Sleep(time.Duration(30) * time.Second)
newImages := []string{}
for _, image := range strings.Split(images, ",") {
image = strings.TrimSpace(image)
if shuffle.ArrayContains(handled, image) {
for _, curimage := range strings.Split(images, ",") {
curimage = strings.TrimSpace(curimage)
if shuffle.ArrayContains(handled, curimage) {
continue
}
handled = append(handled, image)
if !strings.Contains(image, "/") {
image = fmt.Sprintf("frikky/shuffle:%s", image)
handled = append(handled, curimage)
if !strings.Contains(curimage, "/") {
curimage = fmt.Sprintf("frikky/shuffle:%s", curimage)
}
newImages = append(newImages, image)
newImages = append(newImages, curimage)
// Force remove the current image to avoid cached layers
_, err := dockercli.ImageRemove(ctx, curimage, image.RemoveOptions{
Force: true,
PruneChildren: true,
})
// There is no real point in actual removal. This may however be a good idea, as Worker will force download the new one anyway
resp, err := dockercli.ImageRemove(ctx, image, removeOptions)
if err != nil {
log.Printf("[ERROR] Failed removing image: %s. Resp: %#v", err, resp)
// Goroutining images that don't already exist, as they are most likely not the correct one
go shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, image)
log.Printf("[ERROR] Failed removing image for re-download: %s", err)
} else {
log.Printf("[DEBUG] Removed image: %s", image)
log.Printf("[DEBUG] Removed image: %s", curimage)
}
err = shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, image)
if err != nil {
log.Printf("[ERROR] Failed downloading image: %s", err)
} else {
log.Printf("[DEBUG] Downloaded image: %s", image)
//break
}
err = shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, curimage)
if err != nil {
log.Printf("[ERROR] Failed downloading image: %s", err)
} else {
log.Printf("[DEBUG] Downloaded image: %s", curimage)
}
}
@@ -820,20 +821,18 @@ func handleBackendImageDownload(ctx context.Context, images string) error {
)
if err != nil {
log.Printf("[ERROR] Failed finding containers: %s", err)
log.Printf("[ERROR] Failed finding services: %s", err)
} else {
log.Printf("[DEBUG] Found %d services", len(services))
found := false
for _, service := range services {
log.Printf("Imagename: %s", service.Spec.TaskTemplate.ContainerSpec.Image)
//log.Printf("Service image: %s", service.Spec.TaskTemplate.ContainerSpec.Image)
for _, image := range newImages {
if !strings.Contains(service.Spec.TaskTemplate.ContainerSpec.Image, image) {
continue
}
log.Printf("[DEBUG] Found service for image %#v: %#v", service.Spec.Annotations.Name)
log.Printf("[DEBUG] Found service for image: %#v", service.Spec.Annotations.Name)
// Update the service to run with the new image
//docker service update --image username/imagename:latest servicename --force
@@ -853,9 +852,20 @@ func handleBackendImageDownload(ctx context.Context, images string) error {
if !strings.Contains(fmt.Sprintf("%s", resp), "error") {
break
} else {
found = true
log.Printf("[ERROR] Failed updating service %s with the new image %s: %s. Resp: %#v", service.Spec.Annotations.Name, image, err, resp)
}
}
}
if found {
break
}
}
if !found {
log.Printf("[DEBUG] Failed to find service to update for service %s", newImages)
}
}
@@ -1479,29 +1489,34 @@ func initializeImages() {
}
// check whether they are the same first
images := []string{
fmt.Sprintf("frikky/shuffle:app_sdk"),
fmt.Sprintf("shuffle/shuffle:app_sdk"),
fmt.Sprintf("%s/%s/shuffle-app_sdk:%s", baseimageregistry, baseimagename, appSdkVersion),
newWorker,
}
pullOptions := image.PullOptions{}
for _, image := range images {
if isKubernetes == "true" {
log.Printf("[DEBUG] Skipping image pull of '%s' because Kubernetes does it in realtime instead", image)
} else {
log.Printf("[DEBUG] Pulling image %s", image)
reader, err := dockercli.ImagePull(ctx, image, pullOptions)
if err != nil {
log.Printf("[ERROR] Failed getting image %s: %s", image, err)
continue
}
io.Copy(os.Stdout, reader)
log.Printf("[DEBUG] Successfully downloaded and built %s", image)
if os.Getenv("SHUFFLE_AUTO_IMAGE_DOWNLOAD") != "true" {
images := []string{
fmt.Sprintf("frikky/shuffle:app_sdk"),
fmt.Sprintf("shuffle/shuffle:app_sdk"),
fmt.Sprintf("%s/%s/shuffle-app_sdk:%s", baseimageregistry, baseimagename, appSdkVersion),
newWorker,
}
pullOptions := image.PullOptions{}
for _, image := range images {
if isKubernetes == "true" {
log.Printf("[DEBUG] Skipping image pull of '%s' because Kubernetes does it in realtime instead", image)
} else {
log.Printf("[DEBUG] Pulling image %s", image)
reader, err := dockercli.ImagePull(ctx, image, pullOptions)
if err != nil {
log.Printf("[ERROR] Failed getting image %s: %s", image, err)
continue
}
io.Copy(os.Stdout, reader)
log.Printf("[DEBUG] Successfully downloaded and built %s", image)
}
}
} else {
log.Printf("[DEBUG] Skipping image download as SHUFFLE_AUTO_IMAGE_DOWNLOAD is set to true")
}
}
@@ -2245,7 +2260,7 @@ func main() {
toBeRemoved.Data = append(toBeRemoved.Data, incRequest)
} else if incRequest.Type == "DOCKER_IMAGE_DOWNLOAD" {
log.Printf("[INFO] Should delete -> download new images: %#v", incRequest.ExecutionArgument)
log.Printf("[INFO] Re-downloading new image(s): %#v", incRequest.ExecutionArgument)
if len(incRequest.ExecutionArgument) > 0 {
// FIXME: Wait X seconds before running this as the image build may not be done yet. This is shitty, but may be ok to do in Orborus. Easy fix for the future: Just let it run through jobs 5-10 times before actually picking it up
@@ -3462,7 +3477,7 @@ func sendPipelineHealthStatus() (shuffle.LakeConfig, error) {
} else {
tenzirDisabled = true
log.Printf("[ERROR] Disabling pipelines: %s. You will need to restart the Orborus to fix this.", err)
log.Printf("[WARNING] Disabling pipelines: %s. You will need to restart the Orborus to fix this.", err)
}
return pipelinePayload, err
+10 -9
View File
@@ -1,13 +1,14 @@
module worker
go 1.22.7
toolchain go1.24.1
go 1.23.0
toolchain go1.23.7
require (
github.com/docker/docker v27.5.0+incompatible
github.com/gorilla/mux v1.8.1
github.com/satori/go.uuid v1.2.0
github.com/shuffle/shuffle-shared v0.8.7
github.com/shuffle/shuffle-shared v0.8.18
k8s.io/api v0.30.2
k8s.io/apimachinery v0.30.2
k8s.io/client-go v0.30.2
@@ -97,14 +98,14 @@ require (
go.opentelemetry.io/otel/trace v1.33.0 // indirect
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/api v0.162.0 // indirect
-14
View File
@@ -294,8 +294,6 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fc
github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shuffle/shuffle-shared v0.8.7 h1:+UdRx7b/KUy/E92ODNr87UvCI2Dxfnv0samQWuggJu4=
github.com/shuffle/shuffle-shared v0.8.7/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
@@ -359,8 +357,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -415,8 +411,6 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -432,8 +426,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -459,14 +451,10 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -477,8 +465,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+2 -5
View File
@@ -57,11 +57,10 @@ var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP"))
var swarmNetworkName = os.Getenv("SHUFFLE_SWARM_NETWORK_NAME")
var dockerApiVersion = strings.ToLower(os.Getenv("DOCKER_API_VERSION"))
var baseimagename = "frikky/shuffle"
var kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE")
var executionCount int64
// var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME")
var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME")
// var baseimagename = "registry.hub.docker.com/frikky/shuffle"
var registryName = "registry.hub.docker.com"
@@ -106,9 +105,7 @@ var window = shuffle.NewTimeWindow(10 * time.Second)
// Images to be autodeployed in the latest version of Shuffle.
var autoDeploy = map[string]string{
"http:1.4.0": "frikky/shuffle:http_1.4.0",
"http:1.3.0": "frikky/shuffle:http_1.3.0",
"shuffle-tools:1.2.0": "frikky/shuffle:shuffle-tools_1.2.0",
"shuffle-subflow:1.0.0": "frikky/shuffle:shuffle-subflow_1.0.0",
"shuffle-subflow:1.1.0": "frikky/shuffle:shuffle-subflow_1.1.0",
// "shuffle-tools-fork:1.0.0": "frikky/shuffle:shuffle-tools-fork_1.0.0",
}
@@ -3726,7 +3723,7 @@ func main() {
}
if baseimagename == "" {
log.Printf("[DEBUG] Setting baseimagename")
log.Printf("[DEBUG] Setting baseimagename to frikky/shuffle")
baseimagename = "frikky/shuffle" // Dockerhub
//baseimagename = "shuffle" // Github (ghcr.io)
}