How do I use vsts-task-lib to get the path where build task's files are dropped? - azure-pipelines-build-task

My custom build task is running on an agent, and its files are located at E:\agent_work\_tasks\MyTaskName_106598a6-d5ba-4038-8dc8-ba0172210a94\0.0.13. Is there a way to use vsts-task-lib to get this path?
If I were able to get the task id, name, and version sourced from the task.json, I could determine the path myself by doing something like, tl.getVariable('Agent.WorkFolder') + '\\_tasks\\' + taskName + '_' + taskIdentifier + '\\' + taskVersion.
I had my eyes on https://github.com/Microsoft/vsts-task-lib/blob/master/node/docs/vsts-task-lib.md#taskgetTaskVariable, but wasn't sure of the variable names to use or if this was even along the right path.

You can get current path by using __dirname (nodejs) or $PSScriptRoot (powershell) in your build/release task extension.
For example:
NodeJs:
console.log(__dirname)
PowerShell:
Write-Host $PSScriptRoot

Related

Python subprocess run or Popen running as specific user on Windows

Been working through this for a minute now, and figured I would ask here as I am sure it is something simple that I am missing.
Trying to silently run an installer using subprocess.run() (Have used subprocess.Popen, and os.system as well). I pass the full installer path, along with the arguments (As a list). Using all of the above, the installer runs successfully, and python waits for the installer to finish. I added a check to look at the PID of the installer process as just a backup check. However, running the script in full, I install some prerequisites (mainly dotnet 4.8) and then I restart. For cleanliness, I add the python script + arguments to the windows RunOnce registry key so it runs on next login. That works fine, but the installer never actually runs. It returns a 0 exit code, but never actually runs. Digging into it with event viewer, child processes (and maybe the parent process) is run by the NT System account. This causes the installer to silently fails since the System account doesn't have the permissions to run it. Looking for a way to run everything as a user to avoid the System account being used. Hence the question about running as an alternate user or the logged in user. (I don't know if this works on windows in all honesty)
Thoughts? Sample code snippets are below as well. Targeting Windows Server 2016+. Using python 3.11.2
Typical install code looks like the below. Installing it with subprocess works fine when run from command prompt manually. Its only when the RunOnce registry key triggers it does it silently fail. This leads me to believe its just the permissions/user error.
I also took a look at Python subprocess.Popen as different user on Windows but didn't have much luck implementing this. (Given it is 13 years old at this point)
# Installer path
installer = os.path.dirname(os.path.realpath(__file__)) + "\\setup.exe"
# Command for installing silently
# variables referenced here are all strings
ss_command = [
installer,
"-q",
"-s",
"InstallSecretServer=1",
"InstallPrivilegeManager=1",
'SecretServerUserDisplayName="Administrator"',
'SecretServerUserName="Administrator"',
"SecretServerUserPassword=" + '"' + administrator_password + '"',
"SecretServerAppUserName=" + '"' + service_account + '"',
"SecretServerAppPassword=" + '"' + service_account_password + '"',
"DatabaseIsUsingWindowsAuthentication=True",
"DatabaseServer=" + '"' + sql_hostname + '"',
"DatabaseName=" + '"' + database_name + '"',
"/l",
log_file
]
# Install attempt using Popen
installer_process = subprocess.Popen(ss_command)
# Install attempt using run
installer_process = subprocess.run(ss_command, capture_output=True, shell=False)
# Below are the functions for running executables and code from the registry.
# These work just fine, and arguments fed to the original script get passed to the registry key as well.
# Define a function to run an executable at boot
def run_at_startup_set(appname, arguments, path=None, user=False):
# Store the entry in the registry for running the application at startup
# Open the registry key path for applications that are run at login
key = win32api.RegOpenKeyEx(
win32con.HKEY_CURRENT_USER if user else win32con.HKEY_LOCAL_MACHINE,
Startup_Key_Path,
0,
win32con.KEY_WRITE | win32con.KEY_QUERY_VALUE
)
# Make sure the application is not already in the registry
i = 0
while True:
try:
name, _, _ = win32api.RegEnumValue(key, i)
except pywintypes.error as e:
if e.winerror == winerror.ERROR_NO_MORE_ITEMS:
break
else:
raise
if name == appname:
win32api.RegCloseKey(key)
return
i += 1
# Add arguments to the key
run_parameters = path or win32api.GetModuleFileName(0)
for arg in arguments:
run_parameters += ' "' + str(arg) + '"'
# Create a new key
win32api.RegSetValueEx(key, appname, 0, win32con.REG_SZ, run_parameters)
# Close the key
win32api.RegCloseKey(key)
return
# Define a function to run a script at boot
def run_script_at_startup_set(appname, arguments, user=False):
# Like run_at_startup_set(), but for source code files
run_at_startup_set(
appname,
arguments,
# Set the interpreter path (returned by GetModuleFileName())
# followed by the path of the current Python file (__file__).
'{} "{}"'.format(win32api.GetModuleFileName(0), __file__),
user
)
return

Node creating file 1 level up from the current path

I was using path.resolve but this command created a monster folder that can't be deleted called lib..
path.resolve(__dirname + "../assets/pic/" + `${fileName}.png`)
Question 1
What is the propper usage to create a folder 1 level up from the current path?
Question 2
How to remove the lib../assets/pic folder? Deleting the entire project or using git reset --hard, git stash doesn't work because Windows 10 says the folder doesn't exist.
Answer to Question 1:
tl;dr
const fs = require('fs')
const folderName1DirUp = '../SomeFolder'
try {
if (!fs.existsSync(folderName1DirUp)){
fs.mkdirSync(folderName1DirUp)
}
} catch (err) {
console.error(err)
}
The back story:
Two ways to reference the current folder in a Node.js script. You can use ./ or __dirname. Note in addition to ./ you can also use ../ which points to the parent folder.
The difference between the two.
__dirname in a Node script will return the path of the folder where the current JavaScript file resides.
./ will give you the current working directory (and ../ the parent). For ./ a call to process.cwd(). returns the same thing.
Initially the current working directory is the path of the folder where you ran the node command, but during the execution of your script it can change by calling process.chdir(...).
There is one place where ./ refers to the current file path which is in a require(...) call. There the ./, for convenience, refers to the JavaScript file path which lets you import other modules based on the folder structure.
Thus the call to fs.mkdirSync('../SomeFolder') with the given folder name makes a folder one level up from the current working directory.
Answer to Question 2:
From a PowerShell prompt Remove-Item './../Folder1UpToDelete' -Force The ./ is for the current folder. The ../ is for one folder up from the current. Finally the Folder1UpToDelete is the one folder up from the current that you want to delete. The -Force makes sure to delete all sub-folders/files under the folder you want deleted including hidden and/or read-only files.
To answer the first question, to create a path 1 level up, you can use path.join():
path.join(__dirname, "../assets/pics", `${fileName}.png`);
For the second question, if deleting it through the explorer doesn't work, you can try:
fs.rmdirSync("E:/path/to/broken/folder..");
Using Git Bash and running
cd /c/path/to/broken/
rmdir folder..

Write txt file in linux for .net Core (Docker)

I am new in Linux and my API was created in .net core and running in Docker. The system i create will write/create a txt file that will input all errors logged in the API. My code to write is this
`public class WriteLogs
{
public void ErrorLogFile(string traceNo, string errorMsg)
{
DirectoryInfo dir = new DirectoryInfo(Startup.errorPath);
if (!dir.Exists)
{
dir.Create();
}
using (StreamWriter swLog = File.AppendText(Startup.errorPath + Startup.errorFileName + DateTime.Now.ToString("MMddyyyy") + ".txt"))
{
swLog.WriteLine(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + " - Trace Number : " + traceNo + " " + errorMsg + "\n");
}
}
}`
the value in my startup is located in my appSettings.json file :
"ErrorPath": "C:\\BP\\",
"ErrorFileName": "BP-ParamLogs_",
This is working in windows environment but when i transfer my program to linux and change the ErrorPath to:
"ErrorPath": "/home/Logs/",
the file was not created.
My question is, do my syntax works in linux to write txt file or my path was wrong?
To answer your specific question about the syntax. \home\Logs\ should be /home/Logs/ as Linux uses forward-slash for path separator.
There's a chance that your program does not have write access to the /home/ directory. I've personally tried a similar program and I ran into System.UnauthorizedAccessException during the logs directory creation step.
Try running your program from the terminal with dotnet run to see the exception you might be getting. If you are also running into the System.UnauthorizedAccessException then run as root with sudo dotnet run
i just solved the problem by using command:
find . -name BP-Param*
from the first directory. After executing it. it was in the docker directory.

Restore by SQL-Script / Error at '+'

I am currently working on a full restore SQL-script and I have following Problem: While restoring you have to decide where the mdf, ndf and ldf - files of the database should be stored. To achieve that I use WITH MOVE. If I insert the path by myself it works just fine.
The problem comes up when I start using string-variables instead of the actual path (for easier use). It says that there is something wrong near +, but I can't figure out what it is. So my question is, what is it?
RESTORE DATABASE Test_EMPI --name of the database
FROM DISK = #EMPIBackupFileLocation --works just fine
WITH MOVE '5_47_4403_Official_GER_EMPI' TO #RestoreFileLocation + '\' + #EMPIName + '1.mdf', --doesnt work
MOVE 'MV_TABLES' TO 'C:\Program Files\....\DATA\Test_EMPI2.NDF', --also works
...........
Executing my script I get the Error: Incorrect syntax near '+'.
Try this way
DECLARE #destination VARCHAR(1000) = #RestoreFileLocation + '\' + #EMPIName + '1.mdf'
RESTORE DATABASE Test_EMPI --name of the database
FROM DISK = #EMPIBackupFileLocation --works just fine
WITH MOVE '5_47_4403_Official_GER_EMPI' TO #destination,
MOVE 'MV_TABLES' TO 'C:\Program Files\....\DATA\Test_EMPI2.NDF', --also works

Windows azure project replace ServiceConfiguration.Cloud.cscfg

I have two MVC projects. And I created two WindowsAzure project:WindowsAzure1-> which package MVC1 , and WindowsAzure2-> which package MVC2 project. After CheckIn on Local TFS 2012, I build my solution. MSBuild Arguments:
/t:Publish /p:PublishDir=c:\drops\app.publish\
After Build I see 3 file, instead 4.
1.WindowsAzure1.cspkg
2.WindowsAzure2.cspkg
3.ServiceConfiguration.Cloud.cscfg//It contain config WindowsAzure2.cspkg
I tried to rename ServiceConfiguration.Cloud.cscfg, but it doesn't rename.
So, I think the better place package on different folder. But problem that in the future MVC and Azure project will be unknown count. So I need automatically create folder contains name project. So how can it do?
Simple Way to create dynamic folders is through PowerShell Script.
Lets say you have folder structure of projects in following way -
Then you can following script to generate package folders -
# Solution directory, which contains all the projects
$path = "C:\Solution"
$folders = Get-ChildItem -Path $path
foreach ($folder in $folders)
{
if ($folder.Attributes -eq "Directory")
{
if($folder.Name -like "*.Cloud")
{
New-Item -Path "$($path)\$($folder.Name)package" -ItemType "Directory"
}
}
}
Output will be -
Then you can use CSPack utility and PowerShell combination to create package and save configuration file to the location of your interest.
http://www.intstrings.com/ramivemula/articles/jumpstart-30-create-azure-cloud-service-package-cspkg-of-visual-studio-2013-project-solution-using-powershell/

Resources