@graftland/package-manager 0.0.0-stage → 0.2.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +250 -2
- package/README.zh-CN.md +183 -0
- package/README.zh-TW.md +184 -0
- package/dist/PackageManagerDeclaration.d.ts +6 -0
- package/dist/PackageManagerDeclaration.d.ts.map +1 -0
- package/dist/PackageManagerDeclaration.js +24 -0
- package/dist/PackageManagerDeclaration.js.map +1 -0
- package/dist/index.d.ts +100 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +324 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +6 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +144 -0
- package/dist/manifest.js.map +1 -0
- package/dist/process.js +50 -0
- package/dist/process.js.map +1 -0
- package/dist/snapshot.d.ts +15 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +104 -0
- package/dist/snapshot.js.map +1 -0
- package/package.json +38 -3
- package/src/PackageManagerDeclaration.ts +42 -0
- package/src/index.ts +642 -0
- package/src/manifest.ts +253 -0
- package/src/process.ts +122 -0
- package/src/snapshot.ts +261 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
|
202
|
+
|
package/README.md
CHANGED
|
@@ -1,3 +1,251 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @graftland/package-manager
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
English |
|
|
4
|
+
[简体中文](https://github.com/graftland/graft/blob/main/packages/package-manager/README.zh-CN.md)
|
|
5
|
+
|
|
|
6
|
+
[繁體中文](https://github.com/graftland/graft/blob/main/packages/package-manager/README.zh-TW.md)
|
|
7
|
+
|
|
8
|
+
Installs dependencies with npm, pnpm, or Yarn, and rolls back on failure.
|
|
9
|
+
|
|
10
|
+
The minimum supported Node.js version is 22.19. Windows is provided as is.
|
|
11
|
+
Process-tree cleanup and rollback on Windows are not verified, and they are not
|
|
12
|
+
promised.
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install @graftland/package-manager effect
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`make` detects the project's package manager. Its `install` adds, updates, or
|
|
19
|
+
removes dependencies. If the install fails, it restores `package.json` and the
|
|
20
|
+
lockfile. `graft add` and `graft remove` use it.
|
|
21
|
+
|
|
22
|
+
## Installation ownership and recovery
|
|
23
|
+
|
|
24
|
+
Use one manager per project. The caller must give the manager and its child
|
|
25
|
+
processes exclusive write access to the project. This lasts from the start of an
|
|
26
|
+
`install` call until it finishes, including recovery.
|
|
27
|
+
|
|
28
|
+
- Do not edit project files during this time.
|
|
29
|
+
- Do not run another installer at the same time.
|
|
30
|
+
- Calls are serialized within one manager. Graft does not enforce a lock across
|
|
31
|
+
managers, editors, or processes.
|
|
32
|
+
|
|
33
|
+
On POSIX, process cleanup covers only the installer's original process group.
|
|
34
|
+
Installation scripts and recovery scripts must not create unmanaged writers.
|
|
35
|
+
Examples are children in independent process groups, and daemons that keep
|
|
36
|
+
writing project files. Graft does not provide OS-level containment. It does not
|
|
37
|
+
discover escaped writers. Confirming that the original group has stopped does
|
|
38
|
+
not prove that every possible descendant has stopped. Scripts that break this
|
|
39
|
+
rule are outside the rollback guarantee.
|
|
40
|
+
|
|
41
|
+
Managed paths must be regular files, or must be absent. `make` and each
|
|
42
|
+
`install` use `lstat` to check the snapshot set. The snapshot set is
|
|
43
|
+
`package.json`, lockfiles, and tracked configuration and installation-state
|
|
44
|
+
files.
|
|
45
|
+
|
|
46
|
+
- Symbolic links are rejected, including dangling links. Other file types are
|
|
47
|
+
rejected too.
|
|
48
|
+
- The error is `UnsupportedFileTypeError`. It is raised before any transaction
|
|
49
|
+
write and before any installer starts.
|
|
50
|
+
- The error shows the `path` and the `fileType`. The link and its target stay
|
|
51
|
+
unchanged.
|
|
52
|
+
- A rejection before the transaction begins does not need recovery. It does not
|
|
53
|
+
make the manager unusable.
|
|
54
|
+
|
|
55
|
+
### Rollback
|
|
56
|
+
|
|
57
|
+
Rollback runs when installation fails or is interrupted before commit. It needs
|
|
58
|
+
confirmation that the original process group has stopped. Then it does these
|
|
59
|
+
things:
|
|
60
|
+
|
|
61
|
+
- It restores the backed-up package files byte for byte. It keeps their recorded
|
|
62
|
+
permission bits and ownership.
|
|
63
|
+
- It removes tracked files that were absent before installation.
|
|
64
|
+
|
|
65
|
+
The tracked files include `package.json`, lockfiles, and the package-manager
|
|
66
|
+
configuration and installation-state files that the manager tracks.
|
|
67
|
+
|
|
68
|
+
Rollback overwrites changes made during installation. It does not detect
|
|
69
|
+
conflicts. It does not merge edits.
|
|
70
|
+
|
|
71
|
+
When the project already had a lockfile for the selected package manager,
|
|
72
|
+
recovery also tries to repair `node_modules`. Recovery errors are kept together
|
|
73
|
+
with the original failure. Recovery after a process crash is not promised.
|
|
74
|
+
|
|
75
|
+
### File writes and process cleanup
|
|
76
|
+
|
|
77
|
+
Accepted file writes have a completion boundary. Cancellation waits for the
|
|
78
|
+
native operation to finish. This happens before rollback, before snapshot
|
|
79
|
+
cleanup, and before the manager's lock is released. The transaction tracks its
|
|
80
|
+
own file writes and the original installer group. Snapshot creation and rollback
|
|
81
|
+
writes have the same completion guarantee.
|
|
82
|
+
|
|
83
|
+
Each installer process and each recovery process has a ten-minute exit wait.
|
|
84
|
+
Cancellation or expiry closes its process scope before files are restored.
|
|
85
|
+
|
|
86
|
+
- The process receives SIGTERM.
|
|
87
|
+
- If it has not stopped after five seconds, it receives SIGKILL.
|
|
88
|
+
- On POSIX, even a successful kill must be followed by evidence that the
|
|
89
|
+
original process group no longer exists. Checks run every 20 milliseconds,
|
|
90
|
+
with at most 100 additional checks (two seconds of waiting) for short-lived
|
|
91
|
+
zombie groups to be reaped. EPERM is treated as continued presence, never as
|
|
92
|
+
absence.
|
|
93
|
+
- If the group remains or a check fails, termination is unconfirmed: the manager
|
|
94
|
+
keeps its snapshot, skips rollback and recovery, and enters
|
|
95
|
+
`RecoveryRequired`.
|
|
96
|
+
- Process cleanup failures stay in the returned cause, together with
|
|
97
|
+
installation and recovery failures.
|
|
98
|
+
- The exit timeout does not include process cleanup or file restoration. It is
|
|
99
|
+
not a deadline for the whole install-and-recover operation.
|
|
100
|
+
|
|
101
|
+
The Node adapter may replace the original OS errno with a generic kill failure.
|
|
102
|
+
Only after a POSIX group check confirms absence does PackageManager resolve
|
|
103
|
+
signal failures carrying ESRCH or the adapter's specific `Unknown` kill error
|
|
104
|
+
with cause `Failed to kill child process`. This does not resolve other reasons
|
|
105
|
+
in the same Cause: join failures, other kill errors, defects, and interruption
|
|
106
|
+
remain failures. If the group still exists, the original Cause is preserved; if
|
|
107
|
+
the group check fails, both complete Causes are retained.
|
|
108
|
+
|
|
109
|
+
## Committed installs and snapshot cleanup
|
|
110
|
+
|
|
111
|
+
A successful `install` result means the dependency changes are committed.
|
|
112
|
+
|
|
113
|
+
`result.snapshotCleanupFailure` is an `Option<SnapshotCleanupFailure>`:
|
|
114
|
+
|
|
115
|
+
- `None` means snapshot deletion succeeded.
|
|
116
|
+
- `Some` contains `snapshotDirectory` and the complete cleanup `cause`,
|
|
117
|
+
including defects.
|
|
118
|
+
|
|
119
|
+
A failure to delete the snapshot after commit does not turn the install into a
|
|
120
|
+
failure. It does not trigger rollback. The manager stays usable, and
|
|
121
|
+
`recoveryRequired` stays `None`.
|
|
122
|
+
|
|
123
|
+
What callers should do:
|
|
124
|
+
|
|
125
|
+
- Inspect this field. Report or record its Cause.
|
|
126
|
+
- Retry the deletion of that snapshot directory after the filesystem problem is
|
|
127
|
+
fixed. The directory may be partly removed.
|
|
128
|
+
- Do not restore the old snapshot. Do not repeat the install only because
|
|
129
|
+
snapshot deletion failed. Those changes are already committed.
|
|
130
|
+
- The kept data may contain credentials, so make sure it is removed in the end.
|
|
131
|
+
|
|
132
|
+
Failures before commit still fail the operation and run rollback. Any snapshot
|
|
133
|
+
cleanup failure is combined with the original failure.
|
|
134
|
+
|
|
135
|
+
Cancellation can stop a result from being delivered after commit. It is not
|
|
136
|
+
proof that the changes were undone. In that case, cleanup failures are kept in
|
|
137
|
+
the interrupted Cause.
|
|
138
|
+
|
|
139
|
+
## When manual recovery is required
|
|
140
|
+
|
|
141
|
+
Before an install changes project files, it saves a snapshot. The snapshot is in
|
|
142
|
+
a private `graft-recovery-*` directory in the OS temporary directory.
|
|
143
|
+
|
|
144
|
+
- Only a successful install or a complete rollback allows the snapshot to be
|
|
145
|
+
deleted and another install to run.
|
|
146
|
+
- A committed install with a failed snapshot deletion is reported as described
|
|
147
|
+
above.
|
|
148
|
+
- Process termination and transaction completion are separate conditions.
|
|
149
|
+
Stopping the writer lets rollback begin. It does not prove that rollback
|
|
150
|
+
succeeded.
|
|
151
|
+
|
|
152
|
+
If any rollback step fails, the manager keeps the snapshot and needs manual
|
|
153
|
+
recovery. This includes restoring files before or after recovery, and running
|
|
154
|
+
the recovery installer. It applies even when the final file restoration
|
|
155
|
+
succeeds, because failed recovery may have left `node_modules` incomplete.
|
|
156
|
+
|
|
157
|
+
How the manager restores a file:
|
|
158
|
+
|
|
159
|
+
1. It creates a temporary file in the target directory, exclusively and with
|
|
160
|
+
mode `0600`.
|
|
161
|
+
2. It restores the saved uid/gid when the platform reports them. Then it
|
|
162
|
+
restores the permission bits.
|
|
163
|
+
3. It syncs and closes the file.
|
|
164
|
+
4. It renames the file over the target.
|
|
165
|
+
|
|
166
|
+
Metadata failures and temporary-file cleanup failures also need manual recovery.
|
|
167
|
+
They stay in the Cause. Timestamps, ACLs, and extended attributes are not
|
|
168
|
+
recorded.
|
|
169
|
+
|
|
170
|
+
If process termination cannot be confirmed, no further restoration or recovery
|
|
171
|
+
runs. This avoids a conflict with a process that may still be writing.
|
|
172
|
+
|
|
173
|
+
### The recovery error
|
|
174
|
+
|
|
175
|
+
The Cause of the failed call keeps the original failure and all rollback and
|
|
176
|
+
termination failures. It includes a `RecoveryRequiredError`, which has these
|
|
177
|
+
fields:
|
|
178
|
+
|
|
179
|
+
- `snapshotDirectory` gives the saved location.
|
|
180
|
+
- `directory` identifies the project.
|
|
181
|
+
- `recoveryCause` keeps the complete original and recovery Cause.
|
|
182
|
+
- `operation` is `rollback` for an incomplete rollback with stopped writers. For
|
|
183
|
+
unconfirmed termination, it is `install` or `recover`. In that case,
|
|
184
|
+
`processId` and `terminationCause` give the process details.
|
|
185
|
+
|
|
186
|
+
After cancellation, callers can also read `manager.recoveryRequired`. It is an
|
|
187
|
+
Effect that yields `Option<RecoveryRequiredError>`. Callers do not need to catch
|
|
188
|
+
an interrupted call.
|
|
189
|
+
|
|
190
|
+
The manager rejects all later `install` calls for good, with this typed error.
|
|
191
|
+
This includes calls that are already queued and calls with empty change sets.
|
|
192
|
+
`readState` stays a read-only diagnostic operation. It may fail while project
|
|
193
|
+
files are incomplete.
|
|
194
|
+
|
|
195
|
+
### Steps to recover manually
|
|
196
|
+
|
|
197
|
+
1. Identify the failed installer and all of its descendants, and stop them. The
|
|
198
|
+
recorded process ID is for diagnosis only. It does not prove that a later
|
|
199
|
+
process with the same ID belongs to the installation. Do not restore files or
|
|
200
|
+
start another installer until all writers have stopped.
|
|
201
|
+
2. Fix the filesystem problem that stopped the restoration, such as file
|
|
202
|
+
permissions. Then restore the files:
|
|
203
|
+
- Open `snapshot.json` in `snapshotDirectory`. Check its project `directory`
|
|
204
|
+
and `agent`.
|
|
205
|
+
- For each `files` entry, copy the bytes from the named `backup` into a
|
|
206
|
+
temporary file beside the project's `name`. Create the temporary file
|
|
207
|
+
privately and exclusively.
|
|
208
|
+
- Restore the recorded `uid` and `gid` when they are not null. Then restore
|
|
209
|
+
`mode`. Do this before you replace the target.
|
|
210
|
+
- These metadata values are JSON integers. `mode` is the numeric permission
|
|
211
|
+
bitmask, not an octal-formatted string.
|
|
212
|
+
- Create parent directories as needed.
|
|
213
|
+
- A null `backup` means that the file was absent. Remove it if it exists now.
|
|
214
|
+
3. Repair `node_modules` with the selected package manager. Use the restored
|
|
215
|
+
manifest and lockfile. After that process has stopped, restore the snapshot
|
|
216
|
+
files again if it rewrote them. The snapshot does not contain `node_modules`.
|
|
217
|
+
4. Verify the project. Then delete the snapshot and create a new manager with
|
|
218
|
+
`make`. The old manager has no reset operation.
|
|
219
|
+
|
|
220
|
+
Snapshots can contain credentials from package-manager configuration files. Keep
|
|
221
|
+
them private. They survive the closing of the manager and of the Effect scope.
|
|
222
|
+
But the OS may clean temporary directories. If manual recovery will be delayed,
|
|
223
|
+
copy a kept snapshot to a safe, private place.
|
|
224
|
+
|
|
225
|
+
This mechanism is not crash recovery. It is not a project lock across processes.
|
|
226
|
+
Creating a new manager does not make a still-live installer safe.
|
|
227
|
+
|
|
228
|
+
## Documentation
|
|
229
|
+
|
|
230
|
+
- [Command line](https://graftland.github.io/graft/cli/)
|
|
231
|
+
|
|
232
|
+
Part of [Graft](https://github.com/graftland/graft). Licensed under
|
|
233
|
+
[Apache 2.0](https://github.com/graftland/graft/blob/main/LICENSE).
|
|
234
|
+
|
|
235
|
+
## Package manager declarations
|
|
236
|
+
|
|
237
|
+
The shared `PackageManagerDeclaration` Schema accepts `npm`, `pnpm`, or `yarn`
|
|
238
|
+
followed by an exact SemVer version (at most 256 characters), including optional
|
|
239
|
+
prerelease identifiers. It rejects empty suffixes, leading zeros in numeric
|
|
240
|
+
version/prerelease components, ranges, tags, URLs, and surrounding whitespace. A
|
|
241
|
+
`+` suffix is reserved for a
|
|
242
|
+
[Corepack hash](https://github.com/nodejs/corepack#when-authoring-packages):
|
|
243
|
+
`sha224`, `sha256`, `sha384`, or `sha512`, followed by a dot and exactly 56, 64,
|
|
244
|
+
96, or 128 lowercase hexadecimal characters respectively. Arbitrary SemVer build
|
|
245
|
+
metadata is not supported here. Hash validation checks the declaration's syntax;
|
|
246
|
+
it does not download or authenticate the package-manager archive.
|
|
247
|
+
|
|
248
|
+
Both project creation and package-manager detection use this Schema, exported
|
|
249
|
+
from `@graftland/package-manager/PackageManagerDeclaration`. Creation preserves
|
|
250
|
+
the supplied declaration verbatim. An explicit detection `agent` still overrides
|
|
251
|
+
the manifest declaration.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# @graftland/package-manager
|
|
2
|
+
|
|
3
|
+
[English](https://github.com/graftland/graft/blob/main/packages/package-manager/README.md)
|
|
4
|
+
| 简体中文 |
|
|
5
|
+
[繁體中文](https://github.com/graftland/graft/blob/main/packages/package-manager/README.zh-TW.md)
|
|
6
|
+
|
|
7
|
+
使用 npm、pnpm 或 Yarn 安装依赖,并在失败时回滚。
|
|
8
|
+
|
|
9
|
+
支持的最低 Node.js 版本是 22.19。Windows 按现状提供。Windows 上的进程树清理和回滚没有经过验证,也不作承诺。
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install @graftland/package-manager effect
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`make` 会检测项目所用的包管理器。它的 `install`
|
|
16
|
+
可以添加、更新或移除依赖。如果安装失败,它会恢复 `package.json`
|
|
17
|
+
和锁文件。`graft add` 和 `graft remove` 使用它。
|
|
18
|
+
|
|
19
|
+
## 安装的所有权与恢复
|
|
20
|
+
|
|
21
|
+
每个项目只使用一个管理器。调用方必须让管理器及其子进程独占项目的写入权限。这从一次
|
|
22
|
+
`install` 调用开始,持续到它结束,包括恢复阶段。
|
|
23
|
+
|
|
24
|
+
- 在此期间,不要编辑项目文件。
|
|
25
|
+
- 不要同时运行另一个安装程序。
|
|
26
|
+
- 同一个管理器内的调用是串行的。Graft 不会在多个管理器、编辑器或进程之间强制加锁。
|
|
27
|
+
|
|
28
|
+
在 POSIX 上,进程清理只覆盖安装程序最初的进程组。安装脚本和恢复脚本不得创建不受管理的写入者。例如,处于独立进程组的子进程,以及持续写入项目文件的守护进程。Graft 不提供操作系统级别的隔离,也不会发现逃逸的写入者。确认最初的进程组已经停止,并不能证明所有可能的后代都已经停止。违反这条规则的脚本不在回滚保证的范围内。
|
|
29
|
+
|
|
30
|
+
受管理的路径必须是普通文件,或者不存在。`make` 和每次 `install` 都使用 `lstat`
|
|
31
|
+
检查快照集合。快照集合包括
|
|
32
|
+
`package.json`、锁文件,以及被跟踪的配置文件和安装状态文件。
|
|
33
|
+
|
|
34
|
+
- 符号链接会被拒绝,包括悬空链接。其他文件类型也会被拒绝。
|
|
35
|
+
- 错误是
|
|
36
|
+
`UnsupportedFileTypeError`。它在任何事务写入之前、任何安装程序启动之前抛出。
|
|
37
|
+
- 错误会给出 `path` 和 `fileType`。链接及其目标保持不变。
|
|
38
|
+
- 在事务开始之前的拒绝,不需要恢复,也不会让管理器不可用。
|
|
39
|
+
|
|
40
|
+
### 回滚
|
|
41
|
+
|
|
42
|
+
当安装在提交之前失败或被中断时,会执行回滚。回滚需要先确认最初的进程组已经停止。然后它会做这些事:
|
|
43
|
+
|
|
44
|
+
- 把已备份的包文件逐字节恢复,并保留它们记录下来的权限位和所有者。
|
|
45
|
+
- 移除安装之前不存在的被跟踪文件。
|
|
46
|
+
|
|
47
|
+
被跟踪的文件包括
|
|
48
|
+
`package.json`、锁文件,以及管理器所跟踪的包管理器配置文件和安装状态文件。
|
|
49
|
+
|
|
50
|
+
回滚会覆盖安装期间所做的更改。它不会检测冲突,也不会合并编辑。
|
|
51
|
+
|
|
52
|
+
如果项目已经有所选包管理器的锁文件,恢复阶段还会尝试修复
|
|
53
|
+
`node_modules`。恢复错误会与原始失败一起保留。不承诺在进程崩溃之后恢复。
|
|
54
|
+
|
|
55
|
+
### 文件写入与进程清理
|
|
56
|
+
|
|
57
|
+
已接受的文件写入有一个完成边界。取消会等待原生操作完成,然后才进行回滚、清理快照,以及释放管理器的锁。事务会跟踪它自己的文件写入以及最初的安装程序进程组。创建快照和回滚写入也有同样的完成保证。
|
|
58
|
+
|
|
59
|
+
每个安装进程和每个恢复进程都有十分钟的退出等待时间。取消或超时会在恢复文件之前关闭它的进程 scope。
|
|
60
|
+
|
|
61
|
+
- 进程会收到 SIGTERM。
|
|
62
|
+
- 如果五秒后它还没有停止,就会收到 SIGKILL。
|
|
63
|
+
- 在 POSIX 上,即使 kill 成功,也必须确认原进程组已不存在。每 20 毫秒检查一次,最多追加 100 次检查(等待两秒),给短暂残留的僵尸进程组留出回收窗口。EPERM 视为仍然存在,不视为已消失。
|
|
64
|
+
- 如果进程组仍存在或检查失败,则终止未获确认:管理器保留快照,不执行回滚和恢复安装,并进入
|
|
65
|
+
`RecoveryRequired`。
|
|
66
|
+
- 进程清理失败会与安装和恢复的失败一起保留在返回的 cause 中。
|
|
67
|
+
- 退出超时不包括进程清理和文件恢复,也不是整个“安装并恢复”操作的截止时间。
|
|
68
|
+
|
|
69
|
+
Node 适配器可能会把原始的操作系统 errno 替换为通用的 kill 失败。只有 POSIX 检查确认进程组不存在后,PackageManager 才消解携带 ESRCH 的信号失败,或适配器特定的
|
|
70
|
+
`Unknown` kill 错误(cause 为
|
|
71
|
+
`Failed to kill child process`)。同一 Cause 中的其他原因仍然保留,包括 join 失败、其他 kill 错误、defect 和中断。进程组仍存在时保留原始 Cause;检查失败时保留两个完整 Cause。
|
|
72
|
+
|
|
73
|
+
## 已提交的安装与快照清理
|
|
74
|
+
|
|
75
|
+
`install` 成功的结果表示依赖更改已经提交。
|
|
76
|
+
|
|
77
|
+
`result.snapshotCleanupFailure` 是 `Option<SnapshotCleanupFailure>`:
|
|
78
|
+
|
|
79
|
+
- `None` 表示快照删除成功。
|
|
80
|
+
- `Some` 包含 `snapshotDirectory` 和完整的清理 `cause`,包括 defect。
|
|
81
|
+
|
|
82
|
+
提交之后删除快照失败,不会让这次安装变成失败,也不会触发回滚。管理器仍然可用,`recoveryRequired`
|
|
83
|
+
仍然是 `None`。
|
|
84
|
+
|
|
85
|
+
调用方应该这样做:
|
|
86
|
+
|
|
87
|
+
- 检查这个字段,报告或记录它的 Cause。
|
|
88
|
+
- 在文件系统问题解决之后,重试删除该快照目录。该目录可能已被部分删除。
|
|
89
|
+
- 不要恢复旧的快照,也不要仅仅因为快照删除失败就重复安装。那些更改已经提交了。
|
|
90
|
+
- 保留下来的数据可能包含凭据,所以要确保它最终被删除。
|
|
91
|
+
|
|
92
|
+
提交之前的失败仍然会使操作失败并执行回滚。任何快照清理失败都会与原始失败合并。
|
|
93
|
+
|
|
94
|
+
取消可能会使提交之后的结果无法送达。这并不能证明更改已被撤销。这种情况下,清理失败会保留在被中断的 Cause 中。
|
|
95
|
+
|
|
96
|
+
## 何时需要手动恢复
|
|
97
|
+
|
|
98
|
+
在安装更改项目文件之前,会先保存一份快照。快照位于操作系统临时目录中一个私有的
|
|
99
|
+
`graft-recovery-*` 目录里。
|
|
100
|
+
|
|
101
|
+
- 只有成功的安装或完整的回滚,才允许删除快照并运行下一次安装。
|
|
102
|
+
- 已提交但快照删除失败的安装,按上文所述报告。
|
|
103
|
+
- 进程终止和事务完成是两个独立的条件。停止写入者,回滚才能开始,但这并不证明回滚成功。
|
|
104
|
+
|
|
105
|
+
只要有任何一个回滚步骤失败,管理器就会保留快照,并需要手动恢复。这包括在恢复之前或之后恢复文件,以及运行恢复安装程序。即使最后的文件恢复成功,这条规则也适用,因为失败的恢复可能让
|
|
106
|
+
`node_modules` 不完整。
|
|
107
|
+
|
|
108
|
+
管理器如何恢复一个文件:
|
|
109
|
+
|
|
110
|
+
1. 在目标目录中以独占方式创建临时文件,模式为 `0600`。
|
|
111
|
+
2. 当平台报告了保存的 uid/gid 时,先恢复它们,然后恢复权限位。
|
|
112
|
+
3. 对文件执行 sync 并关闭。
|
|
113
|
+
4. 把该文件重命名覆盖到目标上。
|
|
114
|
+
|
|
115
|
+
元数据失败和临时文件清理失败同样需要手动恢复,并保留在 Cause 中。时间戳、ACL 和扩展属性不会被记录。
|
|
116
|
+
|
|
117
|
+
如果无法确认进程已经终止,就不会再进行任何恢复。这是为了避免与可能仍在写入的进程发生冲突。
|
|
118
|
+
|
|
119
|
+
### 恢复错误
|
|
120
|
+
|
|
121
|
+
失败调用的 Cause 会保留原始失败以及所有回滚和终止的失败。它包含一个
|
|
122
|
+
`RecoveryRequiredError`,其字段如下:
|
|
123
|
+
|
|
124
|
+
- `snapshotDirectory` 给出保存的位置。
|
|
125
|
+
- `directory` 标识项目。
|
|
126
|
+
- `recoveryCause` 保留完整的原始 Cause 和恢复 Cause。
|
|
127
|
+
- `operation`:在写入者已停止但回滚不完整时是 `rollback`;在无法确认终止时是
|
|
128
|
+
`install` 或 `recover`,此时 `processId` 和 `terminationCause`
|
|
129
|
+
给出进程的详细信息。
|
|
130
|
+
|
|
131
|
+
取消之后,调用方还可以读取 `manager.recoveryRequired`。它是一个产出
|
|
132
|
+
`Option<RecoveryRequiredError>` 的 Effect。调用方不需要去捕获被中断的调用。
|
|
133
|
+
|
|
134
|
+
管理器会永久拒绝之后所有的 `install`
|
|
135
|
+
调用,并返回这个带类型的错误。这包括已经排队的调用,以及变更集为空的调用。`readState`
|
|
136
|
+
仍然是只读的诊断操作。在项目文件不完整时,它可能失败。
|
|
137
|
+
|
|
138
|
+
### 手动恢复的步骤
|
|
139
|
+
|
|
140
|
+
1. 找出失败的安装程序及其所有后代,并停止它们。记录下来的进程 ID 只用于诊断,不能证明之后具有相同 ID 的进程属于这次安装。在所有写入者停止之前,不要恢复文件,也不要启动另一个安装程序。
|
|
141
|
+
2. 解决阻止恢复的文件系统问题,例如文件权限。然后恢复文件:
|
|
142
|
+
- 打开 `snapshotDirectory` 中的 `snapshot.json`。检查其中的项目 `directory`
|
|
143
|
+
和 `agent`。
|
|
144
|
+
- 对每个 `files` 条目,把指定的 `backup` 中的字节复制到项目 `name`
|
|
145
|
+
旁边的临时文件中。以私有、独占的方式创建该临时文件。
|
|
146
|
+
- 当 `uid` 和 `gid` 不为 null 时,恢复记录下来的值,然后恢复
|
|
147
|
+
`mode`。请在替换目标之前完成这些。
|
|
148
|
+
- 这些元数据值是 JSON 整数。`mode`
|
|
149
|
+
是数值形式的权限位掩码,不是八进制格式的字符串。
|
|
150
|
+
- 按需创建父目录。
|
|
151
|
+
- `backup` 为 null 表示该文件原本不存在。如果它现在存在,就删除它。
|
|
152
|
+
3. 使用所选的包管理器,基于已恢复的清单文件和锁文件修复
|
|
153
|
+
`node_modules`。该进程停止之后,如果它重写了快照中的文件,请再次恢复这些文件。快照不包含
|
|
154
|
+
`node_modules`。
|
|
155
|
+
4. 验证项目。然后删除快照,并用 `make`
|
|
156
|
+
创建一个新的管理器。旧的管理器没有重置操作。
|
|
157
|
+
|
|
158
|
+
快照可能包含来自包管理器配置文件的凭据。请保持私有。它们在管理器和 Effect
|
|
159
|
+
scope 关闭之后依然存在。但是操作系统可能会清理临时目录。如果手动恢复会被推迟,请把保留的快照复制到一个安全的私有位置。
|
|
160
|
+
|
|
161
|
+
这个机制不是崩溃恢复,也不是跨进程的项目锁。创建新的管理器,并不会让仍在运行的安装程序变得安全。
|
|
162
|
+
|
|
163
|
+
## 文档
|
|
164
|
+
|
|
165
|
+
- [命令行](https://graftland.github.io/graft/zh-cn/cli/)
|
|
166
|
+
|
|
167
|
+
属于 [Graft](https://github.com/graftland/graft)。基于
|
|
168
|
+
[Apache 2.0](https://github.com/graftland/graft/blob/main/LICENSE) 许可。
|
|
169
|
+
|
|
170
|
+
## 包管理器声明
|
|
171
|
+
|
|
172
|
+
共享的 `PackageManagerDeclaration` Schema 接受 `npm`、`pnpm` 或
|
|
173
|
+
`yarn`,后跟精确的 SemVer 版本(版本部分最多 256 个字符),可包含预发布标识。它拒绝空后缀、版本及预发布数字标识的前导零、版本范围、标签、URL 和首尾空白。
|
|
174
|
+
|
|
175
|
+
`+` 后缀仅用于
|
|
176
|
+
[Corepack hash](https://github.com/nodejs/corepack#when-authoring-packages):支持
|
|
177
|
+
`sha224`、`sha256`、`sha384`、`sha512`,后跟点号及分别为 56、64、96、128 个字符的小写十六进制摘要。不支持普通 SemVer
|
|
178
|
+
build metadata。这里只校验声明语法,不下载或验证包管理器压缩包的真实性。
|
|
179
|
+
|
|
180
|
+
创建项目与检测包管理器使用同一个 Schema,可从
|
|
181
|
+
`@graftland/package-manager/PackageManagerDeclaration`
|
|
182
|
+
导入。创建时逐字保留传入的声明;检测时显式指定的 `agent`
|
|
183
|
+
仍优先于 manifest 声明。
|