Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

postinstall script: support monorepos #191

Merged
merged 42 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
799a309
`postinstall` script finds existing `cds-types` installation
chgeo Aug 12, 2024
7e57113
Add test
chgeo Aug 12, 2024
44750e5
Troubleshoot
chgeo Aug 12, 2024
f3cb19c
Merge branch 'main' into install-monorepos
joergmann Aug 13, 2024
577421a
add echo homedir
joergmann Aug 13, 2024
a56e1bf
add homedir
joergmann Aug 13, 2024
1586516
fix var call
joergmann Aug 13, 2024
274d4ef
fix reg path
joergmann Aug 13, 2024
0c135ae
add user login
joergmann Aug 13, 2024
2fb0558
simpler approach
joergmann Aug 13, 2024
aa6c03c
use net user
joergmann Aug 13, 2024
d5c6ddd
quote cmd
joergmann Aug 13, 2024
76ca8e2
correct quoting
joergmann Aug 13, 2024
bdfafa2
set $HOME
joergmann Aug 13, 2024
656c0d4
try global permissions
joergmann Aug 13, 2024
553d3b1
use vars
joergmann Aug 13, 2024
6f45a54
next
joergmann Aug 13, 2024
eb92743
remove envvar
joergmann Aug 13, 2024
bcc1980
netx
joergmann Aug 13, 2024
b1e89d7
external script
joergmann Aug 13, 2024
1a45ac3
go
joergmann Aug 13, 2024
15ed9a2
use reg
joergmann Aug 13, 2024
d9cf97a
echo sid
joergmann Aug 13, 2024
b371fda
next try
joergmann Aug 13, 2024
7e767f7
next try
joergmann Aug 13, 2024
3af7f48
try env:run
joergmann Aug 13, 2024
5806d30
go
joergmann Aug 13, 2024
e4949ec
escape dollar
joergmann Aug 13, 2024
f6d5f88
use \n
joergmann Aug 13, 2024
1afc505
use ticks
joergmann Aug 13, 2024
47d7c92
more quoting
joergmann Aug 13, 2024
dba0fe9
more quoting
joergmann Aug 13, 2024
465acbf
revert
joergmann Aug 13, 2024
bf3f2d6
fix
joergmann Aug 13, 2024
11dd2a5
next
joergmann Aug 13, 2024
1f7b449
add /T
joergmann Aug 13, 2024
1d4fcb7
use home
joergmann Aug 13, 2024
146b169
next
joergmann Aug 13, 2024
c248941
use runneradmin
joergmann Aug 13, 2024
943efe0
fix windows runner script
joergmann Aug 13, 2024
4587622
Remove logs
chgeo Aug 13, 2024
9be508a
Apply suggestions from code review
chgeo Aug 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions .github/actions/run-as-non-admin/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,43 @@ runs:
# fail if not on windows
if ($env:OS -ne "Windows") { exit 1 }

# make temp folder writable for all users
icacls $env:TEMP /grant "Everyone:(OI)(CI)F"

$username = "nonadminuser"
# random password fulfilling win requirements
$password = ConvertTo-SecureString "abcdEFGH123$%" -AsPlainText -Force
$newHomeDir = "C:\Users\$username"

New-LocalUser $username -Password $password
Add-LocalGroupMember -Group "Users" -Member $username
New-LocalUser $username -Password $password | Out-Null
Add-LocalGroupMember -Group "Users" -Member $username | Out-Null
$credential = New-Object System.Management.Automation.PSCredential ($username, $password)

# remove dev mode so symlink fails if called without junction
reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /f

# call command using non admin user
# create temp folder
New-Item -ItemType Directory -Path "$newHomeDir\AppData\Local\Temp" -Force

# make temp folder writable for nonadmin user
icacls "$newHomeDir" /grant "${username}:(OI)(CI)F" /T

# using start-process to run command as non admin user requires setting env vars
$envVars = @{
HOME = $newHomeDir
HOMEPATH = "\Users\$username"
TEMP = "$newHomeDir\AppData\Local\Temp"
TMP = "$newHomeDir\AppData\Local\Temp"
USERNAME = $username
USERPROFILE = $newHomeDir
}

# call command using non admin user credentials
$process = Start-Process -FilePath "pwsh" `
-ArgumentList "-NoLogo", "-NonInteractive", "-NoProfile", "-Command", $env:RUN `
-Credential $credential `
-PassThru `
-Wait `
-Environment $envVars `
-NoNewWindow `
-RedirectStandardOutput "output.txt" `
-PassThru `
-RedirectStandardError "error.txt" `
-RedirectStandardOutput "output.txt" `
-Wait `

Get-Content output.txt
Get-Content error.txt
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
node-version: ${{ matrix.version }}

- run: |
npm i -g npm
npm ci
npm install file:. --no-save --force
npm run prerelease:ci-fix
Expand All @@ -48,6 +49,8 @@ jobs:
with:
run: |
echo "whoami:$(whoami)"
echo "home:$HOME"
echo "userprofile:$USERPROFILE"
npm run test:integration

- name: Run integration tests
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
The format is based on [Keep a Changelog](http://keepachangelog.com/).

## Version 0.7.0 - TBD
## Version 0.6.5 - TBD
### Fixed
- The `@types/sap__cds` link created by the `postinstall` script now also works in monorepo setups where the target `@cap-js/cds-types` might already preinstalled (often hoisted some levels up).

## Version 0.6.4 - 2024-08-05
### Added
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cap-js/cds-types",
"version": "0.6.4",
"version": "0.6.5",
"description": "Type definitions for main packages of CAP, like `@sap/cds`",
"repository": "github:cap-js/cds-types",
"homepage": "https://cap.cloud.sap/",
Expand All @@ -20,7 +20,7 @@
],
"scripts": {
"test": "jest --silent",
"test:integration": "jest --silent --testMatch \"**/test/**/*.integrationtest.js\"",
"test:integration": "jest --testMatch \"**/test/**/*.integrationtest.js\"",
"test:rollup": "npm run rollup; npm run rollup:on; npm run test; npm run rollup:off",
"rollup": "rm -rf dist/ && mkdir -p etc/ && npx -y @microsoft/api-extractor run --local --verbose && .github/rollup-patch.js",
"rollup:on": "npm pkg set typings=dist/cds-types.d.ts && [ -d 'apis' ] && mv -- apis -apis || true",
Expand Down
20 changes: 15 additions & 5 deletions scripts/postinstall.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
/* eslint-disable no-undef */
/* eslint-disable @typescript-eslint/no-require-imports */
const fs = require('node:fs')
const { join, relative, dirname } = require('node:path')
const { join, relative, dirname, resolve } = require('node:path')

if (!process.env.INIT_CWD) return
// TODO: check if were in a local install

const nodeModules = join(process.env.INIT_CWD, 'node_modules')
if (!fs.existsSync(nodeModules)) return
const typesDir = join(nodeModules, '@types')
if (!fs.existsSync(typesDir)) fs.mkdirSync(typesDir)
// we may have to create node_modules altogether in case of a mono repo
if (!fs.existsSync(typesDir)) fs.mkdirSync(typesDir, {recursive: true})
chgeo marked this conversation as resolved.
Show resolved Hide resolved

// use a relative target, in case the user moves the project
const target = join(typesDir, 'sap__cds')
const src = join(nodeModules, '@cap-js/cds-types')
const src = resolvePkg('@cap-js/cds-types') ?? join(nodeModules, '@cap-js/cds-types')
const rel = relative(dirname(target), src) // need dirname or we'd land one level above node_modules (one too many "../")
console.log(`Creating symlink ${target} -> ${rel}`)

// remove the existing symlink
try {
Expand All @@ -31,3 +32,12 @@ try {
if (e.code !== 'EEXIST') throw e
// else: symlink exists (the previous unlink hasn't worked), ignore
}

function resolvePkg(pkg) {
try {
const pjson = require.resolve(join(pkg, 'package.json'), { paths: [process.env.INIT_CWD] })
return resolve(pjson, '..')
} catch {
return null
}
}
43 changes: 42 additions & 1 deletion test/postinstall.integrationtest.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ describe('postinstall', () => {

beforeEach(async () => {
tempFolder = await fs.mkdtemp(path.join(os.tmpdir(), 'postinstall-'))
// console.log(`tempFolder: ${tempFolder}`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// console.log(`tempFolder: ${tempFolder}`)

})

afterEach(async () => {
Expand All @@ -39,12 +40,52 @@ describe('postinstall', () => {

// after renaming the project folder, the symlink must be recreated on windows
if (IS_WIN) {
await execAsync('npm i', { cwd: newProjectFolder })
await execAsync('npm i --foreground-scripts', { cwd: newProjectFolder })
}

typesPackageJsonFile = path.join(newProjectFolder, 'node_modules/@types/sap__cds/package.json')
typesPackageJsonFileContent = await fs.readFile(typesPackageJsonFile, 'utf8')
packageJson = JSON.parse(typesPackageJsonFileContent)
expect(packageJson.name).toBe('@cap-js/cds-types')
})

test('create symlink in monorepo', async () => {
const rootFolder = path.join(tempFolder, 'monorepo')
await fs.mkdir(rootFolder, { recursive: true, force: true })
await fs.writeFile(path.join(rootFolder, 'package.json'), JSON.stringify({
name: 'monorepo', workspaces: [ 'packages/**' ]
}, null, 2))

// create a first project, add the dependency to cds-types
const project1 = path.join(rootFolder, 'packages/project1')
await fs.mkdir(project1, { recursive: true, force: true })
await fs.writeFile(path.join(project1, 'package.json'), JSON.stringify({
name: 'project1'
}, null, 2))
{
// const {stdout, stderr} =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// const {stdout, stderr} =

await execAsync(`npm i --foreground-scripts -dd -D ${cdsTypesRoot}`, { cwd: project1 })
// console.log(stdout, stderr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// console.log(stdout, stderr)

}
let packageJson = require(path.join(project1, 'node_modules/@types/sap__cds/package.json'))
expect(packageJson.name).toBe('@cap-js/cds-types')

// now create a second project with the dependency
const project2 = path.join(rootFolder, 'packages/project2')
await fs.mkdir(project2, { recursive: true, force: true })
await fs.writeFile(path.join(project2, 'package.json'), JSON.stringify({
name: 'project2',
devDependencies: {
'@cap-js/cds-types': `file:${cdsTypesRoot}`
}
}, null, 2))
{
// const {stdout, stderr} =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// const {stdout, stderr} =

await execAsync(`npm i --foreground-scripts -dd`, { cwd: project2 })
// console.log(stdout, stderr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// console.log(stdout, stderr)

}
packageJson = require(path.join(project2, 'node_modules/@types/sap__cds/package.json'))
expect(packageJson.name).toBe('@cap-js/cds-types')

})
})